Skip to content

Commit a137dac

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 a11cfc6 commit a137dac

16 files changed

Lines changed: 270 additions & 166 deletions

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,38 +1805,43 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
18051805
}
18061806
}
18071807

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

1835-
// Resolve lifetimes found in the bindings, so either in the type `XX` in `Item = XX` or
1836-
// in the trait ref `YY<...>` in `Item: YY<...>`.
18371810
for constraint in generic_args.constraints {
18381811
let scope = Scope::ObjectLifetimeDefault {
1839-
lifetime: if has_lifetime_parameter {
1812+
// FIXME: Ideally we would consider the *item bounds* of assoc types when deducing
1813+
// the trait object lifetime default for the RHS of assoc type bindings.
1814+
// For example, given
1815+
//
1816+
// trait TraitA<'a> { type AssocTy: ?Sized + 'a; }
1817+
// trait TraitB { type AssocTy<'a>: ?Sized + 'a; }
1818+
//
1819+
// we would elaborate the `dyn Bound` in `TraitA<'r, AssocTy = dyn Bound>`
1820+
// and `TraitB<AssocTy<'r> = dyn Bound>` to `dyn Bound + 'r`.
1821+
//
1822+
// FIXME: Moreover, ideally GAT args in bindings could induce
1823+
// trait object lifetime defaults. For example, given
1824+
//
1825+
// trait TraitA<'a> { type AssocTy<T: ?Sized + 'a>; }
1826+
// trait TraitB { type AssocTy<'a, T: ?Sized + 'a>; }
1827+
//
1828+
// we would elab the `dyn Bound` in `TraitA<'r, AssocTy<dyn Bound> = ()>`
1829+
// and `TraitB<AssocTy<'r, dyn Bound> = ()>` to `dyn Bound + 'r`.
1830+
//
1831+
// HACK: For now however, if the user passes any lifetime arguments to the trait or
1832+
// the (generic) assoc type, we will treat the trait object lifetime default
1833+
// as indeterminate thus forcing the user to explicitly specify the lifetime.
1834+
//
1835+
// If the trait or the assoc type have lifetime parameters, it's *possible*
1836+
// that they occur in the predicates or item bounds of the assoc type, so we
1837+
// conservatively reject such cases to allow us to implement the correct
1838+
// behavior in the future (here we assume that the number of arguments equals
1839+
// the number of parameters which is fine since a mismatch would get rejected
1840+
// later anyway).
1841+
//
1842+
// If the items don't have any lifetime parameters we can safely use `'static`
1843+
// since there is no other possibility.
1844+
lifetime: if has_lifetime_args || constraint.gen_args.has_lifetime_args() {
18401845
None
18411846
} else {
18421847
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 cannot deduce the lifetime bound for this trait object type from context
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]: cannot deduce the lifetime bound for this trait object type from context
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: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Check that assoc item bindings correctly induce trait object lifetime defaults `'static` if the
2+
// the trait & assoc ty doesn't have any lifetime params & the assoc ty isn't bounded by a lifetime.
3+
//
4+
//@ check-pass
5+
6+
trait Foo {
7+
type Item: ?Sized;
8+
9+
fn item(&self) -> Box<Self::Item> { loop {} }
10+
}
11+
12+
trait Bar {}
13+
14+
impl<T> Foo for T {
15+
type Item = dyn Bar;
16+
}
17+
18+
fn is_static<T>(_: T) where T: 'static {}
19+
20+
// We elaborate `dyn Bar` to `dyn Bar + 'static` since the assoc ty isn't bounded by any lifetime.
21+
// Notably, we don't elaborate it to `dyn Bar + 'r` since the trait object lifetime default induced
22+
// by `Foo` (i.e., `'static`) shadows the one induced by `&` (`'r`).
23+
fn bar<'r>(x: &'r str) -> &'r dyn Foo<Item = dyn Bar> { &() }
24+
25+
fn main() {
26+
let s = format!("foo");
27+
let r = bar(&s);
28+
29+
is_static(r.item());
30+
}
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 cannot deduce the lifetime bound for this trait object type from context
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 cannot deduce the lifetime bound for this trait object type from context
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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0228]: cannot deduce the lifetime bound for this trait object type from context
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+
help: please supply an explicit bound
8+
|
9+
LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar + /* 'a */> { &() }
10+
| ++++++++++
11+
12+
error[E0228]: cannot deduce the lifetime bound for this trait object type from context
13+
--> $DIR/object-lifetime-default-assoc-ty-binding-item-bounds.rs:28:36
14+
|
15+
LL | fn baz(x: &str) -> &dyn Foo<Item = dyn Bar> { &() }
16+
| ^^^^^^^
17+
|
18+
help: please supply an explicit bound
19+
|
20+
LL | fn baz(x: &str) -> &dyn Foo<Item = dyn Bar + /* 'a */> { &() }
21+
| ++++++++++
22+
23+
error: aborting due to 2 previous errors
24+
25+
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 & 14 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.

0 commit comments

Comments
 (0)