Skip to content

Commit 749349f

Browse files
committed
Refactor async fn return type lowering
async fn now lowers directly to an existential type declaration rather than reusing the `impl Trait` return type lowering. As part of this, it lowers all argument-position elided lifetimes using the in-band-lifetimes machinery, creating fresh parameter names for each of them, using each lifetime parameter as a generic argument to the generated existential type. This doesn't currently successfully allow multiple argument-position elided lifetimes since `existential type` doesn't yet support multiple lifetimes where neither outlive the other. This requires a separate fix.
1 parent f694222 commit 749349f

File tree

13 files changed

+553
-336
lines changed

13 files changed

+553
-336
lines changed

src/librustc/hir/intravisit.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,11 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
490490
visitor.visit_ty(ty);
491491
visitor.visit_generics(generics)
492492
}
493-
ItemKind::Existential(ExistTy { ref generics, ref bounds, impl_trait_fn: _ }) => {
493+
ItemKind::Existential(ExistTy {
494+
ref generics,
495+
ref bounds,
496+
..
497+
}) => {
494498
visitor.visit_id(item.hir_id);
495499
walk_generics(visitor, generics);
496500
walk_list!(visitor, visit_param_bound, bounds);

src/librustc/hir/lowering.rs

+377-284
Large diffs are not rendered by default.

src/librustc/hir/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1799,6 +1799,18 @@ pub struct ExistTy {
17991799
pub generics: Generics,
18001800
pub bounds: GenericBounds,
18011801
pub impl_trait_fn: Option<DefId>,
1802+
pub origin: ExistTyOrigin,
1803+
}
1804+
1805+
/// Where the existential type came from
1806+
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
1807+
pub enum ExistTyOrigin {
1808+
/// `existential type Foo: Trait;`
1809+
ExistentialType,
1810+
/// `-> impl Trait`
1811+
ReturnImplTrait,
1812+
/// `async fn`
1813+
AsyncFn,
18021814
}
18031815

18041816
/// The various kinds of types recognized by the compiler.

src/librustc/infer/opaque_types/mod.rs

+60-20
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ pub struct OpaqueTypeDecl<'tcx> {
6767
/// the fn body). (Ultimately, writeback is responsible for this
6868
/// check.)
6969
pub has_required_region_bounds: bool,
70+
71+
/// The origin of the existential type
72+
pub origin: hir::ExistTyOrigin,
7073
}
7174

7275
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
@@ -326,14 +329,39 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
326329
// There are two regions (`lr` and
327330
// `subst_arg`) which are not relatable. We can't
328331
// find a best choice.
329-
self.tcx
332+
let context_name = match opaque_defn.origin {
333+
hir::ExistTyOrigin::ExistentialType => "existential type",
334+
hir::ExistTyOrigin::ReturnImplTrait => "impl Trait",
335+
hir::ExistTyOrigin::AsyncFn => "async fn",
336+
};
337+
let msg = format!("ambiguous lifetime bound in `{}`", context_name);
338+
let mut err = self.tcx
330339
.sess
331-
.struct_span_err(span, "ambiguous lifetime bound in `impl Trait`")
332-
.span_label(
333-
span,
334-
format!("neither `{}` nor `{}` outlives the other", lr, subst_arg),
335-
)
336-
.emit();
340+
.struct_span_err(span, &msg);
341+
342+
let lr_name = lr.to_string();
343+
let subst_arg_name = subst_arg.to_string();
344+
let label_owned;
345+
let label = match (&*lr_name, &*subst_arg_name) {
346+
("'_", "'_") => "the elided lifetimes here do not outlive one another",
347+
_ => {
348+
label_owned = format!(
349+
"neither `{}` nor `{}` outlives the other",
350+
lr_name,
351+
subst_arg_name,
352+
);
353+
&label_owned
354+
}
355+
};
356+
err.span_label(span, label);
357+
358+
if let hir::ExistTyOrigin::AsyncFn = opaque_defn.origin {
359+
err.note("multiple unrelated lifetimes are not allowed in \
360+
`async fn`.");
361+
err.note("if you're using argument-position elided lifetimes, consider \
362+
switching to a single named lifetime.");
363+
}
364+
err.emit();
337365

338366
least_region = Some(self.tcx.mk_region(ty::ReEmpty));
339367
break;
@@ -692,39 +720,49 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
692720
parent_def_id == tcx.hir()
693721
.local_def_id_from_hir_id(opaque_parent_hir_id)
694722
};
695-
let in_definition_scope = match tcx.hir().find_by_hir_id(opaque_hir_id) {
723+
let (in_definition_scope, origin) =
724+
match tcx.hir().find_by_hir_id(opaque_hir_id)
725+
{
696726
Some(Node::Item(item)) => match item.node {
697727
// impl trait
698728
hir::ItemKind::Existential(hir::ExistTy {
699729
impl_trait_fn: Some(parent),
730+
origin,
700731
..
701-
}) => parent == self.parent_def_id,
732+
}) => (parent == self.parent_def_id, origin),
702733
// named existential types
703734
hir::ItemKind::Existential(hir::ExistTy {
704735
impl_trait_fn: None,
736+
origin,
705737
..
706-
}) => may_define_existential_type(
707-
tcx,
708-
self.parent_def_id,
709-
opaque_hir_id,
738+
}) => (
739+
may_define_existential_type(
740+
tcx,
741+
self.parent_def_id,
742+
opaque_hir_id,
743+
),
744+
origin,
710745
),
711-
_ => def_scope_default(),
746+
_ => (def_scope_default(), hir::ExistTyOrigin::ExistentialType),
712747
},
713748
Some(Node::ImplItem(item)) => match item.node {
714-
hir::ImplItemKind::Existential(_) => may_define_existential_type(
715-
tcx,
716-
self.parent_def_id,
717-
opaque_hir_id,
749+
hir::ImplItemKind::Existential(_) => (
750+
may_define_existential_type(
751+
tcx,
752+
self.parent_def_id,
753+
opaque_hir_id,
754+
),
755+
hir::ExistTyOrigin::ExistentialType,
718756
),
719-
_ => def_scope_default(),
757+
_ => (def_scope_default(), hir::ExistTyOrigin::ExistentialType),
720758
},
721759
_ => bug!(
722760
"expected (impl) item, found {}",
723761
tcx.hir().hir_to_string(opaque_hir_id),
724762
),
725763
};
726764
if in_definition_scope {
727-
return self.fold_opaque_ty(ty, def_id, substs);
765+
return self.fold_opaque_ty(ty, def_id, substs, origin);
728766
}
729767

730768
debug!(
@@ -746,6 +784,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
746784
ty: Ty<'tcx>,
747785
def_id: DefId,
748786
substs: SubstsRef<'tcx>,
787+
origin: hir::ExistTyOrigin,
749788
) -> Ty<'tcx> {
750789
let infcx = self.infcx;
751790
let tcx = infcx.tcx;
@@ -795,6 +834,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
795834
substs,
796835
concrete_ty: ty_var,
797836
has_required_region_bounds: !required_region_bounds.is_empty(),
837+
origin,
798838
},
799839
);
800840
debug!("instantiate_opaque_types: ty_var={:?}", ty_var);

