From 7a89da30ba86884ce1f947b4f6380077b6bd77ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 18 Apr 2025 13:16:49 +0200 Subject: [PATCH 1/4] Propagate the object lifetime defaults of GAT's own params Notably, this excludes the self ty. This automatically fixes object lifetime defaulting for trait refs, too. These used to be broken because the index calculation for going from middle generic args back to HIR ones didn't take into account the implicit self ty param present of traits. --- .../src/collect/resolve_bound_vars.rs | 142 ++++++++++++------ src/librustdoc/clean/mod.rs | 3 + tests/ui/deriving/issue-89188-gat-hrtb.rs | 3 +- tests/ui/deriving/issue-89188-gat-hrtb.stderr | 25 +++ .../bad-assoc-ty.edition2015.stderr | 15 +- tests/ui/did_you_mean/bad-assoc-ty.rs | 4 +- ...ifetime-default-assoc-ty-self-ty-static.rs | 13 ++ ...ime-default-assoc-ty-self-ty-static.stderr | 9 ++ ...bject-lifetime-default-assoc-ty-self-ty.rs | 13 ++ ...t-lifetime-default-assoc-ty-self-ty.stderr | 9 ++ .../object-lifetime-default-gat-resolved.rs | 57 +++++++ ...ject-lifetime-default-gat-type-relative.rs | 10 ++ ...-lifetime-default-gat-type-relative.stderr | 9 ++ .../object-lifetime-default-trait-ref.rs | 26 ++++ 14 files changed, 281 insertions(+), 57 deletions(-) create mode 100644 tests/ui/deriving/issue-89188-gat-hrtb.stderr create mode 100644 tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr create mode 100644 tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.stderr create mode 100644 tests/ui/object-lifetime/object-lifetime-default-gat-resolved.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-gat-type-relative.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-gat-type-relative.stderr create mode 100644 tests/ui/object-lifetime/object-lifetime-default-trait-ref.rs 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 14e48594c006c..b7a6e53968835 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -777,7 +777,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // use the object lifetime defaulting // rules. So e.g., `Box` becomes // `Box`. - self.resolve_object_lifetime_default(&*lifetime) + self.resolve_object_lifetime_default(&*lifetime); } LifetimeKind::Infer => { // If the user writes `'_`, we use the *ordinary* elision @@ -796,7 +796,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { hir::TyKind::Ref(lifetime_ref, ref mt) => { self.visit_lifetime(lifetime_ref); let scope = Scope::ObjectLifetimeDefault { - lifetime: self.rbv.defs.get(&lifetime_ref.hir_id.local_id).cloned(), + lifetime: self.rbv.defs.get(&lifetime_ref.hir_id.local_id).copied(), s: self.scope, }; self.with(scope, |this| this.visit_ty_unambig(mt.ty)); @@ -884,11 +884,35 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } + fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: HirId, _: Span) { + match qpath { + hir::QPath::Resolved(maybe_qself, path) => { + if let Some(qself) = maybe_qself { + // FIXME: Actually determine the ambient object lifetime defaults for the self ty! + let scope = Scope::ObjectLifetimeDefault { lifetime: None, s: self.scope }; + self.with(scope, |this| this.visit_ty_unambig(qself)); + } + self.visit_path(path, id); + } + hir::QPath::TypeRelative(qself, segment) => { + // Resolving object lifetime defaults for type-relative paths requires full + // type-dependent resolution as performed by HIR ty lowering whose results + // we don't have access to (and we'd results for both FnCtxts and ItemCtxts). + // FIXME: Figure out if there's a feasible way to obtain the map of + // type-dependent definitions. + let scope = Scope::ObjectLifetimeDefault { lifetime: None, s: self.scope }; + self.with(scope, |this| { + this.visit_ty_unambig(qself); + this.visit_path_segment(segment) + }); + } + } + } + fn visit_path(&mut self, path: &hir::Path<'tcx>, hir_id: HirId) { - for (i, segment) in path.segments.iter().enumerate() { - let depth = path.segments.len() - i - 1; + for (index, segment) in path.segments.iter().enumerate() { if let Some(args) = segment.args { - self.visit_segment_args(path.res, depth, args); + self.visit_segment_args(path, index, args); } } if let Res::Def(DefKind::TyParam | DefKind::ConstParam, param_def_id) = path.res { @@ -1617,8 +1641,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn visit_segment_args( &mut self, - res: Res, - depth: usize, + path: &hir::Path<'tcx>, + index: usize, generic_args: &'tcx hir::GenericArgs<'tcx>, ) { if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { @@ -1626,35 +1650,53 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { return; } + // Let's first resolve all lifetime arguments because we need their resolution + // for computing the ambient object lifetime defaults. for arg in generic_args.args { if let hir::GenericArg::Lifetime(lt) = arg { self.visit_lifetime(lt); } } - // Figure out if this is a type/trait segment, - // which requires object lifetime defaults. - let type_def_id = match res { - Res::Def(DefKind::AssocTy, def_id) if depth == 1 => Some(self.tcx.parent(def_id)), - Res::Def(DefKind::Variant, def_id) if depth == 0 => Some(self.tcx.parent(def_id)), - Res::Def( - DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::TyAlias - | DefKind::Trait, - def_id, - ) if depth == 0 => Some(def_id), + // Figure out if this is an "eligible generic container" that brings along ambient object + // lifetime defaults for trait object types contained in any of the type arguments passed to + // it (any inner generic containers will of course end up shadowing that the default). + let depth = path.segments.len() - index - 1; + let container = match (path.res, depth) { + (Res::Def(DefKind::AssocTy, def_id), 1) => { + Some((self.tcx.parent(def_id), &path.segments[..=index])) + } + (Res::Def(DefKind::Variant, def_id), 0) => { + Some((self.tcx.parent(def_id), path.segments)) + } + // FIXME(trait_alias): Arguably, trait aliases are eligible generic containers. + ( + Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::TyAlias + | DefKind::Trait + | DefKind::AssocTy, + def_id, + ), + 0, + ) => Some((def_id, path.segments)), + // Note: We don't need to care about definition kinds that may have generics if they + // can only ever appear in positions where we can perform type inference (i.e., bodies). + // FIXME(mgca): @fmease thinks that under (m)GCA we now also need to care about e.g., + // type-level Consts (GCI) and AssocConsts (maybe also Fns, AssocFns) here + // since they appear outside of bodies (once the feature is more complete). _ => None, }; - debug!(?type_def_id); + debug!(?container); - // Compute a vector of defaults, one for each type parameter, - // per the rules given in RFCs 599 and 1156. Example: + // Compute a vector of ambient object lifetime defaults, one for each type parameter, + // per the rules initially given in RFCs 599 and 1156. Example: // // ```rust - // struct Foo<'a, T: 'a, U> { } + // struct Foo<'a, T: 'a + ?Sized, U: ?Sized>(&'a T, &'a U); // ``` // // If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to default @@ -1662,10 +1704,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // and `dyn Baz` to `dyn Baz + 'static` (because there is no // such bound). // - // Therefore, we would compute `object_lifetime_defaults` to a - // vector like `['x, 'static]`. Note that the vector only - // includes type parameters. - let object_lifetime_defaults = type_def_id.map_or_else(Vec::new, |def_id| { + // Therefore, we would compute a vector like `['x, 'static]`. + // Note that the vector only includes type parameters. + let object_lifetime_defaults = container.map_or_else(Vec::new, |(def_id, segments)| { let in_body = { let mut scope = self.scope; loop { @@ -1689,9 +1730,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { let rbv = &self.rbv; let generics = self.tcx.generics_of(def_id); - // `type_def_id` points to an item, so there is nothing to inherit generics from. - debug_assert_eq!(generics.parent_count, 0); - let set_to_region = |set: ObjectLifetimeDefault| match set { ObjectLifetimeDefault::Empty => { if in_body { @@ -1702,12 +1740,31 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } ObjectLifetimeDefault::Static => Some(ResolvedArg::StaticLifetime), ObjectLifetimeDefault::Param(param_def_id) => { - // This index can be used with `generic_args` since `parent_count == 0`. - let index = generics.param_def_id_to_index[¶m_def_id] as usize; - generic_args.args.get(index).and_then(|arg| match arg { - GenericArg::Lifetime(lt) => rbv.defs.get(<.hir_id.local_id).copied(), - _ => None, - }) + fn param_to_depth_and_index( + generics: &ty::Generics, + tcx: TyCtxt<'_>, + def_id: DefId, + ) -> (usize, usize) { + if let Some(&index) = generics.param_def_id_to_index.get(&def_id) { + let has_self = generics.parent.is_none() && generics.has_self; + (0, index as usize - generics.parent_count - has_self as usize) + } else if let Some(parent) = generics.parent { + let parent = tcx.generics_of(parent); + let (depth, index) = param_to_depth_and_index(parent, tcx, def_id); + (depth + 1, index) + } else { + unreachable!() + } + } + + let (depth, index) = param_to_depth_and_index(generics, self.tcx, param_def_id); + segments[segments.len() - depth - 1] + .args + .and_then(|args| args.args.get(index)) + .and_then(|arg| match arg { + GenericArg::Lifetime(lt) => rbv.defs.get(<.hir_id.local_id).copied(), + _ => None, + }) } ObjectLifetimeDefault::Ambiguous => None, }; @@ -1737,6 +1794,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { let mut i = 0; for arg in generic_args.args { match arg { + // We've already visited all lifetime arguments at the start. GenericArg::Lifetime(_) => {} GenericArg::Type(ty) => { if let Some(<) = object_lifetime_defaults.get(i) { @@ -1811,11 +1869,11 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // `for<'a> for<'r> >::x::<'r, T>::{opaque#0}: for<'b> Other<'b>`. if constraint.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation { - let bound_vars = if let Some(type_def_id) = type_def_id - && self.tcx.def_kind(type_def_id) == DefKind::Trait + let bound_vars = if let Some((container_def_id, _)) = container + && self.tcx.def_kind(container_def_id) == DefKind::Trait && let Some((mut bound_vars, assoc_fn)) = BoundVarContext::supertrait_hrtb_vars( self.tcx, - type_def_id, + container_def_id, constraint.ident, ty::AssocTag::Fn, ) { @@ -1844,10 +1902,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { this.visit_assoc_item_constraint(constraint) }); }); - } else if let Some(type_def_id) = type_def_id { + } else if let Some((container_def_id, _)) = container { let bound_vars = BoundVarContext::supertrait_hrtb_vars( self.tcx, - type_def_id, + container_def_id, constraint.ident, ty::AssocTag::Type, ) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1f9ce68584ce5..17f9c0a958959 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1968,6 +1968,9 @@ impl<'tcx> ContainerTy<'_, 'tcx> { match self { Self::Ref(region) => ObjectLifetimeDefault::Arg(region), Self::Regular { ty: container, args, arg: index } => { + // FIXME(fmease): rustc now also computes ambient object lifetime defaults for + // `AssocTy`s. Re-elide these, too! + let (DefKind::Struct | DefKind::Union | DefKind::Enum diff --git a/tests/ui/deriving/issue-89188-gat-hrtb.rs b/tests/ui/deriving/issue-89188-gat-hrtb.rs index a7b43159f16fd..79609be45b740 100644 --- a/tests/ui/deriving/issue-89188-gat-hrtb.rs +++ b/tests/ui/deriving/issue-89188-gat-hrtb.rs @@ -1,4 +1,5 @@ -//@ check-pass +// FIXME(fmease): I've regressed this one since we now reject TypeRelative paths as too complex. +//@ known-bug: unknown trait CallWithShim: Sized { type Shim<'s> diff --git a/tests/ui/deriving/issue-89188-gat-hrtb.stderr b/tests/ui/deriving/issue-89188-gat-hrtb.stderr new file mode 100644 index 0000000000000..ec8ea32a6d779 --- /dev/null +++ b/tests/ui/deriving/issue-89188-gat-hrtb.stderr @@ -0,0 +1,25 @@ +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/issue-89188-gat-hrtb.rs:27:56 + | +LL | &'s mut T::Shim Fn(&'s mut T::Shim Trait<'s, 't, 'u>>)>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/issue-89188-gat-hrtb.rs:27:56 + | +LL | &'s mut T::Shim Fn(&'s mut T::Shim Trait<'s, 't, 'u>>)>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/issue-89188-gat-hrtb.rs:27:56 + | +LL | &'s mut T::Shim Fn(&'s mut T::Shim Trait<'s, 't, 'u>>)>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr b/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr index af0a9d064d571..ca3e07976e9ad 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr +++ b/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr @@ -193,20 +193,11 @@ help: if this is a dyn-compatible trait, use `dyn` LL | type H = (u8)>::Output; | ++++ + -error[E0223]: ambiguous associated type +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound --> $DIR/bad-assoc-ty.rs:37:10 | LL | type H = Fn(u8) -> (u8)::Output; - | ^^^^^^^^^^^^^^^^^^^^^^ - | -help: use fully-qualified syntax - | -LL - type H = Fn(u8) -> (u8)::Output; -LL + type H = <(dyn Fn(u8) -> u8 + 'static) as BitOr>::Output; - | -LL - type H = Fn(u8) -> (u8)::Output; -LL + type H = <(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output; - | + | ^^^^^^^^^^^^^^ error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:44:19 @@ -310,5 +301,5 @@ LL | fn foo(_: F) where F: Fn() -> _ {} error: aborting due to 30 previous errors; 1 warning emitted -Some errors have detailed explanations: E0121, E0223, E0740. +Some errors have detailed explanations: E0121, E0223, E0228, E0740. For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/did_you_mean/bad-assoc-ty.rs b/tests/ui/did_you_mean/bad-assoc-ty.rs index 39f0a84855af7..00d74ec3c2799 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.rs +++ b/tests/ui/did_you_mean/bad-assoc-ty.rs @@ -35,10 +35,10 @@ type G = dyn 'static + (Send)::AssocTy; // This is actually a legal path with fn-like generic arguments in the middle! // Recovery should not apply in this context. type H = Fn(u8) -> (u8)::Output; -//[edition2015]~^ ERROR ambiguous associated type +//[edition2021]~^ ERROR expected a type, found a trait +//[edition2015]~^^ ERROR the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound //[edition2015]~| WARN trait objects without an explicit `dyn` are deprecated //[edition2015]~| WARN this is accepted in the current edition -//[edition2021]~^^^^ ERROR expected a type, found a trait macro_rules! ty { ($ty: ty) => ($ty::AssocTy); diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs new file mode 100644 index 0000000000000..86a0ac86e4459 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs @@ -0,0 +1,13 @@ +// FIXME: Explainer. +//@ known-bug: unknown + +trait Outer { type Ty; } +trait Inner {} + +impl<'a> Outer for dyn Inner + 'a { type Ty = &'a (); } + +// FIXME: Deduce `dyn Inner + 'static` from absence of any bounds on self ty param of trait `Outer`. +fn f<'r>(x: &'r ::Ty) { g(x) } +fn g<'r>(x: &'r ::Ty) {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr new file mode 100644 index 0000000000000..93c531b3d65ca --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr @@ -0,0 +1,9 @@ +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/object-lifetime-default-assoc-ty-self-ty-static.rs:10:18 + | +LL | fn f<'r>(x: &'r ::Ty) { g(x) } + | ^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs new file mode 100644 index 0000000000000..aef7dbe020b6d --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs @@ -0,0 +1,13 @@ +// FIXME: Explainer. +//@ known-bug: unknown + +trait Outer<'a>: 'a { type Ty; } +trait Inner {} + +impl<'a> Outer<'a> for dyn Inner + 'a { type Ty = &'a (); } + +fn f<'r>(x: >::Ty) { g(x) } +// FIXME: Deduce `dyn Inner + 'r` from bound `'a` on self ty param of trait `Outer`. +fn g<'r>(x: >::Ty) {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.stderr b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.stderr new file mode 100644 index 0000000000000..0fc886f787a80 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.stderr @@ -0,0 +1,9 @@ +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/object-lifetime-default-assoc-ty-self-ty.rs:11:14 + | +LL | fn g<'r>(x: >::Ty) {} + | ^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-resolved.rs b/tests/ui/object-lifetime/object-lifetime-default-gat-resolved.rs new file mode 100644 index 0000000000000..aff71f0a87753 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-resolved.rs @@ -0,0 +1,57 @@ +// Check that we correctly deduce object lifetime defaults inside resolved GAT *paths*. +// issue: +//@ check-pass + +mod own { // the object lifetime default comes from the own generics + trait Outer { + type Ty<'a, T: ?Sized + 'a>; + } + impl Outer for () { + type Ty<'a, T: ?Sized + 'a> = &'a T; + } + trait Inner {} + + fn f<'r>(x: <() as Outer>::Ty<'r, dyn Inner + 'r>) { g(x) } + // We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`. + fn g<'r>(_: <() as Outer>::Ty<'r, dyn Inner>) {} +} + +mod parent { // the object lifetime default comes from the parent generics + trait Outer<'a> { + type Ty; + } + impl<'a> Outer<'a> for () { + type Ty = &'a T; + } + trait Inner {} + + fn f<'r>(x: <() as Outer<'r>>::Ty) { g(x) } + // We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`. + fn g<'r>(_: <() as Outer<'r>>::Ty) {} +} + +mod complex { + // We need to perform several delicate index calculations to map between the middle::ty and + // the HIR representation of generic args. This is a smoke test. + + trait Outer<'_pad0, 'a, '_pad1, _Pad0> { + type Ty<'_pad2, 'b, '_pad3, _Pad1, T: ?Sized + 'a, _Pad2, U: ?Sized + 'b>; + } + impl<'a, _Pad0> Outer<'_, 'a, '_, _Pad0> for () { + type Ty<'_pad2, 'b, '_pad3, _Pad1, T: ?Sized + 'a, _Pad2, U: ?Sized + 'b> = (&'a T, &'b U); + } + trait Inner {} + + fn f<'r, 's>( + x: <() as Outer<'static, 'r, 'static, ()>>:: + Ty<'static, 's, 'static, (), dyn Inner + 'r, (), dyn Inner + 's>, + ) { + g(x) + } + fn g<'r, 's>( + _: <() as Outer<'static, 'r, 'static, ()>>:: + Ty<'static, 's, 'static, (), dyn Inner, (), dyn Inner>, + ) {} +} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-type-relative.rs b/tests/ui/object-lifetime/object-lifetime-default-gat-type-relative.rs new file mode 100644 index 0000000000000..4c204abfaf99a --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-type-relative.rs @@ -0,0 +1,10 @@ +trait Outer { type Ty<'a, T: 'a + ?Sized>; } +trait Inner {} + +// FIXME: Ideally, we would deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty` +// but for that we'd need to somehow obtain the resolution of the type-relative path `T::Ty` +// from HIR ty lowering (it resolves to `::Ty`). +fn f<'r, T: Outer>(x: T::Ty<'r, dyn Inner>) {} +//~^ ERROR lifetime bound for this object type cannot be deduced from context + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-type-relative.stderr b/tests/ui/object-lifetime/object-lifetime-default-gat-type-relative.stderr new file mode 100644 index 0000000000000..9c535da22700a --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-type-relative.stderr @@ -0,0 +1,9 @@ +error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound + --> $DIR/object-lifetime-default-gat-type-relative.rs:7:33 + | +LL | fn f<'r, T: Outer>(x: T::Ty<'r, dyn Inner>) {} + | ^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-trait-ref.rs b/tests/ui/object-lifetime/object-lifetime-default-trait-ref.rs new file mode 100644 index 0000000000000..88cef0810d671 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-trait-ref.rs @@ -0,0 +1,26 @@ +// Check that we correctly deduce object lifetime defaults inside *trait refs*! +// For the longest time these examples used to get rejected as "inderminate" due to an off-by-one. +//@ check-pass + +trait Inner {} +trait Outer<'a, T: 'a + ?Sized> { type Project where Self: Sized; } + +fn bound0<'r, T>() where T: Outer<'r, dyn Inner + 'r> { bound1::<'r, T>() } +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn bound1<'r, T>() where T: Outer<'r, dyn Inner> {} + +fn dyn0<'r>(x: Box>) { dyn1(x) } +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn dyn1<'r>(_: Box>) {} + +fn impl0<'r>(x: impl Outer<'r, dyn Inner + 'r>) { impl1(x) } +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn impl1<'r>(_: impl Outer<'r, dyn Inner>) {} // deduce `dyn Inner + 'r` + +fn proj<'r>(x: <() as Outer<'r, dyn Inner + 'r>>::Project) { proj1(x) } +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn proj1<'r>(_: <() as Outer<'r, dyn Inner>>::Project) {} + +impl<'a, T: 'a + ?Sized> Outer<'a, T> for () { type Project = &'a T; } + +fn main() {} From 30ad2c1e36949a98e04756ab33440e3b8a69cbee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 19 Apr 2025 15:10:55 +0200 Subject: [PATCH 2/4] Don't needlessly search for already-found HIR generic param --- compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 b7a6e53968835..d868a69ca7868 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1064,8 +1064,6 @@ fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: LocalDefId) -> ObjectL hir::GenericParamSource::Generics => { let parent_def_id = tcx.local_parent(param_def_id); let generics = tcx.hir_get_generics(parent_def_id).unwrap(); - let param_hir_id = tcx.local_def_id_to_hir_id(param_def_id); - let param = generics.params.iter().find(|p| p.hir_id == param_hir_id).unwrap(); // Scan the bounds and where-clauses on parameters to extract bounds // of the form `T:'a` so as to determine the `ObjectLifetimeDefault` @@ -1099,7 +1097,7 @@ fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: LocalDefId) -> ObjectL } } _ => { - bug!("object_lifetime_default_raw must only be called on a type parameter") + bug!("object_lifetime_default must only be called on a type parameter") } } } From 0f8c502c4e64a347c3521ff4c427170f3bfe9387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 20 Apr 2025 22:21:01 +0200 Subject: [PATCH 3/4] Propagate the object lifetime default to the self ty of resolved projections --- .../src/collect/resolve_bound_vars.rs | 317 ++++++++++-------- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- ...ifetime-default-assoc-ty-self-ty-static.rs | 10 +- ...ime-default-assoc-ty-self-ty-static.stderr | 9 - ...bject-lifetime-default-assoc-ty-self-ty.rs | 10 +- ...t-lifetime-default-assoc-ty-self-ty.stderr | 9 - 6 files changed, 190 insertions(+), 167 deletions(-) delete mode 100644 tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr delete mode 100644 tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.stderr 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 d868a69ca7868..88f24b65af08b 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -887,12 +887,28 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: HirId, _: Span) { match qpath { hir::QPath::Resolved(maybe_qself, path) => { + // Visit the path before the self type since computing the ambient object lifetime default + // for the latter requires all lifetime arguments of the trait ref to be already resolved. + self.visit_path(path, id); if let Some(qself) = maybe_qself { - // FIXME: Actually determine the ambient object lifetime defaults for the self ty! - let scope = Scope::ObjectLifetimeDefault { lifetime: None, s: self.scope }; - self.with(scope, |this| this.visit_ty_unambig(qself)); + let container = match path.res { + Res::Def(DefKind::AssocTy, def_id) => Some(( + self.tcx.parent(def_id), + &path.segments[..path.segments.len() - 1], + )), + _ => None, + }; + let object_lifetime_defaults = + container.map_or(Vec::new(), |(def_id, segs)| { + self.compute_ambient_object_lifetime_defaults(def_id, segs) + }); + if let Some(<) = object_lifetime_defaults.get(0) { + let scope = Scope::ObjectLifetimeDefault { lifetime: lt, s: self.scope }; + self.with(scope, |this| this.visit_ty_unambig(qself)); + } else { + self.visit_ty_unambig(qself); + } } - self.visit_path(path, id); } hir::QPath::TypeRelative(qself, segment) => { // Resolving object lifetime defaults for type-relative paths requires full @@ -1056,52 +1072,57 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: LocalDefId) -> ObjectLifetimeDefault { - debug_assert_eq!(tcx.def_kind(param_def_id), DefKind::TyParam); - let hir::Node::GenericParam(param) = tcx.hir_node_by_def_id(param_def_id) else { - bug!("expected GenericParam for object_lifetime_default"); - }; - match param.source { - hir::GenericParamSource::Generics => { - let parent_def_id = tcx.local_parent(param_def_id); - let generics = tcx.hir_get_generics(parent_def_id).unwrap(); - - // Scan the bounds and where-clauses on parameters to extract bounds - // of the form `T:'a` so as to determine the `ObjectLifetimeDefault` - // for each type parameter. - match param.kind { + // Scan the bounds and where-clauses on parameters to extract bounds of the form `T: 'a` + // so as to determine the `ObjectLifetimeDefault` for each type parameter. + + let Ok((generics, bounds)) = (match tcx.hir_node_by_def_id(param_def_id) { + hir::Node::GenericParam(param) => match param.source { + hir::GenericParamSource::Generics => match param.kind { GenericParamKind::Type { .. } => { - let mut set = Set1::Empty; - - // Look for `type: ...` where clauses. - for bound in generics.bounds_for_param(param_def_id) { - // Ignore `for<'a> type: ...` as they can change what - // lifetimes mean (although we could "just" handle it). - if !bound.bound_generic_params.is_empty() { - continue; - } + Ok((tcx.hir_get_generics(tcx.local_parent(param_def_id)).unwrap(), &[][..])) + } + _ => Err(()), + }, + hir::GenericParamSource::Binder => return ObjectLifetimeDefault::Empty, + }, + // For `Self` type parameters + hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Trait(_, _, _, _, generics, bounds, _), + .. + }) => Ok((generics, bounds)), + _ => Err(()), + }) else { + bug!("`object_lifetime_default` must only be called on type parameters") + }; - for bound in bound.bounds { - if let hir::GenericBound::Outlives(lifetime) = bound { - set.insert(lifetime.kind); - } - } - } + let mut set = Set1::Empty; - match set { - Set1::Empty => ObjectLifetimeDefault::Empty, - Set1::One(hir::LifetimeKind::Static) => ObjectLifetimeDefault::Static, - Set1::One(hir::LifetimeKind::Param(param_def_id)) => { - ObjectLifetimeDefault::Param(param_def_id.to_def_id()) - } - _ => ObjectLifetimeDefault::Ambiguous, - } - } - _ => { - bug!("object_lifetime_default must only be called on a type parameter") - } + let mut add_outlives_bounds = |bounds: &[hir::GenericBound<'_>]| { + for bound in bounds { + if let hir::GenericBound::Outlives(lifetime) = bound { + set.insert(lifetime.kind); } } - hir::GenericParamSource::Binder => ObjectLifetimeDefault::Empty, + }; + + add_outlives_bounds(bounds); + + // Look for `Type: ...` where clauses. + for bound in generics.bounds_for_param(param_def_id) { + // Ignore `for<'a> Type: ...` as they can change what + // lifetimes mean (although we could "just" handle it). + if bound.bound_generic_params.is_empty() { + add_outlives_bounds(&bound.bounds); + } + } + + match set { + Set1::Empty => ObjectLifetimeDefault::Empty, + Set1::One(hir::LifetimeKind::Static) => ObjectLifetimeDefault::Static, + Set1::One(hir::LifetimeKind::Param(param_def_id)) => { + ObjectLifetimeDefault::Param(param_def_id.to_def_id()) + } + _ => ObjectLifetimeDefault::Ambiguous, } } @@ -1690,106 +1711,16 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { debug!(?container); - // Compute a vector of ambient object lifetime defaults, one for each type parameter, - // per the rules initially given in RFCs 599 and 1156. Example: - // - // ```rust - // struct Foo<'a, T: 'a + ?Sized, U: ?Sized>(&'a T, &'a U); - // ``` - // - // If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to default - // `dyn Bar` to `dyn Bar + 'x` (because of the `T: 'a` bound) - // and `dyn Baz` to `dyn Baz + 'static` (because there is no - // such bound). - // - // Therefore, we would compute a vector like `['x, 'static]`. - // Note that the vector only includes type parameters. - let object_lifetime_defaults = container.map_or_else(Vec::new, |(def_id, segments)| { - let in_body = { - let mut scope = self.scope; - loop { - match *scope { - Scope::Root { .. } => break false, - - Scope::Body { .. } => break true, - - Scope::Binder { s, .. } - | Scope::ObjectLifetimeDefault { s, .. } - | Scope::Opaque { s, .. } - | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } - | Scope::LateBoundary { s, .. } => { - scope = s; - } - } - } - }; - - let rbv = &self.rbv; - let generics = self.tcx.generics_of(def_id); - - let set_to_region = |set: ObjectLifetimeDefault| match set { - ObjectLifetimeDefault::Empty => { - if in_body { - None - } else { - Some(ResolvedArg::StaticLifetime) - } - } - ObjectLifetimeDefault::Static => Some(ResolvedArg::StaticLifetime), - ObjectLifetimeDefault::Param(param_def_id) => { - fn param_to_depth_and_index( - generics: &ty::Generics, - tcx: TyCtxt<'_>, - def_id: DefId, - ) -> (usize, usize) { - if let Some(&index) = generics.param_def_id_to_index.get(&def_id) { - let has_self = generics.parent.is_none() && generics.has_self; - (0, index as usize - generics.parent_count - has_self as usize) - } else if let Some(parent) = generics.parent { - let parent = tcx.generics_of(parent); - let (depth, index) = param_to_depth_and_index(parent, tcx, def_id); - (depth + 1, index) - } else { - unreachable!() - } - } - - let (depth, index) = param_to_depth_and_index(generics, self.tcx, param_def_id); - segments[segments.len() - depth - 1] - .args - .and_then(|args| args.args.get(index)) - .and_then(|arg| match arg { - GenericArg::Lifetime(lt) => rbv.defs.get(<.hir_id.local_id).copied(), - _ => None, - }) - } - ObjectLifetimeDefault::Ambiguous => None, - }; - generics - .own_params - .iter() - .filter_map(|param| { - match self.tcx.def_kind(param.def_id) { - // Generic consts don't impose any constraints. - // - // We still store a dummy value here to allow generic parameters - // in an arbitrary order. - DefKind::ConstParam => Some(ObjectLifetimeDefault::Empty), - DefKind::TyParam => Some(self.tcx.object_lifetime_default(param.def_id)), - // We may also get a `Trait` or `TraitAlias` because of how generics `Self` parameter - // works. Ignore it because it can't have a meaningful lifetime default. - DefKind::LifetimeParam | DefKind::Trait | DefKind::TraitAlias => None, - dk => bug!("unexpected def_kind {:?}", dk), - } - }) - .map(set_to_region) - .collect() + let object_lifetime_defaults = container.map_or_else(Vec::new, |(def_id, segs)| { + self.compute_ambient_object_lifetime_defaults(def_id, segs) }); debug!(?object_lifetime_defaults); - let mut i = 0; + let has_self = container + .map(|(def_id, _)| self.tcx.generics_of(def_id)) + .is_some_and(|generics| generics.parent.is_none() && generics.has_self); + let mut i = has_self as usize; for arg in generic_args.args { match arg { // We've already visited all lifetime arguments at the start. @@ -1921,6 +1852,110 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } } + /// Compute a vector of ambient object lifetime defaults, one for each type parameter, + /// per the rules initially given in RFCs 599 and 1156. + /// + /// # Example + /// + /// ```ignore (illustrative) + /// struct Foo<'a, T: 'a + ?Sized, U: ?Sized>(&'a T, &'a U); + /// ``` + /// + /// If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to default + /// `dyn Bar` to `dyn Bar + 'x` (because of the `T: 'a` bound) + /// and `dyn Baz` to `dyn Baz + 'static` (because there is no + /// such bound). + /// + /// Therefore, we would compute a vector like `['x, 'static]`. + /// Note that the vector only includes type parameters. + fn compute_ambient_object_lifetime_defaults( + &self, + def_id: DefId, + segments: &[hir::PathSegment<'_>], + ) -> Vec> { + let in_body = { + let mut scope = self.scope; + loop { + match *scope { + Scope::Root { .. } => break false, + + Scope::Body { .. } => break true, + + Scope::Binder { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Opaque { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } + | Scope::LateBoundary { s, .. } => { + scope = s; + } + } + } + }; + + let generics = self.tcx.generics_of(def_id); + + let set_to_region = |set: ObjectLifetimeDefault| match set { + ObjectLifetimeDefault::Empty => { + if in_body { + None + } else { + Some(ResolvedArg::StaticLifetime) + } + } + ObjectLifetimeDefault::Static => Some(ResolvedArg::StaticLifetime), + ObjectLifetimeDefault::Param(param_def_id) => { + fn param_to_depth_and_index( + generics: &ty::Generics, + tcx: TyCtxt<'_>, + def_id: DefId, + ) -> (usize, usize) { + if let Some(&index) = generics.param_def_id_to_index.get(&def_id) { + let has_self = generics.parent.is_none() && generics.has_self; + (0, index as usize - generics.parent_count - has_self as usize) + } else if let Some(parent) = generics.parent { + let parent = tcx.generics_of(parent); + let (depth, index) = param_to_depth_and_index(parent, tcx, def_id); + (depth + 1, index) + } else { + unreachable!() + } + } + + let (depth, index) = param_to_depth_and_index(generics, self.tcx, param_def_id); + segments[segments.len() - depth - 1] + .args + .and_then(|args| args.args.get(index)) + .and_then(|arg| match arg { + GenericArg::Lifetime(lt) => self.rbv.defs.get(<.hir_id.local_id).copied(), + _ => None, + }) + } + ObjectLifetimeDefault::Ambiguous => None, + }; + generics + .own_params + .iter() + .filter_map(|param| { + match self.tcx.def_kind(param.def_id) { + // Generic consts don't impose any constraints. + // + // We still store a dummy value here to allow generic parameters + // in an arbitrary order. + DefKind::ConstParam => Some(ObjectLifetimeDefault::Empty), + // `Self` type params share the `DefId` of the corresp. trait. + DefKind::TyParam | DefKind::Trait => { + Some(self.tcx.object_lifetime_default(param.def_id)) + } + // `Self` type params of trait aliases may show up here, ignore them. + DefKind::LifetimeParam | DefKind::TraitAlias => None, + dk => bug!("unexpected def_kind {:?}", dk), + } + }) + .map(set_to_region) + .collect() + } + /// Returns all the late-bound vars that come into scope from supertrait HRTBs, based on the /// associated type name and starting trait. /// For example, imagine we have diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 9fac68039f52c..bee7ed2eca5fd 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1534,7 +1534,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let Some(name) = tcx.intrinsic(def_id) { record!(self.tables.intrinsic[def_id] <- name); } - if let DefKind::TyParam = def_kind { + if let DefKind::TyParam | DefKind::Trait = def_kind { let default = self.tcx.object_lifetime_default(def_id); record!(self.tables.object_lifetime_default[def_id] <- default); } diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs index 86a0ac86e4459..47f8b4cf57165 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs @@ -1,12 +1,16 @@ -// FIXME: Explainer. -//@ known-bug: unknown +// Check that we correctly deduce object lifetime defaults inside self types of qualified paths. +//@ check-pass trait Outer { type Ty; } trait Inner {} impl<'a> Outer for dyn Inner + 'a { type Ty = &'a (); } -// FIXME: Deduce `dyn Inner + 'static` from absence of any bounds on self ty param of trait `Outer`. +// We deduce `dyn Inner + 'static` from absence of any bounds on self ty param of trait `Outer`. +// +// Prior to PR rust-lang/rust#129543, assoc tys weren't considered *eligible generic containers* and +// thus we'd use the *ambient object lifetime default* induced by the reference type ctor `&`, +// namely `'r`. Now however, the assoc ty shadows/overwrites that ambient default to `'static`. fn f<'r>(x: &'r ::Ty) { g(x) } fn g<'r>(x: &'r ::Ty) {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr deleted file mode 100644 index 93c531b3d65ca..0000000000000 --- a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound - --> $DIR/object-lifetime-default-assoc-ty-self-ty-static.rs:10:18 - | -LL | fn f<'r>(x: &'r ::Ty) { g(x) } - | ^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs index aef7dbe020b6d..21921a98bf986 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs @@ -1,13 +1,15 @@ -// FIXME: Explainer. -//@ known-bug: unknown +// Check that we correctly deduce object lifetime defaults inside self types of qualified paths. +//@ check-pass +//@ revisions: bound clause -trait Outer<'a>: 'a { type Ty; } +#[cfg(bound)] trait Outer<'a>: 'a { type Ty; } +#[cfg(clause)] trait Outer<'a> where Self: 'a { type Ty; } trait Inner {} impl<'a> Outer<'a> for dyn Inner + 'a { type Ty = &'a (); } fn f<'r>(x: >::Ty) { g(x) } -// FIXME: Deduce `dyn Inner + 'r` from bound `'a` on self ty param of trait `Outer`. +// We deduce `dyn Inner + 'r` from bound `'a` on self ty param of trait `Outer`. fn g<'r>(x: >::Ty) {} fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.stderr b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.stderr deleted file mode 100644 index 0fc886f787a80..0000000000000 --- a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound - --> $DIR/object-lifetime-default-assoc-ty-self-ty.rs:11:14 - | -LL | fn g<'r>(x: >::Ty) {} - | ^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0228`. From 0f271134cab87296b18c1da970f59f350a3f2e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 18 Apr 2025 14:59:15 +0200 Subject: [PATCH 4/4] Tweak `#[rustc_object_lifetime_default]` * print `Empty` as it's called in code, otherwise it's unnecessarily confusing * go through the middle::ty Generics instead of the HIR ones, so we can dump the default for the implicit `Self` type parameter of traits, too * allow the attribute on more targets (it used to be allowed anywhere for the longest time but someone must've incorrectly restricted it during the migration to the new attribute parsing API) --- .../src/attributes/rustc_internal.rs | 9 ++++++- compiler/rustc_passes/src/check_attr.rs | 26 +++++++++---------- .../object-lifetime-default.rs | 12 +++++++-- .../object-lifetime-default.stderr | 22 +++++++++++++--- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 455c61097d78d..859a0c91de3f4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -46,7 +46,14 @@ impl SingleAttributeParser for RustcObjectLifetimeDefaultParser { const PATH: &[rustc_span::Symbol] = &[sym::rustc_object_lifetime_default]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + Allow(Target::Trait), + Allow(Target::TyAlias), + Allow(Target::AssocTy), + ]); const TEMPLATE: AttributeTemplate = template!(Word); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e3b2a922c9afd..8317a9a0e2d09 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -649,20 +649,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Debugging aid for `object_lifetime_default` query. fn check_object_lifetime_default(&self, hir_id: HirId) { let tcx = self.tcx; - if let Some(owner_id) = hir_id.as_owner() - && let Some(generics) = tcx.hir_get_generics(owner_id.def_id) - { - for p in generics.params { - let hir::GenericParamKind::Type { .. } = p.kind else { continue }; - let default = tcx.object_lifetime_default(p.def_id); - let repr = match default { - ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(), - ObjectLifetimeDefault::Static => "'static".to_owned(), - ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(), - ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(), - }; - tcx.dcx().emit_err(errors::ObjectLifetimeErr { span: p.span, repr }); - } + let Some(owner_id) = hir_id.as_owner() else { return }; + for param in &tcx.generics_of(owner_id.def_id).own_params { + let ty::GenericParamDefKind::Type { .. } = param.kind else { continue }; + let default = tcx.object_lifetime_default(param.def_id); + let repr = match default { + ObjectLifetimeDefault::Empty => "Empty".to_owned(), + ObjectLifetimeDefault::Static => "'static".to_owned(), + ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(), + ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(), + }; + tcx.dcx() + .emit_err(errors::ObjectLifetimeErr { span: tcx.def_span(param.def_id), repr }); } } /// Checks if `#[collapse_debuginfo]` is applied to a macro. diff --git a/tests/ui/object-lifetime/object-lifetime-default.rs b/tests/ui/object-lifetime/object-lifetime-default.rs index 74f5bb7ddb0ec..034928cbd5165 100644 --- a/tests/ui/object-lifetime/object-lifetime-default.rs +++ b/tests/ui/object-lifetime/object-lifetime-default.rs @@ -2,13 +2,13 @@ #[rustc_object_lifetime_default] struct A< - T, //~ ERROR BaseDefault + T, //~ ERROR Empty >(T); #[rustc_object_lifetime_default] struct B< 'a, - T, //~ ERROR BaseDefault + T, //~ ERROR Empty >(&'a (), T); #[rustc_object_lifetime_default] @@ -47,4 +47,12 @@ struct G< U: 'a + 'b, //~ ERROR Ambiguous >(&'a T, &'b U); +// Check that we also dump the default for the implicit `Self` type param of traits. +#[rustc_object_lifetime_default] +trait H< //~ ERROR 'a + 'a, + 'b, + T: 'b, //~ ERROR 'b +>: 'a {} + fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default.stderr b/tests/ui/object-lifetime/object-lifetime-default.stderr index a58afad3ef2be..6606676c69f72 100644 --- a/tests/ui/object-lifetime/object-lifetime-default.stderr +++ b/tests/ui/object-lifetime/object-lifetime-default.stderr @@ -1,10 +1,10 @@ -error: BaseDefault +error: Empty --> $DIR/object-lifetime-default.rs:5:5 | LL | T, | ^ -error: BaseDefault +error: Empty --> $DIR/object-lifetime-default.rs:11:5 | LL | T, @@ -52,5 +52,21 @@ error: Ambiguous LL | U: 'a + 'b, | ^ -error: aborting due to 9 previous errors +error: 'a + --> $DIR/object-lifetime-default.rs:52:1 + | +LL | / trait H< +LL | | 'a, +LL | | 'b, +LL | | T: 'b, +LL | | >: 'a {} + | |_____^ + +error: 'b + --> $DIR/object-lifetime-default.rs:55:5 + | +LL | T: 'b, + | ^ + +error: aborting due to 11 previous errors