Skip to content

Commit 960b8e9

Browse files
committed
DI: New way of modeling factory initializers
Previously, we were only able to detect factory initializers dispatched through class_method. This didn't work for factory initializers defined in protocol extensions. The end result would be that we would strong_release an uninitialized class instance, which could cause crashes. Fix DI to correctly release the old instance using dealloc_partial_ref instead. Fixes <rdar://problem/27713221>.
1 parent ae69ffb commit 960b8e9

File tree

6 files changed

+345
-134
lines changed

6 files changed

+345
-134
lines changed

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

Lines changed: 40 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,15 +1107,30 @@ static bool isSelfInitUse(SILInstruction *I) {
11071107
}
11081108

11091109
// This is a self.init call if structured like this:
1110+
//
11101111
// (call_expr type='SomeClass'
11111112
// (dot_syntax_call_expr type='() -> SomeClass' self
11121113
// (other_constructor_ref_expr implicit decl=SomeClass.init)
11131114
// (decl_ref_expr type='SomeClass', "self"))
11141115
// (...some argument...)
1115-
if (auto AE = dyn_cast<ApplyExpr>(LocExpr)) {
1116-
if ((AE = dyn_cast<ApplyExpr>(AE->getFn())) &&
1117-
isa<OtherConstructorDeclRefExpr>(AE->getFn()))
1118-
return true;
1116+
//
1117+
// Or like this:
1118+
//
1119+
// (call_expr type='SomeClass'
1120+
// (dot_syntax_call_expr type='() -> SomeClass' self
1121+
// (decr_ref_expr implicit decl=SomeClass.init)
1122+
// (decl_ref_expr type='SomeClass', "self"))
1123+
// (...some argument...)
1124+
//
1125+
if (auto *AE = dyn_cast<ApplyExpr>(LocExpr)) {
1126+
if ((AE = dyn_cast<ApplyExpr>(AE->getFn()))) {
1127+
if (isa<OtherConstructorDeclRefExpr>(AE->getFn()))
1128+
return true;
1129+
if (auto *DRE = dyn_cast<DeclRefExpr>(AE->getFn()))
1130+
if (auto *CD = dyn_cast<ConstructorDecl>(DRE->getDecl()))
1131+
if (CD->isFactoryInit())
1132+
return true;
1133+
}
11191134
}
11201135
return false;
11211136
}
@@ -1128,59 +1143,19 @@ static bool isSelfInitUse(SILArgument *Arg) {
11281143
// of a throwing delegated init.
11291144
auto *BB = Arg->getParent();
11301145
auto *Pred = BB->getSinglePredecessor();
1131-
if (!Pred || !isa<TryApplyInst>(Pred->getTerminator()))
1132-
return false;
1133-
return isSelfInitUse(Pred->getTerminator());
1134-
}
1135-
1136-
1137-
/// Determine if this value_metatype instruction is part of a call to
1138-
/// self.init when delegating to a factory initializer.
1139-
///
1140-
/// FIXME: This is only necessary due to our broken model for factory
1141-
/// initializers.
1142-
static bool isSelfInitUse(ValueMetatypeInst *Inst) {
1143-
// "Inst" is a ValueMetatype instruction. Check to see if it is
1144-
// used by an apply that came from a call to self.init.
1145-
for (auto UI : Inst->getUses()) {
1146-
auto *User = UI->getUser();
1147-
1148-
// Check whether we're looking up a factory initializer with
1149-
// class_method.
1150-
if (auto *CMI = dyn_cast<ClassMethodInst>(User)) {
1151-
// Only works for allocating initializers...
1152-
auto Member = CMI->getMember();
1153-
if (Member.kind != SILDeclRef::Kind::Allocator)
1154-
return false;
1155-
1156-
// ... of factory initializers.
1157-
auto ctor = dyn_cast_or_null<ConstructorDecl>(Member.getDecl());
1158-
return ctor && ctor->isFactoryInit();
1159-
}
1160-
1161-
if (auto apply = ApplySite::isa(User)) {
1162-
auto *LocExpr = apply.getLoc().getAsASTNode<ApplyExpr>();
1163-
if (!LocExpr)
1164-
return false;
1165-
1166-
LocExpr = dyn_cast<ApplyExpr>(LocExpr->getFn());
1167-
if (!LocExpr || !isa<OtherConstructorDeclRefExpr>(LocExpr->getFn()))
1168-
return false;
1169-
1170-
return true;
1171-
}
1172-
1173-
// Ignore the thick_to_objc_metatype instruction.
1174-
if (isa<ThickToObjCMetatypeInst>(User)) {
1175-
continue;
1176-
}
11771146

1147+
// The two interesting cases are where self.init throws, in which case
1148+
// the argument came from a try_apply, or if self.init is failable,
1149+
// in which case we have a switch_enum.
1150+
if (!Pred ||
1151+
(!isa<TryApplyInst>(Pred->getTerminator()) &&
1152+
!isa<SwitchEnumInst>(Pred->getTerminator())))
11781153
return false;
1179-
}
11801154

1181-
return false;
1155+
return isSelfInitUse(Pred->getTerminator());
11821156
}
11831157

