Skip to content

Commit

Permalink
Rollup merge of rust-lang#59286 - cramertj:async-fn-ret-ty, r=varkor
Browse files Browse the repository at this point in the history
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:
```rust
existential type Foo<'a, 'b>:; // error: ambiguous lifetime bound in `impl Trait`
fn foo<'a, 'b>(_: &'a u8, _: &'b u8) -> Foo<'a, 'b> { () }
```

This requires a separate fix.

Fix rust-lang#59001
Fix rust-lang#58885
Fix rust-lang#55324
Fix rust-lang#54974
Progress on rust-lang#56238

r? @nikomatsakis
  • Loading branch information
Centril committed Apr 2, 2019
2 parents c9d9df5 + 749349f commit d86a8f3
Show file tree
Hide file tree
Showing 13 changed files with 553 additions and 336 deletions.
6 changes: 5 additions & 1 deletion src/librustc/hir/intravisit.rs
Expand Up @@ -490,7 +490,11 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
visitor.visit_ty(ty);
visitor.visit_generics(generics)
}
ItemKind::Existential(ExistTy { ref generics, ref bounds, impl_trait_fn: _ }) => {
ItemKind::Existential(ExistTy {
ref generics,
ref bounds,
..
}) => {
visitor.visit_id(item.hir_id);
walk_generics(visitor, generics);
walk_list!(visitor, visit_param_bound, bounds);
Expand Down
661 changes: 377 additions & 284 deletions src/librustc/hir/lowering.rs

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions src/librustc/hir/mod.rs
Expand Up @@ -1799,6 +1799,18 @@ pub struct ExistTy {
pub generics: Generics,
pub bounds: GenericBounds,
pub impl_trait_fn: Option<DefId>,
pub origin: ExistTyOrigin,
}

/// Where the existential type came from
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
pub enum ExistTyOrigin {
/// `existential type Foo: Trait;`
ExistentialType,
/// `-> impl Trait`
ReturnImplTrait,
/// `async fn`
AsyncFn,
}

/// The various kinds of types recognized by the compiler.
Expand Down
80 changes: 60 additions & 20 deletions src/librustc/infer/opaque_types/mod.rs
Expand Up @@ -67,6 +67,9 @@ pub struct OpaqueTypeDecl<'tcx> {
/// the fn body). (Ultimately, writeback is responsible for this
/// check.)
pub has_required_region_bounds: bool,

/// The origin of the existential type
pub origin: hir::ExistTyOrigin,
}

impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
Expand Down Expand Up @@ -326,14 +329,39 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// There are two regions (`lr` and
// `subst_arg`) which are not relatable. We can't
// find a best choice.
self.tcx
let context_name = match opaque_defn.origin {
hir::ExistTyOrigin::ExistentialType => "existential type",
hir::ExistTyOrigin::ReturnImplTrait => "impl Trait",
hir::ExistTyOrigin::AsyncFn => "async fn",
};
let msg = format!("ambiguous lifetime bound in `{}`", context_name);
let mut err = self.tcx
.sess
.struct_span_err(span, "ambiguous lifetime bound in `impl Trait`")
.span_label(
span,
format!("neither `{}` nor `{}` outlives the other", lr, subst_arg),
)
.emit();
.struct_span_err(span, &msg);

let lr_name = lr.to_string();
let subst_arg_name = subst_arg.to_string();
let label_owned;
let label = match (&*lr_name, &*subst_arg_name) {
("'_", "'_") => "the elided lifetimes here do not outlive one another",
_ => {
label_owned = format!(
"neither `{}` nor `{}` outlives the other",
lr_name,
subst_arg_name,
);
&label_owned
}
};
err.span_label(span, label);

if let hir::ExistTyOrigin::AsyncFn = opaque_defn.origin {
err.note("multiple unrelated lifetimes are not allowed in \
`async fn`.");
err.note("if you're using argument-position elided lifetimes, consider \
switching to a single named lifetime.");
}
err.emit();

least_region = Some(self.tcx.mk_region(ty::ReEmpty));
break;
Expand Down Expand Up @@ -692,39 +720,49 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
parent_def_id == tcx.hir()
.local_def_id_from_hir_id(opaque_parent_hir_id)
};
let in_definition_scope = match tcx.hir().find_by_hir_id(opaque_hir_id) {
let (in_definition_scope, origin) =
match tcx.hir().find_by_hir_id(opaque_hir_id)
{
Some(Node::Item(item)) => match item.node {
// impl trait
hir::ItemKind::Existential(hir::ExistTy {
impl_trait_fn: Some(parent),
origin,
..
}) => parent == self.parent_def_id,
}) => (parent == self.parent_def_id, origin),
// named existential types
hir::ItemKind::Existential(hir::ExistTy {
impl_trait_fn: None,
origin,
..
}) => may_define_existential_type(
tcx,
self.parent_def_id,
opaque_hir_id,
}) => (
may_define_existential_type(
tcx,
self.parent_def_id,
opaque_hir_id,
),
origin,
),
_ => def_scope_default(),
_ => (def_scope_default(), hir::ExistTyOrigin::ExistentialType),
},
Some(Node::ImplItem(item)) => match item.node {
hir::ImplItemKind::Existential(_) => may_define_existential_type(
tcx,
self.parent_def_id,
opaque_hir_id,
hir::ImplItemKind::Existential(_) => (
may_define_existential_type(
tcx,
self.parent_def_id,
opaque_hir_id,
),
hir::ExistTyOrigin::ExistentialType,
),
_ => def_scope_default(),
_ => (def_scope_default(), hir::ExistTyOrigin::ExistentialType),
},
_ => bug!(
"expected (impl) item, found {}",
tcx.hir().hir_to_string(opaque_hir_id),
),
};
if in_definition_scope {
return self.fold_opaque_ty(ty, def_id, substs);
return self.fold_opaque_ty(ty, def_id, substs, origin);
}

