Skip to content

Commit 84e202e

Browse files
authored
Merge pull request #82954 from DougGregor/strict-memory-safety-codable-synthesis
[Strict memory safety] Eliminate spurious warnings with synthesized Codable
2 parents 96d0450 + 1142fa3 commit 84e202e

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
@@ -4735,9 +4735,10 @@ generateForEachStmtConstraints(ConstraintSystem &cs, DeclContext *dc,
47354735
(isAsync &&
47364736
ctx.LangOpts.StrictConcurrencyLevel == StrictConcurrency::Complete)) {
47374737
SourceLoc loc = stmt->getUnsafeLoc();
4738+
bool implicit = stmt->getUnsafeLoc().isInvalid();
47384739
if (loc.isInvalid())
47394740
loc = stmt->getForLoc();
4740-
nextCall = new (ctx) UnsafeExpr(loc, nextCall, Type(), /*implicit=*/true);
4741+
nextCall = new (ctx) UnsafeExpr(loc, nextCall, Type(), implicit);
47414742
}
47424743

47434744
// 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)