@@ -1232,6 +1232,67 @@ void LifetimeChecker::handleInOutUse(const DIMemoryUse &Use) {
1232
1232
}
1233
1233
}
1234
1234
1235
+ // / Failable enum initializer produce a CFG for the return that looks like this,
1236
+ // / where the load is the use of 'self'. Detect this pattern so we can consider
1237
+ // / it a 'return' use of self.
1238
+ // /
1239
+ // / %3 = load %2 : $*Enum
1240
+ // / %4 = enum $Optional<Enum>, #Optional.Some!enumelt, %3 : $Enum
1241
+ // / br bb2(%4 : $Optional<Enum>) // id: %5
1242
+ // / bb1:
1243
+ // / %6 = enum $Optional<Enum>, #Optional.None!enumelt // user: %7
1244
+ // / br bb2(%6 : $Optional<Enum>) // id: %7
1245
+ // / bb2(%8 : $Optional<Enum>): // Preds: bb0 bb1
1246
+ // / dealloc_stack %1 : $*Enum // id: %9
1247
+ // / return %8 : $Optional<Enum> // id: %10
1248
+ // /
1249
+ static bool isFailableInitReturnUseOfEnum (EnumInst *EI) {
1250
+ // Only allow enums forming an optional.
1251
+ if (!EI->getType ().getOptionalObjectType ())
1252
+ return false ;
1253
+
1254
+ if (!EI->hasOneUse ())
1255
+ return false ;
1256
+ auto *BI = dyn_cast<BranchInst>(EI->use_begin ()->getUser ());
1257
+ if (!BI || BI->getNumArgs () != 1 )
1258
+ return false ;
1259
+
1260
+ auto *TargetArg = BI->getDestBB ()->getArgument (0 );
1261
+ if (!TargetArg->hasOneUse ())
1262
+ return false ;
1263
+ return isa<ReturnInst>(TargetArg->use_begin ()->getUser ());
1264
+ }
1265
+
1266
+ // / Given a load instruction, return true iff the result of the load is used
1267
+ // / in a return instruction directly or is lifted to an optional (i.e., wrapped
1268
+ // / into .some) and returned. These conditions are used to detect whether the
1269
+ // / given load instruction is autogenerated for a return from the initalizers:
1270
+ // / `init` or `init?`, respectively. In such cases, the load should not be
1271
+ // / considered as a use of the value but rather as a part of the return
1272
+ // / instruction. We emit a specific diagnostic in this case.
1273
+ static bool isLoadForReturn (SingleValueInstruction *loadInst) {
1274
+ bool hasReturnUse = false , hasUnknownUses = false ;
1275
+
1276
+ for (auto LoadUse : loadInst->getUses ()) {
1277
+ auto *User = LoadUse->getUser ();
1278
+ // Ignore retains of the struct/enum before the return.
1279
+ if (isa<RetainValueInst>(User))
1280
+ continue ;
1281
+ if (isa<ReturnInst>(User)) {
1282
+ hasReturnUse = true ;
1283
+ continue ;
1284
+ }
1285
+ if (auto *EI = dyn_cast<EnumInst>(User))
1286
+ if (isFailableInitReturnUseOfEnum (EI)) {
1287
+ hasReturnUse = true ;
1288
+ continue ;
1289
+ }
1290
+ hasUnknownUses = true ;
1291
+ break ;
1292
+ }
1293
+ return hasReturnUse && !hasUnknownUses;
1294
+ }
1295
+
1235
1296
void LifetimeChecker::handleEscapeUse (const DIMemoryUse &Use) {
1236
1297
// The value must be fully initialized at all escape points. If not, diagnose
1237
1298
// the error.
@@ -1259,8 +1320,7 @@ void LifetimeChecker::handleEscapeUse(const DIMemoryUse &Use) {
1259
1320
// If this is a load with a single user that is a return, then this is
1260
1321
// a return before self.init. Emit a specific diagnostic.
1261
1322
if (auto *LI = dyn_cast<LoadInst>(Inst))
1262
- if (LI->hasOneUse () &&
1263
- isa<ReturnInst>((*LI->use_begin ())->getUser ())) {
1323
+ if (isLoadForReturn (LI)) {
1264
1324
diagnose (Module, Inst->getLoc (),
1265
1325
diag::superselfinit_not_called_before_return,
1266
1326
(unsigned )TheMemory.isDelegatingInit ());
@@ -1358,35 +1418,6 @@ void LifetimeChecker::handleEscapeUse(const DIMemoryUse &Use) {
1358
1418
diagnoseInitError (Use, DiagMessage);
1359
1419
}
1360
1420
1361
-
1362
- // / Failable enum initializer produce a CFG for the return that looks like this,
1363
- // / where the load is the use of 'self'. Detect this pattern so we can consider
1364
- // / it a 'return' use of self.
1365
- // /
1366
- // / %3 = load %2 : $*Enum
1367
- // / %4 = enum $Optional<Enum>, #Optional.Some!enumelt, %3 : $Enum
1368
- // / br bb2(%4 : $Optional<Enum>) // id: %5
1369
- // / bb1:
1370
- // / %6 = enum $Optional<Enum>, #Optional.None!enumelt // user: %7
1371
- // / br bb2(%6 : $Optional<Enum>) // id: %7
1372
- // / bb2(%8 : $Optional<Enum>): // Preds: bb0 bb1
1373
- // / dealloc_stack %1 : $*Enum // id: %9
1374
- // / return %8 : $Optional<Enum> // id: %10
1375
- // /
1376
- static bool isFailableInitReturnUseOfEnum (EnumInst *EI) {
1377
- // Only allow enums forming an optional.
1378
- if (!EI->getType ().getOptionalObjectType ())
1379
- return false ;
1380
-
1381
- if (!EI->hasOneUse ()) return false ;
1382
- auto *BI = dyn_cast<BranchInst>(EI->use_begin ()->getUser ());
1383
- if (!BI || BI->getNumArgs () != 1 ) return false ;
1384
-
1385
- auto *TargetArg = BI->getDestBB ()->getArgument (0 );
1386
- if (!TargetArg->hasOneUse ()) return false ;
1387
- return isa<ReturnInst>(TargetArg->use_begin ()->getUser ());
1388
- }
1389
-
1390
1421
enum BadSelfUseKind {
1391
1422
BeforeStoredPropertyInit,
1392
1423
BeforeSuperInit,
@@ -1680,31 +1711,8 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use,
1680
1711
// diagnostic.
1681
1712
if (isa<LoadInst>(Inst) || isa<LoadBorrowInst>(Inst)) {
1682
1713
auto *LI = Inst;
1683
- bool hasReturnUse = false , hasUnknownUses = false ;
1684
-
1685
- for (auto LoadUse : cast<SingleValueInstruction>(LI)->getUses ()) {
1686
- auto *User = LoadUse->getUser ();
1687
-
1688
- // Ignore retains of the struct/enum before the return.
1689
- if (isa<RetainValueInst>(User))
1690
- continue ;
1691
- if (isa<ReturnInst>(User)) {
1692
- hasReturnUse = true ;
1693
- continue ;
1694
- }
1695
-
1696
- if (auto *EI = dyn_cast<EnumInst>(User))
1697
- if (isFailableInitReturnUseOfEnum (EI)) {
1698
- hasReturnUse = true ;
1699
- continue ;
1700
- }
1701
-
1702
- hasUnknownUses = true ;
1703
- break ;
1704
- }
1705
-
1706
- // Okay, this load is part of a return sequence, diagnose it specially.
1707
- if (hasReturnUse && !hasUnknownUses) {
1714
+ // If this load is part of a return sequence, diagnose it specially.
1715
+ if (isLoadForReturn (cast<SingleValueInstruction>(LI))) {
1708
1716
// The load is probably part of the common epilog for the function, try to
1709
1717
// find a more useful source location than the syntactic end of the
1710
1718
// function.
0 commit comments