Rollup merge of #121857 - compiler-errors:async-closure-signature-deduction, r=oli-obk

Implement async closure signature deduction

Self-explanatory from title.

Regarding the interaction between signature deduction, fulfillment, and the new trait solver: I'm not worried about implementing closure signature deduction here because:

1. async closures are unstable, and
2. I'm reasonably confident we'll need to support signature deduction in the new solver somehow (i.e. via proof trees, which seem very promising).

This is in contrast to #109338, which was closed because it generalizes signature deduction for a *stable* kind of expression (`async {}` blocks and `Future` traits), and which proliferated usage may pose a stabilization hazard for the new solver.

I'll be certain to make sure sure we revisit the closure signature deduction problem by the time that async closures are being stabilized (which isn't particularly soon) (edit: Put it into the async closure tracking issue). cc `````@lcnr`````

r? `````@oli-obk`````
This commit is contained in:
Matthias Krüger 2024-03-05 22:10:00 +01:00 committed by GitHub
commit 2875b10a7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 57 additions and 27 deletions

View File

@ -56,18 +56,12 @@ pub fn check_expr_closure(
// It's always helpful for inference if we know the kind of
// closure sooner rather than later, so first examine the expected
// type, and see if can glean a closure kind from there.
let (expected_sig, expected_kind) = match closure.kind {
hir::ClosureKind::Closure => match expected.to_option(self) {
Some(ty) => {
self.deduce_closure_signature(self.try_structurally_resolve_type(expr_span, ty))
}
None => (None, None),
},
// We don't want to deduce a signature from `Fn` bounds for coroutines
// or coroutine-closures, because the former does not implement `Fn`
// ever, and the latter's signature doesn't correspond to the coroutine
// type that it returns.
hir::ClosureKind::Coroutine(_) | hir::ClosureKind::CoroutineClosure(_) => (None, None),
let (expected_sig, expected_kind) = match expected.to_option(self) {
Some(ty) => self.deduce_closure_signature(
self.try_structurally_resolve_type(expr_span, ty),
closure.kind,
),
None => (None, None),
};
let ClosureSignatures { bound_sig, mut liberated_sig } =
@ -323,11 +317,13 @@ pub fn check_expr_closure(
fn deduce_closure_signature(
&self,
expected_ty: Ty<'tcx>,
closure_kind: hir::ClosureKind,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
match *expected_ty.kind() {
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self
.deduce_closure_signature_from_predicates(
expected_ty,
closure_kind,
self.tcx
.explicit_item_bounds(def_id)
.iter_instantiated_copied(self.tcx, args)
@ -336,7 +332,7 @@ fn deduce_closure_signature(
ty::Dynamic(object_type, ..) => {
let sig = object_type.projection_bounds().find_map(|pb| {
let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self);
self.deduce_sig_from_projection(None, pb)
self.deduce_sig_from_projection(None, closure_kind, pb)
});
let kind = object_type
.principal_def_id()
@ -345,12 +341,18 @@ fn deduce_closure_signature(
}
ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates(
Ty::new_var(self.tcx, self.root_var(vid)),
closure_kind,
self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)),
),
ty::FnPtr(sig) => {
let expected_sig = ExpectedSig { cause_span: None, sig };
(Some(expected_sig), Some(ty::ClosureKind::Fn))
}
ty::FnPtr(sig) => match closure_kind {
hir::ClosureKind::Closure => {
let expected_sig = ExpectedSig { cause_span: None, sig };
(Some(expected_sig), Some(ty::ClosureKind::Fn))
}
hir::ClosureKind::Coroutine(_) | hir::ClosureKind::CoroutineClosure(_) => {
(None, None)
}
},
_ => (None, None),
}
}
@ -358,6 +360,7 @@ fn deduce_closure_signature(
fn deduce_closure_signature_from_predicates(
&self,
expected_ty: Ty<'tcx>,
closure_kind: hir::ClosureKind,
predicates: impl DoubleEndedIterator<Item = (ty::Predicate<'tcx>, Span)>,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
let mut expected_sig = None;
@ -386,6 +389,7 @@ fn deduce_closure_signature_from_predicates(
span,
self.deduce_sig_from_projection(
Some(span),
closure_kind,
bound_predicate.rebind(proj_predicate),
),
);
@ -422,13 +426,22 @@ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => Some(data.def_id()),
_ => None,
};
if let Some(closure_kind) =
trait_def_id.and_then(|def_id| self.tcx.fn_trait_kind_from_def_id(def_id))
{
expected_kind = Some(
expected_kind
.map_or_else(|| closure_kind, |current| cmp::min(current, closure_kind)),
);
if let Some(trait_def_id) = trait_def_id {
let found_kind = match closure_kind {
hir::ClosureKind::Closure => self.tcx.fn_trait_kind_from_def_id(trait_def_id),
hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => {
self.tcx.async_fn_trait_kind_from_def_id(trait_def_id)
}
_ => None,
};
if let Some(found_kind) = found_kind {
expected_kind = Some(
expected_kind
.map_or_else(|| found_kind, |current| cmp::min(current, found_kind)),
);
}
}
}
@ -445,14 +458,21 @@ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
fn deduce_sig_from_projection(
&self,
cause_span: Option<Span>,
closure_kind: hir::ClosureKind,
projection: ty::PolyProjectionPredicate<'tcx>,
) -> Option<ExpectedSig<'tcx>> {
let tcx = self.tcx;
let trait_def_id = projection.trait_def_id(tcx);
// For now, we only do signature deduction based off of the `Fn` traits.
if !tcx.is_fn_trait(trait_def_id) {
return None;
// For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits,
// for closures and async closures, respectively.
match closure_kind {
hir::ClosureKind::Closure
if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => {}
hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async)
if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() => {}
_ => return None,
}
let arg_param_ty = projection.skip_binder().projection_ty.args.type_at(1);

View File

@ -0,0 +1,10 @@
//@ check-pass
//@ edition: 2021
#![feature(async_closure)]
async fn foo(x: impl async Fn(&str) -> &str) {}
fn main() {
foo(async |x| x);
}