src/librustc/middle/resolve_lifetime.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2891,7 +2891,7 @@ fn insert_late_bound_lifetimes(
28912891
}
28922892
}
28932893

2894-
fn report_missing_lifetime_specifiers(
2894+
pub fn report_missing_lifetime_specifiers(
28952895
sess: &Session,
28962896
span: Span,
28972897
count: usize,

src/librustc_typeck/collect.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1979,6 +1979,7 @@ fn explicit_predicates_of<'a, 'tcx>(
19791979
ref bounds,
19801980
impl_trait_fn,
19811981
ref generics,
1982+
origin: _,
19821983
}) => {
19831984
let substs = InternalSubsts::identity_for_item(tcx, def_id);
19841985
let opaque_ty = tcx.mk_opaque(def_id, substs);

src/test/run-pass/async-await.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ async fn async_fn(x: u8) -> u8 {
7979
x
8080
}
8181

82+
async fn generic_async_fn<T>(x: T) -> T {
83+
await!(wake_and_yield_once());
84+
x
85+
}
86+
8287
async fn async_fn_with_borrow(x: &u8) -> u8 {
8388
await!(wake_and_yield_once());
8489
*x
@@ -96,14 +101,21 @@ fn async_fn_with_impl_future_named_lifetime<'a>(x: &'a u8) -> impl Future<Output
96101
}
97102
}
98103

99-
async fn async_fn_with_named_lifetime_multiple_args<'a>(x: &'a u8, _y: &'a u8) -> u8 {
104+
/* FIXME(cramertj) support when `existential type T<'a, 'b>:;` works
105+
async fn async_fn_multiple_args(x: &u8, _y: &u8) -> u8 {
106+
await!(wake_and_yield_once());
107+
*x
108+
}
109+
*/
110+
111+
async fn async_fn_multiple_args_named_lifetime<'a>(x: &'a u8, _y: &'a u8) -> u8 {
100112
await!(wake_and_yield_once());
101113
*x
102114
}
103115

104116
fn async_fn_with_internal_borrow(y: u8) -> impl Future<Output = u8> {
105117
async move {
106-
await!(async_fn_with_borrow(&y))
118+
await!(async_fn_with_borrow_named_lifetime(&y))
107119
}
108120
}
109121

