Skip to content

Commit 8c3bcdd

Browse files
committed
Merge pull request #2540 from nubbel/pr/sr-1469
[SilOptimizer] SR-1469: Issue diagnostic for `init?` at early return statement
2 parents d415523 + 9115003 commit 8c3bcdd

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,9 @@ namespace {
481481
bool isInitializedAtUse(const DIMemoryUse &Use,
482482
bool *SuperInitDone = nullptr,
483483
bool *FailedSelfUse = nullptr);
484+
485+
bool hasUninitializedMemberAtInst(SILInstruction *Inst, unsigned FirstElt,
486+
unsigned NumElts);
484487

485488
void handleStoreUse(unsigned UseID);
486489
void handleInOutUse(const DIMemoryUse &Use);
@@ -1329,13 +1332,23 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use,
13291332
return;
13301333
}
13311334

1335+
SILLocation Loc = Inst->getLoc();
1336+
1337+
// Try to find the return location
1338+
auto TermLoc = Inst->getParent()->getTerminator()->getLoc();
1339+
if (TermLoc.getKind() == SILLocation::ReturnKind) {
1340+
Loc = TermLoc;
1341+
}
1342+
13321343
// If this is a load with a single user that is a return (and optionally a
13331344
// retain_value for non-trivial structs/enums), then this is a return in the
13341345
// enum/struct init case, and we haven't stored to self. Emit a specific
13351346
// diagnostic.
13361347
if (auto *LI = dyn_cast<LoadInst>(Inst)) {
13371348
bool hasReturnUse = false, hasUnknownUses = false;
13381349

1350+
auto *LoadBB = LI->getParent();
1351+
13391352
for (auto LoadUse : LI->getUses()) {
13401353
auto *User = LoadUse->getUser();
13411354

@@ -1349,6 +1362,18 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use,
13491362

13501363
if (auto *EI = dyn_cast<EnumInst>(User))
13511364
if (isFailableInitReturnUseOfEnum(EI)) {
1365+
// Try to find the return location
1366+
for (auto Pred : LoadBB->getPreds()) {
1367+
auto *TI = Pred->getTerminator();
1368+
auto TermLoc = TI->getLoc();
1369+
1370+
// Check if this is an early return with uninitialized members
1371+
if (TermLoc.getKind() == SILLocation::ReturnKind &&
1372+
hasUninitializedMemberAtInst(TI, Use.FirstElement,
1373+
Use.NumElements))
1374+
Loc = TermLoc;
1375+
}
1376+
13521377
hasReturnUse = true;
13531378
continue;
13541379
}
@@ -1360,13 +1385,13 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use,
13601385
if (hasReturnUse && !hasUnknownUses) {
13611386
if (TheMemory.isEnumInitSelf()) {
13621387
if (!shouldEmitError(Inst)) return;
1363-
diagnose(Module, Inst->getLoc(),
1388+
diagnose(Module, Loc,
13641389
diag::return_from_init_without_initing_self);
13651390
return;
13661391
} else if (TheMemory.isAnyInitSelf() && !TheMemory.isClassInitSelf() &&
13671392
!TheMemory.isDelegatingInit()) {
13681393
if (!shouldEmitError(Inst)) return;
1369-
diagnose(Module, Inst->getLoc(),
1394+
diagnose(Module, Loc,
13701395
diag::return_from_init_without_initing_stored_properties);
13711396
noteUninitializedMembers(Use);
13721397
return;
@@ -2446,6 +2471,21 @@ bool LifetimeChecker::isInitializedAtUse(const DIMemoryUse &Use,
24462471
return true;
24472472
}
24482473

2474+
bool LifetimeChecker::hasUninitializedMemberAtInst(SILInstruction *Inst,
2475+
unsigned FirstElt,
2476+
unsigned NumElts) {
2477+
// Determine the liveness states of the elements that we care about.
2478+
AvailabilitySet Liveness =
2479+
getLivenessAtInst(Inst, FirstElt, NumElts);
2480+
2481+
// Find unintialized member
2482+
for (unsigned i = FirstElt, e = i+NumElts;
2483+
i != e; ++i)
2484+
if (Liveness.get(i) != DIKind::Yes)
2485+
return true;
2486+
2487+
return false;
2488+
}
24492489

24502490

24512491

test/SILOptimizer/definite_init_diagnostics.swift

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,3 +1215,71 @@ class r23013334Derived : rdar16119509_Base {
12151215

12161216
}
12171217

1218+
// sr-1469
1219+
struct SR1469_Struct1 {
1220+
let a: Int
1221+
let b: Int // expected-note {{'self.b' not initialized}}
1222+
1223+
init?(x: Int, y: Int) {
1224+
self.a = x
1225+
if y == 42 {
1226+
return // expected-error {{return from initializer without initializing all stored properties}}
1227+
}
1228+
// many lines later
1229+
self.b = y
1230+
}
1231+
}
1232+
1233+
struct SR1469_Struct2 {
1234+
let a: Int
1235+
let b: Int // expected-note {{'self.b' not initialized}}
1236+
1237+
init?(x: Int, y: Int) {
1238+
self.a = x
1239+
return // expected-error {{return from initializer without initializing all stored properties}}
1240+
}
1241+
}
1242+
1243+
struct SR1469_Struct3 {
1244+
let a: Int
1245+
let b: Int // expected-note {{'self.b' not initialized}}
1246+
1247+
init?(x: Int, y: Int) {
1248+
self.a = x
1249+
if y == 42 {
1250+
self.b = y
1251+
return
1252+
}
1253+
} // expected-error {{return from initializer without initializing all stored properties}}
1254+
}
1255+
1256+
enum SR1469_Enum1 {
1257+
case A, B
1258+
1259+
init?(x: Int) {
1260+
if x == 42 {
1261+
return // expected-error {{return from enum initializer method without storing to 'self'}}
1262+
}
1263+
// many lines later
1264+
self = .A
1265+
}
1266+
}
1267+
1268+
enum SR1469_Enum2 {
1269+
case A, B
1270+
1271+
init?() {
1272+
return // expected-error {{return from enum initializer method without storing to 'self'}}
1273+
}
1274+
}
1275+
enum SR1469_Enum3 {
1276+
case A, B
1277+
1278+
init?(x: Int) {
1279+
if x == 42 {
1280+
self = .A
1281+
return
1282+
}
1283+
} // expected-error {{return from enum initializer method without storing to 'self'}}
1284+
}
1285+

0 commit comments

Comments
 (0)