From 5adecad8bfc196cb35278608d59a09d44928ae53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 10 Feb 2026 01:06:06 +0100 Subject: [PATCH 1/7] Make resolved GAT paths induce trait object lifetime defaults for their own args (excl. self ty) This automatically fixes trait object lifetime defaulting for trait refs and trait alias 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 on traits. Moreover, in item signatures reject hidden trait object lifetime bounds inside type-relative paths (excl. the self ty) on grounds of the default being indeterminate. --- .../src/collect/resolve_bound_vars.rs | 152 ++++++++++++------ src/librustdoc/clean/mod.rs | 3 + tests/ui/deriving/issue-89188-gat-hrtb.rs | 2 +- .../bad-assoc-ty.edition2015.stderr | 16 +- tests/ui/did_you_mean/bad-assoc-ty.rs | 4 +- ...ifetime-default-assoc-ty-self-ty-static.rs | 12 ++ ...ime-default-assoc-ty-self-ty-static.stderr | 14 ++ ...bject-lifetime-default-assoc-ty-self-ty.rs | 12 ++ ...t-lifetime-default-assoc-ty-self-ty.stderr | 14 ++ .../object-lifetime-default-inherent-gac.rs | 43 +++++ ...bject-lifetime-default-inherent-gac.stderr | 25 +++ .../object-lifetime-default-inherent-gat.rs | 43 +++++ ...bject-lifetime-default-inherent-gat.stderr | 25 +++ .../object-lifetime-default-resolved-gat.rs | 67 ++++++++ ...object-lifetime-default-trait-alias-ref.rs | 27 ++++ ...lt-trait-ref-lifetime-bound-on-assoc-ty.rs | 27 ++++ .../object-lifetime-default-trait-ref.rs | 34 ++++ ...ject-lifetime-default-type-relative-gat.rs | 32 ++++ ...-lifetime-default-type-relative-gat.stderr | 38 +++++ 19 files changed, 530 insertions(+), 60 deletions(-) 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-inherent-gac.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-inherent-gac.stderr create mode 100644 tests/ui/object-lifetime/object-lifetime-default-inherent-gat.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-inherent-gat.stderr create mode 100644 tests/ui/object-lifetime/object-lifetime-default-resolved-gat.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-trait-alias-ref.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-trait-ref-lifetime-bound-on-assoc-ty.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-trait-ref.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.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 3e687700f11c6..8aa44c4b8cf66 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -787,11 +787,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }); match lifetime.kind { LifetimeKind::ImplicitObjectLifetimeDefault => { - // If the user does not write *anything*, we - // use the object lifetime defaulting - // rules. So e.g., `Box` becomes - // `Box`. - self.resolve_object_lifetime_default(&*lifetime) + // If the user doesn't write *anything*, we apply the + // trait object lifetime defaulting rules. + // E.g., `Box` becomes `Box`. + self.resolve_object_lifetime_default(&*lifetime); } LifetimeKind::Infer => { // If the user writes `'_`, we use the *ordinary* elision @@ -810,7 +809,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)); @@ -898,11 +897,40 @@ 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(fmease): Make assocated type paths induce a trait object lifetime + // default for the self type, too! + 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) => { + // Computing the trait object lifetime defaults that are induced by type-relative + // paths would require full type-dependent resolution as performed by HIR ty + // lowering whose results we don't have access to here (esp. in ItemCtxts which + // don't "persist" any resolutions during lowering). + // For maximum forward compatibility, in ItemCtxts we make HIR ty lowering reject + // implicit trait object lifetime bounds inside such paths on grounds of + // the default being *indeterminate*. + // FIXME: Figure out if there's a feasible way to obtain the map of type-dependent + // definitions here / interleave RBV and HIR ty lowering. + 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_path_segment_args(args, index, path); } } if let Res::Def(DefKind::TyParam | DefKind::ConstParam, param_def_id) = path.res { @@ -1668,58 +1696,70 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn visit_segment_args( + fn visit_path_segment_args( &mut self, - res: Res, - depth: usize, generic_args: &'tcx hir::GenericArgs<'tcx>, + index: usize, + path: &hir::Path<'tcx>, ) { if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { self.visit_fn_like_elision(inputs, Some(output), false); return; } + // Let's first resolve all lifetime arguments because we need their + // resolution for computing the trait 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)), + // Figure out if this is an eligible container that induces lifetime defaults for + // trait object types contained in any of the type arguments passed to it + // (any inner containers will of course end up shadowing that default). + // + // FIXME(mgca, #151649): Assoc (and free?) consts should also qualify. + // FIXME(return_type_notation, #151662): Assoc fns should also qualify. + let depth = path.segments.len() - index - 1; + let container = match path.res { + Res::Def(DefKind::AssocTy, def_id) if depth == 1 => { + Some((self.tcx.parent(def_id), &path.segments[..=index])) + } + Res::Def(DefKind::Variant, def_id) if depth == 0 => { + Some((self.tcx.parent(def_id), path.segments)) + } Res::Def( DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias | DefKind::Trait - | DefKind::TraitAlias, + | DefKind::TraitAlias + | DefKind::AssocTy, def_id, - ) if depth == 0 => Some(def_id), + ) if depth == 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). _ => 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 list of trait 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 - // `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). + // If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to elaborate + // * `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 `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 list like `['x, 'static]`. + // Note that the list only includes the lifetimes for type parameters. + let object_lifetime_defaults = container.map_or_else(Vec::new, |(def_id, segments)| { let in_body = { let mut scope = self.scope; loop { @@ -1743,9 +1783,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 { @@ -1756,12 +1793,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.has_own_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, }; @@ -1770,7 +1826,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { .iter() .filter_map(|param| { match self.tcx.def_kind(param.def_id) { - // Generic consts don't impose any constraints. + // Const parameters don't impose any constraints. // // We still store a dummy value here to allow generic parameters // in an arbitrary order. @@ -1791,6 +1847,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) { @@ -1865,11 +1922,12 @@ 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 - && let DefKind::Trait | DefKind::TraitAlias = self.tcx.def_kind(type_def_id) + let bound_vars = if let Some((container_def_id, _)) = container + && let DefKind::Trait | DefKind::TraitAlias = + self.tcx.def_kind(container_def_id) && let Some((mut bound_vars, assoc_fn)) = BoundVarContext::supertrait_hrtb_vars( self.tcx, - type_def_id, + container_def_id, constraint.ident, ty::AssocTag::Fn, ) { @@ -1898,10 +1956,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, ) @@ -2077,7 +2135,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // `for<'a, 'b, 'r> >::x::<'r, T>::{opaque#0}: Other<'b>`. // // We handle this similarly for associated-type-bound style return-type-notation - // in `visit_segment_args`. + // in `visit_path_segment_args`. fn try_append_return_type_notation_params( &mut self, hir_id: HirId, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c09e17d3787e1..5664842d349a2 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1989,6 +1989,9 @@ impl<'tcx> ContainerTy<'_, 'tcx> { match self { Self::Ref(region) => ObjectLifetimeDefault::Arg(region), Self::Regular { ty: container, args, arg: index } => { + // FIXME(fmease): Since #129543 assoc tys can now also induce trait object + // lifetime defaults. 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..7cedae4ef61f0 100644 --- a/tests/ui/deriving/issue-89188-gat-hrtb.rs +++ b/tests/ui/deriving/issue-89188-gat-hrtb.rs @@ -23,7 +23,7 @@ trait Trait<'s, 't, 'u> {} #[derive(Clone)] struct ShimMethod3( pub &'static dyn for<'s> Fn( - &'s mut T::Shim Fn(&'s mut T::Shim Trait<'s, 't, 'u>>)>, + &'s mut T::Shim Fn(&'s mut T::Shim Trait<'s, 't, 'u> + 's>) + 's>, ), ); 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..a7966f87ad379 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,16 @@ help: if this is a dyn-compatible trait, use `dyn` LL | type H = (u8)>::Output; | ++++ + -error[E0223]: ambiguous associated type +error[E0228]: cannot deduce the lifetime bound for this trait object type from context --> $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; +help: please supply an explicit bound | +LL | type H = Fn(u8) -> (u8) + /* 'a */::Output; + | ++++++++++ error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:44:19 @@ -310,5 +306,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..531e1846e04fb 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 cannot deduce the lifetime bound for this trait object type from context //[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..ac00bf6b74ff6 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs @@ -0,0 +1,12 @@ +//@ 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) { /*check*/ 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..59dbe5a4ddd58 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr @@ -0,0 +1,14 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-assoc-ty-self-ty-static.rs:9:18 + | +LL | fn f<'r>(x: &'r ::Ty) { /*check*/ g(x) } + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn f<'r>(x: &'r ::Ty) { /*check*/ 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..7eb9045491c90 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs @@ -0,0 +1,12 @@ +//@ 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) { /*check*/ 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..f8d2dbca5671c --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.stderr @@ -0,0 +1,14 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-assoc-ty-self-ty.rs:10:14 + | +LL | fn g<'r>(x: >::Ty) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +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-inherent-gac.rs b/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.rs new file mode 100644 index 0000000000000..6c6ba8002d45c --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.rs @@ -0,0 +1,43 @@ +// Exercise implicit trait object lifetime bounds inside +// type-level inherent (generic) associated const paths. +// See also . + +// FIXME: Ideally, this test would be check-pass. See below for details. + +#![feature(min_generic_const_args, inherent_associated_types, generic_const_items)] +#![expect(incomplete_features)] + +mod own { // the lifetime comes from the own generics + struct Parent; + impl Parent { + type const CT<'a, T: 'a + super::AbideBy<'a> + ?Sized>: usize = 0; + } + + // FIXME: Ideally, we would deduce `dyn Trait + 'r` from the bound `'a` on ty param `T` of + // type-level inherent assoc const `CT` but for that we'd need to somehow obtain the + // resolution of the type-relative path `Parent::CT` from HIR ty lowering in RBV. + fn check<'r>() where [(); Parent::CT::<'r, dyn super::Trait>]: {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +mod parent { // the lifetime comes from the parent generics + struct Parent<'a>(&'a ()); + impl<'a> Parent<'a> { + type const CT + ?Sized>: usize = 0; + } + + //FIXME: Ideally, we would deduce `dyn Trait + 'r` from the bound `'a` on ty param `T` of + // type-level inherent assoc const `CT` but for that we'd need to somehow obtain the + // resolution of the type-relative path `Parent::<'r>::CT` from HIR ty lowering in RBV. + fn check<'r>() where [(); Parent::<'r>::CT::]: {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +trait Trait {} + +// We use this to ensure that a given trait object lifetime bound is +// *exactly equal* to a given lifetime (not longer, not shorter). +trait AbideBy<'a> {} +impl<'a> AbideBy<'a> for dyn Trait + 'a {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.stderr b/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.stderr new file mode 100644 index 0000000000000..8323c2a94ce3a --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.stderr @@ -0,0 +1,25 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-inherent-gac.rs:19:48 + | +LL | fn check<'r>() where [(); Parent::CT::<'r, dyn super::Trait>]: {} + | ^^^^^^^^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn check<'r>() where [(); Parent::CT::<'r, dyn super::Trait + /* 'a */>]: {} + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-inherent-gac.rs:32:50 + | +LL | fn check<'r>() where [(); Parent::<'r>::CT::]: {} + | ^^^^^^^^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn check<'r>() where [(); Parent::<'r>::CT::]: {} + | ++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.rs b/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.rs new file mode 100644 index 0000000000000..4469dc84d3eb7 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.rs @@ -0,0 +1,43 @@ +// Exercise implicit trait object lifetime bounds inside +// inherent (generic) associated type paths. +// See also: + +// FIXME: Ideally, this test would be check-pass. See below for details. + +#![feature(inherent_associated_types)] +#![expect(incomplete_features)] + +mod own { // the lifetime comes from the own generics + struct Parent; + impl Parent { + type Ty<'a, T: 'a + super::AbideBy<'a> + ?Sized> = (); + } + + // FIXME: Ideally, we would deduce `dyn Trait + 'r` from the bound `'a` on ty param `T` of + // inherent assoc ty `Ty` but for that we'd need to somehow obtain the resolution of the + // type-relative path `Parent::Ty` from HIR ty lowering in RBV. + fn check<'r>() where Parent::Ty<'r, dyn super::Trait>: {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +mod parent { // the lifetime comes from the parent generics + struct Parent<'a>(&'a ()); + impl<'a> Parent<'a> { + type Ty + ?Sized> = (); + } + + // FIXME: Ideally, we would deduce `dyn Trait + 'r` from the bound `'a` on ty param `T` of + // inherent assoc ty `Ty` but for that we'd need to somehow obtain the resolution of the + // type-relative path `Parent<'r>::Ty` from HIR ty lowering in RBV. + fn check<'r>() where Parent<'r>::Ty: {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +trait Trait {} + +// We use this to ensure that a given trait object lifetime bound is +// *exactly equal* to a given lifetime (not longer, not shorter). +trait AbideBy<'a> {} +impl<'a> AbideBy<'a> for dyn Trait + 'a {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.stderr b/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.stderr new file mode 100644 index 0000000000000..c41eafe19abee --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.stderr @@ -0,0 +1,25 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-inherent-gat.rs:19:41 + | +LL | fn check<'r>() where Parent::Ty<'r, dyn super::Trait>: {} + | ^^^^^^^^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn check<'r>() where Parent::Ty<'r, dyn super::Trait + /* 'a */>: {} + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-inherent-gat.rs:32:41 + | +LL | fn check<'r>() where Parent<'r>::Ty: {} + | ^^^^^^^^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn check<'r>() where Parent<'r>::Ty: {} + | ++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-resolved-gat.rs b/tests/ui/object-lifetime/object-lifetime-default-resolved-gat.rs new file mode 100644 index 0000000000000..14e3aab9612b4 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-resolved-gat.rs @@ -0,0 +1,67 @@ +// Check that resolved GAT paths correctly induce trait object lifetime defaults. +// issue: + +//@ check-pass + +mod own { // the trait object lifetime default comes from the own generics + trait Outer { + type Ty<'a, T: 'a + super::AbideBy<'a> + ?Sized>; + } + impl Outer for () { + type Ty<'a, T: 'a + super::AbideBy<'a> + ?Sized> = (); + } + trait Inner {} + + // We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`. + fn check<'r>(_: <() as Outer>::Ty<'r, dyn super::Inner>) {} +} + +mod parent { // the trait object lifetime default comes from the parent generics + trait Outer<'a> { + type Ty + ?Sized>; + } + impl<'a> Outer<'a> for () { + type Ty + ?Sized> = (); + } + + // We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`. + fn check<'r>(_: <() as Outer<'r>>::Ty) {} +} + +#[rustfmt::skip] +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: 'a + super::AbideBy<'a> + ?Sized, + _Pad2, + U: 'b + super::AbideBy<'b> + ?Sized, + >; + } + impl<'a, _Pad0> Outer<'_, 'a, '_, _Pad0> for () { + type Ty< + '_pad2, 'b, '_pad3, _Pad1, + T: 'a + super::AbideBy<'a> + ?Sized, + _Pad2, + U: 'b + super::AbideBy<'b> + ?Sized, + > = (); + } + + // We elaborate the 1st `dyn Inner` to `dyn Inner + 'r` and the 2nd one to `dyn Inner + 's`. + fn g<'r, 's>( + _: <() as Outer<'static, 'r, 'static, ()>> + ::Ty<'static, 's, 'static, (), dyn super::Inner, (), dyn super::Inner>, + ) {} +} + +trait Inner {} + +// We use this to ensure that a given trait object lifetime bound is +// *exactly equal* to a given lifetime (not longer, not shorter). +trait AbideBy<'a> {} +impl<'a> AbideBy<'a> for dyn Inner + 'a {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-trait-alias-ref.rs b/tests/ui/object-lifetime/object-lifetime-default-trait-alias-ref.rs new file mode 100644 index 0000000000000..d6efa32791b72 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-trait-alias-ref.rs @@ -0,0 +1,27 @@ +// Check that trait alias refs correctly induce trait object lifetime defaults. +// issue: +#![feature(trait_alias)] + +//@ check-pass + +trait Outer<'a, T: 'a + ?Sized> = Carry; +trait Carry {} + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn bound<'r, T>() where T: Outer<'r, dyn Inner> {} +fn check_bound<'r, T>() where T: Outer<'r, dyn Inner + 'r> { bound::<'r, T>();} + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn dyn_trait<'r>(_: Box>) {} +fn check_dyn_trait<'r>(x: Box>) { dyn_trait(x) } + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn impl_trait<'r>(_: impl Outer<'r, dyn Inner>) {} +fn check_impl_trait<'r>(x: impl Outer<'r, dyn Inner + 'r>) { impl_trait(x) } + +// Reasonably, trait aliases can't be used as the qself in fully qualified paths, +// so we don't need to test them here. + +trait Inner {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-trait-ref-lifetime-bound-on-assoc-ty.rs b/tests/ui/object-lifetime/object-lifetime-default-trait-ref-lifetime-bound-on-assoc-ty.rs new file mode 100644 index 0000000000000..ede0dc8832f0b --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-trait-ref-lifetime-bound-on-assoc-ty.rs @@ -0,0 +1,27 @@ +// Check that "children (assoc items) can't influence the parent (trait) wrt. obj lt defs". +// More concretely, check that lifetime bounds on assoc item defs don't influence the trait object +// lifetime defaults that are induced by the relevant trait ref +// So "information doesn't flow backwards". + +//@ check-pass + +trait Trait { type Assoc<'a> where T: 'a; } +impl Trait for () { type Assoc<'a> = &'a T where T: 'a; } + +trait Bound {} + +// We deduce `dyn Bound + 'static`. +fn f0(x: impl Trait) { /*check*/ g0(x) } +fn g0(_: impl Trait) {} + +// We deduce `dyn Bound + 'static` (and not `dyn Bound + 'r`). +// We intentionally don't make use of the `T: 'a` / `dyn Trait: 'r` bound +// that has to hold for the associated type binding to be valid. +fn f1<'r>(x: impl Trait = ()>) { /*check*/ g1(x) } +fn g1(_: impl Trait) {} + +// We deduce `dyn Bound + 'static` (and not `dyn Bound + 'r`). +fn f2<'r>(x: <() as Trait>::Assoc<'r>) { /*check*/ g2(x) } +fn g2<'r>(_: <() as Trait>::Assoc<'r>) {} + +fn main() {} 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..1bed7369baa96 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-trait-ref.rs @@ -0,0 +1,34 @@ +// Check that trait refs correctly induce trait object lifetime defaults. +// +// For the longest time, all of the cases below used to get +// rejected as "inderminate" due to an off-by-one error. + +//@ check-pass + +trait Outer<'a, T: 'a + AbideBy<'a> + ?Sized> { type Ty where Self: Sized; } + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn bound<'r, T>() where T: Outer<'r, dyn Inner> {} + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn dyn_trait<'r>(_: Box>) {} +// Wfck doesn't require the type arguments in dyn trait to meet the bounds declared in the trait. +// Thus we can't rely on `AbideBy` here. Instead, just check if `'r` outlives the implicit bound. +fn check_dyn_trait<'r>(x: Box>) { dyn_trait(x) } + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn impl_trait<'r>(_: impl Outer<'r, dyn Inner>) {} + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn assoc_ty_proj<'r>(_: <() as Outer<'r, dyn Inner>>::Ty) {} + +impl<'a, T: 'a + AbideBy<'a> + ?Sized> Outer<'a, T> for () { type Ty = &'a T; } + +trait Inner {} + +// We use this to ensure that a given trait object lifetime bound is +// *exactly equal* to a given lifetime (not longer, not shorter). +trait AbideBy<'a> {} +impl<'a> AbideBy<'a> for dyn Inner + 'a {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.rs b/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.rs new file mode 100644 index 0000000000000..6e75610847d08 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.rs @@ -0,0 +1,32 @@ +// Exercise implicit trait object lifetime bounds inside type-relative assoc ty paths. + +mod own { // the lifetime comes from the own generics + trait Outer { type Ty<'a, T: 'a + ?Sized>; } + trait Inner {} + + fn f<'r, T: Outer>(x: T::Ty<'r, dyn Inner + 'r>) { /*check*/ g::(x) } + // 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 in RBV (it resolves to `::Ty`). + fn g<'r, T: Outer>(x: T::Ty<'r, dyn Inner>) {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +mod parent { // the lifetime comes from the parent generics + trait Outer<'a> { type Ty; } + trait Inner {} + + fn f<'r, T: Outer<'r>>(x: T::Ty) { /*check*/ g::(x) } + // FIXME: Ideally, we would deduce `dyn Inner + 'r` from the 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 in RBV (it resolved to `>::Ty`). + fn g<'r, T: Outer<'r>>(_: T::Ty) {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context + + // Assuming RBV was able to resolve type-relative paths, it should not crash on "overly lifetime + // polymorphic" paths (`for<'r> >::Ty` isn't a valid Rust type). + fn escaping Outer<'r>>(_: T::Ty) {} + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters +} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.stderr b/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.stderr new file mode 100644 index 0000000000000..01f74e056f206 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.stderr @@ -0,0 +1,38 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-type-relative-gat.rs:11:37 + | +LL | fn g<'r, T: Outer>(x: T::Ty<'r, dyn Inner>) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn g<'r, T: Outer>(x: T::Ty<'r, dyn Inner + /* 'a */>) {} + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-type-relative-gat.rs:23:37 + | +LL | fn g<'r, T: Outer<'r>>(_: T::Ty) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn g<'r, T: Outer<'r>>(_: T::Ty) {} + | ++++++++++ + +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters + --> $DIR/object-lifetime-default-type-relative-gat.rs:28:42 + | +LL | fn escaping Outer<'r>>(_: T::Ty) {} + | ^^^^^^^^^^^^^^^^ + | +help: use a fully qualified path with inferred lifetimes + | +LL - fn escaping Outer<'r>>(_: T::Ty) {} +LL + fn escaping Outer<'r>>(_: >::Ty) {} + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0212, E0228. +For more information about an error, try `rustc --explain E0212`. From ff1afc44e5e2d5a92bd23ea772d68710e9d2deb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 3 Feb 2026 00:57:08 +0100 Subject: [PATCH 2/7] Split out fn `compute_object_lifetime_defaults` --- .../src/collect/resolve_bound_vars.rs | 200 ++++++++++-------- 1 file changed, 106 insertions(+), 94 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 8aa44c4b8cf66..8b9a27da2554a 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1746,100 +1746,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { debug!(?container); - // Compute a list of trait 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 elaborate - // * `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 list like `['x, 'static]`. - // Note that the list only includes the lifetimes for 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.has_own_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) { - // Const parameters 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_object_lifetime_defaults(def_id, segs) }); debug!(?object_lifetime_defaults); @@ -1977,6 +1885,110 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } } + /// Compute a list of trait object lifetime defaults, one for each type parameter, + /// per the rules initially given in RFCs [599] and [1156]. Example: + /// + /// ``` + /// struct Foo<'a, T: 'a + ?Sized, U: ?Sized>(&'a T, &'a U); + /// ``` + /// + /// If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to elaborate + /// * `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 list like `['x, 'static]`. + /// Note that the list only includes the lifetimes for type parameters. + /// + /// [599]: https://rust-lang.github.io/rfcs/0599-default-object-bound.html + /// [1156]: https://rust-lang.github.io/rfcs/1156-adjust-default-object-bounds.html + fn compute_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 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.has_own_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) { + // Const parameters 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 the `Self` type parameter + // reuses the `DefId` of the trait (alias). + // 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() + } + /// 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 From b98151d33c5832d134391f65511eb2c3503639ea 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 3/7] 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 8b9a27da2554a..b57be3ea26e51 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1082,8 +1082,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` @@ -1117,7 +1115,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 961aa56657235bb793a2e5ca14db77622af290b8 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 4/7] Make resolved assoc ty paths induce a trait object lifetime default for their self ty --- .../src/collect/resolve_bound_vars.rs | 167 +++++++++++------- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- ...ifetime-default-assoc-ty-self-ty-static.rs | 11 +- ...ime-default-assoc-ty-self-ty-static.stderr | 14 -- ...bject-lifetime-default-assoc-ty-self-ty.rs | 13 +- ...t-lifetime-default-assoc-ty-self-ty.stderr | 14 -- .../object-lifetime-default-inferred-args.rs | 29 +++ .../object-lifetime-default-inferred.rs | 33 ---- 8 files changed, 148 insertions(+), 135 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 create mode 100644 tests/ui/object-lifetime/object-lifetime-default-inferred-args.rs delete mode 100644 tests/ui/object-lifetime/object-lifetime-default-inferred.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 b57be3ea26e51..062b64a2c9fe3 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -900,13 +900,31 @@ 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 trait 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(fmease): Make assocated type paths induce a trait object lifetime - // default for the self type, too! - let scope = Scope::ObjectLifetimeDefault { lifetime: None, s: self.scope }; - self.with(scope, |this| this.visit_ty_unambig(qself)); + // Read more about containers in fn `visit_path_segment_args`. + 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)| { + let generics = self.tcx.generics_of(def_id); + self.compute_object_lifetime_defaults(generics, 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) => { // Computing the trait object lifetime defaults that are induced by type-relative @@ -1074,52 +1092,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, } } @@ -1744,13 +1767,17 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { debug!(?container); - let object_lifetime_defaults = container.map_or_else(Vec::new, |(def_id, segs)| { - self.compute_object_lifetime_defaults(def_id, segs) - }); + let (has_self, object_lifetime_defaults) = container + .map(|(def_id, segs)| { + let generics = self.tcx.generics_of(def_id); + let defaults = self.compute_object_lifetime_defaults(generics, segs); + (generics.has_own_self(), defaults) + }) + .unwrap_or_default(); debug!(?object_lifetime_defaults); - let mut i = 0; + 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. @@ -1894,14 +1921,14 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { /// * `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 list like `['x, 'static]`. - /// Note that the list only includes the lifetimes for type parameters. + /// Therefore, we would compute a list like `['x, 'static]`. Note that the list only + /// includes entries for type and const parameters, not for lifetime parameters. /// /// [599]: https://rust-lang.github.io/rfcs/0599-default-object-bound.html /// [1156]: https://rust-lang.github.io/rfcs/1156-adjust-default-object-bounds.html fn compute_object_lifetime_defaults( &self, - def_id: DefId, + generics: &ty::Generics, segments: &[hir::PathSegment<'_>], ) -> Vec> { let in_body = { @@ -1924,9 +1951,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } }; - 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 { @@ -1959,7 +1983,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { .args .and_then(|args| args.args.get(index)) .and_then(|arg| match arg { - GenericArg::Lifetime(lt) => rbv.defs.get(<.hir_id.local_id).copied(), + GenericArg::Lifetime(lt) => self.rbv.defs.get(<.hir_id.local_id).copied(), _ => None, }) } @@ -1969,18 +1993,25 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { .own_params .iter() .filter_map(|param| { + // NB: `Self` type params share the `DefId` with the corresponding trait (alias). + // + // Since trait aliases can't be used as the qself of fully qualified paths, the + // trait object lifetime default for their `Self` type param is never needed. + // Thus, we don't even try to compute it. + // + // We still need to map const params & trait aliases to *some* default to make it + // easy & predictable for the caller how to map the defaults back to generic args. + // As they can't tell if a given inferred arg refers to a type or a const at this + // stage of analysis, they can't skip it and thus we need to provide (dummy) + // defaults for const args. Otherwise, they wouldn't properly align. + match self.tcx.def_kind(param.def_id) { - // Const parameters 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 the `Self` type parameter - // reuses the `DefId` of the trait (alias). - // Ignore it because it can't have a meaningful lifetime default. - DefKind::LifetimeParam | DefKind::Trait | DefKind::TraitAlias => None, - dk => bug!("unexpected def_kind {:?}", dk), + DefKind::TyParam | DefKind::Trait => { + Some(self.tcx.object_lifetime_default(param.def_id)) + } + DefKind::ConstParam | DefKind::TraitAlias => Some(ObjectLifetimeDefault::Empty), + DefKind::LifetimeParam => None, + kind => bug!("unexpected def kind {kind:?}"), } }) .map(set_to_region) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 4988cafdd3637..ecc84267cb843 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1527,7 +1527,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 ac00bf6b74ff6..67d5b8b56d811 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,11 +1,18 @@ -//@ known-bug: unknown +// Check that resolved associated type paths induce the correct +// trait object lifetime default for the self type. + +//@ 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 *containers* and +// thus we'd use the *trait object lifetime default* induced by the reference type ctor `&`, +// namely `'r`. Now however, the assoc ty overrides that default to be `'static`. fn f<'r>(x: &'r ::Ty) { /*check*/ 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 59dbe5a4ddd58..0000000000000 --- a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0228]: cannot deduce the lifetime bound for this trait object type from context - --> $DIR/object-lifetime-default-assoc-ty-self-ty-static.rs:9:18 - | -LL | fn f<'r>(x: &'r ::Ty) { /*check*/ g(x) } - | ^^^^^^^^^ - | -help: please supply an explicit bound - | -LL | fn f<'r>(x: &'r ::Ty) { /*check*/ 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 7eb9045491c90..bdf05338894d1 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,12 +1,19 @@ -//@ known-bug: unknown +// Check that resolved associated type paths induce the correct +// trait object lifetime default for the self type. -trait Outer<'a>: 'a { type Ty; } +//@ check-pass +//@ revisions: bound clause + +// RBV works on the HIR where where-clauses and item bounds of traits aren't merged yet. +// It's therefore wise to check both forms and make sure both are treated the same by RBV. +#[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) { /*check*/ 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 f8d2dbca5671c..0000000000000 --- a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0228]: cannot deduce the lifetime bound for this trait object type from context - --> $DIR/object-lifetime-default-assoc-ty-self-ty.rs:10:14 - | -LL | fn g<'r>(x: >::Ty) {} - | ^^^^^^^^^ - | -help: please supply an explicit bound - | -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-inferred-args.rs b/tests/ui/object-lifetime/object-lifetime-default-inferred-args.rs new file mode 100644 index 0000000000000..a191ac32a9e35 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-inferred-args.rs @@ -0,0 +1,29 @@ +// Ensure that trait object lifetime defaulting properly deals with inferred args `_`. +// RBV which computes the defaults can't tell whether `_` refers to a type or const. +// Therefore it needs to map const params to a dummy trait object lifetime default +// in order to properly align params and args. +//@ check-pass + +struct Ty0<'a, Bait: 'static, T: 'a + ?Sized>(Bait, &'a T); + +fn check0<'r>(x: Ty0<'r, (), dyn Inner>) { + // The algorithm can't just avoid mapping const params to defaults and skip const & inferred + // args when mapping defaults to type args since the inferred arg may be a type. + // If it did, it would wrongly obtain the default provided by param `Bait` and elaborate + // `dyn Trait` to `dyn Trait + 'static` (which would fail since `'r` doesn't outlive `'static`). + let _: Ty0<'r, _, dyn Inner> = x; +} + +struct Ty1<'a, const BAIT: usize, T: 'a + ?Sized, Bait: 'static>(&'a T, Bait); + +fn check1<'r>(x: Ty1<'r, 0, dyn Inner, ()>) { + // The algorithm can't just avoid mapping const params to defaults, skip const args and count + // inferred args as types when mapping defaults to type args since the inferred arg may be a + // const. If it did, it would wrongly obtain the default provided by param `Bait` and elaborate + // `dyn Trait` to `dyn Trait + 'static` (which would fail since `'r` doesn't outlive `'static`). + let _: Ty1<'r, _, dyn Inner, _> = x; +} + +trait Inner {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-inferred.rs b/tests/ui/object-lifetime/object-lifetime-default-inferred.rs deleted file mode 100644 index d3162231faf77..0000000000000 --- a/tests/ui/object-lifetime/object-lifetime-default-inferred.rs +++ /dev/null @@ -1,33 +0,0 @@ -//@ run-pass -// Test that even with prior inferred parameters, object lifetimes of objects after are still -// valid. - - -#![allow(dead_code, unused)] - -trait Test { - fn foo(&self) { } -} - -struct Foo; -impl Test for Foo {} - -struct SomeStruct<'a> { - t: &'a dyn Test, - u: &'a (dyn Test+'a), -} - -fn a<'a, const N: usize>(_: [u8; N], t: &'a (dyn Test+'a), mut ss: SomeStruct<'a>) { - ss.t = t; -} - -fn b<'a, T>(_: T, t: &'a (dyn Test+'a), mut ss: SomeStruct<'a>) { - ss.u = t; -} - -fn main() { - // Inside a function body, we can just infer both - // lifetimes, to allow &'tmp (Display+'static). - a::<_>([], &Foo as &dyn Test, SomeStruct{t:&Foo,u:&Foo}); - b::<_>(0u8, &Foo as &dyn Test, SomeStruct{t:&Foo,u:&Foo}); -} From 8cd98715eb10419fa76a8f71f462e0d41f495e89 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 5/7] 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 0d19dc25d402c..cbcb8a1e34548 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -578,7 +578,14 @@ pub(crate) struct RustcObjectLifetimeDefaultParser; impl NoArgsAttributeParser for RustcObjectLifetimeDefaultParser { const PATH: &[Symbol] = &[sym::rustc_object_lifetime_default]; 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 CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcObjectLifetimeDefault; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2287b8bf483f6..651715b68f63a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -758,20 +758,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 }); } } 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 From b1a12c39a6f07c610881291c0dc0a9c56428df67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 25 Jan 2026 20:23:22 +0100 Subject: [PATCH 6/7] Rename `hir::GenericArgs::{num,has}_generic_params` to `*_generic_args` --- compiler/rustc_hir/src/hir.rs | 4 ++-- .../src/errors/wrong_number_of_generic_args.rs | 2 +- .../src/hir_ty_lowering/generics.rs | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 68fc7653b6368..ccce849e81a65 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -707,12 +707,12 @@ impl<'hir> GenericArgs<'hir> { } #[inline] - pub fn num_lifetime_params(&self) -> usize { + pub fn num_lifetime_args(&self) -> usize { self.args.iter().filter(|arg| matches!(arg, GenericArg::Lifetime(_))).count() } #[inline] - pub fn has_lifetime_params(&self) -> bool { + pub fn has_lifetime_args(&self) -> bool { self.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) } diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 2b7854769b426..ccad1649f2c9d 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -180,7 +180,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { AngleBrackets::Missing => 0, // Only lifetime arguments can be implied AngleBrackets::Implied => self.gen_args.args.len(), - AngleBrackets::Available => self.gen_args.num_lifetime_params(), + AngleBrackets::Available => self.gen_args.num_lifetime_args(), } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index ff0a5a8df0faf..05605b010efdc 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -421,7 +421,7 @@ pub(crate) fn check_generic_arg_count( let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count; let named_const_param_count = param_counts.consts; let infer_lifetimes = - (gen_pos != GenericArgPosition::Type || seg.infer_args) && !gen_args.has_lifetime_params(); + (gen_pos != GenericArgPosition::Type || seg.infer_args) && !gen_args.has_lifetime_args(); if gen_pos != GenericArgPosition::Type && let Some(c) = gen_args.constraints.first() @@ -471,7 +471,7 @@ pub(crate) fn check_generic_arg_count( let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; let max_expected_lifetime_args = param_counts.lifetimes; - let num_provided_lifetime_args = gen_args.num_lifetime_params(); + let num_provided_lifetime_args = gen_args.num_lifetime_args(); let lifetimes_correct = check_lifetime_args( min_expected_lifetime_args, @@ -595,7 +595,7 @@ pub(crate) fn check_generic_arg_count( - default_counts.consts }; debug!(?expected_min); - debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); + debug!(arg_counts.lifetimes=?gen_args.num_lifetime_args()); let provided = gen_args.num_generic_params(); @@ -605,7 +605,7 @@ pub(crate) fn check_generic_arg_count( named_const_param_count + named_type_param_count + synth_type_param_count, provided, param_counts.lifetimes + has_self as usize, - gen_args.num_lifetime_params(), + gen_args.num_lifetime_args(), ) }; @@ -628,7 +628,7 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( let param_counts = def.own_counts(); if let Some(span_late) = def.has_late_bound_regions - && args.has_lifetime_params() + && args.has_lifetime_args() { let msg = "cannot specify lifetime arguments explicitly \ if late bound lifetime parameters are present"; @@ -636,7 +636,7 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( let span = args.args[0].span(); if position == GenericArgPosition::Value - && args.num_lifetime_params() != param_counts.lifetimes + && args.num_lifetime_args() != param_counts.lifetimes { struct_span_code_err!(cx.dcx(), span, E0794, "{}", msg) .with_span_note(span_late, note) From 2fec74f55d4ee73bc456f2d723166032acf8008c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 25 Jan 2026 22:45:07 +0100 Subject: [PATCH 7/7] Render trait object lifetime defaults indeterminate in lifetime-instantiated assoc ty bindings Namely, on the RHS and in the args of the bindings. --- .../src/collect/resolve_bound_vars.rs | 63 ++++++++++--------- ...assoc-ty-binding-item-bounds-non-static.rs | 33 ++++++++++ ...c-ty-binding-item-bounds-non-static.stderr | 14 +++++ ...me-default-assoc-ty-binding-item-bounds.rs | 37 +++++++++++ ...efault-assoc-ty-binding-item-bounds.stderr | 25 ++++++++ ...ifetime-default-gat-binding-item-bounds.rs | 26 ++++++++ ...ime-default-gat-binding-item-bounds.stderr | 25 ++++++++ .../object-lifetime-default-gat-binding.rs | 34 ++++++++++ ...object-lifetime-default-gat-binding.stderr | 25 ++++++++ 9 files changed, 253 insertions(+), 29 deletions(-) create mode 100644 tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.stderr create mode 100644 tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.stderr create mode 100644 tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.stderr create mode 100644 tests/ui/object-lifetime/object-lifetime-default-gat-binding.rs create mode 100644 tests/ui/object-lifetime/object-lifetime-default-gat-binding.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 062b64a2c9fe3..514b96a5b9395 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1802,38 +1802,43 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } } - // Hack: When resolving the type `XX` in an assoc ty binding like - // `dyn Foo<'b, Item = XX>`, the current object-lifetime default - // would be to examine the trait `Foo` to check whether it has - // a lifetime bound declared on `Item`. e.g., if `Foo` is - // declared like so, then the default object lifetime bound in - // `XX` should be `'b`: - // - // ```rust - // 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. - // - // Instead, we do something hacky: if there are no lifetime parameters - // to the trait, then we simply use a default object lifetime - // bound of `'static`, because there is no other possibility. On the other hand, - // if there ARE lifetime parameters, then we require the user to give an - // explicit bound for now. - // - // This is intended to leave room for us to implement the - // correct behavior in the future. - let has_lifetime_parameter = - generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))); + let has_lifetime_args = generic_args.has_lifetime_args(); - // Resolve lifetimes found in the bindings, so either in the type `XX` in `Item = XX` or - // in the trait ref `YY<...>` in `Item: YY<...>`. for constraint in generic_args.constraints { let scope = Scope::ObjectLifetimeDefault { - lifetime: if has_lifetime_parameter { + // FIXME: Ideally we would consider the *item bounds* of assoc types when deducing + // the trait object lifetime default for the RHS of assoc type bindings. + // For example, given + // + // trait TraitA<'a> { type AssocTy: ?Sized + 'a; } + // trait TraitB { type AssocTy<'a>: ?Sized + 'a; } + // + // we would elaborate the `dyn Bound` in `TraitA<'r, AssocTy = dyn Bound>` + // and `TraitB = dyn Bound>` to `dyn Bound + 'r`. + // + // FIXME: Moreover, ideally GAT args in bindings could induce + // trait object lifetime defaults. For example, given + // + // trait TraitA<'a> { type AssocTy; } + // trait TraitB { type AssocTy<'a, T: ?Sized + 'a>; } + // + // we would elab the `dyn Bound` in `TraitA<'r, AssocTy = ()>` + // and `TraitB = ()>` to `dyn Bound + 'r`. + // + // HACK: For now however, if the user passes any lifetime arguments to the trait or + // the (generic) assoc type, we will treat the trait object lifetime default + // as indeterminate thus forcing the user to explicitly specify the lifetime. + // + // If the trait or the assoc type have generic parameters, it's *possible* + // that they occur in the predicates or item bounds of the assoc type, so we + // conservatively reject such cases to allow us to implement the correct + // behavior in the future (here we assume that the number of arguments equals + // the number of parameters which is fine since a mismatch would get rejected + // later anyway). + // + // If the items don't have any lifetime parameters we can safely use `'static` + // since there is no other possibility. + lifetime: if has_lifetime_args || constraint.gen_args.has_lifetime_args() { None } else { Some(ResolvedArg::StaticLifetime) diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.rs b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.rs new file mode 100644 index 0000000000000..a7ab2d3dca4e9 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.rs @@ -0,0 +1,33 @@ +// Ideally, given an assoc type binding `dyn Trait`, we'd factor in the item bounds of +// assoc type `AssocTy` when computing the trait object lifetime default for type `Ty`. +// +// However, since the current implementation can't handle this we instead conservatively and hackily +// treat the trait object lifetime default of the RHS as indeterminate if any lifetime arguments are +// passed to the trait ref (or the GAT) thus rejecting any implicit trait object lifetime bounds. +// This way, we can still implement the desired behavior in the future. + +trait Foo<'a> { + type Item: 'a + ?Sized; + + fn item(&self) -> Box { panic!() } +} + +trait Bar {} + +impl Foo<'_> for T { + type Item = dyn Bar; +} + +fn is_static(_: T) where T: 'static {} + +// FIXME: Ideally, we'd elaborate `dyn Bar` to `dyn Bar + 'a` instead of rejecting it. +fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } +//~^ ERROR cannot deduce the lifetime bound for this trait object type from context + +fn main() { + let s = format!("foo"); + let r = bar(&s); + + // If it weren't for the conservative path above, we'd expect an error here. + is_static(r.item()); +} diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.stderr b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.stderr new file mode 100644 index 0000000000000..2e9c415548b92 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.stderr @@ -0,0 +1,14 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.rs:24:50 + | +LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } + | ^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar + /* 'a */> { &() } + | ++++++++++ + +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-binding-item-bounds.rs b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.rs new file mode 100644 index 0000000000000..0899ab08dd9dc --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.rs @@ -0,0 +1,37 @@ +// Ideally, given an assoc type binding `dyn Trait`, we'd factor in the item bounds of +// assoc type `AssocTy` when computing the trait object lifetime default for type `Ty`. +// +// However, since the current implementation can't handle this we instead conservatively and hackily +// treat the trait object lifetime default of the RHS as indeterminate if any lifetime arguments are +// passed to the trait ref (or the GAT) thus rejecting any implicit trait object lifetime bounds. +// This way, we can still implement the desired behavior in the future. + +trait Foo<'a> { + type Item: ?Sized; + + fn item(&self) -> Box { panic!() } +} + +trait Bar {} + +impl Foo<'_> for T { + type Item = dyn Bar; +} + +fn is_static(_: T) where T: 'static {} + +// FIXME: Ideally, we'd elaborate `dyn Bar` to `dyn Bar + 'static` instead of rejecting it. +fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } +//~^ ERROR cannot deduce the lifetime bound for this trait object type from context + +// FIXME: Ideally, we'd elaborate `dyn Bar` to `dyn Bar + 'static` instead of rejecting it. +fn baz(x: &str) -> &dyn Foo { &() } +//~^ ERROR cannot deduce the lifetime bound for this trait object type from context + +fn main() { + let s = format!("foo"); + let r = bar(&s); + is_static(r.item()); + let r = baz(&s); + is_static(r.item()); +} diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.stderr b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.stderr new file mode 100644 index 0000000000000..f47326297e58f --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.stderr @@ -0,0 +1,25 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-assoc-ty-binding-item-bounds.rs:24:50 + | +LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } + | ^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar + /* 'a */> { &() } + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-assoc-ty-binding-item-bounds.rs:28:36 + | +LL | fn baz(x: &str) -> &dyn Foo { &() } + | ^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn baz(x: &str) -> &dyn Foo { &() } + | ++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.rs b/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.rs new file mode 100644 index 0000000000000..723d23f86d485 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.rs @@ -0,0 +1,26 @@ +// Ideally, given an assoc type binding `dyn Trait`, we'd factor in the item bounds of +// assoc type `AssocTy` when computing the trait object lifetime default for type `Ty`. +// +// However, since the current implementation can't handle this we instead conservatively and hackily +// treat the trait object lifetime default of the RHS as indeterminate if any lifetime arguments are +// passed to (the trait ref or) the GAT thus rejecting any implicit trait object lifetime bounds. +// This way, we can still implement the desired behavior in the future. + +trait Outer { + type A<'r>: ?Sized; + type B<'r>: ?Sized + 'r; +} + +trait Inner {} + +// FIXME: Ideally, we'd elaborate `dyn Inner` to `dyn Inner + 'static` here instead of rejecting it. +fn f0<'r>(x: impl Outer = dyn Inner>) { /*check*/ g0(x) } +//~^ ERROR cannot deduce the lifetime bound for this trait object type from context +fn g0<'r>(_: impl Outer = dyn Inner + 'static>) {} + +fn f1<'r>(x: impl Outer = dyn Inner + 'r>) { /*check*/ g1(x) } +// FIXME: Ideally, we'd elaborate `dyn Inner` to `dyn Inner + 'r` here instead of rejecting it. +fn g1<'r>(_: impl Outer = dyn Inner>) {} +//~^ ERROR cannot deduce the lifetime bound for this trait object type from context + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.stderr b/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.stderr new file mode 100644 index 0000000000000..2ef05321862a9 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.stderr @@ -0,0 +1,25 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-gat-binding-item-bounds.rs:17:33 + | +LL | fn f0<'r>(x: impl Outer = dyn Inner>) { /*check*/ g0(x) } + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn f0<'r>(x: impl Outer = dyn Inner + /* 'a */>) { /*check*/ g0(x) } + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-gat-binding-item-bounds.rs:23:33 + | +LL | fn g1<'r>(_: impl Outer = dyn Inner>) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn g1<'r>(_: impl Outer = dyn Inner + /* 'a */>) {} + | ++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-binding.rs b/tests/ui/object-lifetime/object-lifetime-default-gat-binding.rs new file mode 100644 index 0000000000000..ecc4bdc17fd82 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-binding.rs @@ -0,0 +1,34 @@ +// Ideally, GAT args in bindings would induce trait object lifetime defaults. +// +// However, since the current implementation can't handle this we instead conservatively and hackily +// treat the trait object lifetime default as indeterminate if any lifetime arguments are passed +// to the trait ref (or the GAT) thus rejecting any implicit trait object lifetime bounds. +// This way, we can still implement the desired behavior in the future. + +mod own { // the object lifetime default comes from the own generics + trait Outer { + type Ty<'a, T: ?Sized + 'a>; + } + + trait Inner {} + + fn f<'r>(x: impl Outer = ()>) { /*check*/ g(x) } + // FIXME: Ideally, we'd elaborate `dyn Inner` to `dyn Inner + 'r` instead of rejecting it. + fn g<'r>(_: impl Outer = ()>) {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +mod parent { // the object lifetime default comes from the parent generics + trait Outer<'a> { + type Ty; + } + + trait Inner {} + + fn f<'r>(x: impl Outer<'r, Ty = ()>) { /*check*/ g(x) } + // FIXME: Ideally, we'd elaborate `dyn Inner` to `dyn Inner + 'r` instead of rejecting it. + fn g<'r>(_: impl Outer<'r, Ty = ()>) {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-binding.stderr b/tests/ui/object-lifetime/object-lifetime-default-gat-binding.stderr new file mode 100644 index 0000000000000..83809d9abc9fd --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-binding.stderr @@ -0,0 +1,25 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-gat-binding.rs:17:35 + | +LL | fn g<'r>(_: impl Outer = ()>) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn g<'r>(_: impl Outer = ()>) {} + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-gat-binding.rs:30:35 + | +LL | fn g<'r>(_: impl Outer<'r, Ty = ()>) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn g<'r>(_: impl Outer<'r, Ty = ()>) {} + | ++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0228`.