Skip to content

Commit 5978b6f

Browse files
Rollup merge of #121654 - compiler-errors:async-fn-for-fn-def, r=oli-obk
Fix `async Fn` confirmation for `FnDef`/`FnPtr`/`Closure` types Fixes three issues: 1. The code in `extract_tupled_inputs_and_output_from_async_callable` was accidentally getting the *future* type and the *output* type (returned by the future) messed up for fnptr/fndef/closure types. :/ 2. We have a (class of) bug(s) in the old solver where we don't really support higher ranked built-in `Future` goals for generators. This is not possible to hit on stable code, but [can be hit with `unboxed_closures`](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=e935de7181e37e13515ad01720bcb899) (#121653). * I'm opting not to fix that in this PR. Instead, I just instantiate placeholders when confirming `async Fn` goals. 4. Fixed a bug when generating `FnPtr` shims for `async Fn` trait goals. r? oli-obk
2 parents 1ee5453 + c8e3f35 commit 5978b6f

File tree

12 files changed

+330
-104
lines changed

12 files changed

+330
-104
lines changed

compiler/rustc_mir_transform/src/shim.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
3737
}
3838
ty::InstanceDef::FnPtrShim(def_id, ty) => {
3939
let trait_ = tcx.trait_of_item(def_id).unwrap();
40-
let adjustment = match tcx.fn_trait_kind_from_def_id(trait_) {
40+
// Supports `Fn` or `async Fn` traits.
41+
let adjustment = match tcx
42+
.fn_trait_kind_from_def_id(trait_)
43+
.or_else(|| tcx.async_fn_trait_kind_from_def_id(trait_))
44+
{
4145
Some(ty::ClosureKind::FnOnce) => Adjustment::Identity,
4246
Some(ty::ClosureKind::Fn) => Adjustment::Deref { source: DerefSource::ImmRef },
4347
Some(ty::ClosureKind::FnMut) => Adjustment::Deref { source: DerefSource::MutRef },

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

+156-45
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,58 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
281281
}
282282

283283
// Coroutine-closures don't implement `Fn` traits the normal way.
284-
ty::CoroutineClosure(..) => Err(NoSolution),
284+
// Instead, they always implement `FnOnce`, but only implement
285+
// `FnMut`/`Fn` if they capture no upvars, since those may borrow
286+
// from the closure.
287+
ty::CoroutineClosure(def_id, args) => {
288+
let args = args.as_coroutine_closure();
289+
let kind_ty = args.kind_ty();
290+
let sig = args.coroutine_closure_sig().skip_binder();
291+
292+
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
293+
if !closure_kind.extends(goal_kind) {
294+
return Err(NoSolution);
295+
}
296+
297+
// If `Fn`/`FnMut`, we only implement this goal if we
298+
// have no captures.
299+
let no_borrows = match args.tupled_upvars_ty().kind() {
300+
ty::Tuple(tys) => tys.is_empty(),
301+
ty::Error(_) => false,
302+
_ => bug!("tuple_fields called on non-tuple"),
303+
};
304+
if closure_kind != ty::ClosureKind::FnOnce && !no_borrows {
305+
return Err(NoSolution);
306+
}
307+
308+
coroutine_closure_to_certain_coroutine(
309+
tcx,
310+
goal_kind,
311+
// No captures by ref, so this doesn't matter.
312+
tcx.lifetimes.re_static,
313+
def_id,
314+
args,
315+
sig,
316+
)
317+
} else {
318+
// Closure kind is not yet determined, so we return ambiguity unless
319+
// the expected kind is `FnOnce` as that is always implemented.
320+
if goal_kind != ty::ClosureKind::FnOnce {
321+
return Ok(None);
322+
}
323+
324+
coroutine_closure_to_ambiguous_coroutine(
325+
tcx,
326+
goal_kind, // No captures by ref, so this doesn't matter.
327+
tcx.lifetimes.re_static,
328+
def_id,
329+
args,
330+
sig,
331+
)
332+
};
333+
334+
Ok(Some(args.coroutine_closure_sig().rebind((sig.tupled_inputs_ty, coroutine_ty))))
335+
}
285336

286337
ty::Bool
287338
| ty::Char
@@ -313,6 +364,19 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
313364
}
314365
}
315366

