Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 99962d8

Browse files
committed
Auto merge of rust-lang#129543 - fmease:obj-lt-def-gat, r=<try>
Properly deduce object lifetime defaults in projections & trait refs #### Object Lifetime Defaults (Primer, Refresher & Definitions) You can read [this section](https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes) in The Reference but it's not perfect IMO. Here's a small explainer by me that only mentions the parts relevant to this PR: Basically, given `dyn Trait` (≠ `dyn Trait + '_`) we want to deduce its *object lifetime bound* from context (without relying on `rustc_infer`'s region inference as we might not be in a body[^1]). The "context" means the closest — what I call — *eligible generic container* `C<X0, …, Xn>` that wraps this trait object type. *(Eligible generic) container* is almost synonymous with type constructor but it also includes type aliases, traits & enum variants. So if we have `C<…, dyn Trait, …>` (e.g., `&'r dyn Trait` or `Struct<'r, dyn Trait>`) or `C<…, N<…, dyn Trait, …>, …>` (e.g., `&'r (dyn Trait,)` or `Struct<'r, (dyn Trait,)>`) where `N` denotes a generic type that is **not** an eligible generic container, we use the explicit[^2] outlives-bounds on the corresp. type param of `C` to determine the object lifetime bound (the details[^3] aren't relevant here) (e.g., given `struct Struct<'a, T: 'a + ?Sized>(…);`, we elaborate `Struct<'r, dyn Trait>` to `Struct<'r, dyn Trait + 'r>`). Lastly, I call object lifetime bounds used as the default for *constituent* trait object types of an eligible generic container `C` the *ambient object lifetime defaults* for / induced by `C` (these ambient defaults may be shadowed by inner containers). --- #### Changes Made by This PR 1. Make associated type paths / projections *eligible generic containers*. * `<Y0 as TraitRef<X0, …, Xn>>::AssocTy<Y1, …, Ym>` now induces *ambient object lifetime defaults* for constituents Y0 to Ym (`TraitRef` is considered a separate container, see also list item **(2)**). * Similar case with type-relative ("shorthand") paths `Y0::AssocTy<Y1, …, Ym>` * Notably, for the self type Y0 of resolved projections we now look at the bounds on the `Self` type param of the relevant trait (e.g., given `trait Outer<'a>: 'a { type Proj; }` or `trait Outer<'a> where Self: 'a { type Proj; }` we elaborate `<dyn Inner as Outer<'r>>::Proj` to `<dyn Inner + 'r as Outer<'r>>::Proj`). 2. Fixes object lifetime defaults inside trait refs `TraitRef<X0, …, X1>` (this fell out from the previous changes). There used be completely broken due to a gnarly off-by-one error for not accounting for the implicit `Self` type param of traits which leads to cases like * `Outer<'r, dyn Inner>` (with `trait Outer<'a, T: 'a + ?Sized> {}`) getting rejected as "inderminate" (it tries to access a *lifetime* at index 1 instead 0) ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=0069c89b2313f0f447ff8b6f7de9adfa)) * `Outer<'r, 's, dyn Inner>` (with `trait Outer<'a, 'b: T: 'a + ?Sized> {}`) elaborating `dyn Inner` to `dyn Inner + 's` instead of `dyn Inner + 'r`(!) ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=9c521165e0ac0d868a8087cd7ca861fe)) **These changes are theoretically breaking** because in certain cases they lead to different object lifetime bounds getting deduced compared to master which is obviously user observable. However, [the latest crater run](rust-lang#129543 (comment)) found 0 non-spurious regressions. **Motivation**: Both object lifetime default RFCs never explicitly specify what constitutes an — what I call — *eligible generic container* but it only makes sense to include any type constructor or (generic) type alias that can bear outlives-bounds … like associated types. So it's only *consistent* to make this change. Fixes rust-lang#115379. r? ghost [^1]: If we *are* in a body, we do however use to normal region inference as a fallback. [^2]: Indeed, we don't consider implied bounds (inferred outlives-bounds). [^3]: Like how we deal with 'ambiguities' or how we look at the bounds of inner TOT as a fallback.
2 parents 3ef8e64 + ec131fe commit 99962d8

