Skip to content

Commit afa4a2a

Browse files
committed
use static as object-lifetime default for type XX in Foo<Item=XX>
Currently the default is "inherited" from context, so e.g. `&impl Foo<Item = dyn Bar>` would default to `&'x impl Foo<Item = dyn Bar + 'x>`, but this triggers an ICE and is not very consistent. This patch doesn't implement what I expect would be the correct semantics, because those are likely too complex. Instead, it handles what I'd expect to be the common case -- where the trait has no lifetime parameters.
1 parent 282cc02 commit afa4a2a

9 files changed

+229
-1
lines changed

src/librustc/middle/resolve_lifetime.rs

+70-1
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
558558

559559
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
560560
debug!("visit_ty: id={:?} ty={:?}", ty.hir_id, ty);
561+
debug!("visit_ty: ty.node={:?}", ty.node);
561562
match ty.node {
562563
hir::TyKind::BareFn(ref c) => {
563564
let next_early_index = self.next_early_index();
@@ -1923,6 +1924,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
19231924
}
19241925

19251926
fn visit_segment_args(&mut self, res: Res, depth: usize, generic_args: &'tcx hir::GenericArgs) {
1927+
debug!(
1928+
"visit_segment_args(res={:?}, depth={:?}, generic_args={:?})",
1929+
res,
1930+
depth,
1931+
generic_args,
1932+
);
1933+
19261934
if generic_args.parenthesized {
19271935
let was_in_fn_syntax = self.is_in_fn_syntax;
19281936
self.is_in_fn_syntax = true;
@@ -1976,6 +1984,23 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
19761984
_ => None,
19771985
};
19781986

1987+
debug!("visit_segment_args: type_def_id={:?}", type_def_id);
1988+
1989+
// Compute a vector of defaults, one for each type parameter,
1990+
// per the rules given in RFCs 599 and 1156. Example:
1991+
//
1992+
// ```rust
1993+
// struct Foo<'a, T: 'a, U> { }
1994+
// ```
1995+
//
1996+
// If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to default
1997+
// `dyn Bar` to `dyn Bar + 'x` (because of the `T: 'a` bound)
1998+
// and `dyn Baz` to `dyn Baz + 'static` (because there is no
1999+
// such bound).
2000+
//
2001+
// Therefore, we would compute `object_lifetime_defaults` to a
2002+
// vector like `['x, 'static]`. Note that the vector only
2003+
// includes type parameters.
19792004
let object_lifetime_defaults = type_def_id.map_or(vec![], |def_id| {
19802005
let in_body = {
19812006
let mut scope = self.scope;
@@ -2015,6 +2040,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20152040
.collect()
20162041
})
20172042
};
2043+
debug!("visit_segment_args: unsubst={:?}", unsubst);
20182044
unsubst
20192045
.iter()
20202046
.map(|set| match *set {
@@ -2035,6 +2061,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20352061
.collect()
20362062
});
20372063

2064+
debug!("visit_segment_args: object_lifetime_defaults={:?}", object_lifetime_defaults);
2065+
20382066
let mut i = 0;
20392067
for arg in &generic_args.args {
20402068
match arg {
@@ -2057,8 +2085,49 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20572085
}
20582086
}
20592087

2088+
// Hack: when resolving the type `XX` in binding like `dyn
2089+
// Foo<'b, Item = XX>`, the current object-lifetime default
2090+
// would be to examine the trait `Foo` to check whether it has
2091+
// a lifetime bound declared on `Item`. e.g., if `Foo` is
2092+
// declared like so, then the default object lifetime bound in
2093+
// `XX` should be `'b`:
2094+
//
2095+
// ```rust
2096+
// trait Foo<'a> {
2097+
// type Item: 'a;
2098+
// }
2099+
// ```
2100+
//
2101+
// but if we just have `type Item;`, then it would be
2102+
// `'static`. However, we don't get all of this logic correct.
2103+
//
2104+
// Instead, we do something hacky: if there are no lifetime parameters
2105+
// to the trait, then we simply use a default object lifetime
2106+
// bound of `'static`, because there is no other possibility. On the other hand,
2107+
// if there ARE lifetime parameters, then we require the user to give an
2108+
// explicit bound for now.
2109+
//
2110+
// This is intended to leave room for us to implement the
2111+
// correct behavior in the future.
2112+
let has_lifetime_parameter = generic_args
2113+
.args
2114+
.iter()
2115+
.any(|arg| match arg {
2116+
GenericArg::Lifetime(_) => true,
2117+
_ => false,
2118+
});
2119+
2120+
// Resolve lifetimes found in the type `XX` from `Item = XX` bindings.
20602121
for b in &generic_args.bindings {
2061-
self.visit_assoc_type_binding(b);
2122+
let scope = Scope::ObjectLifetimeDefault {
2123+
lifetime: if has_lifetime_parameter {
2124+
None
2125+
} else {
2126+
Some(Region::Static)
2127+
},
2128+
s: self.scope,
2129+
};
2130+
self.with(scope, |_, this| this.visit_assoc_type_binding(b));
20622131
}
20632132
}
20642133

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Test that we don't get an error with `dyn Bar` in an impl Trait
2+
// when there are multiple inputs. The `dyn Bar` should default to `+
3+
// 'static`. This used to erroneously generate an error (cc #62517).
4+
//
5+
// check-pass
6+
7+
trait Foo {
8+
type Item: ?Sized;
9+
10+
fn item(&self) -> Box<Self::Item> { panic!() }
11+
}
12+
13+
trait Bar { }
14+
15+
impl<T> Foo for T {
16+
type Item = dyn Bar;
17+
}
18+
19+
fn is_static<T>(_: T) where T: 'static { }
20+
21+
fn bar(x: &str) -> &impl Foo<Item = dyn Bar> { &() }
22+
23+
fn main() {
24+
let s = format!("foo");
25+
let r = bar(&s);
26+
is_static(r.item());
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Test that `dyn Bar<Item = XX>` uses `'static` as the default object
2+
// lifetime bound for the type `XX`.
3+
4+
trait Foo<'a> {
5+
type Item: ?Sized;
6+
7+
fn item(&self) -> Box<Self::Item> { panic!() }
8+
}
9+
10+
trait Bar { }
11+
12+
impl<T> Foo<'_> for T {
13+
type Item = dyn Bar;
14+
}
15+
16+
fn is_static<T>(_: T) where T: 'static { }
17+
18+
// Here, we should default to `dyn Bar + 'static`, but the current
19+
// code forces us into a conservative, hacky path.
20+
fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() }
21+
//~^ ERROR please supply an explicit bound
22+
23+
fn main() {
24+
let s = format!("foo");
25+
let r = bar(&s);
26+
is_static(r.item());
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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-dyn-binding-nonstatic1.rs:20:50
3+
|
4+
LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() }
5+
| ^^^^^^^
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Test that `dyn Bar<Item = XX>` uses `'static` as the default object
2+
// lifetime bound for the type `XX`.
3+
4+
trait Foo<'a> {
5+
type Item: 'a + ?Sized;
6+
7+
fn item(&self) -> Box<Self::Item> { panic!() }
8+
}
9+
10+
trait Bar { }
11+
12+
impl<T> Foo<'_> for T {
13+
type Item = dyn Bar;
14+
}
15+
16+
fn is_static<T>(_: T) where T: 'static { }
17+
18+
// Here, we default to `dyn Bar + 'a`. Or, we *should*, but the
19+
// current code forces us into a conservative, hacky path.
20+
fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() }
21+
//~^ ERROR please supply an explicit bound
22+
23+
fn main() {
24+
let s = format!("foo");
25+
let r = bar(&s);
26+
27+
// If it weren't for the conservative path above, we'd expect an
28+
// error here.
29+
is_static(r.item());
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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-dyn-binding-nonstatic2.rs:20:50
3+
|
4+
LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() }
5+
| ^^^^^^^
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Test that `dyn Bar<Item = XX>` uses `'static` as the default object
2+
// lifetime bound for the type `XX`.
3+
4+
trait Foo<'a> {
5+
type Item: ?Sized;
6+
7+
fn item(&self) -> Box<Self::Item> { panic!() }
8+
}
9+
10+
trait Bar { }
11+
12+
fn is_static<T>(_: T) where T: 'static { }
13+
14+
// Here, we should default to `dyn Bar + 'static`, but the current
15+
// code forces us into a conservative, hacky path.
16+
fn bar(x: &str) -> &dyn Foo<Item = dyn Bar> { &() }
17+
//~^ ERROR please supply an explicit bound
18+
19+
fn main() {
20+
let s = format!("foo");
21+
let r = bar(&s);
22+
is_static(r.item());
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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-dyn-binding-nonstatic3.rs:16:36
3+
|
4+
LL | fn bar(x: &str) -> &dyn Foo<Item = dyn Bar> { &() }
5+
| ^^^^^^^
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Test that `dyn Bar<Item = XX>` uses `'static` as the default object
2+
// lifetime bound for the type `XX`.
3+
//
4+
// check-pass
5+
6+
trait Foo {
7+
type Item: ?Sized;
8+
9+
fn item(&self) -> Box<Self::Item> { panic!() }
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+
// Here, we default to `dyn Bar + 'static`, and not `&'x dyn Foo<Item
21+
// = dyn Bar + 'x>`.
22+
fn bar(x: &str) -> &dyn Foo<Item = dyn Bar> { &() }
23+
24+
fn main() {
25+
let s = format!("foo");
26+
let r = bar(&s);
27+
is_static(r.item());
28+
}

0 commit comments

Comments
 (0)