Skip to content

Commit

Permalink
Auto merge of #120712 - compiler-errors:async-closures-harmonize, r=<…
Browse files Browse the repository at this point in the history
…try>

Harmonize `AsyncFn` implementations, make async closures conditionally impl `Fn*` traits

TODO: description
TODO: tests (specifically: check that `&async ||` impls `AsyncFn`, flesh out the `Fn` tests, add an always-fnonce test maybe using `Option::map`)

r? `@ghost`
  • Loading branch information
bors committed Feb 6, 2024
2 parents 4a2fe44 + e988944 commit 788c8f4
Show file tree
Hide file tree
Showing 16 changed files with 598 additions and 249 deletions.
18 changes: 11 additions & 7 deletions compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,16 +183,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
kind: TypeVariableOriginKind::TypeInference,
span: callee_expr.span,
});
// We may actually receive a coroutine back whose kind is different
// from the closure that this dispatched from. This is because when
// we have no captures, we automatically implement `FnOnce`. This
// impl forces the closure kind to `FnOnce` i.e. `u8`.
let kind_ty = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
span: callee_expr.span,
});
let call_sig = self.tcx.mk_fn_sig(
[coroutine_closure_sig.tupled_inputs_ty],
coroutine_closure_sig.to_coroutine(
self.tcx,
closure_args.parent_args(),
// Inherit the kind ty of the closure, since we're calling this
// coroutine with the most relaxed `AsyncFn*` trait that we can.
// We don't necessarily need to do this here, but it saves us
// computing one more infer var that will get constrained later.
closure_args.kind_ty(),
kind_ty,
self.tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
),
Expand Down Expand Up @@ -263,14 +267,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Try the options that are least restrictive on the caller first.
for (opt_trait_def_id, method_name, borrow) in [
(self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true),
(self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true),
(self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false),
(self.tcx.lang_items().async_fn_trait(), Ident::with_dummy_span(sym::async_call), true),
(self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true),
(
self.tcx.lang_items().async_fn_mut_trait(),
Ident::with_dummy_span(sym::async_call_mut),
true,
),
(self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false),
(
self.tcx.lang_items().async_fn_once_trait(),
Ident::with_dummy_span(sym::async_call_once),
Expand Down
13 changes: 8 additions & 5 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,14 @@ 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 expected.to_option(self) {
Some(ty) => {
self.deduce_closure_signature(self.try_structurally_resolve_type(expr_span, ty))
}
None => (None, None),
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),
},
_ => (None, None),
};

let ClosureSignatures { bound_sig, mut liberated_sig } =
Expand Down
172 changes: 117 additions & 55 deletions compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,34 +318,27 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
self_ty: Ty<'tcx>,
goal_kind: ty::ClosureKind,
env_region: ty::Region<'tcx>,
) -> Result<
(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Option<ty::Predicate<'tcx>>),
NoSolution,
> {
) -> Result<(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Vec<ty::Predicate<'tcx>>), NoSolution>
{
match *self_ty.kind() {
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();

if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
let sig = args.coroutine_closure_sig().skip_binder();
let mut nested = vec![];
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
return Err(NoSolution);
}
Ok((
args.coroutine_closure_sig().map_bound(|sig| {
let coroutine_ty = sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
goal_kind,
env_region,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
);
(sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
}),
None,
))
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
goal_kind,
env_region,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
} else {
let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
Expand All @@ -362,42 +355,111 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind.
Ok((
args.coroutine_closure_sig().map_bound(|sig| {
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
let coroutine_ty = sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
);
(sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
}),
Some(
ty::TraitRef::new(
tcx,
async_fn_kind_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
)
.to_predicate(tcx),
),
))
}
nested.push(
ty::TraitRef::new(
tcx,
async_fn_kind_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
)
.to_predicate(tcx),
);
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
)
};

Ok((
args.coroutine_closure_sig().rebind((
sig.tupled_inputs_ty,
sig.return_ty,
coroutine_ty,
)),
nested,
))
}

ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) => Err(NoSolution),
ty::FnDef(..) | ty::FnPtr(..) => {
let bound_sig = self_ty.fn_sig(tcx);
let sig = bound_sig.skip_binder();
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
let nested = vec![
bound_sig
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
.to_predicate(tcx),
];
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
Ok((
bound_sig.rebind((Ty::new_tup(tcx, sig.inputs()), sig.output(), future_output_ty)),
nested,
))
}
ty::Closure(_, args) => {
let args = args.as_closure();
let bound_sig = args.sig();
let sig = bound_sig.skip_binder();
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
let mut nested = vec![
bound_sig
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
.to_predicate(tcx),
];

let kind_ty = args.kind_ty();
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
return Err(NoSolution);
}
} else {
let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
// When we don't know the closure kind (and therefore also the closure's upvars,
// which are computed at the same time), we must delay the computation of the
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind.
nested.push(
ty::TraitRef::new(
tcx,
async_fn_kind_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
)
.to_predicate(tcx),
);
}

let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
Ok((bound_sig.rebind((sig.inputs()[0], sig.output(), future_output_ty)), nested))
}

ty::Bool
| ty::Char
Expand Down
Loading

0 comments on commit 788c8f4

Please sign in to comment.