You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
Fixesrust-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.
0 commit comments