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::visit::{TypeVisitable, TypeVisitableExt};
|
||||||
use rustc_middle::ty::{self, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
|
use rustc_middle::ty::{self, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
|
||||||
use rustc_span::def_id::LocalDefId;
|
use rustc_span::def_id::LocalDefId;
|
||||||
use rustc_span::Span;
|
use rustc_span::{Span, DUMMY_SP};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
use rustc_trait_selection::error_reporting::traits::ArgKind;
|
use rustc_trait_selection::error_reporting::traits::ArgKind;
|
||||||
use rustc_trait_selection::traits;
|
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
|
/// 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>`
|
/// an inference variable `?Fut`, we check if that is bounded by a `Future<Output = Ty>`
|
||||||
/// projection.
|
/// 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(
|
fn extract_sig_from_projection_and_future_bound(
|
||||||
&self,
|
&self,
|
||||||
cause_span: Option<Span>,
|
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.
|
// 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) {
|
for bound in self.obligations_for_self_ty(return_vid) {
|
||||||
if let Some(ret_projection) = bound.predicate.as_projection_clause()
|
if let Some(ret_projection) = bound.predicate.as_projection_clause()
|
||||||
&& let Some(ret_projection) = ret_projection.no_bound_vars()
|
&& let Some(ret_projection) = ret_projection.no_bound_vars()
|
||||||
&& self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput)
|
&& self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput)
|
||||||
{
|
{
|
||||||
let sig = projection.rebind(self.tcx.mk_fn_sig(
|
return_ty = Some(ret_projection.term.expect_type());
|
||||||
input_tys,
|
break;
|
||||||
ret_projection.term.expect_type(),
|
|
||||||
false,
|
|
||||||
hir::Safety::Safe,
|
|
||||||
Abi::Rust,
|
|
||||||
));
|
|
||||||
|
|
||||||
return Some(ExpectedSig { cause_span, sig });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
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