Skip to content

Commit 7a8b19b

Browse files
authored
Rollup merge of rust-lang#63499 - nikomatsakis:issuee-63388-async-fn-elision-self-mut-self, r=cramertj
handle elision in async fn correctly 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, '_> ``` Fixes rust-lang#63388
2 parents c47cb61 + d7c7c52 commit 7a8b19b

35 files changed

+445
-1772
lines changed

src/librustc/hir/lowering.rs

+102-125
Original file line numberDiff line numberDiff line change
@@ -337,49 +337,6 @@ enum AnonymousLifetimeMode {
337337

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

385342
struct ImplTraitTypeIdVisitor<'a> { ids: &'a mut SmallVec<[NodeId; 1]> }
@@ -1953,8 +1910,7 @@ impl<'a> LoweringContext<'a> {
19531910
err.emit();
19541911
}
19551912
AnonymousLifetimeMode::PassThrough |
1956-
AnonymousLifetimeMode::ReportError |
1957-
AnonymousLifetimeMode::Replace(_) => {
1913+
AnonymousLifetimeMode::ReportError => {
19581914
self.sess.buffer_lint_with_diagnostic(
19591915
ELIDED_LIFETIMES_IN_PATHS,
19601916
CRATE_NODE_ID,
@@ -2141,7 +2097,6 @@ impl<'a> LoweringContext<'a> {
21412097

21422098
// Remember how many lifetimes were already around so that we can
21432099
// only look at the lifetime parameters introduced by the arguments.
2144-
let lifetime_count_before_args = self.lifetimes_to_define.len();
21452100
let inputs = self.with_anonymous_lifetime_mode(lt_mode, |this| {
21462101
decl.inputs
21472102
.iter()
@@ -2156,16 +2111,10 @@ impl<'a> LoweringContext<'a> {
21562111
});
21572112

21582113
let output = if let Some(ret_id) = make_ret_async {
2159-
// Calculate the `LtReplacement` to use for any return-position elided
2160-
// lifetimes based on the elided lifetime parameters introduced in the args.
2161-
let lt_replacement = get_elided_lt_replacement(
2162-
&self.lifetimes_to_define[lifetime_count_before_args..]
2163-
);
21642114
self.lower_async_fn_ret_ty(
21652115
&decl.output,
21662116
in_band_ty_params.expect("`make_ret_async` but no `fn_def_id`").0,
21672117
ret_id,
2168-
lt_replacement,
21692118
)
21702119
} else {
21712120
match decl.output {
@@ -2230,7 +2179,6 @@ impl<'a> LoweringContext<'a> {
22302179
output: &FunctionRetTy,
22312180
fn_def_id: DefId,
22322181
opaque_ty_node_id: NodeId,
2233-
elided_lt_replacement: LtReplacement,
22342182
) -> hir::FunctionRetTy {
22352183
let span = output.span();
22362184

@@ -2248,9 +2196,65 @@ impl<'a> LoweringContext<'a> {
22482196

22492197
self.allocate_hir_id_counter(opaque_ty_node_id);
22502198

2199+
// When we create the opaque type for this async fn, it is going to have
2200+
// to capture all the lifetimes involved in the signature (including in the
2201+
// return type). This is done by introducing lifetime parameters for:
2202+
//
2203+
// - all the explicitly declared lifetimes from the impl and function itself;
2204+
// - all the elided lifetimes in the fn arguments;
2205+
// - all the elided lifetimes in the return type.
2206+
//
2207+
// So for example in this snippet:
2208+
//
2209+
// ```rust
2210+
// impl<'a> Foo<'a> {
2211+
// async fn bar<'b>(&self, x: &'b Vec<f64>, y: &str) -> &u32 {
2212+
// // ^ '0 ^ '1 ^ '2
2213+
// // elided lifetimes used below
2214+
// }
2215+
// }
2216+
// ```
2217+
//
2218+
// we would create an opaque type like:
2219+
//
2220+
// ```
2221+
// type Bar<'a, 'b, '0, '1, '2> = impl Future<Output = &'2 u32>;
2222+
// ```
2223+
//
2224+
// and we would then desugar `bar` to the equivalent of:
2225+
//
2226+
// ```rust
2227+
// impl<'a> Foo<'a> {
2228+
// fn bar<'b, '0, '1>(&'0 self, x: &'b Vec<f64>, y: &'1 str) -> Bar<'a, 'b, '0, '1, '_>
2229+
// }
2230+
// ```
2231+
//
2232+
// Note that the final parameter to `Bar` is `'_`, not `'2` --
2233+
// this is because the elided lifetimes from the return type
2234+
// should be figured out using the ordinary elision rules, and
2235+
// this desugaring achieves that.
2236+
//
2237+
// The variable `input_lifetimes_count` tracks the number of
2238+
// lifetime parameters to the opaque type *not counting* those
2239+
// lifetimes elided in the return type. This includes those
2240+
// that are explicitly declared (`in_scope_lifetimes`) and
2241+
// those elided lifetimes we found in the arguments (current
2242+
// content of `lifetimes_to_define`). Next, we will process
2243+
// the return type, which will cause `lifetimes_to_define` to
2244+
// grow.
2245+
let input_lifetimes_count = self.in_scope_lifetimes.len() + self.lifetimes_to_define.len();
2246+
22512247
let (opaque_ty_id, lifetime_params) = self.with_hir_id_owner(opaque_ty_node_id, |this| {
2248+
// We have to be careful to get elision right here. The
2249+
// idea is that we create a lifetime parameter for each
2250+
// lifetime in the return type. So, given a return type
2251+
// like `async fn foo(..) -> &[&u32]`, we lower to `impl
2252+
// Future<Output = &'1 [ &'2 u32 ]>`.
2253+
//
2254+
// Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
2255+
// hence the elision takes place at the fn site.
22522256
let future_bound = this.with_anonymous_lifetime_mode(
2253-
AnonymousLifetimeMode::Replace(elided_lt_replacement),
2257+
AnonymousLifetimeMode::CreateParameter,
22542258
|this| this.lower_async_fn_output_type_to_future_bound(
22552259
output,
22562260
fn_def_id,
@@ -2304,19 +2308,52 @@ impl<'a> LoweringContext<'a> {
23042308
(opaque_ty_id, lifetime_params)
23052309
});
23062310

2307-
let generic_args =
2308-
lifetime_params
2309-
.iter().cloned()
2310-
.map(|(span, hir_name)| {
2311-
GenericArg::Lifetime(hir::Lifetime {
2312-
hir_id: self.next_id(),
2313-
span,
2314-
name: hir::LifetimeName::Param(hir_name),
2315-
})
2311+
// As documented above on the variable
2312+
// `input_lifetimes_count`, we need to create the lifetime
2313+
// arguments to our opaque type. Continuing with our example,
2314+
// we're creating the type arguments for the return type:
2315+
//
2316+
// ```
2317+
// Bar<'a, 'b, '0, '1, '_>
2318+
// ```
2319+
//
2320+
// For the "input" lifetime parameters, we wish to create
2321+
// references to the parameters themselves, including the
2322+
// "implicit" ones created from parameter types (`'a`, `'b`,
2323+
// '`0`, `'1`).
2324+
//
2325+
// For the "output" lifetime parameters, we just want to
2326+
// generate `'_`.
2327+
let mut generic_args: Vec<_> =
2328+
lifetime_params[..input_lifetimes_count]
2329+
.iter()
2330+
.map(|&(span, hir_name)| {
2331+
// Input lifetime like `'a` or `'1`:
2332+
GenericArg::Lifetime(hir::Lifetime {
2333+
hir_id: self.next_id(),
2334+
span,
2335+
name: hir::LifetimeName::Param(hir_name),
23162336
})
2317-
.collect();
2337+
})
2338+
.collect();
2339+
generic_args.extend(
2340+
lifetime_params[input_lifetimes_count..]
2341+
.iter()
2342+
.map(|&(span, _)| {
2343+
// Output lifetime like `'_`.
2344+
GenericArg::Lifetime(hir::Lifetime {
2345+
hir_id: self.next_id(),
2346+
span,
2347+
name: hir::LifetimeName::Implicit,
2348+
})
2349+
})
2350+
);
23182351

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

23212358
hir::FunctionRetTy::Return(P(hir::Ty {
23222359
node: opaque_ty_ref,
@@ -2412,11 +2449,6 @@ impl<'a> LoweringContext<'a> {
24122449
}
24132450

24142451
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(Some(l.id), span),
2415-
2416-
AnonymousLifetimeMode::Replace(replacement) => {
2417-
let hir_id = self.lower_node_id(l.id);
2418-
self.replace_elided_lifetime(hir_id, span, replacement)
2419-
}
24202452
},
24212453
ident => {
24222454
self.maybe_collect_in_band_lifetime(ident);
@@ -2439,39 +2471,6 @@ impl<'a> LoweringContext<'a> {
24392471
}
24402472
}
24412473

2442-
/// Replace a return-position elided lifetime with the elided lifetime
2443-
/// from the arguments.
2444-
fn replace_elided_lifetime(
2445-
&mut self,
2446-
hir_id: hir::HirId,
2447-
span: Span,
2448-
replacement: LtReplacement,
2449-
) -> hir::Lifetime {
2450-
let multiple_or_none = match replacement {
2451-
LtReplacement::Some(name) => {
2452-
return hir::Lifetime {
2453-
hir_id,
2454-
span,
2455-
name: hir::LifetimeName::Param(name),
2456-
};
2457-
}
2458-
LtReplacement::MultipleLifetimes => "multiple",
2459-
LtReplacement::NoLifetimes => "none",
2460-
};
2461-
2462-
let mut err = crate::middle::resolve_lifetime::report_missing_lifetime_specifiers(
2463-
self.sess,
2464-
span,
2465-
1,
2466-
);
2467-
err.note(&format!(
2468-
"return-position elided lifetimes require exactly one \
2469-
input-position elided lifetime, found {}.", multiple_or_none));
2470-
err.emit();
2471-
2472-
hir::Lifetime { hir_id, span, name: hir::LifetimeName::Error }
2473-
}
2474-
24752474
fn lower_generic_params(
24762475
&mut self,
24772476
params: &[GenericParam],
@@ -3174,10 +3173,6 @@ impl<'a> LoweringContext<'a> {
31743173
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
31753174

31763175
AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
3177-
3178-
AnonymousLifetimeMode::Replace(replacement) => {
3179-
self.new_replacement_lifetime(replacement, span)
3180-
}
31813176
}
31823177
}
31833178

@@ -3231,10 +3226,6 @@ impl<'a> LoweringContext<'a> {
32313226
// This is the normal case.
32323227
AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
32333228

3234-
AnonymousLifetimeMode::Replace(replacement) => {
3235-
self.new_replacement_lifetime(replacement, span)
3236-
}
3237-
32383229
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
32393230
}
32403231
}
@@ -3266,25 +3257,11 @@ impl<'a> LoweringContext<'a> {
32663257

32673258
// This is the normal case.
32683259
AnonymousLifetimeMode::PassThrough => {}
3269-
3270-
// We don't need to do any replacement here as this lifetime
3271-
// doesn't refer to an elided lifetime elsewhere in the function
3272-
// signature.
3273-
AnonymousLifetimeMode::Replace(_) => {}
32743260
}
32753261

32763262
self.new_implicit_lifetime(span)
32773263
}
32783264

3279-
fn new_replacement_lifetime(
3280-
&mut self,
3281-
replacement: LtReplacement,
3282-
span: Span,
3283-
) -> hir::Lifetime {
3284-
let hir_id = self.next_id();
3285-
self.replace_elided_lifetime(hir_id, span, replacement)
3286-
}
3287-
32883265
fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {
32893266
hir::Lifetime {
32903267
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() {}

0 commit comments

Comments
 (0)