Skip to content

Commit 03e7b96

Browse files
committed
revamp how we handle elision in async fn
We now always make fresh lifetimne parameters for all elided lifetimes, whether they are in the inputs or outputs. But then we generate `'_` in the case of elided lifetimes from the outputs. Example: ```rust async fn foo<'a>(x: &'a u32) -> &u32 { .. } ``` becomes ```rust type Foo<'a, 'b> = impl Future<Output = &'b u32>; fn foo<'a>(x: &'a u32) -> Foo<'a, '_> ```
1 parent 5ce8f7a commit 03e7b96

30 files changed

+383
-1637
lines changed

src/librustc/hir/lowering.rs

+56-125
Original file line numberDiff line numberDiff line change
@@ -341,49 +341,6 @@ enum AnonymousLifetimeMode {
341341

342342
/// Pass responsibility to `resolve_lifetime` code for all cases.
343343
PassThrough,
344-
345-
/// Used in the return types of `async fn` where there exists
346-
/// exactly one argument-position elided lifetime.
347-
///
348-
/// In `async fn`, we lower the arguments types using the `CreateParameter`
349-
/// mode, meaning that non-`dyn` elided lifetimes are assigned a fresh name.
350-
/// If any corresponding elided lifetimes appear in the output, we need to
351-
/// replace them with references to the fresh name assigned to the corresponding
352-
/// elided lifetime in the arguments.
353-
///
354-
/// For **Modern cases**, replace the anonymous parameter with a
355-
/// reference to a specific freshly-named lifetime that was
356-
/// introduced in argument
357-
///
358-
/// For **Dyn Bound** cases, pass responsibility to
359-
/// `resole_lifetime` code.
360-
Replace(LtReplacement),
361-
}
362-
363-
/// The type of elided lifetime replacement to perform on `async fn` return types.
364-
#[derive(Copy, Clone)]
365-
enum LtReplacement {
366-
/// Fresh name introduced by the single non-dyn elided lifetime
367-
/// in the arguments of the async fn.
368-
Some(ParamName),
369-
370-
/// There is no single non-dyn elided lifetime because no lifetimes
371-
/// appeared in the arguments.
372-
NoLifetimes,
373-
374-
/// There is no single non-dyn elided lifetime because multiple
375-
/// lifetimes appeared in the arguments.
376-
MultipleLifetimes,
377-
}
378-
379-
/// Calculates the `LtReplacement` to use for elided lifetimes in the return
380-
/// type based on the fresh elided lifetimes introduced in argument position.
381-
fn get_elided_lt_replacement(arg_position_lifetimes: &[(Span, ParamName)]) -> LtReplacement {
382-
match arg_position_lifetimes {
383-
[] => LtReplacement::NoLifetimes,
384-
[(_span, param)] => LtReplacement::Some(*param),
385-
_ => LtReplacement::MultipleLifetimes,
386-
}
387344
}
388345

