Skip to content

Commit 1142fa3

Browse files
committed
[Strict memory safety] Eliminate spurious warnings with synthesized Codable
When synthesizing code for Codable conformances involving unsafe types, make sure to wrap the resulting expressions in "unsafe" when strict memory safety is enabled. Tweak the warning-emission logic to suppress warnings about spurious "unsafe" expressions when the compiler generated the "unsafe" itself, so we don't spam the developer with warnings they can't fix. Also make the checking for other suppression considerations safe when there are no source locations, eliminating a potential assertion. Fixes rdar://153665692.
1 parent 35628cb commit 1142fa3

File tree

6 files changed

+92
-28
lines changed

6 files changed

+92
-28
lines changed

lib/Sema/CSGen.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4734,9 +4734,10 @@ generateForEachStmtConstraints(ConstraintSystem &cs, DeclContext *dc,
47344734
(isAsync &&
47354735
ctx.LangOpts.StrictConcurrencyLevel == StrictConcurrency::Complete)) {
47364736
SourceLoc loc = stmt->getUnsafeLoc();
4737+
bool implicit = stmt->getUnsafeLoc().isInvalid();
47374738
if (loc.isInvalid())
47384739
loc = stmt->getForLoc();
4739-
nextCall = new (ctx) UnsafeExpr(loc, nextCall, Type(), /*implicit=*/true);
4740+
nextCall = new (ctx) UnsafeExpr(loc, nextCall, Type(), implicit);
47404741
}
47414742

47424743
// 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: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4667,18 +4667,25 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
46674667
}
46684668

46694669
void diagnoseRedundantUnsafe(UnsafeExpr *E) const {
4670-
// Silence this warning in the expansion of the _SwiftifyImport macro.
4671-
// This is a hack because it's tricky to determine when to insert "unsafe".
4672-
unsigned bufferID =
4673-
Ctx.SourceMgr.findBufferContainingLoc(E->getUnsafeLoc());
4674-
if (auto sourceInfo = Ctx.SourceMgr.getGeneratedSourceInfo(bufferID)) {
4675-
if (sourceInfo->macroName == "_SwiftifyImport")
4676-
return;
4670+
// Ignore implicitly-generated "unsafe" expressions; they're allowed to be
4671+
// overly conservative.
4672+
if (E->isImplicit())
4673+
return;
4674+
4675+
SourceLoc loc = E->getUnsafeLoc();
4676+
if (loc.isValid()) {
4677+
// Silence this warning in the expansion of the _SwiftifyImport macro.
4678+
// This is a hack because it's tricky to determine when to insert "unsafe".
4679+
unsigned bufferID = Ctx.SourceMgr.findBufferContainingLoc(loc);
4680+
if (auto sourceInfo = Ctx.SourceMgr.getGeneratedSourceInfo(bufferID)) {
4681+
if (sourceInfo->macroName == "_SwiftifyImport")
4682+
return;
4683+
}
46774684
}
46784685

46794686
if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E)) {
46804687
// For an if/switch expression, produce a tailored warning.
4681-
Ctx.Diags.diagnose(E->getUnsafeLoc(),
4688+
Ctx.Diags.diagnose(loc,
46824689
diag::effect_marker_on_single_value_stmt,
46834690
"unsafe", SVE->getStmt()->getKind())
46844691
.highlight(E->getUnsafeLoc());

lib/Sema/TypeCheckPattern.cpp

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

792792
// If there was an "unsafe", put it outside of the match call.
793793
if (unsafeExpr) {
794-
matchCall = UnsafeExpr::createImplicit(ctx, unsafeExpr->getLoc(), matchCall);
794+
matchCall = new (ctx) UnsafeExpr(unsafeExpr->getLoc(), matchCall);
795795
}
796796

797797
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+
}

0 commit comments

Comments
 (0)