Rollup merge of #129072 - compiler-errors:more-powerful-async-closure-inference, r=lcnr
Infer async closure args from `Fn` bound even if there is no corresponding `Future` bound on return In #127482, I implemented the functionality to infer an async closure signature when passed into a function that has `Fn` + `Future` where clauses that look like: ``` fn whatever(callback: F) where F: Fn(Arg) -> Fut, Fut: Future<Output = Out>, ``` However, #127781 demonstrates that this is still incomplete to address the cases users care about. So let's not bail when we fail to find a `Future` bound, and try our best to just use the args from the `Fn` bound if we find it. This is *fine* since most users of closures only really care about the *argument* types for inference guidance, since we require the receiver of a `.` method call to be known in order to probe methods. When I experimented with programmatically rewriting `|| async {}` to `async || {}` in #127827, this also seems to have fixed ~5000 regressions (probably all coming from usages `TryFuture`/`TryStream` from futures-rs): the [before](https://github.com/rust-lang/rust/pull/127827#issuecomment-2254061733) and [after](https://github.com/rust-lang/rust/pull/127827#issuecomment-2255470176) crater runs. Fixes #127781.
This commit is contained in:
commit
53bf554de8
@ -14,7 +14,7 @@
|
||||
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::error_reporting::traits::ArgKind;
|
||||
use rustc_trait_selection::traits;
|
||||
@ -539,6 +539,10 @@ fn extract_sig_from_projection(
|
||||
/// we identify the `FnOnce<Args, Output = ?Fut>` bound, and if the output type is
|
||||
/// an inference variable `?Fut`, we check if that is bounded by a `Future<Output = Ty>`
|
||||
/// projection.
|
||||
///
|
||||
/// This function is actually best-effort with the return type; if we don't find a
|
||||
/// `Future` projection, we still will return arguments that we extracted from the `FnOnce`
|
||||
/// projection, and the output will be an unconstrained type variable instead.
|
||||
fn extract_sig_from_projection_and_future_bound(
|
||||
&self,
|
||||
cause_span: Option<Span>,
|
||||
@ -564,24 +568,43 @@ fn extract_sig_from_projection_and_future_bound(
|
||||
};
|
||||
|
||||
// FIXME: We may want to elaborate here, though I assume this will be exceedingly rare.
|
||||
let mut return_ty = None;
|
||||
for bound in self.obligations_for_self_ty(return_vid) {
|
||||
if let Some(ret_projection) = bound.predicate.as_projection_clause()
|
||||
&& let Some(ret_projection) = ret_projection.no_bound_vars()
|
||||
&& self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput)
|
||||
{
|
||||
let sig = projection.rebind(self.tcx.mk_fn_sig(
|
||||
input_tys,
|
||||
ret_projection.term.expect_type(),
|
||||
false,
|
||||
hir::Safety::Safe,
|
||||
Abi::Rust,
|
||||
));
|
||||
|
||||
return Some(ExpectedSig { cause_span, sig });
|
||||
return_ty = Some(ret_projection.term.expect_type());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
// SUBTLE: If we didn't find a `Future<Output = ...>` bound for the return
|
||||
// vid, we still want to attempt to provide inference guidance for the async
|
||||
// closure's arguments. Instantiate a new vid to plug into the output type.
|
||||
//
|
||||
// You may be wondering, what if it's higher-ranked? Well, given that we
|
||||
// found a type variable for the `FnOnce::Output` projection above, we know
|
||||
// that the output can't mention any of the vars.
|
||||
//
|
||||
// Also note that we use a fresh var here for the signature since the signature
|
||||
// records the output of the *future*, and `return_vid` above is the type
|
||||
// variable of the future, not its output.
|
||||
//
|
||||
// FIXME: We probably should store this signature inference output in a way
|
||||
// that does not misuse a `FnSig` type, but that can be done separately.
|
||||
let return_ty =
|
||||
return_ty.unwrap_or_else(|| self.next_ty_var(cause_span.unwrap_or(DUMMY_SP)));
|
||||
|
||||
let sig = projection.rebind(self.tcx.mk_fn_sig(
|
||||
input_tys,
|
||||
return_ty,
|
||||
false,
|
||||
hir::Safety::Safe,
|
||||
Abi::Rust,
|
||||
));
|
||||
|
||||
return Some(ExpectedSig { cause_span, sig });
|
||||
}
|
||||
|
||||
fn sig_of_closure(
|
||||
|
49
tests/ui/async-await/async-closures/sig-from-bare-fn.rs
Normal file
49
tests/ui/async-await/async-closures/sig-from-bare-fn.rs
Normal file
@ -0,0 +1,49 @@
|
||||
//@ check-pass
|
||||
//@ edition: 2021
|
||||
|
||||
// Make sure that we infer the args of an async closure even if it's passed to
|
||||
// a function that requires the async closure implement `Fn*` but does *not* have
|
||||
// a `Future` bound on the return type.
|
||||
|
||||
#![feature(async_closure)]
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
trait TryStream {
|
||||
type Ok;
|
||||
type Err;
|
||||
}
|
||||
|
||||
trait TryFuture {
|
||||
type Ok;
|
||||
type Err;
|
||||
}
|
||||
|
||||
impl<F, T, E> TryFuture for F where F: Future<Output = Result<T, E>> {
|
||||
type Ok = T;
|
||||
type Err = E;
|
||||
}
|
||||
|
||||
trait TryStreamExt: TryStream {
|
||||
fn try_for_each<F, Fut>(&self, f: F)
|
||||
where
|
||||
F: FnMut(Self::Ok) -> Fut,
|
||||
Fut: TryFuture<Ok = (), Err = Self::Err>;
|
||||
}
|
||||
|
||||
impl<S> TryStreamExt for S where S: TryStream {
|
||||
fn try_for_each<F, Fut>(&self, f: F)
|
||||
where
|
||||
F: FnMut(Self::Ok) -> Fut,
|
||||
Fut: TryFuture<Ok = (), Err = Self::Err>,
|
||||
{ }
|
||||
}
|
||||
|
||||
fn test(stream: impl TryStream<Ok = &'static str, Err = ()>) {
|
||||
stream.try_for_each(async |s| {
|
||||
s.trim(); // Make sure we know the type of `s` at this point.
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user