Skip to content

Commit 88af837

Browse files
committed
Inherit lifetimes for async fn instead of duplicating them.
1 parent 72d4713 commit 88af837

23 files changed

+178
-296
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+19-50
Original file line numberDiff line numberDiff line change
@@ -1649,11 +1649,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16491649

16501650
// When we create the opaque type for this async fn, it is going to have
16511651
// to capture all the lifetimes involved in the signature (including in the
1652-
// return type). This is done by introducing lifetime parameters for:
1652+
// return type). This is done by:
16531653
//
1654-
// - all the explicitly declared lifetimes from the impl and function itself;
1655-
// - all the elided lifetimes in the fn arguments;
1656-
// - all the elided lifetimes in the return type.
1654+
// - making the opaque type inherit all lifetime parameters from its parent;
1655+
// - make all the elided lifetimes in the fn arguments into parameters;
1656+
// - manually introducing parameters on the opaque type for elided
1657+
// lifetimes in the return type.
16571658
//
16581659
// So for example in this snippet:
16591660
//
@@ -1669,44 +1670,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16691670
// we would create an opaque type like:
16701671
//
16711672
// ```
1672-
// type Bar<'a, 'b, '0, '1, '2> = impl Future<Output = &'2 u32>;
1673+
// type Foo<'a>::bar<'b, '0, '1>::Bar<'2> = impl Future<Output = &'2 u32>;
16731674
// ```
16741675
//
16751676
// and we would then desugar `bar` to the equivalent of:
16761677
//
16771678
// ```rust
16781679
// impl<'a> Foo<'a> {
1679-
// fn bar<'b, '0, '1>(&'0 self, x: &'b Vec<f64>, y: &'1 str) -> Bar<'a, 'b, '0, '1, '_>
1680+
// fn bar<'b, '0, '1>(&'0 self, x: &'b Vec<f64>, y: &'1 str) -> Bar<'_>
16801681
// }
16811682
// ```
16821683
//
16831684
// Note that the final parameter to `Bar` is `'_`, not `'2` --
16841685
// this is because the elided lifetimes from the return type
16851686
// should be figured out using the ordinary elision rules, and
16861687
// this desugaring achieves that.
1687-
1688-
debug!("lower_async_fn_ret_ty: in_scope_lifetimes={:#?}", self.in_scope_lifetimes);
1689-
debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", self.lifetimes_to_define);
1690-
1691-
// Calculate all the lifetimes that should be captured
1692-
// by the opaque type. This should include all in-scope
1693-
// lifetime parameters, including those defined in-band.
1694-
//
1695-
// `lifetime_params` is a vector of tuple (span, parameter name, lifetime name).
1696-
1697-
// Input lifetime like `'a` or `'1`:
1698-
let mut lifetime_params: Vec<_> = self
1699-
.in_scope_lifetimes
1700-
.iter()
1701-
.cloned()
1702-
.map(|name| (name.ident().span, name, hir::LifetimeName::Param(name)))
1703-
.chain(
1704-
self.lifetimes_to_define
1705-
.iter()
1706-
.map(|&(span, name)| (span, name, hir::LifetimeName::Param(name))),
1707-
)
1708-
.collect();
1709-
1688+
let mut lifetime_params = Vec::new();
17101689
self.with_hir_id_owner(opaque_ty_node_id, |this| {
17111690
// We have to be careful to get elision right here. The
17121691
// idea is that we create a lifetime parameter for each
@@ -1725,16 +1704,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
17251704
debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
17261705
debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", lifetimes_to_define);
17271706

1728-
lifetime_params.extend(
1729-
// Output lifetime like `'_`:
1730-
lifetimes_to_define
1731-
.into_iter()
1732-
.map(|(span, name)| (span, name, hir::LifetimeName::Implicit)),
1733-
);
1707+
// Output lifetime like `'_`:
1708+
lifetime_params = lifetimes_to_define;
17341709
debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params);
17351710

17361711
let generic_params =
1737-
this.arena.alloc_from_iter(lifetime_params.iter().map(|&(span, hir_name, _)| {
1712+
this.arena.alloc_from_iter(lifetime_params.iter().map(|&(span, hir_name)| {
17381713
this.lifetime_to_generic_param(span, hir_name, opaque_ty_def_id)
17391714
}));
17401715

@@ -1752,28 +1727,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
17521727
this.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
17531728
});
17541729

1755-
// As documented above on the variable
1756-
// `input_lifetimes_count`, we need to create the lifetime
1757-
// arguments to our opaque type. Continuing with our example,
1758-
// we're creating the type arguments for the return type:
1730+
// We need to create the lifetime arguments to our opaque type.
1731+
// Continuing with our example, we're creating the type arguments
1732+
// for the return type:
17591733
//
17601734
// ```
1761-
// Bar<'a, 'b, '0, '1, '_>
1735+
// For<'a>::bar<'b, '0, '1>::Bar<'_>
17621736
// ```
17631737
//
1764-
// For the "input" lifetime parameters, we wish to create
1765-
// references to the parameters themselves, including the
1766-
// "implicit" ones created from parameter types (`'a`, `'b`,
1767-
// '`0`, `'1`).
1768-
//
1769-
// For the "output" lifetime parameters, we just want to
1770-
// generate `'_`.
1738+
// For the "input" lifetime parameters are inherited automatically.
1739+
// For the "output" lifetime parameters, we just want to generate `'_`.
17711740
let generic_args =
1772-
self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, _, name)| {
1741+
self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, _)| {
17731742
GenericArg::Lifetime(hir::Lifetime {
17741743
hir_id: self.next_id(),
17751744
span: self.lower_span(span),
1776-
name,
1745+
name: hir::LifetimeName::Implicit,
17771746
})
17781747
}));
17791748

