Skip to content

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

+340-218
Large diffs are not rendered by default.

compiler/rustc_metadata/src/rmeta/encoder.rs

+1-1
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

+12-14
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

+3
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

+1-1
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

+3-12
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`.
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() {}
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() {}
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() {}
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() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Check that we correctly deduce object lifetime defaults inside *trait refs*!
2+
// For the longest time these examples used to get rejected as "inderminate" due to an off-by-one.
3+
//@ check-pass
4+
5+
trait Inner {}
6+
trait Outer<'a, T: 'a + ?Sized> { type Project where Self: Sized; }
7+
8+
fn bound0<'r, T>() where T: Outer<'r, dyn Inner + 'r> { bound1::<'r, T>() }
9+
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`.
10+
fn bound1<'r, T>() where T: Outer<'r, dyn Inner> {}
11+
12+
fn dyn0<'r>(x: Box<dyn Outer<'r, dyn Inner + 'r>>) { dyn1(x) }
13+
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`.
14+
fn dyn1<'r>(_: Box<dyn Outer<'r, dyn Inner>>) {}
15+
16+
fn impl0<'r>(x: impl Outer<'r, dyn Inner + 'r>) { impl1(x) }
17+
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`.
18+
fn impl1<'r>(_: impl Outer<'r, dyn Inner>) {} // deduce `dyn Inner + 'r`
19+
20+
fn proj<'r>(x: <() as Outer<'r, dyn Inner + 'r>>::Project) { proj1(x) }
21+
// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`.
22+
fn proj1<'r>(_: <() as Outer<'r, dyn Inner>>::Project) {}
23+
24+
impl<'a, T: 'a + ?Sized> Outer<'a, T> for () { type Project = &'a T; }
25+
26+
fn main() {}

tests/ui/object-lifetime/object-lifetime-default.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
#[rustc_object_lifetime_default]
44
struct A<
5-
T, //~ ERROR BaseDefault
5+
T, //~ ERROR Empty
66
>(T);
77

88
#[rustc_object_lifetime_default]
99
struct B<
1010
'a,
11-
T, //~ ERROR BaseDefault
11+
T, //~ ERROR Empty
1212
>(&'a (), T);
1313

1414
#[rustc_object_lifetime_default]
@@ -47,4 +47,12 @@ struct G<
4747
U: 'a + 'b, //~ ERROR Ambiguous
4848
>(&'a T, &'b U);
4949

50+
// Check that we also dump the default for the implicit `Self` type param of traits.
51+
#[rustc_object_lifetime_default]
52+
trait H< //~ ERROR 'a
53+
'a,
54+
'b,
55+
T: 'b, //~ ERROR 'b
56+
>: 'a {}
57+
5058
fn main() {}

tests/ui/object-lifetime/object-lifetime-default.stderr

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
error: BaseDefault
1+
error: Empty
22
--> $DIR/object-lifetime-default.rs:5:5
33
|
44
LL | T,
55
| ^
66

7-
error: BaseDefault
7+
error: Empty
88
--> $DIR/object-lifetime-default.rs:11:5
99
|
1010
LL | T,
@@ -52,5 +52,21 @@ error: Ambiguous
5252
LL | U: 'a + 'b,
5353
| ^
5454

55-
error: aborting due to 9 previous errors
55+
error: 'a
56+
--> $DIR/object-lifetime-default.rs:52:1
57+
|
58+
LL | / trait H<
59+
LL | | 'a,
60+
LL | | 'b,
61+
LL | | T: 'b,
62+
LL | | >: 'a {}
63+
| |_____^
64+
65+
error: 'b
66+
--> $DIR/object-lifetime-default.rs:55:5
67+
|
68+
LL | T: 'b,
69+
| ^
70+
71+
error: aborting due to 11 previous errors
5672

0 commit comments

Comments
 (0)