diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index cd6cf36baa44d..e3b4ec9d0aaef 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -99,6 +99,15 @@ hir_analysis_invalid_union_field = hir_analysis_invalid_union_field_sugg = wrap the field type in `ManuallyDrop<...>` +hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl + .label = const parameter declared here + +hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetimes from an fn or impl + .label = lifetime declared here + +hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl + .label = type parameter declared here + hir_analysis_lifetimes_or_bounds_mismatch_on_trait = lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration .label = lifetimes do not match {$item_kind} in trait 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 d20f39e9b05b9..523ca63e1daa7 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1344,12 +1344,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { Scope::Binder { where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), .. } => { - let mut err = self.tcx.sess.struct_span_err( - lifetime_ref.ident.span, - "`impl Trait` can only mention lifetimes bound at the fn or impl level", - ); - err.span_note(self.tcx.def_span(region_def_id), "lifetime declared here"); - err.emit(); + self.tcx.sess.emit_err(errors::LateBoundInApit::Lifetime { + span: lifetime_ref.ident.span, + param_span: self.tcx.def_span(region_def_id), + }); return; } Scope::Root { .. } => break, @@ -1379,6 +1377,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { let mut late_depth = 0; let mut scope = self.scope; let mut crossed_anon_const = false; + let result = loop { match *scope { Scope::Body { s, .. } => { @@ -1446,6 +1445,50 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { return; } + // We may fail to resolve higher-ranked ty/const vars that are mentioned by APIT. + // AST-based resolution does not care for impl-trait desugaring, which are the + // responsibility of lowering. This may create a mismatch between the resolution + // AST found (`param_def_id`) which points to HRTB, and what HIR allows. + // ``` + // fn foo(x: impl for Trait>) {} + // ``` + // + // In such case, walk back the binders to diagnose it properly. + let mut scope = self.scope; + loop { + match *scope { + Scope::Binder { + where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), .. + } => { + let guar = self.tcx.sess.emit_err(match self.tcx.def_kind(param_def_id) { + DefKind::TyParam => errors::LateBoundInApit::Type { + span: self.tcx.hir().span(hir_id), + param_span: self.tcx.def_span(param_def_id), + }, + DefKind::ConstParam => errors::LateBoundInApit::Const { + span: self.tcx.hir().span(hir_id), + param_span: self.tcx.def_span(param_def_id), + }, + kind => { + bug!("unexpected def-kind: {}", kind.descr(param_def_id.to_def_id())) + } + }); + self.map.defs.insert(hir_id, ResolvedArg::Error(guar)); + return; + } + Scope::Root { .. } => break, + Scope::Binder { s, .. } + | Scope::Body { s, .. } + | Scope::Elision { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } + | Scope::AnonConstBoundary { s } => { + scope = s; + } + } + } + self.tcx.sess.delay_span_bug( self.tcx.hir().span(hir_id), format!("could not resolve {param_def_id:?}"), diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 7dce1272f96df..cb840592edd22 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -875,3 +875,28 @@ pub(crate) enum ReturnTypeNotationIllegalParam { param_span: Span, }, } + +#[derive(Diagnostic)] +pub(crate) enum LateBoundInApit { + #[diag(hir_analysis_late_bound_type_in_apit)] + Type { + #[primary_span] + span: Span, + #[label] + param_span: Span, + }, + #[diag(hir_analysis_late_bound_const_in_apit)] + Const { + #[primary_span] + span: Span, + #[label] + param_span: Span, + }, + #[diag(hir_analysis_late_bound_lifetime_in_apit)] + Lifetime { + #[primary_span] + span: Span, + #[label] + param_span: Span, + }, +} diff --git a/tests/ui/impl-trait/universal_wrong_hrtb.rs b/tests/ui/impl-trait/universal_wrong_hrtb.rs index b9551c2ceb0e5..48561710143b6 100644 --- a/tests/ui/impl-trait/universal_wrong_hrtb.rs +++ b/tests/ui/impl-trait/universal_wrong_hrtb.rs @@ -3,6 +3,6 @@ trait Trait<'a> { } fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {} -//~^ ERROR `impl Trait` can only mention lifetimes bound at the fn or impl level +//~^ ERROR `impl Trait` can only mention lifetimes from an fn or impl fn main() {} diff --git a/tests/ui/impl-trait/universal_wrong_hrtb.stderr b/tests/ui/impl-trait/universal_wrong_hrtb.stderr index 37eb8dfa1a141..b5a091b61faf0 100644 --- a/tests/ui/impl-trait/universal_wrong_hrtb.stderr +++ b/tests/ui/impl-trait/universal_wrong_hrtb.stderr @@ -1,14 +1,8 @@ -error: `impl Trait` can only mention lifetimes bound at the fn or impl level +error: `impl Trait` can only mention lifetimes from an fn or impl --> $DIR/universal_wrong_hrtb.rs:5:73 | LL | fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {} - | ^^ - | -note: lifetime declared here - --> $DIR/universal_wrong_hrtb.rs:5:39 - | -LL | fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {} - | ^^ + | -- lifetime declared here ^^ error: aborting due to previous error diff --git a/tests/ui/traits/non_lifetime_binders/nested-apit-mentioning-outer-bound-var.rs b/tests/ui/traits/non_lifetime_binders/nested-apit-mentioning-outer-bound-var.rs new file mode 100644 index 0000000000000..e9ae00df7a09e --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/nested-apit-mentioning-outer-bound-var.rs @@ -0,0 +1,11 @@ +#![feature(non_lifetime_binders)] +//~^ WARN the feature `non_lifetime_binders` is incomplete + +trait Trait { + type Assoc; +} + +fn uwu(_: impl for Trait<(), Assoc = impl Trait>) {} +//~^ ERROR `impl Trait` can only mention type parameters from an fn or impl + +fn main() {} diff --git a/tests/ui/traits/non_lifetime_binders/nested-apit-mentioning-outer-bound-var.stderr b/tests/ui/traits/non_lifetime_binders/nested-apit-mentioning-outer-bound-var.stderr new file mode 100644 index 0000000000000..1124076c23c9c --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/nested-apit-mentioning-outer-bound-var.stderr @@ -0,0 +1,17 @@ +warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/nested-apit-mentioning-outer-bound-var.rs:1:12 + | +LL | #![feature(non_lifetime_binders)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #108185 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: `impl Trait` can only mention type parameters from an fn or impl + --> $DIR/nested-apit-mentioning-outer-bound-var.rs:8:52 + | +LL | fn uwu(_: impl for Trait<(), Assoc = impl Trait>) {} + | - type parameter declared here ^ + +error: aborting due to previous error; 1 warning emitted +