Skip to content

Commit

Permalink
Improve error message for opaque captures
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Mar 8, 2024
1 parent 74acabe commit ffd30e0
Show file tree
Hide file tree
Showing 25 changed files with 150 additions and 116 deletions.
59 changes: 14 additions & 45 deletions compiler/rustc_error_codes/src/error_codes/E0657.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,26 @@
A lifetime bound on a trait implementation was captured at an incorrect place.
An `impl Trait` captured a higher-ranked lifetime, which is not supported.

Currently, `impl Trait` types are only allowed to capture lifetimes from
their parent items, and not from any `for<'a>` binders in scope.

Erroneous code example:

```compile_fail,E0657
trait Id<T> {}
trait Lt<'a> {}
impl<'a> Lt<'a> for () {}
impl<T> Id<T> for T {}
fn free_fn_capture_hrtb_in_impl_trait()
-> Box<for<'a> Id<impl Lt<'a>>> // error!
{
Box::new(())
}
trait BorrowInto<'a> {
type Target;
struct Foo;
impl Foo {
fn impl_fn_capture_hrtb_in_impl_trait()
-> Box<for<'a> Id<impl Lt<'a>>> // error!
{
Box::new(())
}
fn borrow_into(&'a self) -> Self::Target;
}
```
Here, you have used the inappropriate lifetime in the `impl Trait`,
The `impl Trait` can only capture lifetimes bound at the fn or impl
level.
impl<'a> BorrowInto<'a> for () {
type Target = &'a ();
To fix this we have to define the lifetime at the function or impl
level and use that lifetime in the `impl Trait`. For example you can
define the lifetime at the function:

```
trait Id<T> {}
trait Lt<'a> {}
impl<'a> Lt<'a> for () {}
impl<T> Id<T> for T {}
fn free_fn_capture_hrtb_in_impl_trait<'b>()
-> Box<for<'a> Id<impl Lt<'b>>> // ok!
{
Box::new(())
fn borrow_into(&'a self) -> Self::Target {
self
}
}
struct Foo;
impl Foo {
fn impl_fn_capture_hrtb_in_impl_trait<'b>()
-> Box<for<'a> Id<impl Lt<'b>>> // ok!
{
Box::new(())
}
fn opaque() -> impl for<'a> BorrowInto<'a, Target = impl Sized + 'a> {
()
}
```
4 changes: 4 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ hir_analysis_only_current_traits_primitive = only traits defined in the current
hir_analysis_only_current_traits_ty = `{$ty}` is not defined in the current crate
hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot capture {$bad_place}
.label = `impl Trait` implicitly captures all lifetimes in scope
.note = lifetime declared here
hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation
.help = add `#![feature(unboxed_closures)]` to the crate attributes to use it
Expand Down
68 changes: 40 additions & 28 deletions compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use rustc_ast::visit::walk_list;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::{codes::*, struct_span_code_err};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::LocalDefId;
Expand Down Expand Up @@ -673,34 +672,47 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
// and ban them. Type variables instantiated inside binders aren't
// well-supported at the moment, so this doesn't work.
// In the future, this should be fixed and this error should be removed.
let def = self.map.defs.get(&lifetime.hir_id).cloned();
let Some(ResolvedArg::LateBound(_, _, def_id)) = def else { continue };
let Some(def_id) = def_id.as_local() else { continue };
let hir_id = self.tcx.local_def_id_to_hir_id(def_id);
// Ensure that the parent of the def is an item, not HRTB
let parent_id = self.tcx.parent_hir_id(hir_id);
if !parent_id.is_owner() {
struct_span_code_err!(
self.tcx.dcx(),
lifetime.ident.span,
E0657,
"`impl Trait` can only capture lifetimes bound at the fn or impl level"
)
.emit();
self.uninsert_lifetime_on_error(lifetime, def.unwrap());
}
if let hir::Node::Item(hir::Item {
kind: hir::ItemKind::OpaqueTy { .. }, ..
}) = self.tcx.hir_node(parent_id)
let def = self.map.defs.get(&lifetime.hir_id).copied();
let Some(ResolvedArg::LateBound(_, _, lifetime_def_id)) = def else { continue };
let Some(lifetime_def_id) = lifetime_def_id.as_local() else { continue };
let lifetime_hir_id = self.tcx.local_def_id_to_hir_id(lifetime_def_id);

let bad_place = match self.tcx.hir_node(self.tcx.parent_hir_id(lifetime_hir_id))
{
self.tcx.dcx().struct_span_err(
lifetime.ident.span,
"higher kinded lifetime bounds on nested opaque types are not supported yet",
)
.with_span_note(self.tcx.def_span(def_id), "lifetime declared here")
.emit();
self.uninsert_lifetime_on_error(lifetime, def.unwrap());
}
// Opaques do not declare their own lifetimes, so if a lifetime comes from an opaque
// it must be a reified late-bound lifetime from a trait goal.
hir::Node::Item(hir::Item {
kind: hir::ItemKind::OpaqueTy { .. }, ..
}) => "higher-ranked lifetime from outer `impl Trait`",
// Other items are fine.
hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => {
continue;
}
hir::Node::Ty(hir::Ty { kind: hir::TyKind::BareFn(_), .. }) => {
"higher-ranked lifetime from function pointer"
}
hir::Node::Ty(hir::Ty { kind: hir::TyKind::TraitObject(..), .. }) => {
"higher-ranked lifetime from `dyn` type"
}
_ => "higher-ranked lifetime",
};

