Skip to content

Commit 69473d7

Browse files
committed
Render trait object lifetime defaults indeterminate in lifetime-instantiated assoc ty bindings
Namely, on the RHS and in the args of the bindings.
1 parent 03d4a3e commit 69473d7

14 files changed

Lines changed: 212 additions & 128 deletions

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,38 +1796,45 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
17961796
}
17971797
}
17981798

1799-
// Hack: When resolving the type `XX` in an assoc ty binding like
1800-
// `dyn Foo<'b, Item = XX>`, the current object-lifetime default
1801-
// would be to examine the trait `Foo` to check whether it has
1802-
// a lifetime bound declared on `Item`. e.g., if `Foo` is
1803-
// declared like so, then the default object lifetime bound in
1804-
// `XX` should be `'b`:
1805-
//
1806-
// ```rust
1807-
// trait Foo<'a> {
1808-
// type Item: 'a;
1809-
// }
1810-
// ```
1811-
//
1812-
// but if we just have `type Item;`, then it would be
1813-
// `'static`. However, we don't get all of this logic correct.
1814-
//
1815-
// Instead, we do something hacky: if there are no lifetime parameters
1816-
// to the trait, then we simply use a default object lifetime
1817-
// bound of `'static`, because there is no other possibility. On the other hand,
1818-
// if there ARE lifetime parameters, then we require the user to give an
1819-
// explicit bound for now.
1820-
//
1821-
// This is intended to leave room for us to implement the
1822-
// correct behavior in the future.
1823-
let has_lifetime_parameter =
1824-
generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
1799+
let has_lifetime_args = generic_args.has_lifetime_args();
18251800

