Skip to content

Commit d6deecc

Browse files
committed
[Strict memory safety] Fix "unsafe" checking for the for..in loop
The `$generator` variable we create for the async for..in loop is `nonisolated(unsafe)`, so ensure that we generate an `unsafe` expression when we use it. This uncovered some inconsistencies in how we do `unsafe` checking for for..in loops, so fix those. Fixes rdar://154775389. (cherry picked from commit 35628cb)
1 parent 9f9d265 commit d6deecc

File tree

4 files changed

+23
-10
lines changed

4 files changed

+23
-10
lines changed

lib/Sema/CSGen.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4692,10 +4692,15 @@ generateForEachStmtConstraints(ConstraintSystem &cs, DeclContext *dc,
46924692
}
46934693

46944694
// Wrap the 'next' call in 'unsafe', if the for..in loop has that
4695-
// effect.
4696-
if (stmt->getUnsafeLoc().isValid()) {
4697-
nextCall = new (ctx) UnsafeExpr(
4698-
stmt->getUnsafeLoc(), nextCall, Type(), /*implicit=*/true);
4695+
// effect or if the loop is async (in which case the iterator variable
4696+
// is nonisolated(unsafe).
4697+
if (stmt->getUnsafeLoc().isValid() ||
4698+
(isAsync &&
4699+
ctx.LangOpts.StrictConcurrencyLevel == StrictConcurrency::Complete)) {
4700+
SourceLoc loc = stmt->getUnsafeLoc();
4701+
if (loc.isInvalid())
4702+
loc = stmt->getForLoc();
4703+
nextCall = new (ctx) UnsafeExpr(loc, nextCall, Type(), /*implicit=*/true);
46994704
}
47004705

47014706
// The iterator type must conform to IteratorProtocol.

lib/Sema/TypeCheckEffects.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2439,7 +2439,7 @@ class ApplyClassifier {
24392439
return ShouldRecurse;
24402440
}
24412441
ShouldRecurse_t checkUnsafe(UnsafeExpr *E) {
2442-
return E->isImplicit() ? ShouldRecurse : ShouldNotRecurse;
2442+
return ShouldNotRecurse;
24432443
}
24442444
ShouldRecurse_t checkTry(TryExpr *E) {
24452445
return ShouldRecurse;
@@ -4573,10 +4573,6 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
45734573
diagnoseUnsafeUse(unsafeUse);
45744574
}
45754575
}
4576-
} else if (S->getUnsafeLoc().isValid()) {
4577-
// Extraneous "unsafe" on the sequence.
4578-
Ctx.Diags.diagnose(S->getUnsafeLoc(), diag::no_unsafe_in_unsafe_for)
4579-
.fixItRemove(S->getUnsafeLoc());
45804576
}
45814577

45824578
return ShouldRecurse;
@@ -4636,7 +4632,10 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
46364632
return;
46374633
}
46384634

4639-
Ctx.Diags.diagnose(E->getUnsafeLoc(), diag::no_unsafe_in_unsafe)
4635+
Ctx.Diags.diagnose(E->getUnsafeLoc(),
4636+
forEachNextCallExprs.contains(E)
4637+
? diag::no_unsafe_in_unsafe_for
4638+
: diag::no_unsafe_in_unsafe)
46404639
.fixItRemove(E->getUnsafeLoc());
46414640
}
46424641

test/Unsafe/safe.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ func testUnsafeAsSequenceForEach() {
9898
for _ in unsafe uas { } // expected-warning{{for-in loop uses unsafe constructs but is not marked with 'unsafe'}}{{documentation-file=strict-memory-safety}}{{7-7=unsafe }}
9999

100100
for unsafe _ in unsafe uas { } // okay
101+
102+
for unsafe _ in [1, 2, 3] { } // expected-warning{{no unsafe operations occur within 'unsafe' for-in loop}}
101103
}
102104

103105
func testForInUnsafeAmbiguity(_ integers: [Int]) {

test/Unsafe/unsafe_concurrency.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,10 @@ open class SyntaxVisitor {
5555
open class SyntaxAnyVisitor: SyntaxVisitor {
5656
override open func visit(_ token: TokenSyntax) { }
5757
}
58+
59+
@available(SwiftStdlib 5.1, *)
60+
func testMemorySafetyWithForLoop() async {
61+
let (stream, continuation) = AsyncStream<Int>.makeStream()
62+
for await _ in stream {}
63+
_ = continuation
64+
}

0 commit comments

Comments
 (0)