compiler/rustc_infer/src/infer/opaque_types.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
276276
debug!(?concrete_ty);
277277

278278
let first_own_region = match opaque_defn.origin {
279-
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {
279+
hir::OpaqueTyOrigin::FnReturn(..) => {
280280
// We lower
281281
//
282282
// fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
@@ -291,7 +291,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
291291
}
292292
// These opaque type inherit all lifetime parameters from their
293293
// parent, so we have to check them all.
294-
hir::OpaqueTyOrigin::TyAlias => 0,
294+
hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::TyAlias => 0,
295295
};
296296

297297
// For a case like `impl Foo<'a, 'b>`, we would generate a constraint

compiler/rustc_resolve/src/late/lifetimes.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -731,9 +731,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
731731
match item.kind {
732732
hir::ItemKind::Fn(ref sig, ref generics, _) => {
733733
self.missing_named_lifetime_spots.push(generics.into());
734-
self.visit_early_late(None, item.hir_id(), &sig.decl, generics, |this| {
735-
intravisit::walk_item(this, item);
736-
});
734+
self.visit_early_late(
735+
None,
736+
item.hir_id(),
737+
&sig.decl,
738+
generics,
739+
sig.header.asyncness,
740+
|this| {
741+
intravisit::walk_item(this, item);
742+
},
743+
);
737744
self.missing_named_lifetime_spots.pop();
738745
}
739746

@@ -851,11 +858,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
851858

852859
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
853860
match item.kind {
854-
hir::ForeignItemKind::Fn(ref decl, _, ref generics) => {
855-
self.visit_early_late(None, item.hir_id(), decl, generics, |this| {
861+
hir::ForeignItemKind::Fn(ref decl, _, ref generics) => self.visit_early_late(
862+
None,
863+
item.hir_id(),
864+
decl,
865+
generics,
866+
hir::IsAsync::NotAsync,
867+
|this| {
856868
intravisit::walk_foreign_item(this, item);
857-
})
858-
}
869+
},
870+
),
859871
hir::ForeignItemKind::Static(..) => {
860872
intravisit::walk_foreign_item(self, item);
861873
}
@@ -1141,6 +1153,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
11411153
trait_item.hir_id(),
11421154
&sig.decl,
11431155
&trait_item.generics,
1156+
sig.header.asyncness,
11441157
|this| intravisit::walk_trait_item(this, trait_item),
11451158
);
11461159
self.missing_named_lifetime_spots.pop();
@@ -1210,6 +1223,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
12101223
impl_item.hir_id(),
12111224
&sig.decl,
12121225
&impl_item.generics,
1226+
sig.header.asyncness,
12131227
|this| intravisit::walk_impl_item(this, impl_item),
12141228
);
12151229
self.missing_named_lifetime_spots.pop();
@@ -2187,11 +2201,15 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
21872201
hir_id: hir::HirId,
21882202
decl: &'tcx hir::FnDecl<'tcx>,
21892203
generics: &'tcx hir::Generics<'tcx>,
2204+
asyncness: hir::IsAsync,
21902205
walk: F,
21912206
) where
21922207
F: for<'b, 'c> FnOnce(&'b mut LifetimeContext<'c, 'tcx>),
21932208
{
2194-
insert_late_bound_lifetimes(self.map, decl, generics);
2209+
// Async fns need all their lifetime parameters to be early bound.
2210+
if asyncness != hir::IsAsync::Async {
2211+
insert_late_bound_lifetimes(self.map, decl, generics);
2212+
}
21952213

21962214
// Find the start of nested early scopes, e.g., in methods.
21972215
let mut next_early_index = 0;

compiler/rustc_typeck/src/astconv/mod.rs

+5-10
Original file line numberDiff line numberDiff line change
@@ -2337,16 +2337,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
23372337
let def_id = item_id.def_id.to_def_id();
23382338

23392339
match opaque_ty.kind {
2340-
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => self
2341-
.impl_trait_ty_to_ty(
2342-
def_id,
2343-
lifetimes,
2344-
matches!(
2345-
origin,
2346-
hir::OpaqueTyOrigin::FnReturn(..)
2347-
| hir::OpaqueTyOrigin::AsyncFn(..)
2348-
),
2349-
),
2340+
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
2341+
let replace_parent_lifetimes =
2342+
matches!(origin, hir::OpaqueTyOrigin::FnReturn(..));
2343+
self.impl_trait_ty_to_ty(def_id, lifetimes, replace_parent_lifetimes)
2344+
}
23502345
ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
23512346
}
23522347
}

