Skip to content

Commit

Permalink
Rollup merge of #122100 - compiler-errors:better-capture, r=oli-obk
Browse files Browse the repository at this point in the history
Better comment for implicit captures in RPITIT

Improve the error message for implicit captures. Also always set E0657.

r? oli-obk
  • Loading branch information
matthiaskrgr authored Mar 8, 2024
2 parents 2c3ca09 + ffd30e0 commit e76bd62
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 @@ -9,7 +9,6 @@
use core::ops::ControlFlow;
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 @@ -669,34 +668,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 e76bd62

Please sign in to comment.