Skip to content

Commit af86fb1

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 b51df1d commit af86fb1

File tree

13 files changed

+163
-10
lines changed

13 files changed

+163
-10
lines changed

src/librustc/hir/intravisit.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,11 @@ impl<'a> LoweringContext<'a> {
15601560
}
15611561
}
15621562
hir::LifetimeName::Param(_) => lifetime.name,
1563+
1564+
// Refers to some other lifetime that is "in
1565+
// scope" within the type.
1566+
hir::LifetimeName::ImplicitObjectLifetimeDefault => return,
1567+
15631568
hir::LifetimeName::Error | hir::LifetimeName::Static => return,
15641569
};
15651570

@@ -2550,6 +2555,12 @@ impl<'a> LoweringContext<'a> {
25502555
hir::LifetimeName::Implicit
25512556
| hir::LifetimeName::Underscore
25522557
| hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()),
2558+
hir::LifetimeName::ImplicitObjectLifetimeDefault => {
2559+
span_bug!(
2560+
param.ident.span,
2561+
"object-lifetime-default should not occur here",
2562+
);
2563+
}
25532564
hir::LifetimeName::Error => ParamName::Error,
25542565
};
25552566

@@ -3293,7 +3304,13 @@ impl<'a> LoweringContext<'a> {
32933304
AnonymousLifetimeMode::PassThrough => {}
32943305
}
32953306

3296-
self.new_implicit_lifetime(span)
3307+
let r = hir::Lifetime {
3308+
hir_id: self.next_id(),
3309+
span,
3310+
name: hir::LifetimeName::ImplicitObjectLifetimeDefault,
3311+
};
3312+
debug!("elided_dyn_bound: r={:?}", r);
3313+
r
32973314
}
32983315

32993316
fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {

src/librustc/hir/mod.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,19 @@ pub enum LifetimeName {
221221
/// User wrote nothing (e.g., the lifetime in `&u32`).
222222
Implicit,
223223

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

245260
pub fn is_elided(&self) -> bool {
246261
match self {
247-
LifetimeName::Implicit | LifetimeName::Underscore => true,
262+
LifetimeName::ImplicitObjectLifetimeDefault
263+
| LifetimeName::Implicit
264+
| LifetimeName::Underscore => true,
248265

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

src/librustc/middle/resolve_lifetime.rs

Lines changed: 17 additions & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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
//
Lines changed: 23 additions & 0 deletions
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() { }
Lines changed: 16 additions & 0 deletions
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() { }
Lines changed: 11 additions & 0 deletions
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`.
Lines changed: 16 additions & 0 deletions
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() { }
Lines changed: 11 additions & 0 deletions
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() { }
Lines changed: 23 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 5 additions & 5 deletions
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)