mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-26 06:35:27 +00:00
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:
parent
b51df1def0
commit
af86fb1959
@ -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 => {}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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:
|
||||
//
|
||||
|
23
src/test/ui/async-await/issues/issue-62517-1.rs
Normal file
23
src/test/ui/async-await/issues/issue-62517-1.rs
Normal 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() { }
|
16
src/test/ui/async-await/issues/issue-62517-2.rs
Normal file
16
src/test/ui/async-await/issues/issue-62517-2.rs
Normal 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() { }
|
11
src/test/ui/async-await/issues/issue-62517-2.stderr
Normal file
11
src/test/ui/async-await/issues/issue-62517-2.stderr
Normal 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`.
|
16
src/test/ui/impl-trait/dyn-trait-elided-two-inputs-assoc.rs
Normal file
16
src/test/ui/impl-trait/dyn-trait-elided-two-inputs-assoc.rs
Normal 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() { }
|
11
src/test/ui/impl-trait/dyn-trait-elided-two-inputs-param.rs
Normal file
11
src/test/ui/impl-trait/dyn-trait-elided-two-inputs-param.rs
Normal 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() { }
|
@ -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());
|
||||
}
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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`.
|
||||
|
Loading…
Reference in New Issue
Block a user