Skip to content

Commit 21e7e5d

Browse files
authored
Merge pull request #79284 from DougGregor/unsafe-for-in
[Unsafe] Teach for..in loops to let the sequence's 'unsafe' cover next()
2 parents 57f6af6 + 02ada80 commit 21e7e5d

File tree

4 files changed

+28
-2
lines changed

4 files changed

+28
-2
lines changed

lib/Sema/CSGen.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3854,6 +3854,12 @@ generateForEachStmtConstraints(ConstraintSystem &cs, DeclContext *dc,
38543854
AwaitExpr::createImplicit(ctx, nextCall->getLoc(), nextCall);
38553855
}
38563856

3857+
// Wrap the 'next' call in 'unsafe', if there is one.
3858+
if (unsafeExpr) {
3859+
nextCall = new (ctx) UnsafeExpr(unsafeExpr->getLoc(), nextCall, Type(),
3860+
/*implicit=*/true);
3861+
}
3862+
38573863
// The iterator type must conform to IteratorProtocol.
38583864
{
38593865
ProtocolDecl *iteratorProto = TypeChecker::getProtocol(

lib/Sema/TypeCheckEffects.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4122,6 +4122,12 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
41224122

41234123
if (auto parsedSequence = S->getParsedSequence()) {
41244124
parentMap[typeCheckedExpr] = parsedSequence;
4125+
4126+
if (auto nextCall = S->getNextCall()) {
4127+
auto nextParentMap = nextCall->getParentMap();
4128+
parentMap.insert(nextParentMap.begin(), nextParentMap.end());
4129+
parentMap[nextCall] = parsedSequence;
4130+
}
41254131
}
41264132
}
41274133

test/Unsafe/safe.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,17 @@ func returnsExistentialP() -> any P {
8787
// expected-note@-1{{@unsafe conformance of 'Int' to protocol 'P' involves unsafe code}}
8888
}
8989

90-
struct UnsafeAsSequence: @unsafe Sequence, IteratorProtocol {
91-
mutating func next() -> Int? { nil }
90+
// FIXME: Should work even if the IteratorProtocol conformance is safe
91+
struct UnsafeAsSequence: @unsafe Sequence, @unsafe IteratorProtocol {
92+
@unsafe mutating func next() -> Int? { nil }
9293
}
9394

9495
func testUnsafeAsSequenceForEach() {
9596
let uas = UnsafeAsSequence()
9697

9798
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}{{12-12=unsafe }}
9899
for _ in uas { } // expected-note{{conformance}}
100+
// expected-note@-1{{reference}}
99101

100102
for _ in unsafe uas { } // okay
101103
}

test/Unsafe/unsafe-suppression.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,15 @@ var yieldUnsafeOkay: Int {
143143
yield unsafe &x
144144
}
145145
}
146+
147+
struct UnsafeSequence: @unsafe IteratorProtocol, @unsafe Sequence {
148+
@unsafe func next() -> Int? { nil }
149+
}
150+
151+
func forEachLoop(us: UnsafeSequence) {
152+
for _ in us { } // expected-warning{{expression uses unsafe constructs but is not marked with 'unsafe' [Unsafe]}}{{12-12=unsafe }}
153+
// expected-note@-1{{@unsafe conformance of 'UnsafeSequence' to protocol 'Sequence' involves unsafe code}}
154+
// expected-note@-2{{reference to unsafe instance method 'next()'}}
155+
156+
for _ in unsafe us { }
157+
}

0 commit comments

Comments
 (0)