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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// It's always helpful for inference if we know the kind of // It's always helpful for inference if we know the kind of
// closure sooner rather than later, so first examine the expected // closure sooner rather than later, so first examine the expected
// type, and see if can glean a closure kind from there. // type, and see if can glean a closure kind from there.
let (expected_sig, expected_kind) = match closure.kind { let (expected_sig, expected_kind) = match expected.to_option(self) {
hir::ClosureKind::Closure => match expected.to_option(self) { Some(ty) => self.deduce_closure_signature(
Some(ty) => { self.try_structurally_resolve_type(expr_span, ty),
self.deduce_closure_signature(self.try_structurally_resolve_type(expr_span, ty)) closure.kind,
} ),
None => (None, None), 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 ClosureSignatures { bound_sig, mut liberated_sig } = let ClosureSignatures { bound_sig, mut liberated_sig } =
@ -323,11 +317,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn deduce_closure_signature( fn deduce_closure_signature(
&self, &self,
expected_ty: Ty<'tcx>, expected_ty: Ty<'tcx>,
closure_kind: hir::ClosureKind,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
match *expected_ty.kind() { match *expected_ty.kind() {
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self
.deduce_closure_signature_from_predicates( .deduce_closure_signature_from_predicates(
expected_ty, expected_ty,
closure_kind,
self.tcx self.tcx
.explicit_item_bounds(def_id) .explicit_item_bounds(def_id)
.iter_instantiated_copied(self.tcx, args) .iter_instantiated_copied(self.tcx, args)
@ -336,7 +332,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::Dynamic(object_type, ..) => { ty::Dynamic(object_type, ..) => {
let sig = object_type.projection_bounds().find_map(|pb| { let sig = object_type.projection_bounds().find_map(|pb| {
let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self); 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 let kind = object_type
.principal_def_id() .principal_def_id()
@ -345,12 +341,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates( ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates(
Ty::new_var(self.tcx, self.root_var(vid)), Ty::new_var(self.tcx, self.root_var(vid)),
closure_kind,
self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)), self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)),
), ),
ty::FnPtr(sig) => { ty::FnPtr(sig) => match closure_kind {
let expected_sig = ExpectedSig { cause_span: None, sig }; hir::ClosureKind::Closure => {
(Some(expected_sig), Some(ty::ClosureKind::Fn)) 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), _ => (None, None),
} }
} }
@ -358,6 +360,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn deduce_closure_signature_from_predicates( fn deduce_closure_signature_from_predicates(
&self, &self,
expected_ty: Ty<'tcx>, expected_ty: Ty<'tcx>,
closure_kind: hir::ClosureKind,
predicates: impl DoubleEndedIterator<Item = (ty::Predicate<'tcx>, Span)>, predicates: impl DoubleEndedIterator<Item = (ty::Predicate<'tcx>, Span)>,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
let mut expected_sig = None; let mut expected_sig = None;
@ -386,6 +389,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span, span,
self.deduce_sig_from_projection( self.deduce_sig_from_projection(
Some(span), Some(span),
closure_kind,
bound_predicate.rebind(proj_predicate), bound_predicate.rebind(proj_predicate),
), ),
); );
@ -422,13 +426,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => Some(data.def_id()), ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => Some(data.def_id()),
_ => None, _ => None,
}; };
if let Some(closure_kind) =
trait_def_id.and_then(|def_id| self.tcx.fn_trait_kind_from_def_id(def_id)) if let Some(trait_def_id) = trait_def_id {
{ let found_kind = match closure_kind {
expected_kind = Some( hir::ClosureKind::Closure => self.tcx.fn_trait_kind_from_def_id(trait_def_id),
expected_kind hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => {
.map_or_else(|| closure_kind, |current| cmp::min(current, closure_kind)), 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn deduce_sig_from_projection( fn deduce_sig_from_projection(
&self, &self,
cause_span: Option<Span>, cause_span: Option<Span>,
closure_kind: hir::ClosureKind,
projection: ty::PolyProjectionPredicate<'tcx>, projection: ty::PolyProjectionPredicate<'tcx>,
) -> Option<ExpectedSig<'tcx>> { ) -> Option<ExpectedSig<'tcx>> {
let tcx = self.tcx; let tcx = self.tcx;
let trait_def_id = projection.trait_def_id(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) { // For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits,
return None; // 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); 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);
}