Rollup merge of #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 #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
This commit is contained in:
@@ -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(),
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user