Skip to content

Commit d9679c0

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 12a463f commit d9679c0

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
@@ -3992,6 +3992,21 @@ generateForEachStmtConstraints(
39923992
AwaitExpr::createImplicit(ctx, /*awaitLoc=*/SourceLoc(), nextCall);
39933993
}
39943994

3995+
// The iterator type must conform to IteratorProtocol.
3996+
{
3997+
ProtocolDecl *iteratorProto = TypeChecker::getProtocol(
3998+
cs.getASTContext(), stmt->getForLoc(),
3999+
isAsync ? KnownProtocolKind::AsyncIteratorProtocol
4000+
: KnownProtocolKind::IteratorProtocol);
4001+
if (!iteratorProto)
4002+
return None;
4003+
4004+
cs.setContextualType(
4005+
nextRef->getBase(),
4006+
TypeLoc::withoutLoc(iteratorProto->getDeclaredInterfaceType()),
4007+
CTP_ForEachSequence);
4008+
}
4009+
39954010
SolutionApplicationTarget nextTarget(nextCall, dc, CTP_Unused,
39964011
/*contextualType=*/Type(),
39974012
/*isDiscarded=*/false);

lib/Sema/CSSimplify.cpp

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9260,36 +9260,71 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
92609260
}
92619261
}
92629262

9263-
// Special handling of injected references to `makeIterator` in for-in loops.
9264-
{
9265-
auto memberRef = getAsExpr<UnresolvedDotExpr>(locator->getAnchor());
9263+
// Special handling of injected references to `makeIterator` and `next`
9264+
// in for-in loops.
9265+
if (auto *expr = getAsExpr(locator->getAnchor())) {
9266+
// `next()` could be wrapped in `await` expression.
9267+
auto memberRef =
9268+
getAsExpr<UnresolvedDotExpr>(expr->getSemanticsProvidingExpr());
9269+
92669270
if (memberRef && memberRef->isImplicit() &&
92679271
locator->isLastElement<LocatorPathElt::Member>()) {
9272+
auto &ctx = getASTContext();
9273+
92689274
// Cannot simplify this constraint yet since we don't know whether
92699275
// the base type is going to be existential or not.
92709276
if (baseObjTy->isTypeVariableOrMember())
92719277
return formUnsolved();
92729278

9273-
auto *sequenceExpr = memberRef->getBase();
9279+
// Check whether the given dot expression is a reference
9280+
// to the given name with the given set of argument labels
9281+
// (aka compound name).
9282+
auto isRefTo = [&](UnresolvedDotExpr *UDE, Identifier name,
9283+
ArrayRef<StringRef> labels) {
9284+
auto refName = UDE->getName().getFullName();
9285+
return refName.isCompoundName(name, labels);
9286+
};
9287+
9288+
auto *baseExpr = memberRef->getBase();
92749289
// If base type is an existential, member lookup is fine because
92759290
// it would return a witness.
9276-
if (!baseObjTy->isExistentialType() &&
9277-
getContextualTypePurpose(sequenceExpr) == CTP_ForEachSequence) {
9278-
auto &ctx = getASTContext();
9279-
9280-
auto *sequenceProto = cast<ProtocolDecl>(
9281-
getContextualType(sequenceExpr, /*forConstraint=*/false)
9282-
->getAnyNominal());
9283-
bool isAsync = sequenceProto ==
9284-
TypeChecker::getProtocol(
9285-
ctx, SourceLoc(), KnownProtocolKind::AsyncSequence);
9286-
9287-
auto *makeIterator = isAsync ? ctx.getAsyncSequenceMakeAsyncIterator()
9288-
: ctx.getSequenceMakeIterator();
9291+
if (!baseObjTy->isExistentialType()) {
9292+
// Handle `makeIterator` reference.
9293+
if (getContextualTypePurpose(baseExpr) == CTP_ForEachSequence &&
9294+
isRefTo(memberRef, ctx.Id_makeIterator, /*lables=*/{})) {
9295+
auto *sequenceProto = cast<ProtocolDecl>(
9296+
getContextualType(baseExpr, /*forConstraint=*/false)
9297+
->getAnyNominal());
9298+
bool isAsync = sequenceProto == TypeChecker::getProtocol(
9299+
ctx, SourceLoc(),
9300+
KnownProtocolKind::AsyncSequence);
9301+
9302+
auto *makeIterator = isAsync ? ctx.getAsyncSequenceMakeAsyncIterator()
9303+
: ctx.getSequenceMakeIterator();
9304+
9305+
return simplifyValueWitnessConstraint(
9306+
ConstraintKind::ValueWitness, baseTy, makeIterator, memberTy, DC,
9307+
FunctionRefKind::Compound, flags, locator);
9308+
}
92899309

9290-
return simplifyValueWitnessConstraint(
9291-
ConstraintKind::ValueWitness, baseTy, makeIterator, memberTy, DC,
9292-
FunctionRefKind::Compound, flags, locator);
9310+
// Handle `next` reference.
9311+
if (getContextualTypePurpose(baseExpr) == CTP_ForEachSequence &&
9312+
isRefTo(memberRef, ctx.Id_next, /*labels=*/{})) {
9313+
auto *iteratorProto = cast<ProtocolDecl>(
9314+
getContextualType(baseExpr, /*forConstraint=*/false)
9315+
->getAnyNominal());
9316+
bool isAsync =
9317+
iteratorProto ==
9318+
TypeChecker::getProtocol(
9319+
ctx, SourceLoc(), KnownProtocolKind::AsyncIteratorProtocol);
9320+
9321+
auto *next =
9322+
isAsync ? ctx.getAsyncIteratorNext() : ctx.getIteratorNext();
9323+
9324+
return simplifyValueWitnessConstraint(
9325+
ConstraintKind::ValueWitness, baseTy, next, memberTy, DC,
9326+
FunctionRefKind::Compound, flags, locator);
9327+
}
92939328
}
92949329
}
92959330
}

0 commit comments

Comments
 (0)