Skip to content

Commit

Permalink
Infer async block return type from future expectation
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Mar 19, 2023
1 parent ab9bb3e commit 7456253
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 26 deletions.
30 changes: 24 additions & 6 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let gen_trait = tcx.lang_items().gen_trait();
let is_gen = gen_trait == Some(trait_def_id);

if !is_fn && !is_gen {
let future_trait = tcx.lang_items().future_trait();
let is_future = future_trait == Some(trait_def_id);

if !(is_fn || is_gen || is_future) {
debug!("not fn or generator");
return None;
}
Expand All @@ -305,6 +308,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
}

// Since this is a return parameter type it is safe to unwrap.
let ret_param_ty = projection.skip_binder().term.ty().unwrap();
let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty);
debug!(?ret_param_ty);

let input_tys = if is_fn {
let arg_param_ty = projection.skip_binder().projection_ty.substs.type_at(1);
let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty);
Expand All @@ -314,17 +322,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&ty::Tuple(tys) => tys,
_ => return None,
}
} else if is_future {
// HACK: Skip infer vars to `ui/generic-associated-types/issue-89008.rs` pass.
// Otherwise, we end up with inferring the closure signature to be
// `fn() -> Empty<Repr>` instead of `fn() -> Self::LineStream<'a, Repr>` and
// opaque type inference gets bungled. Similarly, skip opaques, because we don't
// replace them with infer vars, and opaque type inference gets bungled in
// `async fn ..() -> impl Trait {}` cases.
if ret_param_ty.is_ty_var() || ret_param_ty.has_opaque_types() {
return None;
}

let resume_ty_def_id = self.tcx.require_lang_item(hir::LangItem::ResumeTy, cause_span);
self.tcx.mk_type_list(&[self
.tcx
.mk_adt(self.tcx.adt_def(resume_ty_def_id), ty::List::empty())])
} else {
// Generators with a `()` resume type may be defined with 0 or 1 explicit arguments,
// else they must have exactly 1 argument. For now though, just give up in this case.
return None;
};

// Since this is a return parameter type it is safe to unwrap.
let ret_param_ty = projection.skip_binder().term.ty().unwrap();
let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty);
debug!(?ret_param_ty);

let sig = projection.rebind(self.tcx.mk_fn_sig(
input_tys,
ret_param_ty,
Expand Down
11 changes: 11 additions & 0 deletions tests/ui/async-await/expectation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// check-pass
// edition: 2021

use std::fmt::Debug;
use std::future::Future;

fn needs_future(_: impl Future<Output = Box<dyn Debug>>) {}

fn main() {
needs_future(async { Box::new(()) })
}
2 changes: 1 addition & 1 deletion tests/ui/impl-trait/issues/issue-78722.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ type F = impl core::future::Future<Output = u8>;
struct Bug {
V1: [(); {
fn concrete_use() -> F {
//~^ ERROR to be a future that resolves to `u8`, but it resolves to `()`
async {}
//~^ ERROR mismatched types
}
let f: F = async { 1 };
//~^ ERROR `async` blocks are not allowed in constants
Expand Down
12 changes: 6 additions & 6 deletions tests/ui/impl-trait/issues/issue-78722.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ LL | let f: F = async { 1 };
= note: see issue #85368 <https://github.com/rust-lang/rust/issues/85368> for more information
= help: add `#![feature(const_async_blocks)]` to the crate attributes to enable

error[E0271]: expected `[async block@$DIR/issue-78722.rs:11:13: 11:21]` to be a future that resolves to `u8`, but it resolves to `()`
--> $DIR/issue-78722.rs:9:30
error[E0308]: mismatched types
--> $DIR/issue-78722.rs:10:19
|
LL | fn concrete_use() -> F {
| ^ expected `()`, found `u8`
LL | async {}
| ^^ expected `u8`, found `()`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0271, E0658.
For more information about an error, try `rustc --explain E0271`.
Some errors have detailed explanations: E0308, E0658.
For more information about an error, try `rustc --explain E0308`.
16 changes: 4 additions & 12 deletions tests/ui/traits/new-solver/async.fail.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
error[E0271]: expected `[async block@$DIR/async.rs:12:17: 12:25]` to be a future that resolves to `i32`, but it resolves to `()`
--> $DIR/async.rs:12:17
error[E0308]: mismatched types
--> $DIR/async.rs:12:23
|
LL | needs_async(async {});
| ----------- ^^^^^^^^ expected `i32`, found `()`
| |
| required by a bound introduced by this call
|
note: required by a bound in `needs_async`
--> $DIR/async.rs:8:31
|
LL | fn needs_async(_: impl Future<Output = i32>) {}
| ^^^^^^^^^^^^ required by this bound in `needs_async`
| ^^ expected `i32`, found `()`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0271`.
For more information about this error, try `rustc --explain E0308`.
2 changes: 1 addition & 1 deletion tests/ui/traits/new-solver/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fn needs_async(_: impl Future<Output = i32>) {}
#[cfg(fail)]
fn main() {
needs_async(async {});
//[fail]~^ ERROR to be a future that resolves to `i32`, but it resolves to `()`
//[fail]~^ mismatched types
}

#[cfg(pass)]
Expand Down

0 comments on commit 7456253

Please sign in to comment.