1826-
// Resolve lifetimes found in the bindings, so either in the type `XX` in `Item = XX` or
1827-
// in the trait ref `YY<...>` in `Item: YY<...>`.
1801+
// Resolve lifetimes found in the constraints, so either in the type `Ty` in `AssocTy = Ty`
1802+
// or in the trait ref `TraitRef<..>` in `AssocTy: TraitRef<..>`.
18281803
for constraint in generic_args.constraints {
18291804
let scope = Scope::ObjectLifetimeDefault {
1830-
lifetime: if has_lifetime_parameter {
1805+
// FIXME: Ideally we would consider the *item bounds* of assoc types when deducing
1806+
// the trait object lifetime default for the RHS of assoc type bindings.
1807+
// For example, given
1808+
//
1809+
// trait TraitA<'a> { type AssocTy: ?Sized + 'a; }
1810+
// trait TraitB { type AssocTy<'a>: ?Sized + 'a; }
1811+
//
1812+
// we would elaborate the `dyn Bound` in `TraitA<'r, AssocTy = dyn Bound>`
1813+
// and `TraitB<AssocTy<'r> = dyn Bound>` to `dyn Bound + 'r`.
1814+
//
1815+
// FIXME: Moreover, ideally GAT args in bindings could induce
1816+
// trait object lifetime defaults. For example, given
1817+
//
1818+
// trait TraitA<'a> { type AssocTy<T: ?Sized + 'a>; }
1819+
// trait TraitB { type AssocTy<'a, T: ?Sized + 'a>; }
1820+
//
1821+
// we would elab the `dyn Bound` in `TraitA<'r, AssocTy<dyn Bound> = ()>`
1822+
// and `TraitB<AssocTy<'r, dyn Bound> = ()>` to `dyn Bound + 'r`.
1823+
//
1824+
// HACK: For now however, if the user passes any lifetime arguments to the trait or
1825+
// the (generic) assoc type, we will treat the trait object lifetime default
1826+
// as indeterminate thus forcing the user to explicitly specify the lifetime.
1827+
//
1828+
// If the trait or the assoc type have generic parameters, it's *possible*
1829+
// that they occur in the predicates or item bounds of the assoc type, so we
1830+
// conservatively reject such cases to allow us to implement the correct
1831+
// behavior in the future (here we assume that the number of arguments equals
1832+
// the number of parameters which is fine since a mismatch would get rejected
1833+
// later anyway).
1834+
//
1835+
// If the items don't have any lifetime parameters we can safely use `'static`
1836+
// since there is no other possibility.
1837+
lifetime: if has_lifetime_args || constraint.gen_args.has_lifetime_args() {
18311838
None
18321839
} else {
18331840
Some(ResolvedArg::StaticLifetime)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Ideally, given an assoc type binding `dyn Trait<AssocTy = Ty>`, we'd factor in the item bounds of
2+
// assoc type `AssocTy` when computing the trait object lifetime default for type `Ty`.
3+
//
4+
// However, since the current implementation can't handle this we instead conservatively and hackily
5+
// treat the trait object lifetime default of the RHS as indeterminate if any lifetime arguments are
6+
// passed to the trait ref (or the GAT) thus rejecting any implicit trait object lifetime bounds.
7+
// This way, we can still implement the desired behavior in the future.
8+
9+
trait Foo<'a> {
10+
type Item: 'a + ?Sized;
11+
12+
fn item(&self) -> Box<Self::Item> { panic!() }
13+
}
14+
15+
trait Bar {}
16+
17+
impl<T> Foo<'_> for T {
18+
type Item = dyn Bar;
19+
}
20+
21+
fn is_static<T>(_: T) where T: 'static {}
22+
23+
// FIXME: Ideally, we'd elaborate `dyn Bar` to `dyn Bar + 'a` instead of rejecting it.
24+
fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() }
25+
//~^ ERROR please supply an explicit bound
26+
27+
fn main() {
28+
let s = format!("foo");
29+
let r = bar(&s);
30+
31+
// If it weren't for the conservative path above, we'd expect an error here.
32+
is_static(r.item());
33+
}

tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.stderr renamed to tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
2-
--> $DIR/object-lifetime-default-dyn-binding-nonstatic1.rs:20:50
2+
--> $DIR/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.rs:24:50
33
|
44
LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() }
55
| ^^^^^^^
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Ideally, given an assoc type binding `dyn Trait<AssocTy = Ty>`, we'd factor in the item bounds of
2+
// assoc type `AssocTy` when computing the trait object lifetime default for type `Ty`.
3+
//
4+
// However, since the current implementation can't handle this we instead conservatively and hackily
5+
// treat the trait object lifetime default of the RHS as indeterminate if any lifetime arguments are
6+
// passed to the trait ref (or the GAT) thus rejecting any implicit trait object lifetime bounds.
7+
// This way, we can still implement the desired behavior in the future.
8+
9+
trait Foo<'a> {
10+
type Item: ?Sized;
11+
12+
fn item(&self) -> Box<Self::Item> { panic!() }
13+
}
14+
15+
trait Bar {}
16+
17+
impl<T> Foo<'_> for T {
18+
type Item = dyn Bar;
19+
}
20+
21+
fn is_static<T>(_: T) where T: 'static {}
22+
23+
// FIXME: Ideally, we'd elaborate `dyn Bar` to `dyn Bar + 'static` instead of rejecting it.
24+
fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() }
25+
//~^ ERROR please supply an explicit bound
26+
27+
// FIXME: Ideally, we'd elaborate `dyn Bar` to `dyn Bar + 'static` instead of rejecting it.
28+
fn baz(x: &str) -> &dyn Foo<Item = dyn Bar> { &() }
29+
//~^ ERROR please supply an explicit bound
30+
31+
fn main() {
32+
let s = format!("foo");
33+
let r = bar(&s);
34+
is_static(r.item());
35+
let r = baz(&s);
36+
is_static(r.item());
37+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
2+
--> $DIR/object-lifetime-default-assoc-ty-binding-item-bounds.rs:24:50
3+
|
4+
LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() }
5+
| ^^^^^^^
6+
7+
error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
8+
--> $DIR/object-lifetime-default-assoc-ty-binding-item-bounds.rs:28:36
9+
|
10+
LL | fn baz(x: &str) -> &dyn Foo<Item = dyn Bar> { &() }
11+
| ^^^^^^^
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0228`.

tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.rs

Lines changed: 0 additions & 27 deletions
This file was deleted.

tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.rs

Lines changed: 0 additions & 30 deletions
This file was deleted.

tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.stderr

Lines changed: 0 additions & 9 deletions
This file was deleted.

tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs

Lines changed: 0 additions & 23 deletions
This file was deleted.

tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)