@@ -162,6 +174,7 @@ fn main() {
162174
async_nonmove_block,
163175
async_closure,
164176
async_fn,
177+
generic_async_fn,
165178
async_fn_with_internal_borrow,
166179
Foo::async_method,
167180
|x| {
@@ -170,15 +183,14 @@ fn main() {
170183
}
171184
},
172185
}
173-
174186
test_with_borrow! {
175187
async_block_with_borrow_named_lifetime,
176188
async_fn_with_borrow,
177189
async_fn_with_borrow_named_lifetime,
178190
async_fn_with_impl_future_named_lifetime,
179191
|x| {
180192
async move {
181-
await!(async_fn_with_named_lifetime_multiple_args(x, x))
193+
await!(async_fn_multiple_args_named_lifetime(x, x))
182194
}
183195
},
184196
}

src/test/ui/async-fn-multiple-lifetimes.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use std::ops::Add;
66

77
async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {}
8-
//~^ ERROR multiple different lifetimes used in arguments of `async fn`
8+
//~^ ERROR ambiguous lifetime bound in `async fn`
99

1010
async fn multiple_hrtb_and_single_named_lifetime_ok<'c>(
1111
_: impl for<'a> Add<&'a u8>,
@@ -14,7 +14,6 @@ async fn multiple_hrtb_and_single_named_lifetime_ok<'c>(
1414
) {}
1515

1616
async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}
17-
//~^ ERROR multiple elided lifetimes used
18-
//~^^ ERROR missing lifetime specifier
17+
//~^ ambiguous lifetime bound in `async fn`
1918

2019
fn main() {}
+11-23
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,20 @@
1-
error[E0709]: multiple different lifetimes used in arguments of `async fn`
2-
--> $DIR/async-fn-multiple-lifetimes.rs:7:47
1+
error: ambiguous lifetime bound in `async fn`
2+
--> $DIR/async-fn-multiple-lifetimes.rs:7:65
33
|
44
LL | async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {}
5-
| ^^ ^^ different lifetime here
6-
| |
7-
| first lifetime here
5+
| ^ neither `'a` nor `'b` outlives the other
86
|
9-
= help: `async fn` can only accept borrowed values with identical lifetimes
7+
= note: multiple unrelated lifetimes are not allowed in `async fn`.
8+
= note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime.
109

11-
error[E0707]: multiple elided lifetimes used in arguments of `async fn`
12-
--> $DIR/async-fn-multiple-lifetimes.rs:16:39
10+
error: ambiguous lifetime bound in `async fn`
11+
--> $DIR/async-fn-multiple-lifetimes.rs:16:52
1312
|
1413
LL | async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}
15-
| ^ ^ different lifetime here
16-
| |
17-
| first lifetime here
14+
| ^ the elided lifetimes here do not outlive one another
1815
|
19-
= help: consider giving these arguments named lifetimes
16+
= note: multiple unrelated lifetimes are not allowed in `async fn`.
17+
= note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime.
2018

21-
error[E0106]: missing lifetime specifier
22-
--> $DIR/async-fn-multiple-lifetimes.rs:16:39
23-
|
24-
LL | async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}
25-
| ^ expected lifetime parameter
26-
|
27-
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `_` or `_`
28-
29-
error: aborting due to 3 previous errors
19+
error: aborting due to 2 previous errors
3020

31-
Some errors occurred: E0106, E0707, E0709.
32-
For more information about an error, try `rustc --explain E0106`.

src/test/ui/issues/issue-54974.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// compile-pass
2+
// edition:2018
3+
4+
#![feature(async_await, await_macro, futures_api)]
5+
6+
use std::sync::Arc;
7+
8+
trait SomeTrait: Send + Sync + 'static {
9+
fn do_something(&self);
10+
}
11+
12+
async fn my_task(obj: Arc<SomeTrait>) {
13+
unimplemented!()
14+
}
15+
16+
fn main() {}

src/test/ui/issues/issue-55324.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// compile-pass
2+
// edition:2018
3+
4+
#![feature(async_await, await_macro, futures_api)]
5+
6+
use std::future::Future;
7+
8+
#[allow(unused)]
9+
async fn foo<F: Future<Output = i32>>(x: &i32, future: F) -> i32 {
10+
let y = await!(future);
11+
*x + y
12+
}
13+
14+
fn main() {}

src/test/ui/issues/issue-58885.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// compile-pass
2+
// edition:2018
3+
4+
#![feature(async_await, await_macro, futures_api)]
5+
6+
struct Xyz {
7+
a: u64,
8+
}
9+
10+
trait Foo {}
11+
12+
impl Xyz {
13+
async fn do_sth<'a>(
14+
&'a self, foo: &'a dyn Foo
15+
) -> bool
16+
{
17+
true
18+
}
19+
}
20+
21+
fn main() {}

src/test/ui/issues/issue-59001.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// compile-pass
2+
// edition:2018
3+
4+
#![feature(async_await, await_macro, futures_api)]
5+
6+
use std::future::Future;
7+
8+
#[allow(unused)]
9+
async fn enter<'a, F, R>(mut callback: F)
10+
where
11+
F: FnMut(&'a mut i32) -> R,
12+
R: Future<Output = ()> + 'a,
13+
{
14+
unimplemented!()
15+
}
16+
17+
fn main() {}

0 commit comments

Comments
 (0)