Skip to content

Commit 282cc02

Browse files
committed
distinguish object-lifetime-default elision from other elision
Object-lifetime-default elision is distinct from other forms of elision; it always refers to some enclosing lifetime *present in the surrounding type* (e.g., `&dyn Bar` expands to `&'a (dyn Bar + 'a)`. If there is no enclosing lifetime, then it expands to `'static`. Therefore, in an `impl Trait<Item = dyn Bar>` setting, we don't expand to create a lifetime parameter for the `dyn Bar + 'X` bound. It will just be resolved to `'static`. Annoyingly, the responsibility for this resolution is spread across multiple bits of code right now (`middle::resolve_lifetimes`, `lowering`). The lowering code knows that the default is for an object lifetime, but it doesn't know what the correct result would be. Probably this should be fixed, but what we do now is a surgical fix: we have it generate a different result for elided lifetimes in a object context, and then we can ignore those results when figuring out the lifetimes that are captured in the opaque type.
1 parent b94b90a commit 282cc02

13 files changed

+163
-10
lines changed

src/librustc/hir/intravisit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime
433433
LifetimeName::Static |
434434
LifetimeName::Error |
435435
LifetimeName::Implicit |
436+
LifetimeName::ImplicitObjectLifetimeDefault |
436437
LifetimeName::Underscore => {}
437438
}
438439
}

src/librustc/hir/lowering.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -1936,6 +1936,11 @@ impl<'a> LoweringContext<'a> {
19361936
}
19371937
}
19381938
hir::LifetimeName::Param(_) => lifetime.name,
1939+
1940+
// Refers to some other lifetime that is "in
1941+
// scope" within the type.
1942+
hir::LifetimeName::ImplicitObjectLifetimeDefault => return,
1943+
19391944
hir::LifetimeName::Error | hir::LifetimeName::Static => return,
19401945
};
19411946

@@ -2920,6 +2925,12 @@ impl<'a> LoweringContext<'a> {
29202925
hir::LifetimeName::Implicit
29212926
| hir::LifetimeName::Underscore
29222927
| hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()),
2928+
hir::LifetimeName::ImplicitObjectLifetimeDefault => {
2929+
span_bug!(
2930+
param.ident.span,
2931+
"object-lifetime-default should not occur here",
2932+
);
2933+
}
29232934
hir::LifetimeName::Error => ParamName::Error,
29242935
};
29252936

@@ -5929,7 +5940,13 @@ impl<'a> LoweringContext<'a> {
59295940
AnonymousLifetimeMode::Replace(_) => {}
59305941
}
59315942

5932-
self.new_implicit_lifetime(span)
5943+
let r = hir::Lifetime {
5944+
hir_id: self.next_id(),
5945+
span,
5946+
name: hir::LifetimeName::ImplicitObjectLifetimeDefault,
5947+
};
5948+
debug!("elided_dyn_bound: r={:?}", r);
5949+
r
59335950
}
59345951

