Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't rely on upvars being assigned just because coroutine-closure kind is assigned #123662

Merged
merged 1 commit into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2231,7 +2231,7 @@ impl<'tcx> Ty<'tcx> {
pub fn tuple_fields(self) -> &'tcx List<Ty<'tcx>> {
match self.kind() {
Tuple(args) => args,
_ => bug!("tuple_fields called on non-tuple"),
_ => bug!("tuple_fields called on non-tuple: {self:?}"),
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
let kind_ty = args.kind_ty();
let sig = args.coroutine_closure_sig().skip_binder();

let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
&& !args.tupled_upvars_ty().is_ty_var()
{
if !closure_kind.extends(goal_kind) {
return Err(NoSolution);
}
Expand Down Expand Up @@ -401,7 +403,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
let kind_ty = args.kind_ty();
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() {
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
&& !args.tupled_upvars_ty().is_ty_var()
{
if !closure_kind.extends(goal_kind) {
return Err(NoSolution);
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
bug!();
};

// Bail if the upvars haven't been constrained.
if tupled_upvars_ty.expect_ty().is_ty_var() {
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}

let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else {
// We don't need to worry about the self type being an infer var.
return Err(NoSolution);
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1601,7 +1601,10 @@ fn confirm_closure_candidate<'cx, 'tcx>(
// If we know the kind and upvars, use that directly.
// Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay
// the projection, like the `AsyncFn*` traits do.
let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind() {
let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind()
// Fall back to projection if upvars aren't constrained
&& !args.tupled_upvars_ty().is_ty_var()
{
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
Expand Down Expand Up @@ -1731,7 +1734,10 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(

let term = match item_name {
sym::CallOnceFuture | sym::CallRefFuture => {
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
// Fall back to projection if upvars aren't constrained
&& !args.tupled_upvars_ty().is_ty_var()
{
if !closure_kind.extends(goal_kind) {
bug!("we should not be confirming if the closure kind is not met");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,39 +400,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let is_const = self.tcx().is_const_fn_raw(def_id);
match self.infcx.closure_kind(self_ty) {
Some(closure_kind) => {
let no_borrows = match self
.infcx
.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty())
.kind()
{
ty::Tuple(tys) => tys.is_empty(),
ty::Error(_) => false,
_ => bug!("tuple_fields called on non-tuple"),
};
// A coroutine-closure implements `FnOnce` *always*, since it may
// always be called once. It additionally implements `Fn`/`FnMut`
// only if it has no upvars (therefore no borrows from the closure
// that would need to be represented with a lifetime) and if the
// closure kind permits it.
// FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
// if it takes all of its upvars by copy, and none by ref. This would
// require us to record a bit more information during upvar analysis.
if no_borrows && closure_kind.extends(kind) {
candidates.vec.push(ClosureCandidate { is_const });
} else if kind == ty::ClosureKind::FnOnce {
candidates.vec.push(ClosureCandidate { is_const });
}
if let Some(closure_kind) = self.infcx.closure_kind(self_ty)
// Ambiguity if upvars haven't been constrained yet
&& !args.tupled_upvars_ty().is_ty_var()
{
let no_borrows = match args.tupled_upvars_ty().kind() {
ty::Tuple(tys) => tys.is_empty(),
ty::Error(_) => false,
_ => bug!("tuple_fields called on non-tuple"),
};
// A coroutine-closure implements `FnOnce` *always*, since it may
// always be called once. It additionally implements `Fn`/`FnMut`
// only if it has no upvars (therefore no borrows from the closure
// that would need to be represented with a lifetime) and if the
// closure kind permits it.
// FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
// if it takes all of its upvars by copy, and none by ref. This would
// require us to record a bit more information during upvar analysis.
if no_borrows && closure_kind.extends(kind) {
candidates.vec.push(ClosureCandidate { is_const });
} else if kind == ty::ClosureKind::FnOnce {
candidates.vec.push(ClosureCandidate { is_const });
}
None => {
if kind == ty::ClosureKind::FnOnce {
candidates.vec.push(ClosureCandidate { is_const });
} else {
// This stays ambiguous until kind+upvars are determined.
candidates.ambiguous = true;
}
} else {
if kind == ty::ClosureKind::FnOnce {
candidates.vec.push(ClosureCandidate { is_const });
} else {
// This stays ambiguous until kind+upvars are determined.
candidates.ambiguous = true;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//@ edition: 2021
//@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver

#![feature(async_closure)]

fn constrain<T: async FnOnce()>(t: T) -> T {
t
}

fn call_once<T>(f: impl FnOnce() -> T) -> T {
f()
}

async fn async_call_once<T>(f: impl async FnOnce() -> T) -> T {
f().await
}

fn main() {
let c = constrain(async || {});
call_once(c);

let c = constrain(async || {});
async_call_once(c);
}
Loading