367+
/// Relevant types for an async callable, including its inputs, output,
368+
/// and the return type you get from awaiting the output.
369+
#[derive(Copy, Clone, Debug, TypeVisitable, TypeFoldable)]
370+
pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> {
371+
pub tupled_inputs_ty: Ty<'tcx>,
372+
/// Type returned by calling the closure
373+
/// i.e. `f()`.
374+
pub output_coroutine_ty: Ty<'tcx>,
375+
/// Type returned by `await`ing the output
376+
/// i.e. `f().await`.
377+
pub coroutine_return_ty: Ty<'tcx>,
378+
}
379+
316380
// Returns a binder of the tupled inputs types, output type, and coroutine type
317381
// from a builtin coroutine-closure type. If we don't yet know the closure kind of
318382
// the coroutine-closure, emit an additional trait predicate for `AsyncFnKindHelper`
@@ -323,8 +387,10 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
323387
self_ty: Ty<'tcx>,
324388
goal_kind: ty::ClosureKind,
325389
env_region: ty::Region<'tcx>,
326-
) -> Result<(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Vec<ty::Predicate<'tcx>>), NoSolution>
327-
{
390+
) -> Result<
391+
(ty::Binder<'tcx, AsyncCallableRelevantTypes<'tcx>>, Vec<ty::Predicate<'tcx>>),
392+
NoSolution,
393+
> {
328394
match *self_ty.kind() {
329395
ty::CoroutineClosure(def_id, args) => {
330396
let args = args.as_coroutine_closure();
@@ -335,24 +401,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
335401
if !closure_kind.extends(goal_kind) {
336402
return Err(NoSolution);
337403
}
338-
sig.to_coroutine_given_kind_and_upvars(
339-
tcx,
340-
args.parent_args(),
341-
tcx.coroutine_for_closure(def_id),
342-
goal_kind,
343-
env_region,
344-
args.tupled_upvars_ty(),
345-
args.coroutine_captures_by_ref_ty(),
404+
405+
coroutine_closure_to_certain_coroutine(
406+
tcx, goal_kind, env_region, def_id, args, sig,
346407
)
347408
} else {
348-
let async_fn_kind_trait_def_id =
349-
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
350-
let upvars_projection_def_id = tcx
351-
.associated_items(async_fn_kind_trait_def_id)
352-
.filter_by_name_unhygienic(sym::Upvars)
353-
.next()
354-
.unwrap()
355-
.def_id;
356409
// When we don't know the closure kind (and therefore also the closure's upvars,
357410
// which are computed at the same time), we must delay the computation of the
358411
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
@@ -363,38 +416,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
363416
nested.push(
364417
ty::TraitRef::new(
365418
tcx,
366-
async_fn_kind_trait_def_id,
419+
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None),
367420
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
368421
)
369422
.to_predicate(tcx),
370423
);
371-
let tupled_upvars_ty = Ty::new_projection(
372-
tcx,
373-
upvars_projection_def_id,
374-
[
375-
ty::GenericArg::from(kind_ty),
376-
Ty::from_closure_kind(tcx, goal_kind).into(),
377-
env_region.into(),
378-
sig.tupled_inputs_ty.into(),
379-
args.tupled_upvars_ty().into(),
380-
args.coroutine_captures_by_ref_ty().into(),
381-
],
382-
);
383-
sig.to_coroutine(
384-
tcx,
385-
args.parent_args(),
386-
Ty::from_closure_kind(tcx, goal_kind),
387-
tcx.coroutine_for_closure(def_id),
388-
tupled_upvars_ty,
424+
425+
coroutine_closure_to_ambiguous_coroutine(
426+
tcx, goal_kind, env_region, def_id, args, sig,
389427
)
390428
};
391429

392430
Ok((
393-
args.coroutine_closure_sig().rebind((
394-
sig.tupled_inputs_ty,
395-
sig.return_ty,
396-
coroutine_ty,
397-
)),
431+
args.coroutine_closure_sig().rebind(AsyncCallableRelevantTypes {
432+
tupled_inputs_ty: sig.tupled_inputs_ty,
433+
output_coroutine_ty: coroutine_ty,
434+
coroutine_return_ty: sig.return_ty,
435+
}),
398436
nested,
399437
))
400438
}
@@ -418,7 +456,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
418456
.def_id;
419457
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
420458
Ok((
421-
bound_sig.rebind((Ty::new_tup(tcx, sig.inputs()), sig.output(), future_output_ty)),
459+
bound_sig.rebind(AsyncCallableRelevantTypes {
460+
tupled_inputs_ty: Ty::new_tup(tcx, sig.inputs()),
461+
output_coroutine_ty: sig.output(),
462+
coroutine_return_ty: future_output_ty,
463+
}),
422464
nested,
423465
))
424466
}
@@ -469,7 +511,14 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
469511
.unwrap()
470512
.def_id;
471513
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
472-
Ok((bound_sig.rebind((sig.inputs()[0], sig.output(), future_output_ty)), nested))
514+
Ok((
515+
bound_sig.rebind(AsyncCallableRelevantTypes {
516+
tupled_inputs_ty: sig.inputs()[0],
517+
output_coroutine_ty: sig.output(),
518+
coroutine_return_ty: future_output_ty,
519+
}),
520+
nested,
521+
))
473522
}
474523

475524
ty::Bool
@@ -502,6 +551,68 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
502551
}
503552
}
504553