debug!(
Expand All @@ -746,6 +784,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
ty: Ty<'tcx>,
def_id: DefId,
substs: SubstsRef<'tcx>,
origin: hir::ExistTyOrigin,
) -> Ty<'tcx> {
let infcx = self.infcx;
let tcx = infcx.tcx;
Expand Down Expand Up @@ -795,6 +834,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
substs,
concrete_ty: ty_var,
has_required_region_bounds: !required_region_bounds.is_empty(),
origin,
},
);
debug!("instantiate_opaque_types: ty_var={:?}", ty_var);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/resolve_lifetime.rs
Expand Up @@ -2891,7 +2891,7 @@ fn insert_late_bound_lifetimes(
}
}

fn report_missing_lifetime_specifiers(
pub fn report_missing_lifetime_specifiers(
sess: &Session,
span: Span,
count: usize,
Expand Down
1 change: 1 addition & 0 deletions src/librustc_typeck/collect.rs
Expand Up @@ -1979,6 +1979,7 @@ fn explicit_predicates_of<'a, 'tcx>(
ref bounds,
impl_trait_fn,
ref generics,
origin: _,
}) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id);
let opaque_ty = tcx.mk_opaque(def_id, substs);
Expand Down
20 changes: 16 additions & 4 deletions src/test/run-pass/async-await.rs
Expand Up @@ -79,6 +79,11 @@ async fn async_fn(x: u8) -> u8 {
x
}

async fn generic_async_fn<T>(x: T) -> T {
await!(wake_and_yield_once());
x
}

async fn async_fn_with_borrow(x: &u8) -> u8 {
await!(wake_and_yield_once());
*x
Expand All @@ -96,14 +101,21 @@ fn async_fn_with_impl_future_named_lifetime<'a>(x: &'a u8) -> impl Future<Output
}
}

async fn async_fn_with_named_lifetime_multiple_args<'a>(x: &'a u8, _y: &'a u8) -> u8 {
/* FIXME(cramertj) support when `existential type T<'a, 'b>:;` works
async fn async_fn_multiple_args(x: &u8, _y: &u8) -> u8 {
await!(wake_and_yield_once());
*x
}
*/

async fn async_fn_multiple_args_named_lifetime<'a>(x: &'a u8, _y: &'a u8) -> u8 {
await!(wake_and_yield_once());
*x
}