1158+
11841159
void ElementUseCollector::
11851160
collectClassSelfUses(SILValue ClassPointer, SILType MemorySILType,
11861161
llvm::SmallDenseMap<VarDecl*, unsigned> &EltNumbering) {
@@ -1233,18 +1208,12 @@ collectClassSelfUses(SILValue ClassPointer, SILType MemorySILType,
12331208
recordFailableInitCall(User);
12341209
}
12351210

1236-
// If this is a ValueMetatypeInst, check to see if it's part of a
1237-
// self.init call to a factory initializer in a delegating
1238-
// initializer.
1239-
if (auto *VMI = dyn_cast<ValueMetatypeInst>(User)) {
1240-
if (isSelfInitUse(VMI))
1241-
Kind = DIUseKind::SelfInit;
1242-
else
1243-
// Otherwise, this is a simple reference to "type(of:)", which is
1244-
// always fine, even if self is uninitialized.
1245-
continue;
1246-
}
1247-
1211+
// If this is a ValueMetatypeInst, this is a simple reference
1212+
// to "type(of:)", which is always fine, even if self is
1213+
// uninitialized.
1214+
if (isa<ValueMetatypeInst>(User))
1215+
continue;
1216+
12481217
// If this is a partial application of self, then this is an escape point
12491218
// for it.
12501219
if (isa<PartialApplyInst>(User))
@@ -1289,27 +1258,26 @@ void ElementUseCollector::collectDelegatingClassInitSelfUses() {
12891258
if (auto apply = dyn_cast<ApplyInst>(cast<AssignInst>(User)->getSrc())) {
12901259
if (auto fn = apply->getCalleeFunction()) {
12911260
if (fn->getRepresentation()
1292-
== SILFunctionTypeRepresentation::CFunctionPointer)
1261+
== SILFunctionTypeRepresentation::CFunctionPointer) {
12931262
Uses.push_back(DIMemoryUse(User, DIUseKind::SelfInit, 0, 1));
1263+
continue;
1264+
}
12941265
}
12951266
}
12961267

1297-
continue;
12981268
}
12991269