let (span, label) = if lifetime.ident.span == self.tcx.def_span(lifetime_def_id)
{
let opaque_span = self.tcx.def_span(item_id.owner_id);
(opaque_span, Some(opaque_span))
} else {
(lifetime.ident.span, None)
};

// Ensure that the parent of the def is an item, not HRTB
self.tcx.dcx().emit_err(errors::OpaqueCapturesHigherRankedLifetime {
span,
label,
decl_span: self.tcx.def_span(lifetime_def_id),
bad_place,
});
self.uninsert_lifetime_on_error(lifetime, def.unwrap());
}
}
_ => intravisit::walk_ty(self, ty),
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1607,3 +1607,15 @@ pub struct UnnamedFieldsReprFieldDefined {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_opaque_captures_higher_ranked_lifetime, code = E0657)]
pub struct OpaqueCapturesHigherRankedLifetime {
#[primary_span]
pub span: Span,
#[label]
pub label: Option<Span>,
#[note]
pub decl_span: Span,
pub bad_place: &'static str,
}
4 changes: 2 additions & 2 deletions tests/ui/error-codes/E0657.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ impl<T> Id<T> for T {}

fn free_fn_capture_hrtb_in_impl_trait()
-> Box<for<'a> Id<impl Lt<'a>>>
//~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level [E0657]
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
{
Box::new(())
}
Expand All @@ -17,7 +17,7 @@ struct Foo;
impl Foo {
fn impl_fn_capture_hrtb_in_impl_trait()
-> Box<for<'a> Id<impl Lt<'a>>>
//~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
{
Box::new(())
}
Expand Down
16 changes: 14 additions & 2 deletions tests/ui/error-codes/E0657.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
error[E0657]: `impl Trait` can only capture lifetimes bound at the fn or impl level
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
--> $DIR/E0657.rs:10:31
|
LL | -> Box<for<'a> Id<impl Lt<'a>>>
| ^^
|
note: lifetime declared here
--> $DIR/E0657.rs:10:16
|
LL | -> Box<for<'a> Id<impl Lt<'a>>>
| ^^

error[E0657]: `impl Trait` can only capture lifetimes bound at the fn or impl level
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
--> $DIR/E0657.rs:19:35
|
LL | -> Box<for<'a> Id<impl Lt<'a>>>
| ^^
|
note: lifetime declared here
--> $DIR/E0657.rs:19:20
|
LL | -> Box<for<'a> Id<impl Lt<'a>>>
| ^^

error: aborting due to 2 previous errors

Expand Down
6 changes: 3 additions & 3 deletions tests/ui/impl-trait/impl-fn-hrtb-bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
use std::fmt::Debug;

fn a() -> impl Fn(&u8) -> (impl Debug + '_) {
//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
|x| x
//~^ ERROR lifetime may not live long enough
}

fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) {
//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
|x| x
//~^ ERROR lifetime may not live long enough
}

fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) {
//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
|x| x
//~^ ERROR lifetime may not live long enough
}
Expand Down
9 changes: 5 additions & 4 deletions tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're
LL | fn d() -> impl Fn() -> (impl Debug + 'static) {
| ~~~~~~~

error: higher kinded lifetime bounds on nested opaque types are not supported yet
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
--> $DIR/impl-fn-hrtb-bounds.rs:4:41
|
LL | fn a() -> impl Fn(&u8) -> (impl Debug + '_) {
Expand All @@ -31,7 +31,7 @@ LL | |x| x
| |return type of closure is impl Debug + '2
| has type `&'1 u8`

error: higher kinded lifetime bounds on nested opaque types are not supported yet
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
--> $DIR/impl-fn-hrtb-bounds.rs:10:52
|
LL | fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) {
Expand All @@ -52,7 +52,7 @@ LL | |x| x
| |return type of closure is impl Debug + '2
| has type `&'1 u8`

error: higher kinded lifetime bounds on nested opaque types are not supported yet
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
--> $DIR/impl-fn-hrtb-bounds.rs:16:52
|
LL | fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) {
Expand All @@ -75,4 +75,5 @@ LL | |x| x

error: aborting due to 7 previous errors

For more information about this error, try `rustc --explain E0106`.
Some errors have detailed explanations: E0106, E0657.
For more information about an error, try `rustc --explain E0106`.
2 changes: 1 addition & 1 deletion tests/ui/impl-trait/impl-fn-parsing-ambiguities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt::Debug;

fn a() -> impl Fn(&u8) -> impl Debug + '_ {
//~^ ERROR ambiguous `+` in a type
//~| ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
//~| ERROR cannot capture higher-ranked lifetime from outer `impl Trait`
|x| x
//~^ ERROR lifetime may not live long enough
}
Expand Down
3 changes: 2 additions & 1 deletion tests/ui/impl-trait/impl-fn-parsing-ambiguities.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ error: ambiguous `+` in a type
LL | fn b() -> impl Fn() -> impl Debug + Send {
| ^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(impl Debug + Send)`

error: higher kinded lifetime bounds on nested opaque types are not supported yet
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
--> $DIR/impl-fn-parsing-ambiguities.rs:4:40
|
LL | fn a() -> impl Fn(&u8) -> impl Debug + '_ {
Expand All @@ -33,3 +33,4 @@ LL | |x| x

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0657`.
8 changes: 7 additions & 1 deletion tests/ui/impl-trait/implicit-capture-late.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
error[E0657]: `impl Trait` can only capture lifetimes bound at the fn or impl level
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
--> $DIR/implicit-capture-late.rs:10:55
|
LL | fn foo(x: Vec<i32>) -> Box<dyn for<'a> Deref<Target = impl ?Sized>> {
| ^^^^^^^^^^^ `impl Trait` implicitly captures all lifetimes in scope
|
note: lifetime declared here
--> $DIR/implicit-capture-late.rs:10:36
|
LL | fn foo(x: Vec<i32>) -> Box<dyn for<'a> Deref<Target = impl ?Sized>> {
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/impl-trait/issues/issue-54895.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ impl<'a> Trait<'a> for X {
}

fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {
//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
X(())
}

Expand Down
3 changes: 2 additions & 1 deletion tests/ui/impl-trait/issues/issue-54895.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: higher kinded lifetime bounds on nested opaque types are not supported yet
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
--> $DIR/issue-54895.rs:15:53
|
LL | fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {
Expand All @@ -12,3 +12,4 @@ LL | fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0657`.
2 changes: 1 addition & 1 deletion tests/ui/impl-trait/issues/issue-67830.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ where

struct A;
fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> {
//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
Wrap(|a| Some(a).into_iter())
//~^ ERROR implementation of `FnOnce` is not general enough
//~| ERROR implementation of `FnOnce` is not general enough
Expand Down
3 changes: 2 additions & 1 deletion tests/ui/impl-trait/issues/issue-67830.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: higher kinded lifetime bounds on nested opaque types are not supported yet
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
--> $DIR/issue-67830.rs:21:62
|
LL | fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> {
Expand Down Expand Up @@ -31,3 +31,4 @@ LL | Wrap(|a| Some(a).into_iter())

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0657`.
6 changes: 3 additions & 3 deletions tests/ui/impl-trait/issues/issue-88236-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ impl<'a> Hrtb<'a> for &'a () {
}

fn make_impl() -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {}
//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`

fn make_weird_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
&()
//~^ ERROR implementation of `Hrtb` is not general enough
//~| ERROR implementation of `Hrtb` is not general enough
}

fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
//~^ ERROR higher kinded lifetime bounds on nested opaque types are not supported yet
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
x
//~^ ERROR implementation of `Hrtb` is not general enough
//~| ERROR implementation of `Hrtb` is not general enough
Expand Down
Loading

0 comments on commit ffd30e0

Please sign in to comment.