Skip to content

Commit

Permalink
Regular closures now built-in impls for AsyncFn*
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Feb 6, 2024
1 parent 0dd4078 commit 4436bb4
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,72 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
))
}

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
221 changes: 146 additions & 75 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2450,99 +2450,170 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
) -> Progress<'tcx> {
let tcx = selcx.tcx();
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let ty::CoroutineClosure(def_id, args) = *self_ty.kind() else {
unreachable!(
"expected coroutine-closure self type for coroutine-closure candidate, found {self_ty}"
)
};
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
let sig = args.coroutine_closure_sig().skip_binder();

let goal_kind =
tcx.async_fn_trait_kind_from_def_id(obligation.predicate.trait_def_id(tcx)).unwrap();
let env_region = match goal_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => obligation.predicate.args.region_at(2),
ty::ClosureKind::FnOnce => tcx.lifetimes.re_static,
};

let item_name = tcx.item_name(obligation.predicate.def_id);
let term = match item_name {
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => {
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
bug!("we should not be confirming if the closure kind is not met");

let poly_cache_entry = match *self_ty.kind() {
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
let sig = args.coroutine_closure_sig().skip_binder();

let term = match item_name {
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => {
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
bug!("we should not be confirming if the closure kind is not met");
}
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);
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(
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,
)
}
}
sig.to_coroutine_given_kind_and_upvars(
sym::Output => sig.return_ty,
name => bug!("no such associated type: {name}"),
};
let projection_ty = match item_name {
sym::CallOnceFuture | sym::Output => ty::AliasTy::new(
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);
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(
obligation.predicate.def_id,
[self_ty, sig.tupled_inputs_ty],
),
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
tcx,
upvars_projection_def_id,
obligation.predicate.def_id,
[ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()],
),
name => bug!("no such associated type: {name}"),
};

args.coroutine_closure_sig()
.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
}
ty::FnDef(..) | ty::FnPtr(..) => {
let bound_sig = self_ty.fn_sig(tcx);
let sig = bound_sig.skip_binder();

let term = match item_name {
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(),
sym::Output => {
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
Ty::new_projection(tcx, future_output_def_id, [sig.output()])
}
name => bug!("no such associated type: {name}"),
};
let projection_ty = match item_name {
sym::CallOnceFuture | sym::Output => ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[self_ty, Ty::new_tup(tcx, sig.inputs())],
),
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
ty::GenericArg::from(self_ty),
Ty::new_tup(tcx, sig.inputs()).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,
)
}
),
name => bug!("no such associated type: {name}"),
};

bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
}
sym::Output => sig.return_ty,
name => bug!("no such associated type: {name}"),
};
let projection_ty = match item_name {
sym::CallOnceFuture | sym::Output => {
ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.tupled_inputs_ty])
ty::Closure(_, args) => {
let args = args.as_closure();
let bound_sig = args.sig();
let sig = bound_sig.skip_binder();

let term = match item_name {
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(),
sym::Output => {
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
Ty::new_projection(tcx, future_output_def_id, [sig.output()])
}
name => bug!("no such associated type: {name}"),
};
let projection_ty = match item_name {
sym::CallOnceFuture | sym::Output => {
ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.inputs()[0]])
}
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[ty::GenericArg::from(self_ty), sig.inputs()[0].into(), env_region.into()],
),
name => bug!("no such associated type: {name}"),
};

bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
}
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()],
),
name => bug!("no such associated type: {name}"),
_ => bug!("expected callable type for AsyncFn candidate"),
};

confirm_param_env_candidate(
selcx,
obligation,
args.coroutine_closure_sig()
.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() }),
true,
)
.with_addl_obligations(nested)
confirm_param_env_candidate(selcx, obligation, poly_cache_entry, true)
.with_addl_obligations(nested)
}

fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
candidates.vec.push(AsyncClosureCandidate);
}
ty::Infer(ty::TyVar(_)) => {
candidates.ambiguous = true;
ty::Closure(_, args) => {
if let Some(closure_kind) = args.as_closure().kind_ty().to_opt_closure_kind()
&& !closure_kind.extends(goal_kind)
{
return;
}
candidates.vec.push(AsyncClosureCandidate);
}
ty::FnDef(..) | ty::FnPtr(..) => {
candidates.vec.push(AsyncClosureCandidate);
}
_ => {}
}
Expand Down
Loading

0 comments on commit 4436bb4

Please sign in to comment.