Auto merge of #91403 - cjgillot:inherit-async, r=oli-obk
Inherit lifetimes for async fn instead of duplicating them. The current desugaring of `async fn foo<'a>(&usize) -> &u8` is equivalent to ```rust fn foo<'a, '0>(&'0 usize) -> foo<'static, 'static>::Opaque<'a, '0, '_>; type foo<'_a, '_0>::Opaque<'a, '0, '1> = impl Future<Output = &'1 u8>; ``` following the RPIT model. Duplicating all the inherited lifetime parameters and setting the inherited version to `'static` makes lowering more complex and causes issues like #61949. This PR removes the duplication of inherited lifetimes to directly use ```rust fn foo<'a, '0>(&'0 usize) -> foo<'a, '0>::Opaque<'_>; type foo<'a, '0>::Opaque<'1> = impl Future<Output = &'1 u8>; ``` following the TAIT model. Fixes https://github.com/rust-lang/rust/issues/61949
This commit is contained in:
commit
3cfa4def7c
34 changed files with 227 additions and 281 deletions
|
|
@ -1659,11 +1659,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
// When we create the opaque type for this async fn, it is going to have
|
||||
// to capture all the lifetimes involved in the signature (including in the
|
||||
// return type). This is done by introducing lifetime parameters for:
|
||||
// return type). This is done by:
|
||||
//
|
||||
// - all the explicitly declared lifetimes from the impl and function itself;
|
||||
// - all the elided lifetimes in the fn arguments;
|
||||
// - all the elided lifetimes in the return type.
|
||||
// - making the opaque type inherit all lifetime parameters from its parent;
|
||||
// - make all the elided lifetimes in the fn arguments into parameters;
|
||||
// - manually introducing parameters on the opaque type for elided
|
||||
// lifetimes in the return type.
|
||||
//
|
||||
// So for example in this snippet:
|
||||
//
|
||||
|
|
@ -1679,14 +1680,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// we would create an opaque type like:
|
||||
//
|
||||
// ```
|
||||
// type Bar<'a, 'b, '0, '1, '2> = impl Future<Output = &'2 u32>;
|
||||
// type Foo<'a>::bar<'b, '0, '1>::Bar<'2> = impl Future<Output = &'2 u32>;
|
||||
// ```
|
||||
//
|
||||
// and we would then desugar `bar` to the equivalent of:
|
||||
//
|
||||
// ```rust
|
||||
// impl<'a> Foo<'a> {
|
||||
// fn bar<'b, '0, '1>(&'0 self, x: &'b Vec<f64>, y: &'1 str) -> Bar<'a, 'b, '0, '1, '_>
|
||||
// fn bar<'b, '0, '1>(&'0 self, x: &'b Vec<f64>, y: &'1 str) -> Bar<'_>
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
|
|
@ -1694,29 +1695,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// this is because the elided lifetimes from the return type
|
||||
// should be figured out using the ordinary elision rules, and
|
||||
// this desugaring achieves that.
|
||||
|
||||
debug!("lower_async_fn_ret_ty: in_scope_lifetimes={:#?}", self.in_scope_lifetimes);
|
||||
debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", self.lifetimes_to_define);
|
||||
|
||||
// Calculate all the lifetimes that should be captured
|
||||
// by the opaque type. This should include all in-scope
|
||||
// lifetime parameters, including those defined in-band.
|
||||
//
|
||||
// `lifetime_params` is a vector of tuple (span, parameter name, lifetime name).
|
||||
|
||||
// Input lifetime like `'a` or `'1`:
|
||||
let mut lifetime_params: Vec<_> = self
|
||||
.in_scope_lifetimes
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|name| (name.ident().span, name, hir::LifetimeName::Param(name)))
|
||||
.chain(
|
||||
self.lifetimes_to_define
|
||||
.iter()
|
||||
.map(|&(span, name)| (span, name, hir::LifetimeName::Param(name))),
|
||||
)
|
||||
.collect();
|
||||
|
||||
let mut lifetime_params = Vec::new();
|
||||
self.with_hir_id_owner(opaque_ty_node_id, |this| {
|
||||
// We have to be careful to get elision right here. The
|
||||
// idea is that we create a lifetime parameter for each
|
||||
|
|
@ -1735,16 +1714,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
|
||||
debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", lifetimes_to_define);
|
||||
|
||||
lifetime_params.extend(
|
||||
// Output lifetime like `'_`:
|
||||
lifetimes_to_define
|
||||
.into_iter()
|
||||
.map(|(span, name)| (span, name, hir::LifetimeName::Implicit(false))),
|
||||
);
|
||||
// Output lifetime like `'_`:
|
||||
lifetime_params = lifetimes_to_define;
|
||||
debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params);
|
||||
|
||||
let generic_params =
|
||||
this.arena.alloc_from_iter(lifetime_params.iter().map(|&(span, hir_name, _)| {
|
||||
this.arena.alloc_from_iter(lifetime_params.iter().map(|&(span, hir_name)| {
|
||||
this.lifetime_to_generic_param(span, hir_name, opaque_ty_def_id)
|
||||
}));
|
||||
|
||||
|
|
@ -1762,28 +1737,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
this.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
|
||||
});
|
||||
|
||||
// As documented above on the variable
|
||||
// `input_lifetimes_count`, we need to create the lifetime
|
||||
// arguments to our opaque type. Continuing with our example,
|
||||
// we're creating the type arguments for the return type:
|
||||
// We need to create the lifetime arguments to our opaque type.
|
||||
// Continuing with our example, we're creating the type arguments
|
||||
// for the return type:
|
||||
//
|
||||
// ```
|
||||
// Bar<'a, 'b, '0, '1, '_>
|
||||
// For<'a>::bar<'b, '0, '1>::Bar<'_>
|
||||
// ```
|
||||
//
|
||||
// For the "input" lifetime parameters, we wish to create
|
||||
// references to the parameters themselves, including the
|
||||
// "implicit" ones created from parameter types (`'a`, `'b`,
|
||||
// '`0`, `'1`).
|
||||
//
|
||||
// For the "output" lifetime parameters, we just want to
|
||||
// generate `'_`.
|
||||
// For the "input" lifetime parameters are inherited automatically.
|
||||
// For the "output" lifetime parameters, we just want to generate `'_`.
|
||||
let generic_args =
|
||||
self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, _, name)| {
|
||||
self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, _)| {
|
||||
GenericArg::Lifetime(hir::Lifetime {
|
||||
hir_id: self.next_id(),
|
||||
span: self.lower_span(span),
|
||||
name,
|
||||
name: hir::LifetimeName::Implicit(false),
|
||||
})
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -2156,6 +2156,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// When in async fn, prefer errors that come from inside the closure.
|
||||
if !categorized_path[i].from_closure {
|
||||
let span = categorized_path.iter().find_map(|p| {
|
||||
if p.from_closure
|
||||
&& p.category == categorized_path[i].category
|
||||
&& categorized_path[i].cause.span.contains(p.cause.span)
|
||||
{
|
||||
Some(p.cause.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(span) = span {
|
||||
categorized_path[i].cause.span = span;
|
||||
}
|
||||
}
|
||||
|
||||
return categorized_path[i].clone();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
`async fn`/`impl trait` return type cannot contain a projection
|
||||
`impl trait` return type cannot contain a projection
|
||||
or `Self` that references lifetimes from a parent scope.
|
||||
|
||||
Erroneous code example:
|
||||
|
|
@ -7,7 +7,7 @@ Erroneous code example:
|
|||
struct S<'a>(&'a i32);
|
||||
|
||||
impl<'a> S<'a> {
|
||||
async fn new(i: &'a i32) -> Self {
|
||||
fn new(i: &'a i32) -> impl Into<Self> {
|
||||
S(&22)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ To fix this error we need to spell out `Self` to `S<'a>`:
|
|||
struct S<'a>(&'a i32);
|
||||
|
||||
impl<'a> S<'a> {
|
||||
async fn new(i: &'a i32) -> S<'a> {
|
||||
fn new(i: &'a i32) -> impl Into<S<'a>> {
|
||||
S(&22)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
debug!(?concrete_ty);
|
||||
|
||||
let first_own_region = match opaque_defn.origin {
|
||||
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {
|
||||
hir::OpaqueTyOrigin::FnReturn(..) => {
|
||||
// We lower
|
||||
//
|
||||
// fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
|
||||
|
|
@ -291,7 +291,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
}
|
||||
// These opaque type inherit all lifetime parameters from their
|
||||
// parent, so we have to check them all.
|
||||
hir::OpaqueTyOrigin::TyAlias => 0,
|
||||
hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::TyAlias => 0,
|
||||
};
|
||||
|
||||
// For a case like `impl Foo<'a, 'b>`, we would generate a constraint
|
||||
|
|
|
|||
|
|
@ -729,9 +729,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
match item.kind {
|
||||
hir::ItemKind::Fn(ref sig, ref generics, _) => {
|
||||
self.missing_named_lifetime_spots.push(generics.into());
|
||||
self.visit_early_late(None, item.hir_id(), &sig.decl, generics, |this| {
|
||||
intravisit::walk_item(this, item);
|
||||
});
|
||||
self.visit_early_late(
|
||||
None,
|
||||
item.hir_id(),
|
||||
&sig.decl,
|
||||
generics,
|
||||
sig.header.asyncness,
|
||||
|this| {
|
||||
intravisit::walk_item(this, item);
|
||||
},
|
||||
);
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
|
||||
|
|
@ -849,11 +856,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
|
||||
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
|
||||
match item.kind {
|
||||
hir::ForeignItemKind::Fn(ref decl, _, ref generics) => {
|
||||
self.visit_early_late(None, item.hir_id(), decl, generics, |this| {
|
||||
hir::ForeignItemKind::Fn(ref decl, _, ref generics) => self.visit_early_late(
|
||||
None,
|
||||
item.hir_id(),
|
||||
decl,
|
||||
generics,
|
||||
hir::IsAsync::NotAsync,
|
||||
|this| {
|
||||
intravisit::walk_foreign_item(this, item);
|
||||
})
|
||||
}
|
||||
},
|
||||
),
|
||||
hir::ForeignItemKind::Static(..) => {
|
||||
intravisit::walk_foreign_item(self, item);
|
||||
}
|
||||
|
|
@ -1130,6 +1142,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
trait_item.hir_id(),
|
||||
&sig.decl,
|
||||
&trait_item.generics,
|
||||
sig.header.asyncness,
|
||||
|this| intravisit::walk_trait_item(this, trait_item),
|
||||
);
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
|
|
@ -1199,6 +1212,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
impl_item.hir_id(),
|
||||
&sig.decl,
|
||||
&impl_item.generics,
|
||||
sig.header.asyncness,
|
||||
|this| intravisit::walk_impl_item(this, impl_item),
|
||||
);
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
|
|
@ -2159,11 +2173,15 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
hir_id: hir::HirId,
|
||||
decl: &'tcx hir::FnDecl<'tcx>,
|
||||
generics: &'tcx hir::Generics<'tcx>,
|
||||
asyncness: hir::IsAsync,
|
||||
walk: F,
|
||||
) where
|
||||
F: for<'b, 'c> FnOnce(&'b mut LifetimeContext<'c, 'tcx>),
|
||||
{
|
||||
insert_late_bound_lifetimes(self.map, decl, generics);
|
||||
// Async fns need all their lifetime parameters to be early bound.
|
||||
if asyncness != hir::IsAsync::Async {
|
||||
insert_late_bound_lifetimes(self.map, decl, generics);
|
||||
}
|
||||
|
||||
// Find the start of nested early scopes, e.g., in methods.
|
||||
let mut next_early_index = 0;
|
||||
|
|
|
|||
|
|
@ -2408,16 +2408,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
let def_id = item_id.def_id.to_def_id();
|
||||
|
||||
match opaque_ty.kind {
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => self
|
||||
.impl_trait_ty_to_ty(
|
||||
def_id,
|
||||
lifetimes,
|
||||
matches!(
|
||||
origin,
|
||||
hir::OpaqueTyOrigin::FnReturn(..)
|
||||
| hir::OpaqueTyOrigin::AsyncFn(..)
|
||||
),
|
||||
),
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
|
||||
let replace_parent_lifetimes =
|
||||
matches!(origin, hir::OpaqueTyOrigin::FnReturn(..));
|
||||
self.impl_trait_ty_to_ty(def_id, lifetimes, replace_parent_lifetimes)
|
||||
}
|
||||
ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -535,10 +535,8 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
if let ItemKind::OpaqueTy(hir::OpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
|
||||
..
|
||||
}) = item.kind
|
||||
if let ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(..), .. }) =
|
||||
item.kind
|
||||
{
|
||||
let mut visitor = ProhibitOpaqueVisitor {
|
||||
opaque_identity_ty: tcx.mk_opaque(
|
||||
|
|
@ -560,20 +558,13 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
|
|||
|
||||
if let Some(ty) = prohibit_opaque.break_value() {
|
||||
visitor.visit_item(&item);
|
||||
let is_async = match item.kind {
|
||||
ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
|
||||
matches!(origin, hir::OpaqueTyOrigin::AsyncFn(..))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut err = struct_span_err!(
|
||||
tcx.sess,
|
||||
span,
|
||||
E0760,
|
||||
"`{}` return type cannot contain a projection or `Self` that references lifetimes from \
|
||||
"`impl Trait` return type cannot contain a projection or `Self` that references lifetimes from \
|
||||
a parent scope",
|
||||
if is_async { "async fn" } else { "impl Trait" },
|
||||
);
|
||||
|
||||
for (span, name) in visitor.selftys {
|
||||
|
|
|
|||
|
|
@ -2162,8 +2162,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
|
|||
generics
|
||||
}
|
||||
ItemKind::OpaqueTy(OpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
|
||||
..
|
||||
origin: hir::OpaqueTyOrigin::FnReturn(..), ..
|
||||
}) => {
|
||||
// return-position impl trait
|
||||
//
|
||||
|
|
@ -2183,7 +2182,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
|
|||
}
|
||||
ItemKind::OpaqueTy(OpaqueTy {
|
||||
ref generics,
|
||||
origin: hir::OpaqueTyOrigin::TyAlias,
|
||||
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::TyAlias,
|
||||
..
|
||||
}) => {
|
||||
// type-alias impl trait
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue