Skip to content

Commit

Permalink
Rollup merge of rust-lang#121857 - compiler-errors:async-closure-sign…
Browse files Browse the repository at this point in the history
…ature-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 rust-lang#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`
  • Loading branch information
matthiaskrgr committed Mar 5, 2024
2 parents 63cd563 + d74d67c commit 2614564
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 27 deletions.
74 changes: 47 additions & 27 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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 } =
Expand Down Expand Up @@ -323,11 +317,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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)
Expand All @@ -336,7 +332,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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()
Expand All @@ -345,19 +341,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
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),
}
}

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;
Expand Down Expand Up @@ -386,6 +389,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span,
self.deduce_sig_from_projection(
Some(span),
closure_kind,
bound_predicate.rebind(proj_predicate),
),
);
Expand Down Expand Up @@ -422,13 +426,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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)),
);
}
}
}

Expand All @@ -445,14 +458,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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);
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/async-await/async-closures/signature-deduction.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 2614564

Please sign in to comment.