Skip to content

Commit 7b0795d

Browse files
committed
[CSSimplify] Member ref decays into value witness for next in for-in
Reference to `$geneator.next` in for-in loop context needs to be treated as a reference to a witness of `IteratorProtocol#next` requirement, otherwise it could lead to problems with retroactive conformances.
1 parent 4d25ffc commit 7b0795d

File tree

2 files changed

+70
-20
lines changed

2 files changed

+70
-20
lines changed

lib/Sema/CSGen.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4049,6 +4049,21 @@ generateForEachStmtConstraints(
40494049
AwaitExpr::createImplicit(ctx, /*awaitLoc=*/SourceLoc(), nextCall);
40504050
}
40514051

4052+
// The iterator type must conform to IteratorProtocol.
4053+
{
4054+
ProtocolDecl *iteratorProto = TypeChecker::getProtocol(
4055+
cs.getASTContext(), stmt->getForLoc(),
4056+
isAsync ? KnownProtocolKind::AsyncIteratorProtocol
4057+
: KnownProtocolKind::IteratorProtocol);
4058+
if (!iteratorProto)
4059+
return None;
4060+
4061+
cs.setContextualType(
4062+
nextRef->getBase(),
4063+
TypeLoc::withoutLoc(iteratorProto->getDeclaredInterfaceType()),
4064+
CTP_ForEachSequence);
4065+
}
4066+
40524067
SolutionApplicationTarget nextTarget(nextCall, dc, CTP_Unused,
40534068
/*contextualType=*/Type(),
40544069
/*isDiscarded=*/false);

lib/Sema/CSSimplify.cpp

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9249,36 +9249,71 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
92499249
}
92509250
}
92519251

9252-
// Special handling of injected references to `makeIterator` in for-in loops.
9253-
{
9254-
auto memberRef = getAsExpr<UnresolvedDotExpr>(locator->getAnchor());
9252+
// Special handling of injected references to `makeIterator` and `next`
9253+
// in for-in loops.
9254+
if (auto *expr = getAsExpr(locator->getAnchor())) {
9255+
// `next()` could be wrapped in `await` expression.
9256+
auto memberRef =
9257+
getAsExpr<UnresolvedDotExpr>(expr->getSemanticsProvidingExpr());
9258+
92559259
if (memberRef && memberRef->isImplicit() &&
92569260
locator->isLastElement<LocatorPathElt::Member>()) {
9261+
auto &ctx = getASTContext();
9262+
92579263
// Cannot simplify this constraint yet since we don't know whether
92589264
// the base type is going to be existential or not.
92599265
if (baseObjTy->isTypeVariableOrMember())
92609266
return formUnsolved();
92619267

9262-
auto *sequenceExpr = memberRef->getBase();
9268+
// Check whether the given dot expression is a reference
9269+
// to the given name with the given set of argument labels
9270+
// (aka compound name).
9271+
auto isRefTo = [&](UnresolvedDotExpr *UDE, Identifier name,
9272+
ArrayRef<StringRef> labels) {
9273+
auto refName = UDE->getName().getFullName();
9274+
return refName.isCompoundName(name, labels);
9275+
};
9276+
9277+
auto *baseExpr = memberRef->getBase();
92639278
// If base type is an existential, member lookup is fine because
92649279
// it would return a witness.
9265-
if (!baseObjTy->isExistentialType() &&
9266-
getContextualTypePurpose(sequenceExpr) == CTP_ForEachSequence) {
9267-
auto &ctx = getASTContext();
9268-
9269-
auto *sequenceProto = cast<ProtocolDecl>(
9270-
getContextualType(sequenceExpr, /*forConstraint=*/false)
9271-
->getAnyNominal());
9272-
bool isAsync = sequenceProto ==
9273-
TypeChecker::getProtocol(
9274-
ctx, SourceLoc(), KnownProtocolKind::AsyncSequence);
9275-
9276-
auto *makeIterator = isAsync ? ctx.getAsyncSequenceMakeAsyncIterator()
9277-
: ctx.getSequenceMakeIterator();
9280+
if (!baseObjTy->isExistentialType()) {
9281+
// Handle `makeIterator` reference.
9282+
if (getContextualTypePurpose(baseExpr) == CTP_ForEachSequence &&
9283+
isRefTo(memberRef, ctx.Id_makeIterator, /*lables=*/{})) {
9284+
auto *sequenceProto = cast<ProtocolDecl>(
9285+
getContextualType(baseExpr, /*forConstraint=*/false)
9286+
->getAnyNominal());
9287+
bool isAsync = sequenceProto == TypeChecker::getProtocol(
9288+
ctx, SourceLoc(),
9289+
KnownProtocolKind::AsyncSequence);
9290+
9291+
auto *makeIterator = isAsync ? ctx.getAsyncSequenceMakeAsyncIterator()
9292+
: ctx.getSequenceMakeIterator();
9293+
9294+
return simplifyValueWitnessConstraint(
9295+
ConstraintKind::ValueWitness, baseTy, makeIterator, memberTy, DC,
9296+
FunctionRefKind::Compound, flags, locator);
9297+
}
92789298

9279-
return simplifyValueWitnessConstraint(
9280-
ConstraintKind::ValueWitness, baseTy, makeIterator, memberTy, DC,
9281-
FunctionRefKind::Compound, flags, locator);
9299+
// Handle `next` reference.
9300+
if (getContextualTypePurpose(baseExpr) == CTP_ForEachSequence &&
9301+
isRefTo(memberRef, ctx.Id_next, /*labels=*/{})) {
9302+
auto *iteratorProto = cast<ProtocolDecl>(
9303+
getContextualType(baseExpr, /*forConstraint=*/false)
9304+
->getAnyNominal());
9305+
bool isAsync =
9306+
iteratorProto ==
9307+
TypeChecker::getProtocol(
9308+
ctx, SourceLoc(), KnownProtocolKind::AsyncIteratorProtocol);
9309+
9310+
auto *next =
9311+
isAsync ? ctx.getAsyncIteratorNext() : ctx.getIteratorNext();
9312+
9313+
return simplifyValueWitnessConstraint(
9314+
ConstraintKind::ValueWitness, baseTy, next, memberTy, DC,
9315+
FunctionRefKind::Compound, flags, locator);
9316+
}
92829317
}
92839318
}
92849319
}

0 commit comments

Comments
 (0)