Skip to content

Commit 4501026

Browse files
authored
Merge pull request #82955 from DougGregor/strict-memory-safety-spurious-warning-fixes-6.2
[6.2] Fix spurious warnings with strict memory safety
2 parents e3f0cfb + 8c34d57 commit 4501026

File tree

8 files changed

+114
-37
lines changed

8 files changed

+114
-37
lines changed

lib/Sema/CSGen.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4692,10 +4692,16 @@ 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+
bool implicit = stmt->getUnsafeLoc().isInvalid();
4702+
if (loc.isInvalid())
4703+
loc = stmt->getForLoc();
4704+
nextCall = new (ctx) UnsafeExpr(loc, nextCall, Type(), implicit);
46994705
}
47004706

47014707
// The iterator type must conform to IteratorProtocol.

lib/Sema/DerivedConformance/DerivedConformanceCodable.cpp

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -772,10 +772,20 @@ lookupVarDeclForCodingKeysCase(DeclContext *conformanceDC,
772772
llvm_unreachable("Should have found at least 1 var decl");
773773
}
774774

775-
static TryExpr *createEncodeCall(ASTContext &C, Type codingKeysType,
776-
EnumElementDecl *codingKey,
777-
Expr *containerExpr, Expr *varExpr,
778-
bool useIfPresentVariant) {
775+
/// If strict memory safety checking is enabled, wrap the expression in an
776+
/// implicit "unsafe".
777+
static Expr *wrapInUnsafeIfNeeded(ASTContext &ctx, Expr *expr) {
778+
if (ctx.LangOpts.hasFeature(Feature::StrictMemorySafety,
779+
/*allowMigration=*/true))
780+
return UnsafeExpr::createImplicit(ctx, expr->getStartLoc(), expr);
781+
782+
return expr;
783+
}
784+
785+
static Expr *createEncodeCall(ASTContext &C, Type codingKeysType,
786+
EnumElementDecl *codingKey,
787+
Expr *containerExpr, Expr *varExpr,
788+
bool useIfPresentVariant) {
779789
// CodingKeys.x
780790
auto *metaTyRef = TypeExpr::createImplicit(codingKeysType, C);
781791
auto *keyExpr = new (C) MemberRefExpr(metaTyRef, SourceLoc(), codingKey,
@@ -794,7 +804,7 @@ static TryExpr *createEncodeCall(ASTContext &C, Type codingKeysType,
794804
// try container.encode(x, forKey: CodingKeys.x)
795805
auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(),
796806
/*Implicit=*/true);
797-
return tryExpr;
807+
return wrapInUnsafeIfNeeded(C, tryExpr);
798808
}
799809

800810
/// Synthesizes the body for `func encode(to encoder: Encoder) throws`.
@@ -929,7 +939,7 @@ deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *) {
929939
// try super.encode(to: container.superEncoder())
930940
auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(),
931941
/*Implicit=*/true);
932-
statements.push_back(tryExpr);
942+
statements.push_back(wrapInUnsafeIfNeeded(C, tryExpr));
933943
}
934944

935945
auto *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
@@ -1112,8 +1122,10 @@ deriveBodyEncodable_enum_encode(AbstractFunctionDecl *encodeDecl, void *) {
11121122

11131123
// generate: switch self { }
11141124
auto enumRef =
1115-
new (C) DeclRefExpr(ConcreteDeclRef(selfRef), DeclNameLoc(),
1116-
/*implicit*/ true, AccessSemantics::Ordinary);
1125+
wrapInUnsafeIfNeeded(
1126+
C,
1127+
new (C) DeclRefExpr(ConcreteDeclRef(selfRef), DeclNameLoc(),
1128+
/*implicit*/ true, AccessSemantics::Ordinary));
11171129
auto switchStmt = createEnumSwitch(
11181130
C, funcDC, enumRef, enumDecl, codingKeysEnum,
11191131
/*createSubpattern*/ true,
@@ -1276,11 +1288,11 @@ static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) {
12761288
return encodeDecl;
12771289
}
12781290

1279-
static TryExpr *createDecodeCall(ASTContext &C, Type resultType,
1280-
Type codingKeysType,
1281-
EnumElementDecl *codingKey,
1282-
Expr *containerExpr,
1283-
bool useIfPresentVariant) {
1291+
static Expr *createDecodeCall(ASTContext &C, Type resultType,
1292+
Type codingKeysType,
1293+
EnumElementDecl *codingKey,
1294+
Expr *containerExpr,
1295+
bool useIfPresentVariant) {
12841296
auto methodName = useIfPresentVariant ? C.Id_decodeIfPresent : C.Id_decode;
12851297

12861298
// Type.self
@@ -1470,7 +1482,7 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) {
14701482
varDecl->getName());
14711483
auto *assignExpr = new (C) AssignExpr(varExpr, SourceLoc(), tryExpr,
14721484
/*Implicit=*/true);
1473-
statements.push_back(assignExpr);
1485+
statements.push_back(wrapInUnsafeIfNeeded(C, assignExpr));
14741486
}
14751487
}
14761488

