Skip to content

Commit 6f96d7d

Browse files
Don't rely on upvars being assigned just because coroutine-closure kind is assigned
1 parent ab5bda1 commit 6f96d7d

File tree

6 files changed

+75
-36
lines changed

6 files changed

+75
-36
lines changed

compiler/rustc_middle/src/ty/sty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2231,7 +2231,7 @@ impl<'tcx> Ty<'tcx> {
22312231
pub fn tuple_fields(self) -> &'tcx List<Ty<'tcx>> {
22322232
match self.kind() {
22332233
Tuple(args) => args,
2234-
_ => bug!("tuple_fields called on non-tuple"),
2234+
_ => bug!("tuple_fields called on non-tuple: {self:?}"),
22352235
}
22362236
}
22372237

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
292292
let kind_ty = args.kind_ty();
293293
let sig = args.coroutine_closure_sig().skip_binder();
294294

295-
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
295+
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
296+
&& !args.tupled_upvars_ty().is_ty_var()
297+
{
296298
if !closure_kind.extends(goal_kind) {
297299
return Err(NoSolution);
298300
}
@@ -401,7 +403,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
401403
let kind_ty = args.kind_ty();
402404
let sig = args.coroutine_closure_sig().skip_binder();
403405
let mut nested = vec![];
404-
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
406+
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
407+
&& !args.tupled_upvars_ty().is_ty_var()
408+
{
405409
if !closure_kind.extends(goal_kind) {
406410
return Err(NoSolution);
407411
}

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

+5
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
487487
bug!();
488488
};
489489

490+
// Bail if the upvars haven't been constrained.
491+
if tupled_upvars_ty.expect_ty().is_ty_var() {
492+
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
493+
}
494+
490495
let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else {
491496
// We don't need to worry about the self type being an infer var.
492497
return Err(NoSolution);

compiler/rustc_trait_selection/src/traits/project.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1601,7 +1601,10 @@ fn confirm_closure_candidate<'cx, 'tcx>(
16011601
// If we know the kind and upvars, use that directly.
16021602
// Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay
16031603
// the projection, like the `AsyncFn*` traits do.
1604-
let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind() {
1604+
let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind()
1605+
// Fall back to projection if upvars aren't constrained
1606+
&& !args.tupled_upvars_ty().is_ty_var()
1607+
{
16051608
sig.to_coroutine_given_kind_and_upvars(
16061609
tcx,
16071610
args.parent_args(),
@@ -1731,7 +1734,10 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
17311734

17321735
let term = match item_name {
17331736
sym::CallOnceFuture | sym::CallRefFuture => {
1734-
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
1737+
if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
1738+
// Fall back to projection if upvars aren't constrained
1739+
&& !args.tupled_upvars_ty().is_ty_var()
1740+
{
17351741
if !closure_kind.extends(goal_kind) {
17361742
bug!("we should not be confirming if the closure kind is not met");
17371743
}

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+28-31
Original file line numberDiff line numberDiff line change
@@ -400,39 +400,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
400400
}
401401
}
402402
ty::CoroutineClosure(def_id, args) => {
403+
let args = args.as_coroutine_closure();
403404
let is_const = self.tcx().is_const_fn_raw(def_id);
404-
match self.infcx.closure_kind(self_ty) {
405-
Some(closure_kind) => {
406-
let no_borrows = match self
407-
.infcx
408-
.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty())
409-
.kind()
410-
{
411-
ty::Tuple(tys) => tys.is_empty(),
412-
ty::Error(_) => false,
413-
_ => bug!("tuple_fields called on non-tuple"),
414-
};
415-
// A coroutine-closure implements `FnOnce` *always*, since it may
416-
// always be called once. It additionally implements `Fn`/`FnMut`
417-
// only if it has no upvars (therefore no borrows from the closure
418-
// that would need to be represented with a lifetime) and if the
419-
// closure kind permits it.
420-
// FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
421-
// if it takes all of its upvars by copy, and none by ref. This would
422-
// require us to record a bit more information during upvar analysis.
423-
if no_borrows && closure_kind.extends(kind) {
424-
candidates.vec.push(ClosureCandidate { is_const });
425-
} else if kind == ty::ClosureKind::FnOnce {
426-
candidates.vec.push(ClosureCandidate { is_const });
427-
}
405+
if let Some(closure_kind) = self.infcx.closure_kind(self_ty)
406+
// Ambiguity if upvars haven't been constrained yet
407+
&& !args.tupled_upvars_ty().is_ty_var()
408+
{
409+
let no_borrows = match args.tupled_upvars_ty().kind() {
410+
ty::Tuple(tys) => tys.is_empty(),
411+
ty::Error(_) => false,
412+
_ => bug!("tuple_fields called on non-tuple"),
413+
};
414+
// A coroutine-closure implements `FnOnce` *always*, since it may
415+
// always be called once. It additionally implements `Fn`/`FnMut`
416+
// only if it has no upvars (therefore no borrows from the closure
417+
// that would need to be represented with a lifetime) and if the
418+
// closure kind permits it.
419+
// FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
420+
// if it takes all of its upvars by copy, and none by ref. This would
421+
// require us to record a bit more information during upvar analysis.
422+
if no_borrows && closure_kind.extends(kind) {
423+
candidates.vec.push(ClosureCandidate { is_const });
424+
} else if kind == ty::ClosureKind::FnOnce {
425+
candidates.vec.push(ClosureCandidate { is_const });
428426
}
429-
None => {
430-
if kind == ty::ClosureKind::FnOnce {
431-
candidates.vec.push(ClosureCandidate { is_const });
432-
} else {
433-
// This stays ambiguous until kind+upvars are determined.
434-
candidates.ambiguous = true;
435-
}
427+
} else {
428+
if kind == ty::ClosureKind::FnOnce {
429+
candidates.vec.push(ClosureCandidate { is_const });
430+
} else {
431+
// This stays ambiguous until kind+upvars are determined.
432+
candidates.ambiguous = true;
436433
}
437434
}
438435
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//@ edition: 2021
2+
//@ check-pass
3+
//@ revisions: current next
4+
//@ ignore-compare-mode-next-solver (explicit revisions)
5+
//@[next] compile-flags: -Znext-solver
6+
7+
#![feature(async_closure)]
8+
9+
fn constrain<T: async FnOnce()>(t: T) -> T {
10+
t
11+
}
12+
13+
fn call_once<T>(f: impl FnOnce() -> T) -> T {
14+
f()
15+
}
16+
17+
async fn async_call_once<T>(f: impl async FnOnce() -> T) -> T {
18+
f().await
19+
}
20+
21+
fn main() {
22+
let c = constrain(async || {});
23+
call_once(c);
24+
25+
let c = constrain(async || {});
26+
async_call_once(c);
27+
}

0 commit comments

Comments
 (0)