389346
struct ImplTraitTypeIdVisitor<'a> { ids: &'a mut SmallVec<[NodeId; 1]> }
@@ -2318,8 +2275,7 @@ impl<'a> LoweringContext<'a> {
23182275
err.emit();
23192276
}
23202277
AnonymousLifetimeMode::PassThrough |
2321-
AnonymousLifetimeMode::ReportError |
2322-
AnonymousLifetimeMode::Replace(_) => {
2278+
AnonymousLifetimeMode::ReportError => {
23232279
self.sess.buffer_lint_with_diagnostic(
23242280
ELIDED_LIFETIMES_IN_PATHS,
23252281
CRATE_NODE_ID,
@@ -2515,7 +2471,6 @@ impl<'a> LoweringContext<'a> {
25152471

25162472
// Remember how many lifetimes were already around so that we can
25172473
// only look at the lifetime parameters introduced by the arguments.
2518-
let lifetime_count_before_args = self.lifetimes_to_define.len();
25192474
let inputs = self.with_anonymous_lifetime_mode(lt_mode, |this| {
25202475
decl.inputs
25212476
.iter()
@@ -2530,16 +2485,10 @@ impl<'a> LoweringContext<'a> {
25302485
});
25312486

25322487
let output = if let Some(ret_id) = make_ret_async {
2533-
// Calculate the `LtReplacement` to use for any return-position elided
2534-
// lifetimes based on the elided lifetime parameters introduced in the args.
2535-
let lt_replacement = get_elided_lt_replacement(
2536-
&self.lifetimes_to_define[lifetime_count_before_args..]
2537-
);
25382488
self.lower_async_fn_ret_ty(
25392489
&decl.output,
25402490
in_band_ty_params.expect("`make_ret_async` but no `fn_def_id`").0,
25412491
ret_id,
2542-
lt_replacement,
25432492
)
25442493
} else {
25452494
match decl.output {
@@ -2604,7 +2553,6 @@ impl<'a> LoweringContext<'a> {
26042553
output: &FunctionRetTy,
26052554
fn_def_id: DefId,
26062555
opaque_ty_node_id: NodeId,
2607-
elided_lt_replacement: LtReplacement,
26082556
) -> hir::FunctionRetTy {
26092557
let span = output.span();
26102558

@@ -2622,9 +2570,18 @@ impl<'a> LoweringContext<'a> {
26222570

26232571
self.allocate_hir_id_counter(opaque_ty_node_id);
26242572

2573+
let input_lifetimes_count = self.in_scope_lifetimes.len() + self.lifetimes_to_define.len();
26252574
let (opaque_ty_id, lifetime_params) = self.with_hir_id_owner(opaque_ty_node_id, |this| {
2575+
// We have to be careful to get elision right here. The
2576+
// idea is that we create a lifetime parameter for each
2577+
// lifetime in the return type. So, given a return type
2578+
// like `async fn foo(..) -> &[&u32]`, we lower to `impl
2579+
// Future<Output = &'1 [ &'2 u32 ]>`.
2580+
//
2581+
// Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
2582+
// hence the elision takes place at the fn site.
26262583
let future_bound = this.with_anonymous_lifetime_mode(
2627-
AnonymousLifetimeMode::Replace(elided_lt_replacement),
2584+
AnonymousLifetimeMode::CreateParameter,
26282585
|this| this.lower_async_fn_output_type_to_future_bound(
26292586
output,
26302587
fn_def_id,
@@ -2678,19 +2635,53 @@ impl<'a> LoweringContext<'a> {
26782635
(opaque_ty_id, lifetime_params)
26792636
});
26802637

2681-
let generic_args =
2682-
lifetime_params
2683-
.iter().cloned()
2684-
.map(|(span, hir_name)| {
2685-
GenericArg::Lifetime(hir::Lifetime {
2686-
hir_id: self.next_id(),
2687-
span,
2688-
name: hir::LifetimeName::Param(hir_name),
2689-
})
2638+
// Create the generic lifetime arguments that we will supply
2639+
// to the opaque return type. Consider:
2640+
//
2641+
// ```rust
2642+
// async fn foo(x: &u32, ) -> &[&u32] { .. }
2643+
// ```
2644+
//
2645+
// Here, we would create something like:
2646+
//
2647+
// ```rust
2648+
// type Foo<'a, 'b, 'c> = impl Future<Output = &'a [&'b u32]>;
2649+
// fn foo<'a>(x: &'a u32) -> Foo<'a, '_, '_>
2650+
// ```
2651+
//
2652+
// Note that for the lifetimes which came from the input
2653+
// (`'a`, here), we supply them as arguments to the return
2654+
// type `Foo`. But for those lifetime parameters (`'b`, `'c`)
2655+
// that we created from the return type, we want to use `'_`
2656+
// in the return type, so as to trigger elision.
2657+
let mut generic_args: Vec<_> =
2658+
lifetime_params[..input_lifetimes_count]
2659+
.iter()
2660+
.map(|&(span, hir_name)| {
2661+
GenericArg::Lifetime(hir::Lifetime {
2662+
hir_id: self.next_id(),
2663+
span,
2664+
name: hir::LifetimeName::Param(hir_name),
26902665
})
2691-
.collect();
2666+
})
2667+
.collect();
2668+
generic_args.extend(
2669+
lifetime_params[input_lifetimes_count..]
2670+
.iter()
2671+
.map(|&(span, _)| {
2672+
GenericArg::Lifetime(hir::Lifetime {
2673+
hir_id: self.next_id(),
2674+
span,
2675+
name: hir::LifetimeName::Implicit,
2676+
})
2677+
})
2678+
);
26922679

2693-
let opaque_ty_ref = hir::TyKind::Def(hir::ItemId { id: opaque_ty_id }, generic_args);
2680+
// Create the `Foo<...>` refernece itself. Note that the `type
2681+
// Foo = impl Trait` is, internally, created as a child of the
2682+
// async fn, so the *type parameters* are inherited. It's
2683+
// only the lifetime parameters that we must supply.
2684+
let opaque_ty_ref = hir::TyKind::Def(hir::ItemId { id: opaque_ty_id }, generic_args.into());
26942685

26952686
hir::FunctionRetTy::Return(P(hir::Ty {
26962687
node: opaque_ty_ref,
@@ -2786,11 +2777,6 @@ impl<'a> LoweringContext<'a> {
27862777
}
27872778

27882779
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(Some(l.id), span),
2789-
2790-
AnonymousLifetimeMode::Replace(replacement) => {
2791-
let hir_id = self.lower_node_id(l.id);
2792-
self.replace_elided_lifetime(hir_id, span, replacement)
2793-
}
27942780
},
27952781
ident => {
27962782
self.maybe_collect_in_band_lifetime(ident);
@@ -2813,39 +2799,6 @@ impl<'a> LoweringContext<'a> {
28132799
}
28142800
}
28152801

2816-
/// Replace a return-position elided lifetime with the elided lifetime
2817-
/// from the arguments.
2818-
fn replace_elided_lifetime(
2819-
&mut self,
2820-
hir_id: hir::HirId,
2821-
span: Span,
2822-
replacement: LtReplacement,
2823-
) -> hir::Lifetime {
2824-
let multiple_or_none = match replacement {
2825-
LtReplacement::Some(name) => {
2826-
return hir::Lifetime {
2827-
hir_id,
2828-
span,
2829-
name: hir::LifetimeName::Param(name),
2830-
};
2831-
}
2832-
LtReplacement::MultipleLifetimes => "multiple",
2833-
LtReplacement::NoLifetimes => "none",
2834-
};
2835-
2836-
let mut err = crate::middle::resolve_lifetime::report_missing_lifetime_specifiers(
2837-
self.sess,
2838-
span,
2839-
1,
2840-
);
2841-
err.note(&format!(
2842-
"return-position elided lifetimes require exactly one \
2843-
input-position elided lifetime, found {}.", multiple_or_none));
2844-
err.emit();
2845-
2846-
hir::Lifetime { hir_id, span, name: hir::LifetimeName::Error }
2847-
}
2848-
28492802
fn lower_generic_params(
28502803
&mut self,
28512804
params: &[GenericParam],
@@ -5791,10 +5744,6 @@ impl<'a> LoweringContext<'a> {
57915744
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
57925745

57935746
AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
5794-
5795-
AnonymousLifetimeMode::Replace(replacement) => {
5796-
self.new_replacement_lifetime(replacement, span)
5797-
}
57985747
}
57995748
}
58005749

@@ -5848,10 +5797,6 @@ impl<'a> LoweringContext<'a> {
58485797
// This is the normal case.
58495798
AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
58505799

5851-
AnonymousLifetimeMode::Replace(replacement) => {
5852-
self.new_replacement_lifetime(replacement, span)
5853-
}
5854-
58555800
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
58565801
}
58575802
}
@@ -5883,25 +5828,11 @@ impl<'a> LoweringContext<'a> {
58835828

58845829
// This is the normal case.
58855830
AnonymousLifetimeMode::PassThrough => {}
5886-
5887-
// We don't need to do any replacement here as this lifetime
5888-
// doesn't refer to an elided lifetime elsewhere in the function
5889-
// signature.
5890-
AnonymousLifetimeMode::Replace(_) => {}
58915831
}
58925832

58935833
self.new_implicit_lifetime(span)
58945834
}
58955835

5896-
fn new_replacement_lifetime(
5897-
&mut self,
5898-
replacement: LtReplacement,
5899-
span: Span,
5900-
) -> hir::Lifetime {
5901-
let hir_id = self.next_id();
5902-
self.replace_elided_lifetime(hir_id, span, replacement)
5903-
}
5904-
59055836
fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {
59065837
hir::Lifetime {
59075838
hir_id: self.next_id(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// edition:2018
2+
3+
#![feature(async_await)]
4+
5+
struct Xyz {
6+
a: u64,
7+
}
8+
9+
trait Foo {}
10+
11+
impl Xyz {
12+
async fn do_sth<'a>(
13+
&'a self, foo: &dyn Foo
14+
) -> &dyn Foo //~ ERROR lifetime mismatch
15+
{
16+
foo
17+
}
18+
}
19+
20+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0623]: lifetime mismatch
2+
--> $DIR/issue-63388-1.rs:14:10
3+
|
4+
LL | &'a self, foo: &dyn Foo
5+
| -------- this parameter and the return type are declared with different lifetimes...
6+
LL | ) -> &dyn Foo
7+
| ^^^^^^^^
8+
| |
9+
| ...but data from `foo` is returned here
10+
11+
error: aborting due to previous error
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// edition:2018
2+
3+
#![feature(async_await)]
4+
5+
struct Xyz {
6+
a: u64,
7+
}
8+
9+
trait Foo {}
10+
11+
impl Xyz {
12+
async fn do_sth<'a>(
13+
foo: &dyn Foo, bar: &'a dyn Foo //~ ERROR cannot infer
14+
) -> &dyn Foo //~ ERROR missing lifetime specifier
15+
{
16+
foo
17+
}
18+
}
19+
20+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error[E0106]: missing lifetime specifier
2+
--> $DIR/issue-63388-2.rs:14:10
3+
|
4+
LL | ) -> &dyn Foo
5+
| ^ help: consider using the named lifetime: `&'a`
6+
|
7+
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `foo` or `bar`
8+
9+
error: cannot infer an appropriate lifetime
10+
--> $DIR/issue-63388-2.rs:13:9
11+
|
12+
LL | foo: &dyn Foo, bar: &'a dyn Foo
13+
| ^^^ ...but this borrow...
14+
LL | ) -> &dyn Foo
15+
| -------- this return type evaluates to the `'static` lifetime...
16+
|
17+
note: ...can't outlive the lifetime '_ as defined on the method body at 13:14
18+
--> $DIR/issue-63388-2.rs:13:14
19+
|
20+
LL | foo: &dyn Foo, bar: &'a dyn Foo
21+
| ^
22+
help: you can add a constraint to the return type to make it last less than `'static` and match the lifetime '_ as defined on the method body at 13:14
23+
|
24+
LL | ) -> &dyn Foo + '_
25+
| ^^^^^^^^^^^^^
26+
27+
error: aborting due to 2 previous errors
28+
29+
For more information about this error, try `rustc --explain E0106`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// edition:2018
2+
// check-pass
3+
4+
#![feature(async_await)]
5+
6+
struct Xyz {
7+
a: u64,
8+
}
9+
10+
trait Foo {}
11+
12+
impl Xyz {
13+
async fn do_sth(
14+
&self, foo: &dyn Foo
15+
) {
16+
}
17+
}
18+
19+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// check-pass
2+
// edition:2018
3+
4+
#![feature(async_await)]
5+
6+
struct A;
7+
8+
impl A {
9+
async fn foo(&self, f: &u32) -> &A { self }
10+
}
11+
12+
fn main() { }

0 commit comments

Comments
 (0)