Skip to content

Commit 9115003

Browse files
author
Dominique d'Argent
committed
[SilOptimizer] SR-1469: Issue diagnostic for init? at early return statement
In failable initializers the "return from initializer without initializing all stored properties" (resp. "return from enum initializer method without storing to 'self'" for enums) diagnostic is now reported at the early return instead of the end of the initializer.
1 parent 16ec49d commit 9115003

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
@@ -1214,3 +1214,71 @@ class r23013334Derived : rdar16119509_Base {
12141214

12151215
}
12161216

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

0 commit comments

Comments
 (0)