Skip to content

Commit c798a7f

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 e39a81f commit c798a7f

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
@@ -9254,6 +9254,40 @@ 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());
9260+
if (memberRef && memberRef->isImplicit() &&
9261+
locator->isLastElement<LocatorPathElt::Member>()) {
9262+
// Cannot simplify this constraint yet since we don't know whether
9263+
// the base type is going to be existential or not.
9264+
if (baseObjTy->isTypeVariableOrMember())
9265+
return formUnsolved();
9266+
9267+
auto *sequenceExpr = memberRef->getBase();
9268+
// If base type is an existential, member lookup is fine because
9269+
// 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();
9283+
9284+
return simplifyValueWitnessConstraint(
9285+
ConstraintKind::ValueWitness, baseTy, makeIterator, memberTy, DC,
9286+
FunctionRefKind::Compound, flags, locator);
9287+
}
9288+
}
9289+
}
9290+
92579291
MemberLookupResult result =
92589292
performMemberLookup(kind, member, baseTy, functionRefKind, locator,
92599293
/*includeInaccessibleMembers*/ shouldAttemptFixes());
@@ -9690,6 +9724,17 @@ ConstraintSystem::simplifyValueWitnessConstraint(
96909724
return SolutionKind::Unsolved;
96919725
};
96929726

9727+
auto fail = [&] {
9728+
// The constraint failed, so mark the member type as a "hole".
9729+
// We cannot do anything further here.
9730+
if (!shouldAttemptFixes())
9731+
return SolutionKind::Error;
9732+
9733+
recordAnyTypeVarAsPotentialHole(memberType);
9734+
9735+
return SolutionKind::Solved;
9736+
};
9737+
96939738
// Resolve the base type, if we can. If we can't resolve the base type,
96949739
// then we can't solve this constraint.
96959740
Type baseObjectType = getFixedTypeRecursive(
@@ -9706,16 +9751,8 @@ ConstraintSystem::simplifyValueWitnessConstraint(
97069751
assert(proto && "Value witness constraint for a non-requirement");
97079752
auto conformance = useDC->getParentModule()->lookupConformance(
97089753
baseObjectType, proto);
9709-
if (!conformance) {
9710-
// The conformance failed, so mark the member type as a "hole". We cannot
9711-
// do anything further here.
9712-
if (!shouldAttemptFixes())
9713-
return SolutionKind::Error;
9714-
9715-
recordAnyTypeVarAsPotentialHole(memberType);
9716-
9717-
return SolutionKind::Solved;
9718-
}
9754+
if (!conformance)
9755+
return fail();
97199756

97209757
// Reference the requirement.
97219758
Type resolvedBaseType = simplifyType(baseType, flags);
@@ -9725,7 +9762,7 @@ ConstraintSystem::simplifyValueWitnessConstraint(
97259762
auto witness =
97269763
conformance.getWitnessByName(baseObjectType, requirement->getName());
97279764
if (!witness)
9728-
return SolutionKind::Error;
9765+
return fail();
97299766

97309767
auto choice = OverloadChoice(resolvedBaseType, witness.getDecl(), functionRefKind);
97319768
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)