13 files changed

+488
-251
lines changed

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

Lines changed: 340 additions & 218 deletions
Large diffs are not rendered by default.

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1481,7 +1481,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
14811481
if let Some(name) = tcx.intrinsic(def_id) {
14821482
record!(self.tables.intrinsic[def_id] <- name);
14831483
}
1484-
if let DefKind::TyParam = def_kind {
1484+
if let DefKind::TyParam | DefKind::Trait = def_kind {
14851485
let default = self.tcx.object_lifetime_default(def_id);
14861486
record!(self.tables.object_lifetime_default[def_id] <- default);
14871487
}

compiler/rustc_passes/src/check_attr.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -714,20 +714,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
714714
/// Debugging aid for `object_lifetime_default` query.
715715
fn check_object_lifetime_default(&self, hir_id: HirId) {
716716
let tcx = self.tcx;
717-
if let Some(owner_id) = hir_id.as_owner()
718-
&& let Some(generics) = tcx.hir_get_generics(owner_id.def_id)
719-
{
720-
for p in generics.params {
721-
let hir::GenericParamKind::Type { .. } = p.kind else { continue };
722-
let default = tcx.object_lifetime_default(p.def_id);
723-
let repr = match default {
724-
ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),
725-
ObjectLifetimeDefault::Static => "'static".to_owned(),
726-
ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
727-
ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
728-
};
729-
tcx.dcx().emit_err(errors::ObjectLifetimeErr { span: p.span, repr });
730-
}
717+
let Some(owner_id) = hir_id.as_owner() else { return };
718+
for param in &tcx.generics_of(owner_id.def_id).own_params {
719+
let ty::GenericParamDefKind::Type { .. } = param.kind else { continue };
720+
let default = tcx.object_lifetime_default(param.def_id);
721+
let repr = match default {
722+
ObjectLifetimeDefault::Empty => "Empty".to_owned(),
723+
ObjectLifetimeDefault::Static => "'static".to_owned(),
724+
ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
725+
ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
726+
};
727+
tcx.dcx()
728+
.emit_err(errors::ObjectLifetimeErr { span: tcx.def_span(param.def_id), repr });
731729
}
732730
}
733731

src/librustdoc/clean/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,9 @@ impl<'tcx> ContainerTy<'_, 'tcx> {
19941994
match self {
19951995
Self::Ref(region) => ObjectLifetimeDefault::Arg(region),
19961996
Self::Regular { ty: container, args, arg: index } => {
1997+
// FIXME(fmease): rustc now also computes ambient object lifetime defaults for
1998+
// `AssocTy`s. Re-elide these, too!
1999+
19972000
let (DefKind::Struct
19982001
| DefKind::Union
19992002
| DefKind::Enum

tests/ui/did_you_mean/bad-assoc-ty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type G = dyn 'static + (Send)::AssocTy;
3131
// This is actually a legal path with fn-like generic arguments in the middle!
3232
// Recovery should not apply in this context.
3333
type H = Fn(u8) -> (u8)::Output;
34-
//~^ ERROR ambiguous associated type
34+
//~^ ERROR the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
3535
//~| WARN trait objects without an explicit `dyn` are deprecated
3636
//~| WARN this is accepted in the current edition
3737

tests/ui/did_you_mean/bad-assoc-ty.stderr

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -193,20 +193,11 @@ help: if this is a dyn-compatible trait, use `dyn`
193193
LL | type H = <dyn Fn(u8) -> (u8)>::Output;
194194
| ++++ +
195195

196-
error[E0223]: ambiguous associated type
196+
error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
197197
--> $DIR/bad-assoc-ty.rs:33:10
198198
|
199199
LL | type H = Fn(u8) -> (u8)::Output;
200-
| ^^^^^^^^^^^^^^^^^^^^^^
201-
|
202-
help: use fully-qualified syntax
203-
|
204-
LL - type H = Fn(u8) -> (u8)::Output;
205-
LL + type H = <(dyn Fn(u8) -> u8 + 'static) as BitOr>::Output;
206-
|
207-
LL - type H = Fn(u8) -> (u8)::Output;
208-
LL + type H = <(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output;
209-
|
200+
| ^^^^^^^^^^^^^^
210201

211202
error[E0223]: ambiguous associated type
212203
--> $DIR/bad-assoc-ty.rs:39:19
@@ -354,5 +345,5 @@ LL + trait P<F, T> where F: Fn() -> T {
354345

355346
error: aborting due to 29 previous errors; 1 warning emitted
356347

357-
Some errors have detailed explanations: E0121, E0223, E0740.
348+
Some errors have detailed explanations: E0121, E0223, E0228, E0740.
358349
For more information about an error, try `rustc --explain E0121`.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Check that we correctly deduce object lifetime defaults inside self types of qualified paths.
2+
//@ check-pass
3+
4+
trait Outer { type Ty; }
5+
trait Inner {}
6+
7+
impl<'a> Outer for dyn Inner + 'a { type Ty = &'a (); }
8+
9+
// We deduce `dyn Inner + 'static` from absence of any bounds on self ty param of trait `Outer`.
10+
fn f<'r>(x: &'r <dyn Inner as Outer>::Ty) { g(x) }
11+
fn g<'r>(x: &'r <dyn Inner + 'static as Outer>::Ty) {}
12+
13+
fn main() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Check that we correctly deduce object lifetime defaults inside self types of qualified paths.
2+
//@ check-pass
3+
//@ revisions: bound clause
4+
5+
#[cfg(bound)] trait Outer<'a>: 'a { type Ty; }
6+
#[cfg(clause)] trait Outer<'a> where Self: 'a { type Ty; }
7+
trait Inner {}
8+
9+
impl<'a> Outer<'a> for dyn Inner + 'a { type Ty = &'a (); }
10+
11+
fn f<'r>(x: <dyn Inner + 'r as Outer<'r>>::Ty) { g(x) }
12+
// We deduce `dyn Inner + 'r` from bound `'a` on self ty param of trait `Outer`.
13+
fn g<'r>(x: <dyn Inner as Outer<'r>>::Ty) {}
14+
15+
fn main() {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Check that we correctly deduce object lifetime defaults inside resolved GAT *paths*.
2+
// issue: <https://github.com/rust-lang/rust/issues/115379>
3+
//@ check-pass
4+
5+
mod own { // the object lifetime default comes from the own generics
6+
trait Outer {
7+
type Ty<'a, T: ?Sized + 'a>;
8+
}
9+
impl Outer for () {
10+
type Ty<'a, T: ?Sized + 'a> = &'a T;
11+
}
12+
trait Inner {}
13+
14+
fn f<'r>(x: <() as Outer>::Ty<'r, dyn Inner + 'r>) { g(x) }
15+
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`.
16+
fn g<'r>(_: <() as Outer>::Ty<'r, dyn Inner>) {}
17+
}
18+
19+
mod parent { // the object lifetime default comes from the parent generics
20+
trait Outer<'a> {
21+
type Ty<T: ?Sized + 'a>;
22+
}
23+
impl<'a> Outer<'a> for () {
24+
type Ty<T: ?Sized + 'a> = &'a T;
25+
}
26+
trait Inner {}
27+
28+
fn f<'r>(x: <() as Outer<'r>>::Ty<dyn Inner + 'r>) { g(x) }
29+
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`.
30+
fn g<'r>(_: <() as Outer<'r>>::Ty<dyn Inner>) {}
31+
}
32+
33+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Properly deduce the object lifetime default in type-relative generic associated type *paths*.
2+
// issue: <https://github.com/rust-lang/rust/issues/115379>
3+
//@ check-pass
4+
5+
trait Outer { type Ty<'a, T: 'a + ?Sized>; }
6+
trait Inner {}
7+
8+
fn f<'r, T: Outer>(x: T::Ty<'r, dyn Inner + 'r>) { g::<T>(x) }
9+
// Deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`
10+
fn g<'r, T: Outer>(x: T::Ty<'r, dyn Inner>) {}
11+
12+
fn main() {}

0 commit comments

Comments
 (0)