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.
This commit is contained in:
Niko Matsakis 2019-08-08 03:36:24 -04:00
parent b51df1def0
commit af86fb1959
13 changed files with 163 additions and 10 deletions

View File

@ -433,6 +433,7 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime
LifetimeName::Static |
LifetimeName::Error |
LifetimeName::Implicit |
LifetimeName::ImplicitObjectLifetimeDefault |
LifetimeName::Underscore => {}
}
}

View File

@ -1560,6 +1560,11 @@ impl<'a> LoweringContext<'a> {
}
}
hir::LifetimeName::Param(_) => lifetime.name,
// Refers to some other lifetime that is "in
// scope" within the type.
hir::LifetimeName::ImplicitObjectLifetimeDefault => return,
hir::LifetimeName::Error | hir::LifetimeName::Static => return,
};
@ -2550,6 +2555,12 @@ impl<'a> LoweringContext<'a> {
hir::LifetimeName::Implicit
| hir::LifetimeName::Underscore
| hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()),
hir::LifetimeName::ImplicitObjectLifetimeDefault => {
span_bug!(
param.ident.span,
"object-lifetime-default should not occur here",
);
}
hir::LifetimeName::Error => ParamName::Error,
};
@ -3293,7 +3304,13 @@ impl<'a> LoweringContext<'a> {
AnonymousLifetimeMode::PassThrough => {}
}
self.new_implicit_lifetime(span)
let r = hir::Lifetime {
hir_id: self.next_id(),
span,
name: hir::LifetimeName::ImplicitObjectLifetimeDefault,
};
debug!("elided_dyn_bound: r={:?}", r);
r
}
fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {

View File

@ -221,6 +221,19 @@ pub enum LifetimeName {
/// User wrote nothing (e.g., the lifetime in `&u32`).
Implicit,
/// Implicit lifetime in a context like `dyn Foo`. This is
/// distinguished from implicit lifetimes elsewhere because the
/// lifetime that they default to must appear elsewhere within the
/// enclosing type. This means that, in an `impl Trait` context, we
/// don't have to create a parameter for them. That is, `impl
/// Trait<Item = &u32>` expands to an opaque type like `type
/// Foo<'a> = impl Trait<Item = &'a u32>`, but `impl Trait<item =
/// dyn Bar>` expands to `type Foo = impl Trait<Item = dyn Bar +
/// 'static>`. The latter uses `ImplicitObjectLifetimeDefault` so
/// that surrounding code knows not to create a lifetime
/// parameter.
ImplicitObjectLifetimeDefault,
/// Indicates an error during lowering (usually `'_` in wrong place)
/// that was already reported.
Error,
@ -235,7 +248,9 @@ pub enum LifetimeName {
impl LifetimeName {
pub fn ident(&self) -> Ident {
match *self {
LifetimeName::Implicit | LifetimeName::Error => Ident::invalid(),
LifetimeName::ImplicitObjectLifetimeDefault
| LifetimeName::Implicit
| LifetimeName::Error => Ident::invalid(),
LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime),
LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime),
LifetimeName::Param(param_name) => param_name.ident(),
@ -244,7 +259,9 @@ impl LifetimeName {
pub fn is_elided(&self) -> bool {
match self {
LifetimeName::Implicit | LifetimeName::Underscore => true,
LifetimeName::ImplicitObjectLifetimeDefault
| LifetimeName::Implicit
| LifetimeName::Underscore => true,
// It might seem surprising that `Fresh(_)` counts as
// *not* elided -- but this is because, as far as the code

View File

@ -5,6 +5,8 @@
//! used between functions, and they operate in a purely top-down
//! way. Therefore, we break lifetime name resolution into a separate pass.
// ignore-tidy-filelength
use crate::hir::def::{Res, DefKind};
use crate::hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
use crate::hir::map::Map;
@ -591,6 +593,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
}
match lifetime.name {
LifetimeName::Implicit => {
// For types like `dyn Foo`, we should
// generate a special form of elided.
span_bug!(
ty.span,
"object-lifetime-default expected, not implict",
);
}
LifetimeName::ImplicitObjectLifetimeDefault => {
// If the user does not write *anything*, we
// use the object lifetime defaulting
// rules. So e.g., `Box<dyn Debug>` becomes
@ -2643,6 +2653,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => {
self.resolve_lifetime_ref(lt);
}
hir::LifetimeName::ImplicitObjectLifetimeDefault => {
self.tcx.sess.delay_span_bug(
lt.span,
"lowering generated `ImplicitObjectLifetimeDefault` \
outside of an object type",
)
}
hir::LifetimeName::Error => {
// No need to do anything, error already reported.
}

View File

@ -578,7 +578,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
})
}
hir::LifetimeName::Implicit => {
hir::LifetimeName::ImplicitObjectLifetimeDefault
| hir::LifetimeName::Implicit => {
// In this case, the user left off the lifetime; so
// they wrote something like:
//

View File

@ -0,0 +1,23 @@
// Regression test for #62517. We used to ICE when you had an `async
// fn` with an `impl Trait` return that mentioned a `dyn Bar` with no
// explicit lifetime bound.
//
// edition:2018
// check-pass
#![feature(async_await)]
trait FirstTrait {}
trait SecondTrait {
type Item: ?Sized;
}
async fn foo(x: &str) -> impl SecondTrait<Item = dyn FirstTrait> {
}
impl<T> SecondTrait for T {
type Item = dyn FirstTrait;
}
fn main() { }

View File

@ -0,0 +1,16 @@
// Regression test for #62517. We used to ICE when you had an `async
// fn` with an `impl Trait` return that mentioned a `dyn Bar` with no
// explicit lifetime bound.
//
// edition:2018
#![feature(async_await)]
trait Object {}
trait Alpha<Param> {}
async fn foo<'a>(_: &'a ()) -> impl Alpha<dyn Object> {}
//~^ ERROR not satisfied
fn main() { }

View File

@ -0,0 +1,11 @@
error[E0277]: the trait bound `(): Alpha<(dyn Object + 'static)>` is not satisfied
--> $DIR/issue-62517-2.rs:13:32
|
LL | async fn foo<'a>(_: &'a ()) -> impl Alpha<dyn Object> {}
| ^^^^^^^^^^^^^^^^^^^^^^ the trait `Alpha<(dyn Object + 'static)>` is not implemented for `()`
|
= note: the return type of a function must have a statically known size
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,16 @@
// Test that we don't get an error with `dyn Bar` in an impl Trait
// when there are multiple inputs. The `dyn Bar` should default to `+
// 'static`. This used to erroneously generate an error (cc #62517).
//
// check-pass
trait Foo { type Item: ?Sized; }
trait Bar { }
impl<T> Foo for T {
type Item = dyn Bar;
}
fn foo(x: &str, y: &str) -> impl Foo<Item = dyn Bar> { () }
fn main() { }

View File

@ -0,0 +1,11 @@
// Test that we don't get an error with `dyn Object` in an impl Trait
// when there are multiple inputs. The `dyn Object` should default to `+
// 'static`. This used to erroneously generate an error (cc #62517).
//
// check-pass
trait Alpha<Item: ?Sized> {}
trait Object {}
impl<T> Alpha<dyn Object> for T {}
fn alpha(x: &str, y: &str) -> impl Alpha<dyn Object> { () }
fn main() { }

View File

@ -0,0 +1,23 @@
// Test that `impl Alpha<dyn Object>` resets the object-lifetime
// default to `'static`.
//
// check-pass
trait Alpha<Item: ?Sized> {
fn item(&self) -> Box<Item> {
panic!()
}
}
trait Object {}
impl<T> Alpha<dyn Object> for T {}
fn alpha(x: &str, y: &str) -> impl Alpha<dyn Object> { () }
fn is_static<T>(_: T) where T: 'static { }
fn bar(x: &str) -> &impl Alpha<dyn Object> { &() }
fn main() {
let s = format!("foo");
let r = bar(&s);
is_static(r.item());
}

View File

@ -6,7 +6,7 @@ trait Future {
use std::error::Error;
fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
//~^ ERROR missing lifetime
//~^ ERROR not satisfied
Ok(())
}

View File

@ -1,11 +1,11 @@
error[E0106]: missing lifetime specifier
--> $DIR/lifetime-elision-return-type-trait.rs:8:44
error[E0277]: the trait bound `std::result::Result<(), _>: Future` is not satisfied
--> $DIR/lifetime-elision-return-type-trait.rs:8:13
|
LL | fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
| ^^^^^^^^^ help: consider giving it a 'static lifetime: `dyn Error + 'static`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Future` is not implemented for `std::result::Result<(), _>`
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
= note: the return type of a function must have a statically known size
error: aborting due to previous error
For more information about this error, try `rustc --explain E0106`.
For more information about this error, try `rustc --explain E0277`.