diff --git a/compiler/rustc_ast_lowering/src/impl_trait.rs b/compiler/rustc_ast_lowering/src/impl_trait.rs new file mode 100644 index 0000000000000..d10394d441df1 --- /dev/null +++ b/compiler/rustc_ast_lowering/src/impl_trait.rs @@ -0,0 +1,30 @@ +/// Returns `true` if the parent path contains impl trait syntax: +/// For example given `impl Bla`, this function would +/// return true for the path for `impl Foo` +pub(crate) fn parent_contains_impl_trait(cx: &LoweringContext<'_>, path: &ast::Path) -> bool { + let ast::Path { span: path_span, segments, tokens: _ } = path; + + if let Some(parent_path_span) = path_span.parent_callsite() { + return matches!(cx.source_map().span_to_snippet(parent_path_span), Ok(s) if s.starts_with("impl ")); + } + + // This can be from a parameter list: + // like in `fn foo(a: impl Bla..`) somewhere + // in a block or other nested context. + let parent_node = cx.source_map().span_to_enclosing_node(*path_span).next(); + + if let Some(node) = parent_node { + let content_str = cx.source_map().span_to_snippet(node.span).unwrap_or_default(); + let segments_strs = + segments.iter().map(|s| cx.source_map().span_to_snippet(s.span()).unwrap_or_default()); + + let path_str = segments_strs.collect::>().join("::"); + // Check if parent contains "impl Trait", except for the current path: + let impl_trait_pattern = format!("impl {}", path_str); + if content_str.contains("impl") && content_str.contains(&impl_trait_pattern) { + return true; + } + } + + false +} diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 404753875ee5e..cfcfc5f8e8d88 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -151,6 +151,7 @@ enum Scope<'a> { s: ScopeRef<'a>, what: &'static str, deny_late_regions: bool, + deny_late_types_and_consts: bool, }, Root { @@ -190,10 +191,11 @@ impl<'a> Scope<'a> { .field("s", &"..") .finish(), Self::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), - Self::LateBoundary { s: _, what, deny_late_regions } => f + Self::LateBoundary { s: _, what, deny_late_regions, deny_late_types_and_consts } => f .debug_struct("LateBoundary") .field("what", what) .field("deny_late_regions", deny_late_regions) + .field("deny_late_types_and_consts", deny_late_types_and_consts) .finish(), Self::Root { opt_parent_item } => { f.debug_struct("Root").field("opt_parent_item", &opt_parent_item).finish() @@ -586,10 +588,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { this.with(scope, |this| { let scope = Scope::LateBoundary { s: this.scope, - what: "nested `impl Trait`", - // We can capture late-bound regions; we just don't duplicate - // lifetime or const params, so we can't allow those. + what: "associated type bounds", deny_late_regions: false, + deny_late_types_and_consts: false, }; this.with(scope, |this| intravisit::walk_opaque_ty(this, opaque)) }) @@ -809,8 +810,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { self.with(scope, |this| { let scope = Scope::LateBoundary { s: this.scope, - what: "`impl Trait` in binding", - deny_late_regions: true, + what: "nested `impl Trait`", + deny_late_regions: false, + deny_late_types_and_consts: true, }; this.with(scope, |this| { for bound in bounds { @@ -999,7 +1001,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { self.with( - Scope::LateBoundary { s: self.scope, what: "constant", deny_late_regions: true }, + Scope::LateBoundary { + s: self.scope, + what: "constant", + deny_late_regions: true, + deny_late_types_and_consts: true, + }, |this| { intravisit::walk_anon_const(this, c); }, @@ -1298,8 +1305,18 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { scope = s; } - Scope::LateBoundary { s, what, deny_late_regions } => { - if deny_late_regions { + Scope::LateBoundary { + s, + what, + deny_late_regions: _, + deny_late_types_and_consts, + } => { + // For debugging purposes + debug!( + "LateBoundary in resolve_type_ref - what: {}, deny_late_types_and_consts: {}", + what, deny_late_types_and_consts + ); + if deny_late_types_and_consts { crossed_late_boundary = Some(what); } scope = s; @@ -1518,8 +1535,20 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { scope = s; } - Scope::LateBoundary { s, what, deny_late_regions: _ } => { - crossed_late_boundary = Some(what); + Scope::LateBoundary { + s, + what, + deny_late_regions: _, + deny_late_types_and_consts, + } => { + // For debugging purposes + debug!( + "LateBoundary in resolve_type_ref - what: {}, deny_late_types_and_consts: {}", + what, deny_late_types_and_consts + ); + if deny_late_types_and_consts { + crossed_late_boundary = Some(what); + } scope = s; } } @@ -1763,8 +1792,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // trait Foo<'a> { // type Item: 'a; // } - // ``` - // + // // + //``` // but if we just have `type Item;`, then it would be // `'static`. However, we don't get all of this logic correct. // diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index f2560f22874bc..d9ff6c3f01ac4 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -647,24 +647,24 @@ pub(crate) struct VariadicFunctionCompatibleConvention<'a> { #[derive(Diagnostic)] pub(crate) enum CannotCaptureLateBound { - #[diag(hir_analysis_cannot_capture_late_bound_ty)] - Type { + #[diag(hir_analysis_cannot_capture_late_bound_lifetime)] + Lifetime { #[primary_span] use_span: Span, #[label] def_span: Span, what: &'static str, }, - #[diag(hir_analysis_cannot_capture_late_bound_const)] - Const { + #[diag(hir_analysis_cannot_capture_late_bound_ty)] + Type { #[primary_span] use_span: Span, #[label] def_span: Span, what: &'static str, }, - #[diag(hir_analysis_cannot_capture_late_bound_lifetime)] - Lifetime { + #[diag(hir_analysis_cannot_capture_late_bound_const)] + Const { #[primary_span] use_span: Span, #[label] diff --git a/tests/incremental/non-lifetime-binder-in-nested-impl-trait.rs b/tests/incremental/non-lifetime-binder-in-nested-impl-trait.rs new file mode 100644 index 0000000000000..861f469071900 --- /dev/null +++ b/tests/incremental/non-lifetime-binder-in-nested-impl-trait.rs @@ -0,0 +1,18 @@ +//@ revisions: cfail +//@ should-ice +//@ compile-flags: --edition=2021 +//@ error-pattern: assertion failed + +#![feature(non_lifetime_binders)] +#![feature(associated_type_defaults)] +#![allow(incomplete_features)] + +trait Trait { + type Assoc<'a> = i32; +} + +fn produce() -> impl for Trait<(), Assoc = impl Trait> { + 16 +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-bindings/escaping-bound-var.rs b/tests/ui/impl-trait/in-bindings/escaping-bound-var.rs index b57fef9be21c2..2960fae182b8c 100644 --- a/tests/ui/impl-trait/in-bindings/escaping-bound-var.rs +++ b/tests/ui/impl-trait/in-bindings/escaping-bound-var.rs @@ -10,5 +10,5 @@ impl<'a> Foo<'a> for () { fn main() { let x: &dyn for<'a> Foo<'a, Out = impl Sized + 'a> = &(); - //~^ ERROR cannot capture late-bound lifetime in `impl Trait` in binding + //~^ ERROR cannot capture late-bound lifetime in nested `impl Trait` } diff --git a/tests/ui/impl-trait/in-bindings/escaping-bound-var.stderr b/tests/ui/impl-trait/in-bindings/escaping-bound-var.stderr index 640f6f3692797..6b69b0265147f 100644 --- a/tests/ui/impl-trait/in-bindings/escaping-bound-var.stderr +++ b/tests/ui/impl-trait/in-bindings/escaping-bound-var.stderr @@ -1,4 +1,4 @@ -error: cannot capture late-bound lifetime in `impl Trait` in binding +error: cannot capture late-bound lifetime in nested `impl Trait` --> $DIR/escaping-bound-var.rs:12:52 | LL | let x: &dyn for<'a> Foo<'a, Out = impl Sized + 'a> = &(); diff --git a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs index 2a30178852550..dda42580e0f3a 100644 --- a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs +++ b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs @@ -7,7 +7,6 @@ fn produce() -> impl for Trait<(), Assoc = impl Trait> { //~^ ERROR associated type `Assoc` not found for `Trait` //~| ERROR associated type `Assoc` not found for `Trait` //~| the trait bound `{integer}: Trait<()>` is not satisfied - //~| ERROR cannot capture late-bound type parameter in nested `impl Trait` 16 } diff --git a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr index 38dcdbd0af2ad..6e5bd34ce3817 100644 --- a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr +++ b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr @@ -1,9 +1,3 @@ -error: cannot capture late-bound type parameter in nested `impl Trait` - --> $DIR/non-lifetime-binder-in-constraint.rs:6:58 - | -LL | fn produce() -> impl for Trait<(), Assoc = impl Trait> { - | - parameter defined here ^ - error[E0220]: associated type `Assoc` not found for `Trait` --> $DIR/non-lifetime-binder-in-constraint.rs:6:39 | @@ -33,7 +27,7 @@ help: this trait has no implementations, consider adding one LL | trait Trait {} | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0220, E0277. For more information about an error, try `rustc --explain E0220`. diff --git a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-nested.rs b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-nested.rs new file mode 100644 index 0000000000000..d5d41147ce799 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-nested.rs @@ -0,0 +1,12 @@ +#![allow(incomplete_features)] +#![feature(non_lifetime_binders)] + +trait Trait {} + +fn f() -> impl for Trait> { + //~^ ERROR nested `impl Trait` is not allowed + //~| ERROR the trait bound `(): Trait>` is not satisfied + () +} + +fn main() {} diff --git a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-nested.stderr b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-nested.stderr new file mode 100644 index 0000000000000..dd67e2e994270 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-nested.stderr @@ -0,0 +1,28 @@ +error[E0666]: nested `impl Trait` is not allowed + --> $DIR/non-lifetime-binder-nested.rs:6:29 + | +LL | fn f() -> impl for Trait> { + | ------------------^^^^^^^^^^^^^- + | | | + | | nested `impl Trait` here + | outer `impl Trait` + +error[E0277]: the trait bound `(): Trait>` is not satisfied + --> $DIR/non-lifetime-binder-nested.rs:6:11 + | +LL | fn f() -> impl for Trait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait>` is not implemented for `()` +... +LL | () + | -- return type was inferred to be `()` here + | +help: this trait has no implementations, consider adding one + --> $DIR/non-lifetime-binder-nested.rs:4:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0666. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.rs b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.rs index 23f3666618bff..23951c3427006 100644 --- a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.rs +++ b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.rs @@ -5,7 +5,6 @@ trait Trait {} fn f() -> impl for Trait> {} //~^ ERROR nested `impl Trait` is not allowed -//~| ERROR the trait bound `(): Trait>` is not satisfied -//~| ERROR cannot capture late-bound type parameter in nested `impl Trait` +//~| ERROR the trait bound `(): Trait>` is not satisfied fn main() {} diff --git a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.stderr b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.stderr index 3c352c9889cfa..5859d952b75c7 100644 --- a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.stderr +++ b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.stderr @@ -7,19 +7,11 @@ LL | fn f() -> impl for Trait> {} | | nested `impl Trait` here | outer `impl Trait` -error: cannot capture late-bound type parameter in nested `impl Trait` - --> $DIR/non-lifetime-binder.rs:6:40 - | -LL | fn f() -> impl for Trait> {} - | - ^ - | | - | parameter defined here - -error[E0277]: the trait bound `(): Trait>` is not satisfied +error[E0277]: the trait bound `(): Trait>` is not satisfied --> $DIR/non-lifetime-binder.rs:6:11 | LL | fn f() -> impl for Trait> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait>` is not implemented for `()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait>` is not implemented for `()` | help: this trait has no implementations, consider adding one --> $DIR/non-lifetime-binder.rs:4:1 @@ -27,7 +19,7 @@ help: this trait has no implementations, consider adding one LL | trait Trait {} | ^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0666. For more information about an error, try `rustc --explain E0277`.