Flatten confirmation logic

This commit is contained in:
Michael Goulet 2024-02-05 18:33:41 +00:00
parent 4a2fe4491e
commit f3d32f2f0c
2 changed files with 111 additions and 140 deletions

View File

@ -318,34 +318,27 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
goal_kind: ty::ClosureKind, goal_kind: ty::ClosureKind,
env_region: ty::Region<'tcx>, env_region: ty::Region<'tcx>,
) -> Result< ) -> Result<(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Vec<ty::Predicate<'tcx>>), NoSolution>
(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Option<ty::Predicate<'tcx>>), {
NoSolution,
> {
match *self_ty.kind() { match *self_ty.kind() {
ty::CoroutineClosure(def_id, args) => { ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure(); let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty(); let kind_ty = args.kind_ty();
let sig = args.coroutine_closure_sig().skip_binder();
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() { let mut nested = vec![];
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) { if !closure_kind.extends(goal_kind) {
return Err(NoSolution); return Err(NoSolution);
} }
Ok(( sig.to_coroutine_given_kind_and_upvars(
args.coroutine_closure_sig().map_bound(|sig| { tcx,
let coroutine_ty = sig.to_coroutine_given_kind_and_upvars( args.parent_args(),
tcx, tcx.coroutine_for_closure(def_id),
args.parent_args(), goal_kind,
tcx.coroutine_for_closure(def_id), env_region,
goal_kind, args.tupled_upvars_ty(),
env_region, args.coroutine_captures_by_ref_ty(),
args.tupled_upvars_ty(), )
args.coroutine_captures_by_ref_ty(),
);
(sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
}),
None,
))
} else { } else {
let async_fn_kind_trait_def_id = let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
@ -362,39 +355,43 @@ 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` // the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and // will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind. // coroutine upvars respecting the closure kind.
Ok(( nested.push(
args.coroutine_closure_sig().map_bound(|sig| { ty::TraitRef::new(
let tupled_upvars_ty = Ty::new_projection( tcx,
tcx, async_fn_kind_trait_def_id,
upvars_projection_def_id, [kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
[ )
ty::GenericArg::from(kind_ty), .to_predicate(tcx),
Ty::from_closure_kind(tcx, goal_kind).into(), );
env_region.into(), let tupled_upvars_ty = Ty::new_projection(
sig.tupled_inputs_ty.into(), tcx,
args.tupled_upvars_ty().into(), upvars_projection_def_id,
args.coroutine_captures_by_ref_ty().into(), [
], ty::GenericArg::from(kind_ty),
); Ty::from_closure_kind(tcx, goal_kind).into(),
let coroutine_ty = sig.to_coroutine( env_region.into(),
tcx, sig.tupled_inputs_ty.into(),
args.parent_args(), args.tupled_upvars_ty().into(),
Ty::from_closure_kind(tcx, goal_kind), args.coroutine_captures_by_ref_ty().into(),
tcx.coroutine_for_closure(def_id), ],
tupled_upvars_ty, );
); sig.to_coroutine(
(sig.tupled_inputs_ty, sig.return_ty, coroutine_ty) tcx,
}), args.parent_args(),
Some( Ty::from_closure_kind(tcx, goal_kind),
ty::TraitRef::new( tcx.coroutine_for_closure(def_id),
tcx, tupled_upvars_ty,
async_fn_kind_trait_def_id, )
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)], };
)
.to_predicate(tcx), 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(..) | ty::Closure(..) => Err(NoSolution),

View File

@ -2446,8 +2446,9 @@ fn confirm_callable_candidate<'cx, 'tcx>(
fn confirm_async_closure_candidate<'cx, 'tcx>( fn confirm_async_closure_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>, selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>, obligation: &ProjectionTyObligation<'tcx>,
mut nested: Vec<PredicateObligation<'tcx>>, nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> { ) -> Progress<'tcx> {
let tcx = selcx.tcx();
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let ty::CoroutineClosure(def_id, args) = *self_ty.kind() else { let ty::CoroutineClosure(def_id, args) = *self_ty.kind() else {
unreachable!( unreachable!(
@ -2456,47 +2457,48 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
}; };
let args = args.as_coroutine_closure(); let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty(); let kind_ty = args.kind_ty();
let sig = args.coroutine_closure_sig().skip_binder();
let tcx = selcx.tcx();
let goal_kind = let goal_kind =
tcx.async_fn_trait_kind_from_def_id(obligation.predicate.trait_def_id(tcx)).unwrap(); tcx.async_fn_trait_kind_from_def_id(obligation.predicate.trait_def_id(tcx)).unwrap();
let async_fn_kind_helper_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
nested.push(obligation.with(
tcx,
ty::TraitRef::new(
tcx,
async_fn_kind_helper_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
),
));
let env_region = match goal_kind { let env_region = match goal_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => obligation.predicate.args.region_at(2), ty::ClosureKind::Fn | ty::ClosureKind::FnMut => obligation.predicate.args.region_at(2),
ty::ClosureKind::FnOnce => tcx.lifetimes.re_static, ty::ClosureKind::FnOnce => tcx.lifetimes.re_static,
}; };
let upvars_projection_def_id = tcx let item_name = tcx.item_name(obligation.predicate.def_id);
.associated_items(async_fn_kind_helper_trait_def_id) let term = match item_name {
.filter_by_name_unhygienic(sym::Upvars) sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => {
.next() if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
.unwrap() if !closure_kind.extends(goal_kind) {
.def_id; bug!("we should not be confirming if the closure kind is not met");
}
// FIXME(async_closures): Confirmation is kind of a mess here. Ideally, sig.to_coroutine_given_kind_and_upvars(
// we'd short-circuit when we know that the goal_kind >= closure_kind, and not tcx,
// register a nested predicate or create a new projection ty here. But I'm too args.parent_args(),
// lazy to make this more efficient atm, and we can always tweak it later, tcx.coroutine_for_closure(def_id),
// since all this does is make the solver do more work. goal_kind,
// env_region,
// The code duplication due to the different length args is kind of weird, too. args.tupled_upvars_ty(),
// args.coroutine_captures_by_ref_ty(),
// See the logic in `structural_traits` in the new solver to understand a bit )
// more clearly how this *should* look. } else {
let poly_cache_entry = args.coroutine_closure_sig().map_bound(|sig| { let async_fn_kind_trait_def_id =
let (projection_ty, term) = match tcx.item_name(obligation.predicate.def_id) { tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
sym::CallOnceFuture => { let upvars_projection_def_id = tcx
.associated_items(async_fn_kind_trait_def_id)
.filter_by_name_unhygienic(sym::Upvars)
.next()
.unwrap()
.def_id;
// 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.
// N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`.
let tupled_upvars_ty = Ty::new_projection( let tupled_upvars_ty = Ty::new_projection(
tcx, tcx,
upvars_projection_def_id, upvars_projection_def_id,
@ -2509,66 +2511,38 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
args.coroutine_captures_by_ref_ty().into(), args.coroutine_captures_by_ref_ty().into(),
], ],
); );
let coroutine_ty = sig.to_coroutine( sig.to_coroutine(
tcx, tcx,
args.parent_args(), args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind), Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id), tcx.coroutine_for_closure(def_id),
tupled_upvars_ty, tupled_upvars_ty,
);
(
ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[self_ty, sig.tupled_inputs_ty],
),
coroutine_ty.into(),
) )
} }
sym::CallMutFuture | sym::CallFuture => { }
let tupled_upvars_ty = Ty::new_projection( sym::Output => sig.return_ty,
tcx, name => bug!("no such associated type: {name}"),
upvars_projection_def_id, };
[ let projection_ty = match item_name {
ty::GenericArg::from(kind_ty), sym::CallOnceFuture | sym::Output => {
Ty::from_closure_kind(tcx, goal_kind).into(), ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.tupled_inputs_ty])
env_region.into(), }
sig.tupled_inputs_ty.into(), sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
args.tupled_upvars_ty().into(), tcx,
args.coroutine_captures_by_ref_ty().into(), obligation.predicate.def_id,
], [ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()],
); ),
let coroutine_ty = sig.to_coroutine( name => bug!("no such associated type: {name}"),
tcx, };
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
);
(
ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[
ty::GenericArg::from(self_ty),
sig.tupled_inputs_ty.into(),
env_region.into(),
],
),
coroutine_ty.into(),
)
}
sym::Output => (
ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.tupled_inputs_ty]),
sig.return_ty.into(),
),
name => bug!("no such associated type: {name}"),
};
ty::ProjectionPredicate { projection_ty, term }
});
confirm_param_env_candidate(selcx, obligation, poly_cache_entry, true) confirm_param_env_candidate(
.with_addl_obligations(nested) selcx,
obligation,
args.coroutine_closure_sig()
.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() }),
true,
)
.with_addl_obligations(nested)
} }
fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>( fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>(