Skip to content

Commit c610e2b

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 d1ff79e commit c610e2b

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
@@ -4057,6 +4057,21 @@ generateForEachStmtConstraints(
40574057
AwaitExpr::createImplicit(ctx, /*awaitLoc=*/SourceLoc(), nextCall);
40584058
}
40594059

4060+
// The iterator type must conform to IteratorProtocol.
4061+
{
4062+
ProtocolDecl *iteratorProto = TypeChecker::getProtocol(
4063+
cs.getASTContext(), stmt->getForLoc(),
4064+
isAsync ? KnownProtocolKind::AsyncIteratorProtocol
4065+
: KnownProtocolKind::IteratorProtocol);
4066+
if (!iteratorProto)
4067+
return None;
4068+
4069+
cs.setContextualType(
4070+
nextRef->getBase(),
4071+
TypeLoc::withoutLoc(iteratorProto->getDeclaredInterfaceType()),
4072+
CTP_ForEachSequence);
4073+
}
4074+
40604075
SolutionApplicationTarget nextTarget(nextCall, dc, CTP_Unused,
40614076
/*contextualType=*/Type(),
40624077
/*isDiscarded=*/false);

lib/Sema/CSSimplify.cpp

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9254,36 +9254,71 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
92549254
}
92559255
}
92569256

9257-
// Special handling of injected references to `makeIterator` in for-in loops.
9258-
{
9259-
auto memberRef = getAsExpr<UnresolvedDotExpr>(locator->getAnchor());
9257+
// Special handling of injected references to `makeIterator` and `next`
9258+
// in for-in loops.
9259+
if (auto *expr = getAsExpr(locator->getAnchor())) {
9260+
// `next()` could be wrapped in `await` expression.
9261+
auto memberRef =
9262+
getAsExpr<UnresolvedDotExpr>(expr->getSemanticsProvidingExpr());
9263+
92609264
if (memberRef && memberRef->isImplicit() &&
92619265
locator->isLastElement<LocatorPathElt::Member>()) {
9266+
auto &ctx = getASTContext();
9267+
92629268
// Cannot simplify this constraint yet since we don't know whether
92639269
// the base type is going to be existential or not.
92649270
if (baseObjTy->isTypeVariableOrMember())
92659271
return formUnsolved();
92669272

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

9284-
return simplifyValueWitnessConstraint(
9285-
ConstraintKind::ValueWitness, baseTy, makeIterator, memberTy, DC,
9286-
FunctionRefKind::Compound, flags, locator);
9304+
// Handle `next` reference.
9305+
if (getContextualTypePurpose(baseExpr) == CTP_ForEachSequence &&
9306+
isRefTo(memberRef, ctx.Id_next, /*labels=*/{})) {
9307+
auto *iteratorProto = cast<ProtocolDecl>(
9308+
getContextualType(baseExpr, /*forConstraint=*/false)
9309+
->getAnyNominal());
9310+
bool isAsync =
9311+
iteratorProto ==
9312+
TypeChecker::getProtocol(
9313+
ctx, SourceLoc(), KnownProtocolKind::AsyncIteratorProtocol);
9314+
9315+
auto *next =
9316+
isAsync ? ctx.getAsyncIteratorNext() : ctx.getIteratorNext();
9317+
9318+
return simplifyValueWitnessConstraint(
9319+
ConstraintKind::ValueWitness, baseTy, next, memberTy, DC,
9320+
FunctionRefKind::Compound, flags, locator);
9321+
}
92879322
}
92889323
}
92899324
}

0 commit comments

Comments
 (0)