59355952
fn new_replacement_lifetime(

src/librustc/hir/mod.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,19 @@ pub enum LifetimeName {
222222
/// User wrote nothing (e.g., the lifetime in `&u32`).
223223
Implicit,
224224

225+
/// Implicit lifetime in a context like `dyn Foo`. This is
226+
/// distinguished from implicit lifetimes elsewhere because the
227+
/// lifetime that they default to must appear elsewhere within the
228+
/// enclosing type. This means that, in an `impl Trait` context, we
229+
/// don't have to create a parameter for them. That is, `impl
230+
/// Trait<Item = &u32>` expands to an opaque type like `type
231+
/// Foo<'a> = impl Trait<Item = &'a u32>`, but `impl Trait<item =
232+
/// dyn Bar>` expands to `type Foo = impl Trait<Item = dyn Bar +
233+
/// 'static>`. The latter uses `ImplicitObjectLifetimeDefault` so
234+
/// that surrounding code knows not to create a lifetime
235+
/// parameter.
236+
ImplicitObjectLifetimeDefault,
237+
225238
/// Indicates an error during lowering (usually `'_` in wrong place)
226239
/// that was already reported.
227240
Error,
@@ -236,7 +249,9 @@ pub enum LifetimeName {
236249
impl LifetimeName {
237250
pub fn ident(&self) -> Ident {
238251
match *self {
239-
LifetimeName::Implicit | LifetimeName::Error => Ident::invalid(),
252+
LifetimeName::ImplicitObjectLifetimeDefault
253+
| LifetimeName::Implicit
254+
| LifetimeName::Error => Ident::invalid(),
240255
LifetimeName::Underscore => Ident::with_empty_ctxt(kw::UnderscoreLifetime),
241256
LifetimeName::Static => Ident::with_empty_ctxt(kw::StaticLifetime),
242257
LifetimeName::Param(param_name) => param_name.ident(),
@@ -245,7 +260,9 @@ impl LifetimeName {
245260

246261
pub fn is_elided(&self) -> bool {
247262
match self {
248-
LifetimeName::Implicit | LifetimeName::Underscore => true,
263+
LifetimeName::ImplicitObjectLifetimeDefault
264+
| LifetimeName::Implicit
265+
| LifetimeName::Underscore => true,
249266

250267
// It might seem surprising that `Fresh(_)` counts as
251268
// *not* elided -- but this is because, as far as the code

src/librustc/middle/resolve_lifetime.rs

+17
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
//! used between functions, and they operate in a purely top-down
66
//! way. Therefore, we break lifetime name resolution into a separate pass.
77
8+
// ignore-tidy-filelength
9+
810
use crate::hir::def::{Res, DefKind};
911
use crate::hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
1012
use crate::hir::map::Map;
@@ -591,6 +593,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
591593
}
592594
match lifetime.name {
593595
LifetimeName::Implicit => {
596+
// For types like `dyn Foo`, we should
597+
// generate a special form of elided.
598+
span_bug!(
599+
ty.span,
600+
"object-lifetime-default expected, not implict",
601+
);
602+
}
603+
LifetimeName::ImplicitObjectLifetimeDefault => {
594604
// If the user does not write *anything*, we
595605
// use the object lifetime defaulting
596606
// rules. So e.g., `Box<dyn Debug>` becomes
@@ -2643,6 +2653,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
26432653
hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => {
26442654
self.resolve_lifetime_ref(lt);
26452655
}
2656+
hir::LifetimeName::ImplicitObjectLifetimeDefault => {
2657+
self.tcx.sess.delay_span_bug(
2658+
lt.span,
2659+
"lowering generated `ImplicitObjectLifetimeDefault` \
2660+
outside of an object type",
2661+
)
2662+
}
26462663
hir::LifetimeName::Error => {
26472664
// No need to do anything, error already reported.
26482665
}

src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
578578
})
579579
}
580580

581-
hir::LifetimeName::Implicit => {
581+
hir::LifetimeName::ImplicitObjectLifetimeDefault
582+
| hir::LifetimeName::Implicit => {
582583
// In this case, the user left off the lifetime; so
583584
// they wrote something like:
584585
//
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Regression test for #62517. We used to ICE when you had an `async
2+
// fn` with an `impl Trait` return that mentioned a `dyn Bar` with no
3+
// explicit lifetime bound.
4+
//
5+
// edition:2018
6+
// check-pass
7+
8+
#![feature(async_await)]
9+
10+
trait FirstTrait {}
11+
trait SecondTrait {
12+
type Item: ?Sized;
13+
}
14+
15+
async fn foo(x: &str) -> impl SecondTrait<Item = dyn FirstTrait> {
16+
}
17+
18+
19+
impl<T> SecondTrait for T {
20+
type Item = dyn FirstTrait;
21+
}
22+
23+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Regression test for #62517. We used to ICE when you had an `async
2+
// fn` with an `impl Trait` return that mentioned a `dyn Bar` with no
3+
// explicit lifetime bound.
4+
//
5+
// edition:2018
6+
7+
#![feature(async_await)]
8+
9+
trait Object {}
10+
11+
trait Alpha<Param> {}
12+
13+
async fn foo<'a>(_: &'a ()) -> impl Alpha<dyn Object> {}
14+
//~^ ERROR not satisfied
15+
16+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0277]: the trait bound `(): Alpha<(dyn Object + 'static)>` is not satisfied
2+
--> $DIR/issue-62517-2.rs:13:32
3+
|
4+
LL | async fn foo<'a>(_: &'a ()) -> impl Alpha<dyn Object> {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^ the trait `Alpha<(dyn Object + 'static)>` is not implemented for `()`
6+
|
7+
= note: the return type of a function must have a statically known size
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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 { type Item: ?Sized; }
8+
trait Bar { }
9+
10+
impl<T> Foo for T {
11+
type Item = dyn Bar;
12+
}
13+
14+
fn foo(x: &str, y: &str) -> impl Foo<Item = dyn Bar> { () }
15+
16+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Test that we don't get an error with `dyn Object` in an impl Trait
2+
// when there are multiple inputs. The `dyn Object` should default to `+
3+
// 'static`. This used to erroneously generate an error (cc #62517).
4+
//
5+
// check-pass
6+
7+
trait Alpha<Item: ?Sized> {}
8+
trait Object {}
9+
impl<T> Alpha<dyn Object> for T {}
10+
fn alpha(x: &str, y: &str) -> impl Alpha<dyn Object> { () }
11+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Test that `impl Alpha<dyn Object>` resets the object-lifetime
2+
// default to `'static`.
3+
//
4+
// check-pass
5+
6+
trait Alpha<Item: ?Sized> {
7+
fn item(&self) -> Box<Item> {
8+
panic!()
9+
}
10+
}
11+
12+
trait Object {}
13+
impl<T> Alpha<dyn Object> for T {}
14+
fn alpha(x: &str, y: &str) -> impl Alpha<dyn Object> { () }
15+
fn is_static<T>(_: T) where T: 'static { }
16+
17+
fn bar(x: &str) -> &impl Alpha<dyn Object> { &() }
18+
19+
fn main() {
20+
let s = format!("foo");
21+
let r = bar(&s);
22+
is_static(r.item());
23+
}

src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ trait Future {
66
use std::error::Error;
77

88
fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
9-
//~^ ERROR missing lifetime
9+
//~^ ERROR not satisfied
1010
Ok(())
1111
}
1212

Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
error[E0106]: missing lifetime specifier
2-
--> $DIR/lifetime-elision-return-type-trait.rs:8:44
1+
error[E0277]: the trait bound `std::result::Result<(), _>: Future` is not satisfied
2+
--> $DIR/lifetime-elision-return-type-trait.rs:8:13
33
|
44
LL | fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
5-
| ^^^^^^^^^ help: consider giving it a 'static lifetime: `dyn Error + 'static`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Future` is not implemented for `std::result::Result<(), _>`
66
|
7-
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
7+
= note: the return type of a function must have a statically known size
88

99
error: aborting due to previous error
1010

11-
For more information about this error, try `rustc --explain E0106`.
11+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)