fn async_fn_with_internal_borrow(y: u8) -> impl Future<Output = u8> {
async move {
await!(async_fn_with_borrow(&y))
await!(async_fn_with_borrow_named_lifetime(&y))
}
}

Expand Down Expand Up @@ -162,6 +174,7 @@ fn main() {
async_nonmove_block,
async_closure,
async_fn,
generic_async_fn,
async_fn_with_internal_borrow,
Foo::async_method,
|x| {
Expand All @@ -170,15 +183,14 @@ fn main() {
}
},
}

test_with_borrow! {
async_block_with_borrow_named_lifetime,
async_fn_with_borrow,
async_fn_with_borrow_named_lifetime,
async_fn_with_impl_future_named_lifetime,
|x| {
async move {
await!(async_fn_with_named_lifetime_multiple_args(x, x))
await!(async_fn_multiple_args_named_lifetime(x, x))
}
},
}
Expand Down
5 changes: 2 additions & 3 deletions src/test/ui/async-fn-multiple-lifetimes.rs
Expand Up @@ -5,7 +5,7 @@
use std::ops::Add;

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

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

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

fn main() {}
34 changes: 11 additions & 23 deletions src/test/ui/async-fn-multiple-lifetimes.stderr
@@ -1,32 +1,20 @@
error[E0709]: multiple different lifetimes used in arguments of `async fn`
--> $DIR/async-fn-multiple-lifetimes.rs:7:47
error: ambiguous lifetime bound in `async fn`
--> $DIR/async-fn-multiple-lifetimes.rs:7:65
|
LL | async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {}
| ^^ ^^ different lifetime here
| |
| first lifetime here
| ^ neither `'a` nor `'b` outlives the other
|
= help: `async fn` can only accept borrowed values with identical lifetimes
= note: multiple unrelated lifetimes are not allowed in `async fn`.
= note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime.

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

error[E0106]: missing lifetime specifier
--> $DIR/async-fn-multiple-lifetimes.rs:16:39
|
LL | async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `_` or `_`

error: aborting due to 3 previous errors
error: aborting due to 2 previous errors

Some errors occurred: E0106, E0707, E0709.
For more information about an error, try `rustc --explain E0106`.
16 changes: 16 additions & 0 deletions src/test/ui/issues/issue-54974.rs
@@ -0,0 +1,16 @@
// compile-pass
// edition:2018

#![feature(async_await, await_macro, futures_api)]

use std::sync::Arc;

trait SomeTrait: Send + Sync + 'static {
fn do_something(&self);
}

async fn my_task(obj: Arc<SomeTrait>) {
unimplemented!()
}

fn main() {}
14 changes: 14 additions & 0 deletions src/test/ui/issues/issue-55324.rs
@@ -0,0 +1,14 @@
// compile-pass
// edition:2018

#![feature(async_await, await_macro, futures_api)]

use std::future::Future;

#[allow(unused)]
async fn foo<F: Future<Output = i32>>(x: &i32, future: F) -> i32 {
let y = await!(future);
*x + y
}

fn main() {}
21 changes: 21 additions & 0 deletions src/test/ui/issues/issue-58885.rs
@@ -0,0 +1,21 @@
// compile-pass
// edition:2018

#![feature(async_await, await_macro, futures_api)]

struct Xyz {
a: u64,
}

trait Foo {}

impl Xyz {
async fn do_sth<'a>(
&'a self, foo: &'a dyn Foo
) -> bool
{
true
}
}

fn main() {}
17 changes: 17 additions & 0 deletions src/test/ui/issues/issue-59001.rs
@@ -0,0 +1,17 @@
// compile-pass
// edition:2018

#![feature(async_await, await_macro, futures_api)]

use std::future::Future;

#[allow(unused)]
async fn enter<'a, F, R>(mut callback: F)
where
F: FnMut(&'a mut i32) -> R,
R: Future<Output = ()> + 'a,
{
unimplemented!()
}

fn main() {}

0 comments on commit d86a8f3

Please sign in to comment.