554+
/// Given a coroutine-closure, project to its returned coroutine when we are *certain*
555+
/// that the closure's kind is compatible with the goal.
556+
fn coroutine_closure_to_certain_coroutine<'tcx>(
557+
tcx: TyCtxt<'tcx>,
558+
goal_kind: ty::ClosureKind,
559+
goal_region: ty::Region<'tcx>,
560+
def_id: DefId,
561+
args: ty::CoroutineClosureArgs<'tcx>,
562+
sig: ty::CoroutineClosureSignature<'tcx>,
563+
) -> Ty<'tcx> {
564+
sig.to_coroutine_given_kind_and_upvars(
565+
tcx,
566+
args.parent_args(),
567+
tcx.coroutine_for_closure(def_id),
568+
goal_kind,
569+
goal_region,
570+
args.tupled_upvars_ty(),
571+
args.coroutine_captures_by_ref_ty(),
572+
)
573+
}
574+
575+
/// Given a coroutine-closure, project to its returned coroutine when we are *not certain*
576+
/// that the closure's kind is compatible with the goal, and therefore also don't know
577+
/// yet what the closure's upvars are.
578+
///
579+
/// Note that we do not also push a `AsyncFnKindHelper` goal here.
580+
fn coroutine_closure_to_ambiguous_coroutine<'tcx>(
581+
tcx: TyCtxt<'tcx>,
582+
goal_kind: ty::ClosureKind,
583+
goal_region: ty::Region<'tcx>,
584+
def_id: DefId,
585+
args: ty::CoroutineClosureArgs<'tcx>,
586+
sig: ty::CoroutineClosureSignature<'tcx>,
587+
) -> Ty<'tcx> {
588+
let async_fn_kind_trait_def_id = tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
589+
let upvars_projection_def_id = tcx
590+
.associated_items(async_fn_kind_trait_def_id)
591+
.filter_by_name_unhygienic(sym::Upvars)
592+
.next()
593+
.unwrap()
594+
.def_id;
595+
let tupled_upvars_ty = Ty::new_projection(
596+
tcx,
597+
upvars_projection_def_id,
598+
[
599+
ty::GenericArg::from(args.kind_ty()),
600+
Ty::from_closure_kind(tcx, goal_kind).into(),
601+
goal_region.into(),
602+
sig.tupled_inputs_ty.into(),
603+
args.tupled_upvars_ty().into(),
604+
args.coroutine_captures_by_ref_ty().into(),
605+
],
606+
);
607+
sig.to_coroutine(
608+
tcx,
609+
args.parent_args(),
610+
Ty::from_closure_kind(tcx, goal_kind),
611+
tcx.coroutine_for_closure(def_id),
612+
tupled_upvars_ty,
613+
)
614+
}
615+
505616
/// Assemble a list of predicates that would be present on a theoretical
506617
/// user impl for an object type. These predicates must be checked any time
507618
/// we assemble a built-in object candidate for an object type, since they

compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs

+46-35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::traits::{check_args_compatible, specialization_graph};
22

3+
use super::assembly::structural_traits::AsyncCallableRelevantTypes;
34
use super::assembly::{self, structural_traits, Candidate};
45
use super::{EvalCtxt, GoalSource};
56
use rustc_hir::def::DefKind;
@@ -392,46 +393,56 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
392393
goal_kind,
393394
env_region,
394395
)?;
395-
let output_is_sized_pred =
396-
tupled_inputs_and_output_and_coroutine.map_bound(|(_, output, _)| {
397-
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
398-
});
396+
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
397+
|AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| {
398+
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output_ty])
399+
},
400+
);
399401

400402
let pred = tupled_inputs_and_output_and_coroutine
401-
.map_bound(|(inputs, output, coroutine)| {
402-
let (projection_ty, term) = match tcx.item_name(goal.predicate.def_id()) {
403-
sym::CallOnceFuture => (
404-
ty::AliasTy::new(
405-
tcx,
406-
goal.predicate.def_id(),
407-
[goal.predicate.self_ty(), inputs],
403+
.map_bound(
404+
|AsyncCallableRelevantTypes {
405+
tupled_inputs_ty,
406+
output_coroutine_ty,
407+
coroutine_return_ty,
408+
}| {
409+
let (projection_ty, term) = match tcx.item_name(goal.predicate.def_id()) {
410+
sym::CallOnceFuture => (
411+
ty::AliasTy::new(
412+
tcx,
413+
goal.predicate.def_id(),
414+
[goal.predicate.self_ty(), tupled_inputs_ty],
415+
),
416+
output_coroutine_ty.into(),
408417
),
409-
coroutine.into(),
410-
),
411-
sym::CallMutFuture | sym::CallFuture => (
412-
ty::AliasTy::new(
413-
tcx,
414-
goal.predicate.def_id(),
415-
[
416-
ty::GenericArg::from(goal.predicate.self_ty()),
417-
inputs.into(),
418-
env_region.into(),
419-
],
418+
sym::CallMutFuture | sym::CallFuture => (
419+
ty::AliasTy::new(
420+
tcx,
421+
goal.predicate.def_id(),
422+
[
423+
ty::GenericArg::from(goal.predicate.self_ty()),
424+
tupled_inputs_ty.into(),
425+
env_region.into(),
426+
],
427+
),
428+
output_coroutine_ty.into(),
420429
),
421-
coroutine.into(),
422-
),
423-
sym::Output => (
424-
ty::AliasTy::new(
425-
tcx,
426-
goal.predicate.def_id(),
427-
[ty::GenericArg::from(goal.predicate.self_ty()), inputs.into()],
430+
sym::Output => (
431+
ty::AliasTy::new(
432+
tcx,
433+
goal.predicate.def_id(),
434+
[
435+
ty::GenericArg::from(goal.predicate.self_ty()),
436+
tupled_inputs_ty.into(),
437+
],
438+
),
439+
coroutine_return_ty.into(),
428440
),
429-
output.into(),
430-
),
431-
name => bug!("no such associated type: {name}"),
432-
};
433-
ty::ProjectionPredicate { projection_ty, term }
434-
})
441+
name => bug!("no such associated type: {name}"),
442+
};
443+
ty::ProjectionPredicate { projection_ty, term }
444+
},
445+
)
435446
.to_predicate(tcx);
436447

437448
// A built-in `AsyncFn` impl only holds if the output is sized.

0 commit comments

Comments
 (0)