Skip to content

Commit 788c8f4

Browse files
committed
Auto merge of rust-lang#120712 - compiler-errors:async-closures-harmonize, r=<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`
2 parents 4a2fe44 + e988944 commit 788c8f4

File tree

16 files changed

+598
-249
lines changed

16 files changed

+598
-249
lines changed

compiler/rustc_hir_typeck/src/callee.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -183,16 +183,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
183183
kind: TypeVariableOriginKind::TypeInference,
184184
span: callee_expr.span,
185185
});
186+
// We may actually receive a coroutine back whose kind is different
187+
// from the closure that this dispatched from. This is because when
188+
// we have no captures, we automatically implement `FnOnce`. This
189+
// impl forces the closure kind to `FnOnce` i.e. `u8`.
190+
let kind_ty = self.next_ty_var(TypeVariableOrigin {
191+
kind: TypeVariableOriginKind::TypeInference,
192+
span: callee_expr.span,
193+
});
186194
let call_sig = self.tcx.mk_fn_sig(
187195
[coroutine_closure_sig.tupled_inputs_ty],
188196
coroutine_closure_sig.to_coroutine(
189197
self.tcx,
190198
closure_args.parent_args(),
191-
// Inherit the kind ty of the closure, since we're calling this
192-
// coroutine with the most relaxed `AsyncFn*` trait that we can.
193-
// We don't necessarily need to do this here, but it saves us
194-
// computing one more infer var that will get constrained later.
195-
closure_args.kind_ty(),
199+
kind_ty,
196200
self.tcx.coroutine_for_closure(def_id),
197201
tupled_upvars_ty,
198202
),
@@ -263,14 +267,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
263267
// Try the options that are least restrictive on the caller first.
264268
for (opt_trait_def_id, method_name, borrow) in [
265269
(self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true),
266-
(self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true),
267-
(self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false),
268270
(self.tcx.lang_items().async_fn_trait(), Ident::with_dummy_span(sym::async_call), true),
271+
(self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true),
269272
(
270273
self.tcx.lang_items().async_fn_mut_trait(),
271274
Ident::with_dummy_span(sym::async_call_mut),
272275
true,
273276
),
277+
(self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false),
274278
(
275279
self.tcx.lang_items().async_fn_once_trait(),
276280
Ident::with_dummy_span(sym::async_call_once),

compiler/rustc_hir_typeck/src/closure.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5656
// It's always helpful for inference if we know the kind of
5757
// closure sooner rather than later, so first examine the expected
5858
// type, and see if can glean a closure kind from there.
59-
let (expected_sig, expected_kind) = match expected.to_option(self) {
60-
Some(ty) => {
61-
self.deduce_closure_signature(self.try_structurally_resolve_type(expr_span, ty))
62-
}
63-
None => (None, None),
59+
let (expected_sig, expected_kind) = match closure.kind {
60+
hir::ClosureKind::Closure => match expected.to_option(self) {
61+
Some(ty) => {
62+
self.deduce_closure_signature(self.try_structurally_resolve_type(expr_span, ty))
63+
}
64+
None => (None, None),
65+
},
66+
_ => (None, None),
6467
};
6568

6669
let ClosureSignatures { bound_sig, mut liberated_sig } =

compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs

+117-55
Original file line numberDiff line numberDiff line change
@@ -318,34 +318,27 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
318318
self_ty: Ty<'tcx>,
319319
goal_kind: ty::ClosureKind,
320320
env_region: ty::Region<'tcx>,
321-
) -> Result<
322-
(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Option<ty::Predicate<'tcx>>),
323-
NoSolution,
324-
> {
321+
) -> Result<(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Vec<ty::Predicate<'tcx>>), NoSolution>
322+
{
325323
match *self_ty.kind() {
326324
ty::CoroutineClosure(def_id, args) => {
327325
let args = args.as_coroutine_closure();
328326
let kind_ty = args.kind_ty();
329-
330-
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
327+
let sig = args.coroutine_closure_sig().skip_binder();
328+
let mut nested = vec![];
329+
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
331330
if !closure_kind.extends(goal_kind) {
332331
return Err(NoSolution);
333332
}
334-
Ok((
335-
args.coroutine_closure_sig().map_bound(|sig| {
336-
let coroutine_ty = sig.to_coroutine_given_kind_and_upvars(
337-
tcx,
338-
args.parent_args(),
339-
tcx.coroutine_for_closure(def_id),
340-
goal_kind,
341-
env_region,
342-
args.tupled_upvars_ty(),
343-
args.coroutine_captures_by_ref_ty(),
344-
);
345-
(sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
346-
}),
347-
None,
348-
))
333+
sig.to_coroutine_given_kind_and_upvars(
334+
tcx,
335+
args.parent_args(),
336+
tcx.coroutine_for_closure(def_id),
337+
goal_kind,
338+
env_region,
339+
args.tupled_upvars_ty(),
340+
args.coroutine_captures_by_ref_ty(),
341+
)
349342
} else {
350343
let async_fn_kind_trait_def_id =
351344
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
@@ -362,42 +355,111 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
362355
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
363356
// will project to the right upvars for the generator, appending the inputs and
364357
// coroutine upvars respecting the closure kind.
365-
Ok((
366-
args.coroutine_closure_sig().map_bound(|sig| {
367-
let tupled_upvars_ty = Ty::new_projection(
368-
tcx,
369-
upvars_projection_def_id,
370-
[
371-
ty::GenericArg::from(kind_ty),
372-
Ty::from_closure_kind(tcx, goal_kind).into(),
373-
env_region.into(),
374-
sig.tupled_inputs_ty.into(),
375-
args.tupled_upvars_ty().into(),
376-
args.coroutine_captures_by_ref_ty().into(),
377-
],
378-
);
379-
let coroutine_ty = sig.to_coroutine(
380-
tcx,
381-
args.parent_args(),
382-
Ty::from_closure_kind(tcx, goal_kind),
383-
tcx.coroutine_for_closure(def_id),
384-
tupled_upvars_ty,
385-
);
386-
(sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
387-
}),
388-
Some(
389-
ty::TraitRef::new(
390-
tcx,
391-
async_fn_kind_trait_def_id,
392-
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
393-
)
394-
.to_predicate(tcx),
395-
),
396-
))
397-
}
358+
nested.push(
359+
ty::TraitRef::new(
360+
tcx,
361+
async_fn_kind_trait_def_id,
362+
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
363+
)
364+
.to_predicate(tcx),
365+
);
366+
let tupled_upvars_ty = Ty::new_projection(
367+
tcx,
368+
upvars_projection_def_id,
369+
[
370+
ty::GenericArg::from(kind_ty),
371+
Ty::from_closure_kind(tcx, goal_kind).into(),
372+
env_region.into(),
373+
sig.tupled_inputs_ty.into(),
374+
args.tupled_upvars_ty().into(),
375+
args.coroutine_captures_by_ref_ty().into(),
376+
],
377+
);
378+
sig.to_coroutine(
379+
tcx,
380+
args.parent_args(),
381+
Ty::from_closure_kind(tcx, goal_kind),
382+
tcx.coroutine_for_closure(def_id),
383+
tupled_upvars_ty,
384+
)
385+
};
386+
387+
Ok((
388+
args.coroutine_closure_sig().rebind((
389+
sig.tupled_inputs_ty,
390+
sig.return_ty,
391+
coroutine_ty,
392+
)),
393+
nested,
394+
))
398395
}
399396

400-
ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) => Err(NoSolution),
397+
ty::FnDef(..) | ty::FnPtr(..) => {
398+
let bound_sig = self_ty.fn_sig(tcx);
399+
let sig = bound_sig.skip_binder();
400+
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
401+
let nested = vec![
402+
bound_sig
403+
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
404+
.to_predicate(tcx),
405+
];
406+
let future_output_def_id = tcx
407+
.associated_items(future_trait_def_id)
408+
.filter_by_name_unhygienic(sym::Output)
409+
.next()
410+
.unwrap()
411+
.def_id;
412+
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
413+
Ok((
414+
bound_sig.rebind((Ty::new_tup(tcx, sig.inputs()), sig.output(), future_output_ty)),
415+
nested,
416+
))
417+
}
418+
ty::Closure(_, args) => {
419+
let args = args.as_closure();
420+
let bound_sig = args.sig();
421+
let sig = bound_sig.skip_binder();
422+
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
423+
let mut nested = vec![
424+
bound_sig
425+
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
426+
.to_predicate(tcx),
427+
];
428+
429+
let kind_ty = args.kind_ty();
430+
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
431+
if !closure_kind.extends(goal_kind) {
432+
return Err(NoSolution);
433+
}
434+
} else {
435+
let async_fn_kind_trait_def_id =
436+
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
437+
// When we don't know the closure kind (and therefore also the closure's upvars,
438+
// which are computed at the same time), we must delay the computation of the
439+
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
440+
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
441+
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
442+
// will project to the right upvars for the generator, appending the inputs and
443+
// coroutine upvars respecting the closure kind.
444+
nested.push(
445+
ty::TraitRef::new(
446+
tcx,
447+
async_fn_kind_trait_def_id,
448+
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
449+
)
450+
.to_predicate(tcx),
451+
);
452+
}
453+
454+
let future_output_def_id = tcx
455+
.associated_items(future_trait_def_id)
456+
.filter_by_name_unhygienic(sym::Output)
457+
.next()
458+
.unwrap()
459+
.def_id;
460+
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
461+
Ok((bound_sig.rebind((sig.inputs()[0], sig.output(), future_output_ty)), nested))
462+
}
401463

402464
ty::Bool
403465
| ty::Char

0 commit comments

Comments
 (0)