@@ -1506,7 +1518,7 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) {
15061518
// try super.init(from: container.superDecoder())
15071519
auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(),
15081520
/*Implicit=*/true);
1509-
statements.push_back(tryExpr);
1521+
statements.push_back(wrapInUnsafeIfNeeded(C, tryExpr));
15101522
} else {
15111523
// The explicit constructor name is a compound name taking no arguments.
15121524
DeclName initName(C, DeclBaseName::createConstructor(),
@@ -1538,7 +1550,7 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) {
15381550
callExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(),
15391551
/*Implicit=*/true);
15401552

1541-
statements.push_back(callExpr);
1553+
statements.push_back(wrapInUnsafeIfNeeded(C, callExpr));
15421554
}
15431555
}
15441556
}
@@ -1827,7 +1839,7 @@ deriveBodyDecodable_enum_init(AbstractFunctionDecl *initDecl, void *) {
18271839
new (C) AssignExpr(selfRef, SourceLoc(), selfCaseExpr,
18281840
/*Implicit=*/true);
18291841

1830-
caseStatements.push_back(assignExpr);
1842+
caseStatements.push_back(wrapInUnsafeIfNeeded(C, assignExpr));
18311843
} else {
18321844
// Foo.bar(x:)
18331845
SmallVector<Identifier, 3> scratch;
@@ -1845,7 +1857,7 @@ deriveBodyDecodable_enum_init(AbstractFunctionDecl *initDecl, void *) {
18451857
new (C) AssignExpr(selfRef, SourceLoc(), caseCallExpr,
18461858
/*Implicit=*/true);
18471859

1848-
caseStatements.push_back(assignExpr);
1860+
caseStatements.push_back(wrapInUnsafeIfNeeded(C, assignExpr));
18491861
}
18501862

18511863
auto body =

lib/Sema/TypeCheckEffects.cpp

Lines changed: 20 additions & 14 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;
@@ -4618,25 +4614,35 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
46184614
}
46194615

46204616
void diagnoseRedundantUnsafe(UnsafeExpr *E) const {
4621-
// Silence this warning in the expansion of the _SwiftifyImport macro.
4622-
// This is a hack because it's tricky to determine when to insert "unsafe".
4623-
unsigned bufferID =
4624-
Ctx.SourceMgr.findBufferContainingLoc(E->getUnsafeLoc());
4625-
if (auto sourceInfo = Ctx.SourceMgr.getGeneratedSourceInfo(bufferID)) {
4626-
if (sourceInfo->macroName == "_SwiftifyImport")
4627-
return;
4617+
// Ignore implicitly-generated "unsafe" expressions; they're allowed to be
4618+
// overly conservative.
4619+
if (E->isImplicit())
4620+
return;
4621+
4622+
SourceLoc loc = E->getUnsafeLoc();
4623+
if (loc.isValid()) {
4624+
// Silence this warning in the expansion of the _SwiftifyImport macro.
4625+
// This is a hack because it's tricky to determine when to insert "unsafe".
4626+
unsigned bufferID = Ctx.SourceMgr.findBufferContainingLoc(loc);
4627+
if (auto sourceInfo = Ctx.SourceMgr.getGeneratedSourceInfo(bufferID)) {
4628+
if (sourceInfo->macroName == "_SwiftifyImport")
4629+
return;
4630+
}
46284631
}
46294632

46304633
if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E)) {
46314634
// For an if/switch expression, produce a tailored warning.
4632-
Ctx.Diags.diagnose(E->getUnsafeLoc(),
4635+
Ctx.Diags.diagnose(loc,
46334636
diag::effect_marker_on_single_value_stmt,
46344637
"unsafe", SVE->getStmt()->getKind())
46354638
.highlight(E->getUnsafeLoc());
46364639
return;
46374640
}
46384641

4639-
Ctx.Diags.diagnose(E->getUnsafeLoc(), diag::no_unsafe_in_unsafe)
4642+
Ctx.Diags.diagnose(E->getUnsafeLoc(),
4643+
forEachNextCallExprs.contains(E)
4644+
? diag::no_unsafe_in_unsafe_for
4645+
: diag::no_unsafe_in_unsafe)
46404646
.fixItRemove(E->getUnsafeLoc());
46414647
}
46424648

lib/Sema/TypeCheckPattern.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ ExprPatternMatchRequest::evaluate(Evaluator &evaluator,
786786

787787
// If there was an "unsafe", put it outside of the match call.
788788
if (unsafeExpr) {
789-
matchCall = UnsafeExpr::createImplicit(ctx, unsafeExpr->getLoc(), matchCall);
789+
matchCall = new (ctx) UnsafeExpr(unsafeExpr->getLoc(), matchCall);
790790
}
791791

792792
return {matchVar, matchCall};

test/Unsafe/codable_synthesis.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-typecheck-verify-swift -strict-memory-safety
2+
3+
@unsafe public struct UnsafeStruct: Codable {
4+
public var string: String
5+
}
6+
7+
8+
@unsafe public enum UnsafeEnum: Codable {
9+
case something(Int)
10+
}
11+
12+
@safe public struct SafeStruct: Codable {
13+
public var us: UnsafeStruct
14+
}
15+
16+
@safe public enum SafeEnum: Codable {
17+
case something(UnsafeEnum)
18+
}
19+
20+
@unsafe public class C1: Codable {
21+
public var string: String = ""
22+
}
23+
24+
@unsafe public class C2: C1 {
25+
public var otherString: String = ""
26+
}

test/Unsafe/hashable_synthesis.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %target-typecheck-verify-swift -strict-memory-safety
2+
3+
@unsafe public struct UnsafeStruct: Hashable {
4+
public var string: String
5+
}
6+
7+
8+
@unsafe public enum UnsafeEnum: Hashable {
9+
case something(Int)
10+
}
11+
12+
@safe public struct SafeStruct: Hashable {
13+
public var us: UnsafeStruct
14+
}
15+
16+
@safe public enum SafeEnum: Hashable {
17+
case something(UnsafeEnum)
18+
}

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)