compiler/rustc_typeck/src/check/check.rs

+3-12
Original file line numberDiff line numberDiff line change
@@ -540,10 +540,8 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
540540
}
541541
}
542542

543-
if let ItemKind::OpaqueTy(hir::OpaqueTy {
544-
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
545-
..
546-
}) = item.kind
543+
if let ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(..), .. }) =
544+
item.kind
547545
{
548546
let mut visitor = ProhibitOpaqueVisitor {
549547
opaque_identity_ty: tcx.mk_opaque(
@@ -565,20 +563,13 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
565563

566564
if let Some(ty) = prohibit_opaque.break_value() {
567565
visitor.visit_item(&item);
568-
let is_async = match item.kind {
569-
ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
570-
matches!(origin, hir::OpaqueTyOrigin::AsyncFn(..))
571-
}
572-
_ => unreachable!(),
573-
};
574566

575567
let mut err = struct_span_err!(
576568
tcx.sess,
577569
span,
578570
E0760,
579-
"`{}` return type cannot contain a projection or `Self` that references lifetimes from \
571+
"`impl Trait` return type cannot contain a projection or `Self` that references lifetimes from \
580572
a parent scope",
581-
if is_async { "async fn" } else { "impl Trait" },
582573
);
583574

584575
for (span, name) in visitor.selftys {

compiler/rustc_typeck/src/collect.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -2063,8 +2063,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
20632063
generics
20642064
}
20652065
ItemKind::OpaqueTy(OpaqueTy {
2066-
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
2067-
..
2066+
origin: hir::OpaqueTyOrigin::FnReturn(..), ..
20682067
}) => {
20692068
// return-position impl trait
20702069
//
@@ -2084,7 +2083,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
20842083
}
20852084
ItemKind::OpaqueTy(OpaqueTy {
20862085
ref generics,
2087-
origin: hir::OpaqueTyOrigin::TyAlias,
2086+
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::TyAlias,
20882087
..
20892088
}) => {
20902089
// type-alias impl trait

src/test/ui/async-await/issue-61949-self-return-type.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ pub struct Foo<'a> {
88

99
impl<'a> Foo<'a> {
1010
pub async fn new(_bar: &'a i32) -> Self {
11-
//~^ ERROR `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
1211
Foo {
1312
bar: &22
1413
}
@@ -19,6 +18,7 @@ async fn foo() {
1918
let x = {
2019
let bar = 22;
2120
Foo::new(&bar).await
21+
//~^ ERROR `bar` does not live long enough [E0597]
2222
};
2323
drop(x);
2424
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
error[E0760]: `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
2-
--> $DIR/issue-61949-self-return-type.rs:10:40
1+
error[E0597]: `bar` does not live long enough
2+
--> $DIR/issue-61949-self-return-type.rs:20:18
33
|
4-
LL | pub async fn new(_bar: &'a i32) -> Self {
5-
| ^^^^ help: consider spelling out the type instead: `Foo<'a>`
4+
LL | let x = {
5+
| - borrow later stored here
6+
LL | let bar = 22;
7+
LL | Foo::new(&bar).await
8+
| ^^^^ borrowed value does not live long enough
9+
LL |
10+
LL | };
11+
| - `bar` dropped here while still borrowed
612

713
error: aborting due to previous error
814

9-
For more information about this error, try `rustc --explain E0760`.
15+
For more information about this error, try `rustc --explain E0597`.

src/test/ui/async-await/issue-74072-lifetime-name-annotations.stderr

-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
error[E0506]: cannot assign to `*x` because it is borrowed
22
--> $DIR/issue-74072-lifetime-name-annotations.rs:9:5
33
|
4-
LL | pub async fn async_fn(x: &mut i32) -> &i32 {
5-
| - let's call the lifetime of this reference `'1`
64
LL | let y = &*x;
75
| --- borrow of `*x` occurs here
86
LL | *x += 1;
97
| ^^^^^^^ assignment to borrowed `*x` occurs here
10-
LL | y
11-
| - returning this value requires that `*x` is borrowed for `'1`
128

139
error[E0506]: cannot assign to `*x` because it is borrowed
1410
--> $DIR/issue-74072-lifetime-name-annotations.rs:16:9

src/test/ui/async-await/issue-75785-confusing-named-region.stderr

-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
error[E0506]: cannot assign to `*x` because it is borrowed
22
--> $DIR/issue-75785-confusing-named-region.rs:9:5
33
|
4-
LL | pub async fn async_fn(x: &mut i32) -> (&i32, &i32) {
5-
| - let's call the lifetime of this reference `'1`
64
LL | let y = &*x;
75
| --- borrow of `*x` occurs here
86
LL | *x += 1;
97
| ^^^^^^^ assignment to borrowed `*x` occurs here
10-
LL | (&32, y)
11-
| -------- returning this value requires that `*x` is borrowed for `'1`
128

139
error: aborting due to previous error
1410

src/test/ui/async-await/issue-76547.stderr

+4-10
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,17 @@ error[E0623]: lifetime mismatch
22
--> $DIR/issue-76547.rs:20:13
33
|
44
LL | async fn fut(bufs: &mut [&mut [u8]]) {
5-
| --------- -
6-
| | |
7-
| | this `async fn` implicitly returns an `impl Future<Output = ()>`
8-
| this parameter and the returned future are declared with different lifetimes...
5+
| ---------------- these two types are declared with different lifetimes...
96
LL | ListFut(bufs).await
10-
| ^^^^ ...but data from `bufs` is held across an await point here
7+
| ^^^^ ...but data from `bufs` flows into `bufs` here
118

129
error[E0623]: lifetime mismatch
1310
--> $DIR/issue-76547.rs:34:14
1411
|
1512
LL | async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
16-
| --------- ---
17-
| | |
18-
| | this `async fn` implicitly returns an `impl Future<Output = i32>`
19-
| this parameter and the returned future are declared with different lifetimes...
13+
| ---------------- these two types are declared with different lifetimes...
2014
LL | ListFut2(bufs).await
21-
| ^^^^ ...but data from `bufs` is held across an await point here
15+
| ^^^^ ...but data from `bufs` flows into `bufs` here
2216

2317
error: aborting due to 2 previous errors
2418

0 commit comments

Comments
 (0)