Skip to content

Commit 88f24e5

Browse files
committed
[CSSimplify] Member ref decays into value witness for makeIterator in for-in
Reference to `makeIterator` in for-in loop context needs to be treated as a reference to a witness of `Sequence#makeIterator` requirement, otherwise it could lead to problems with retroactive conformances.
1 parent 805df61 commit 88f24e5

File tree

3 files changed

+138
-11
lines changed

3 files changed

+138
-11
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9260,6 +9260,40 @@ 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());
9266+
if (memberRef && memberRef->isImplicit() &&
9267+
locator->isLastElement<LocatorPathElt::Member>()) {
9268+
// Cannot simplify this constraint yet since we don't know whether
9269+
// the base type is going to be existential or not.
9270+
if (baseObjTy->isTypeVariableOrMember())
9271+
return formUnsolved();
9272+
9273+
auto *sequenceExpr = memberRef->getBase();
9274+
// If base type is an existential, member lookup is fine because
9275+
// 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();
9289+
9290+
return simplifyValueWitnessConstraint(
9291+
ConstraintKind::ValueWitness, baseTy, makeIterator, memberTy, DC,
9292+
FunctionRefKind::Compound, flags, locator);
9293+
}
9294+
}
9295+
}
9296+
92639297
MemberLookupResult result =
92649298
performMemberLookup(kind, member, baseTy, functionRefKind, locator,
92659299
/*includeInaccessibleMembers*/ shouldAttemptFixes());
@@ -9696,6 +9730,17 @@ ConstraintSystem::simplifyValueWitnessConstraint(
96969730
return SolutionKind::Unsolved;
96979731
};
96989732

9733+
auto fail = [&] {
9734+
// The constraint failed, so mark the member type as a "hole".
9735+
// We cannot do anything further here.
9736+
if (!shouldAttemptFixes())
9737+
return SolutionKind::Error;
9738+
9739+
recordAnyTypeVarAsPotentialHole(memberType);
9740+
9741+
return SolutionKind::Solved;
9742+
};
9743+
96999744
// Resolve the base type, if we can. If we can't resolve the base type,
97009745
// then we can't solve this constraint.
97019746
Type baseObjectType = getFixedTypeRecursive(
@@ -9712,16 +9757,8 @@ ConstraintSystem::simplifyValueWitnessConstraint(
97129757
assert(proto && "Value witness constraint for a non-requirement");
97139758
auto conformance = useDC->getParentModule()->lookupConformance(
97149759
baseObjectType, proto);
9715-
if (!conformance) {
9716-
// The conformance failed, so mark the member type as a "hole". We cannot
9717-
// do anything further here.
9718-
if (!shouldAttemptFixes())
9719-
return SolutionKind::Error;
9720-
9721-
recordAnyTypeVarAsPotentialHole(memberType);
9722-
9723-
return SolutionKind::Solved;
9724-
}
9760+
if (!conformance)
9761+
return fail();
97259762

97269763
// Reference the requirement.
97279764
Type resolvedBaseType = simplifyType(baseType, flags);
@@ -9731,7 +9768,7 @@ ConstraintSystem::simplifyValueWitnessConstraint(
97319768
auto witness =
97329769
conformance.getWitnessByName(baseObjectType, requirement->getName());
97339770
if (!witness)
9734-
return SolutionKind::Error;
9771+
return fail();
97359772

97369773
auto choice = OverloadChoice(resolvedBaseType, witness.getDecl(), functionRefKind);
97379774
resolveOverload(getConstraintLocator(locator), memberType, choice,
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s
2+
3+
// https://github.com/apple/swift/issues/60514
4+
// Make sure that `makeIterator` witness is picked over the non-witness.
5+
6+
public protocol VectorSectionReader: Sequence where Element == Result<Item, Error> {
7+
associatedtype Item
8+
var count: UInt32 { get }
9+
mutating func read() throws -> Item
10+
}
11+
12+
public struct VectorSectionIterator<Reader: VectorSectionReader>: IteratorProtocol {
13+
private(set) var reader: Reader
14+
private(set) var left: UInt32
15+
16+
init(reader: Reader, count: UInt32) {
17+
self.reader = reader
18+
self.left = count
19+
}
20+
21+
private var end: Bool = false
22+
public mutating func next() -> Reader.Element? {
23+
guard !end else { return nil }
24+
guard left != 0 else { return nil }
25+
let result = Result(catching: { try reader.read() })
26+
left -= 1
27+
switch result {
28+
case .success: return result
29+
case .failure:
30+
end = true
31+
return result
32+
}
33+
}
34+
}
35+
36+
extension VectorSectionReader {
37+
__consuming public func makeIterator() -> VectorSectionIterator<Self> {
38+
VectorSectionIterator(reader: self, count: count)
39+
}
40+
41+
// CHECK: sil [ossa] @$s4main19VectorSectionReaderPAAE7collectSay4ItemQzGyKF
42+
public func collect() throws -> [Item] {
43+
var items: [Item] = []
44+
items.reserveCapacity(Int(count))
45+
for result in self {
46+
// CHECK: [[ITERATOR:%.*]] = project_box {{.*}} : $<τ_0_0 where τ_0_0 : VectorSectionReader> { var τ_0_0.Iterator } <Self>, 0
47+
// CHECK-NEXT: [[SELF:%.*]] = alloc_stack $Self
48+
// CHECK: [[MAKE_ITERATOR_REF:%.*]] = witness_method $Self, #Sequence.makeIterator : <Self where Self : Sequence> (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator
49+
// CHECK-NEXT: apply [[MAKE_ITERATOR_REF]]<Self>([[ITERATOR]], [[SELF]]) : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator
50+
try items.append(result.get())
51+
}
52+
return items
53+
}
54+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s
2+
3+
// https://github.com/apple/swift/issues/60958
4+
// Make sure that `makeIterator` witness is picked over the non-witness.
5+
6+
struct S: Sequence {
7+
private var _storage: [UInt8] = []
8+
9+
func makeIterator() -> Array<UInt8>.Iterator {
10+
_storage.makeIterator()
11+
}
12+
13+
typealias Element = UInt8
14+
15+
class Iterator: IteratorProtocol {
16+
func next() -> UInt8? { 0 }
17+
typealias Element = UInt8
18+
}
19+
20+
func makeIterator() -> Iterator {
21+
Iterator()
22+
}
23+
}
24+
25+
extension S {
26+
// CHECK: sil hidden [ossa] @$s4main1SV1fyyF
27+
func f() {
28+
for elt in self {
29+
// CHECK: [[ITERATOR_VAR:%.*]] = project_box {{.*}} : ${ var S.Iterator }, 0
30+
// CHECK: [[MAKE_ITERATOR_REF:%.*]] = function_ref @$s4main1SV12makeIteratorAC0C0CyF : $@convention(method) (@guaranteed S) -> @owned S.Iterator
31+
// CHECK-NEXT: [[ITERATOR:%.*]] = apply [[MAKE_ITERATOR_REF]](%0) : $@convention(method) (@guaranteed S) -> @owned S.Iterator
32+
// CHECK-NEXT: store [[ITERATOR]] to [init] [[ITERATOR_VAR]] : $*S.Iterator
33+
print(elt)
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)