13001270
// Stores *to* the allocation are writes. If the value being stored is a
13011271
// call to self.init()... then we have a self.init call.
13021272
if (auto *AI = dyn_cast<AssignInst>(User)) {
13031273
if (auto *AssignSource = dyn_cast<SILInstruction>(AI->getOperand(0)))
13041274
if (isSelfInitUse(AssignSource)) {
1305-
assert(isa<ArchetypeType>(TheMemory.getType()));
13061275
Uses.push_back(DIMemoryUse(User, DIUseKind::SelfInit, 0, 1));
13071276
continue;
13081277
}
13091278
if (auto *AssignSource = dyn_cast<SILArgument>(AI->getOperand(0)))
13101279
if (AssignSource->getParent() == AI->getParent())
13111280
if (isSelfInitUse(AssignSource)) {
1312-
assert(isa<ArchetypeType>(TheMemory.getType()));
13131281
Uses.push_back(DIMemoryUse(User, DIUseKind::SelfInit, 0, 1));
13141282
continue;
13151283
}
@@ -1379,16 +1347,10 @@ void ElementUseCollector::collectDelegatingClassInitSelfUses() {
13791347
recordFailableInitCall(User);
13801348
}
13811349

1382-
// If this is a ValueMetatypeInst, check to see if it's part of a
1383-
// self.init call to a factory initializer in a delegating
1384-
// initializer.
1385-
if (auto *VMI = dyn_cast<ValueMetatypeInst>(User)) {
1386-
if (isSelfInitUse(VMI))
1387-
Kind = DIUseKind::SelfInit;
1388-
else
1389-
// Otherwise, this is a simple reference to "type(of:)", which is
1390-
// always fine, even if self is uninitialized.
1391-
continue;
1350+
// A simple reference to "type(of:)" is always fine,
1351+
// even if self is uninitialized.
1352+
if (isa<ValueMetatypeInst>(User)) {
1353+
continue;
13921354
}
13931355

13941356
Uses.push_back(DIMemoryUse(User, Kind, 0, 1));

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,39 +35,70 @@ static void diagnose(SILModule &M, SILLocation loc, ArgTypes... args) {
3535
M.getASTContext().Diags.diagnose(loc.getSourceLoc(), Diagnostic(args...));
3636
}
3737

38+
enum class PartialInitializationKind {
39+
/// The box contains a fully-initialized value.
40+
IsNotInitialization,
41+
42+
/// The box contains a class instance that we own, but the instance has
43+
/// not been initialized and should be freed with a special SIL
44+
/// instruction made for this purpose.
45+
IsReinitialization,
46+
47+
/// The box contains an undefined value that should be ignored.
48+
IsInitialization,
49+
};
50+
3851
/// Emit the sequence that an assign instruction lowers to once we know
3952
/// if it is an initialization or an assignment. If it is an assignment,
4053
/// a live-in value can be provided to optimize out the reload.
4154
static void LowerAssignInstruction(SILBuilder &B, AssignInst *Inst,
42-
IsInitialization_t isInitialization) {
43-
DEBUG(llvm::dbgs() << " *** Lowering [isInit=" << (bool)isInitialization
55+
PartialInitializationKind isInitialization) {
56+
DEBUG(llvm::dbgs() << " *** Lowering [isInit=" << unsigned(isInitialization)
4457
<< "]: " << *Inst << "\n");
4558

4659
++NumAssignRewritten;
4760

4861
SILValue Src = Inst->getSrc();
62+
SILLocation Loc = Inst->getLoc();
4963

50-
// If this is an initialization, or the storage type is trivial, we
51-
// can just replace the assignment with a store.
52-
53-
// Otherwise, if it has trivial type, we can always just replace the
54-
// assignment with a store. If it has non-trivial type and is an
55-
// initialization, we can also replace it with a store.
56-
if (isInitialization == IsInitialization ||
64+
if (isInitialization == PartialInitializationKind::IsInitialization ||
5765
Inst->getDest()->getType().isTrivial(Inst->getModule())) {
58-
B.createStore(Inst->getLoc(), Src, Inst->getDest());
66+
67+
// If this is an initialization, or the storage type is trivial, we
68+
// can just replace the assignment with a store.
69+
assert(isInitialization != PartialInitializationKind::IsReinitialization);
70+
B.createStore(Loc, Src, Inst->getDest());
71+
} else if (isInitialization == PartialInitializationKind::IsReinitialization) {
72+
73+
// We have a case where a convenience initializer on a class
74+
// delegates to a factory initializer from a protocol extension.
75+
// Factory initializers give us a whole new instance, so the existing
76+
// instance, which has not been initialized and never will be, must be
77+
// freed using dealloc_partial_ref.
78+
SILValue Pointer = B.createLoad(Loc, Inst->getDest());
79+
B.createStore(Loc, Src, Inst->getDest());
80+
81+
auto MetatypeTy = CanMetatypeType::get(
82+
Inst->getDest()->getType().getSwiftRValueType(),
83+
MetatypeRepresentation::Thick);
84+
auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy);
85+
SILValue Metatype = B.createValueMetatype(Loc, SILMetatypeTy, Pointer);
86+
87+
B.createDeallocPartialRef(Loc, Pointer, Metatype);
5988
} else {
89+
assert(isInitialization == PartialInitializationKind::IsNotInitialization);
90+
6091
// Otherwise, we need to replace the assignment with the full
61-
// load/store/release dance. Note that the new value is already
92+
// load/store/release dance. Note that the new value is already
6293
// considered to be retained (by the semantics of the storage type),
6394
// and we're transferring that ownership count into the destination.
6495

6596
// This is basically TypeLowering::emitStoreOfCopy, except that if we have
6697
// a known incoming value, we can avoid the load.
67-
SILValue IncomingVal = B.createLoad(Inst->getLoc(), Inst->getDest());
98+
SILValue IncomingVal = B.createLoad(Loc, Inst->getDest());
6899
B.createStore(Inst->getLoc(), Src, Inst->getDest());
69100

70-
B.emitReleaseValueOperation(Inst->getLoc(), IncomingVal);
101+
B.emitReleaseValueOperation(Loc, IncomingVal);
71102
}
72103

73104
Inst->eraseFromParent();
@@ -1632,12 +1663,24 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &InstInfo) {
16321663
InstInfo.Inst = nullptr;
16331664
NonLoadUses.erase(Inst);
16341665

1666+
PartialInitializationKind PartialInitKind;
1667+
1668+
if (TheMemory.isClassInitSelf() &&
1669+
Kind == DIUseKind::SelfInit) {
1670+
assert(InitKind == IsInitialization);
1671+
PartialInitKind = PartialInitializationKind::IsReinitialization;
1672+
} else {
1673+
PartialInitKind = (InitKind == IsInitialization
1674+
? PartialInitializationKind::IsInitialization
1675+
: PartialInitializationKind::IsNotInitialization);
1676+
}
1677+
16351678
unsigned FirstElement = InstInfo.FirstElement;
16361679
unsigned NumElements = InstInfo.NumElements;
16371680

16381681
SmallVector<SILInstruction*, 4> InsertedInsts;
16391682
SILBuilderWithScope B(Inst, &InsertedInsts);
1640-
LowerAssignInstruction(B, AI, InitKind);
1683+
LowerAssignInstruction(B, AI, PartialInitKind);
16411684

16421685
// If lowering of the assign introduced any new loads or stores, keep track
16431686
// of them.
@@ -1646,7 +1689,11 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &InstInfo) {
16461689
NonLoadUses[I] = Uses.size();
16471690
Uses.push_back(DIMemoryUse(I, Kind, FirstElement, NumElements));
16481691
} else if (isa<LoadInst>(I)) {
1649-
Uses.push_back(DIMemoryUse(I, Load, FirstElement, NumElements));
1692+
// If we have a re-initialization, the value must be a class,
1693+
// and the load is just there so we can free the uninitialized
1694+
// object husk; it's not an actual use of 'self'.
1695+
if (PartialInitKind != PartialInitializationKind::IsReinitialization)
1696+
Uses.push_back(DIMemoryUse(I, Load, FirstElement, NumElements));
16501697
}
16511698
}
16521699
return;
@@ -2541,7 +2588,8 @@ static bool lowerRawSILOperations(SILFunction &Fn) {
25412588
// Unprocessed assigns just lower into assignments, not initializations.
25422589
if (auto *AI = dyn_cast<AssignInst>(Inst)) {
25432590
SILBuilderWithScope B(AI);
2544-
LowerAssignInstruction(B, AI, IsNotInitialization);
2591+
LowerAssignInstruction(B, AI,
2592+
PartialInitializationKind::IsNotInitialization);
25452593
// Assign lowering may split the block. If it did,
25462594
// reset our iteration range to the block after the insertion.
25472595
if (B.getInsertionBB() != &BB)

0 commit comments

Comments
 (0)