Skip to content

Commit 8bb676b

Browse files
authored
Unrolled build for rust-lang#123662
Rollup merge of rust-lang#123662 - compiler-errors:no-upvars-yet, r=oli-obk Don't rely on upvars being assigned just because coroutine-closure kind is assigned Previously, code relied on the implicit assumption that if a coroutine-closure's kind variable was constrained, then its upvars were also constrained. This is because we assign all of them at once at the end up upvar analysis. However, there's another way that a coroutine-closure's kind can be constrained: from a signature hint in closure signature deduction. After rust-lang#123350, we use these hints, which means the implicit assumption above no longer holds. This PR adds the necessary checks so that we don't ICE. r? oli-obk
2 parents 2805aed + 6f96d7d commit 8bb676b

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)