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