Skip to content

Commit 85e84a8

Browse files
committed
DI: Handle cross-module initializers for empty structs in pre-Swift-5
These also have to delegate to another initializer even though there are no stored properties to initialize.
1 parent 8bf0b0c commit 85e84a8

File tree

6 files changed

+162
-23
lines changed

6 files changed

+162
-23
lines changed

lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ DIMemoryObjectInfo::DIMemoryObjectInfo(SingleValueInstruction *MI)
117117
// If this is a derived class init method, track an extra element to determine
118118
// whether super.init has been called at each program point.
119119
NumElements += unsigned(isDerivedClassSelf());
120+
121+
// Make sure we track /something/ in a cross-module struct initializer.
122+
if (NumElements == 0 && isCrossModuleStructInitSelf()) {
123+
NumElements = 1;
124+
HasDummyElement = true;
125+
}
120126
}
121127

122128
SILInstruction *DIMemoryObjectInfo::getFunctionEntryPoint() const {

lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,19 @@ class DIMemoryObjectInfo {
6060
/// This is the base type of the memory allocation.
6161
SILType MemorySILType;
6262

63-
/// True if the memory object being analyzed represents a 'let', which is
64-
/// initialize-only (reassignments are not allowed).
65-
bool IsLet = false;
66-
6763
/// This is the count of elements being analyzed. For memory objects that are
6864
/// tuples, this is the flattened element count. For 'self' members in init
6965
/// methods, this is the local field count (+1 for derive classes).
7066
unsigned NumElements;
7167

68+
/// True if the memory object being analyzed represents a 'let', which is
69+
/// initialize-only (reassignments are not allowed).
70+
bool IsLet = false;
71+
72+
/// True if NumElements has a dummy value in it to force a struct to be
73+
/// non-empty.
74+
bool HasDummyElement = false;
75+
7276
public:
7377
DIMemoryObjectInfo(SingleValueInstruction *MemoryInst);
7478

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ namespace {
526526
// Keep track of whether we've emitted an error. We only emit one error per
527527
// location as a policy decision.
528528
std::vector<SILLocation> EmittedErrorLocs;
529-
SmallPtrSet<SILBasicBlock*, 16> BlocksReachableFromEntry;
529+
SmallPtrSet<const SILBasicBlock *, 16> BlocksReachableFromEntry;
530530

531531
public:
532532
LifetimeChecker(const DIMemoryObjectInfo &TheMemory,
@@ -564,6 +564,9 @@ namespace {
564564
void handleInOutUse(const DIMemoryUse &Use);
565565
void handleEscapeUse(const DIMemoryUse &Use);
566566

567+
bool diagnoseReturnWithoutInitializingStoredProperties(
568+
const SILInstruction *Inst, SILLocation loc, const DIMemoryUse &Use);
569+
567570
void handleLoadUseFailure(const DIMemoryUse &Use,
568571
bool SuperInitDone,
569572
bool FailedSelfUse);
@@ -588,7 +591,7 @@ namespace {
588591
void getOutAvailability(SILBasicBlock *BB, AvailabilitySet &Result);
589592
void getOutSelfInitialized(SILBasicBlock *BB, Optional<DIKind> &Result);
590593

591-
bool shouldEmitError(SILInstruction *Inst);
594+
bool shouldEmitError(const SILInstruction *Inst);
592595
std::string getUninitElementName(const DIMemoryUse &Use);
593596
void noteUninitializedMembers(const DIMemoryUse &Use);
594597
void diagnoseInitError(const DIMemoryUse &Use,
@@ -597,7 +600,7 @@ namespace {
597600
bool diagnoseMethodCall(const DIMemoryUse &Use,
598601
bool SuperInitDone);
599602

600-
bool isBlockIsReachableFromEntry(SILBasicBlock *BB);
603+
bool isBlockIsReachableFromEntry(const SILBasicBlock *BB);
601604
};
602605
} // end anonymous namespace
603606

@@ -658,17 +661,17 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory,
658661
/// Determine whether the specified block is reachable from the entry of the
659662
/// containing function's entrypoint. This allows us to avoid diagnosing DI
660663
/// errors in synthesized code that turns out to be unreachable.
661-
bool LifetimeChecker::isBlockIsReachableFromEntry(SILBasicBlock *BB) {
664+
bool LifetimeChecker::isBlockIsReachableFromEntry(const SILBasicBlock *BB) {
662665
// Lazily compute reachability, so we only have to do it in the case of an
663666
// error.
664667
if (BlocksReachableFromEntry.empty()) {
665-
SmallVector<SILBasicBlock*, 128> Worklist;
668+
SmallVector<const SILBasicBlock*, 128> Worklist;
666669
Worklist.push_back(&BB->getParent()->front());
667670
BlocksReachableFromEntry.insert(Worklist.back());
668671

669672
// Collect all reachable blocks by walking the successors.
670673
while (!Worklist.empty()) {
671-
SILBasicBlock *BB = Worklist.pop_back_val();
674+
const SILBasicBlock *BB = Worklist.pop_back_val();
672675
for (auto &Succ : BB->getSuccessors()) {
673676
if (BlocksReachableFromEntry.insert(Succ).second)
674677
Worklist.push_back(Succ);
@@ -683,7 +686,7 @@ bool LifetimeChecker::isBlockIsReachableFromEntry(SILBasicBlock *BB) {
683686
/// shouldEmitError - Check to see if we've already emitted an error at the
684687
/// specified instruction. If so, return false. If not, remember the
685688
/// instruction and return true.
686-
bool LifetimeChecker::shouldEmitError(SILInstruction *Inst) {
689+
bool LifetimeChecker::shouldEmitError(const SILInstruction *Inst) {
687690
// If this instruction is in a dead region, don't report the error. This can
688691
// occur because we haven't run DCE before DI and this may be a synthesized
689692
// statement. If it isn't synthesized, then DCE will report an error on the
@@ -1589,6 +1592,39 @@ bool LifetimeChecker::diagnoseMethodCall(const DIMemoryUse &Use,
15891592
return false;
15901593
}
15911594

1595+
bool LifetimeChecker::diagnoseReturnWithoutInitializingStoredProperties(
1596+
const SILInstruction *Inst, SILLocation loc, const DIMemoryUse &Use) {
1597+
if (!TheMemory.isAnyInitSelf())
1598+
return false;
1599+
if (TheMemory.isClassInitSelf() || TheMemory.isDelegatingInit())
1600+
return false;
1601+
1602+
if (!shouldEmitError(Inst))
1603+
return true;
1604+
1605+
if (TheMemory.isCrossModuleStructInitSelf() &&
1606+
TheMemory.HasDummyElement) {
1607+
Type selfTy = TheMemory.getType();
1608+
const StructDecl *theStruct = selfTy->getStructOrBoundGenericStruct();
1609+
assert(theStruct);
1610+
1611+
bool fullyUnitialized;
1612+
(void)isInitializedAtUse(Use, nullptr, nullptr, &fullyUnitialized);
1613+
1614+
diagnose(Module, loc,
1615+
diag::designated_init_in_cross_module_extension,
1616+
selfTy, !fullyUnitialized,
1617+
theStruct->getParentModule()->getName(),
1618+
theStruct->hasClangNode());
1619+
} else {
1620+
diagnose(Module, loc,
1621+
diag::return_from_init_without_initing_stored_properties);
1622+
noteUninitializedMembers(Use);
1623+
}
1624+
1625+
return true;
1626+
}
1627+
15921628
/// Check and diagnose various failures when a load use is not fully
15931629
/// initialized.
15941630
///
@@ -1657,12 +1693,8 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use,
16571693
}
16581694
}
16591695

1660-
if (TheMemory.isAnyInitSelf() && !TheMemory.isClassInitSelf() &&
1661-
!TheMemory.isDelegatingInit()) {
1662-
if (!shouldEmitError(Inst)) return;
1663-
diagnose(Module, returnLoc,
1664-
diag::return_from_init_without_initing_stored_properties);
1665-
noteUninitializedMembers(Use);
1696+
if (diagnoseReturnWithoutInitializingStoredProperties(Inst, returnLoc,
1697+
Use)) {
16661698
return;
16671699
}
16681700
}
@@ -1676,12 +1708,9 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use,
16761708
if (CA->isInitializationOfDest() &&
16771709
!CA->getFunction()->getArguments().empty() &&
16781710
SILValue(CA->getFunction()->getArgument(0)) == CA->getDest()) {
1679-
if (TheMemory.isAnyInitSelf() && !TheMemory.isClassInitSelf() &&
1680-
!TheMemory.isDelegatingInit()) {
1681-
if (!shouldEmitError(Inst)) return;
1682-
diagnose(Module, Inst->getLoc(),
1683-
diag::return_from_init_without_initing_stored_properties);
1684-
noteUninitializedMembers(Use);
1711+
if (diagnoseReturnWithoutInitializingStoredProperties(Inst,
1712+
Inst->getLoc(),
1713+
Use)) {
16851714
return;
16861715
}
16871716
}

test/SILOptimizer/Inputs/definite_init_cross_module/OtherModule.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,11 @@ public struct PrivatePoint {
3333
self.y = y
3434
}
3535
}
36+
37+
public struct Empty {
38+
public init() {}
39+
}
40+
41+
public struct GenericEmpty<T> {
42+
public init() {}
43+
}

test/SILOptimizer/definite_init_cross_module.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ extension Point {
1010
self.y = yy // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
1111
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
1212

13+
init(xx: Double) {
14+
self.x = xx // expected-error {{'self' used before 'self.init' call or assignment to 'self'}}
15+
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
16+
1317
init(xxx: Double, yyy: Double) {
1418
// This is OK
1519
self.init(x: xxx, y: yyy)
@@ -194,6 +198,48 @@ extension PrivatePoint {
194198
self = other
195199
}
196200

201+
init(other: PrivatePoint, cond: Bool) {
202+
if cond { self = other }
203+
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
204+
197205
init() {
198206
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
199207
}
208+
209+
extension Empty {
210+
init(x: Double) {
211+
// This is OK
212+
self.init()
213+
}
214+
215+
init(other: Empty) {
216+
// This is okay
217+
self = other
218+
}
219+
220+
init(other: Empty, cond: Bool) {
221+
if cond { self = other }
222+
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
223+
224+
init(xx: Double) {
225+
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
226+
}
227+
228+
extension GenericEmpty {
229+
init(x: Double) {
230+
// This is OK
231+
self.init()
232+
}
233+
234+
init(other: GenericEmpty<T>) {
235+
// This is okay
236+
self = other
237+
}
238+
239+
init(other: GenericEmpty<T>, cond: Bool) {
240+
if cond { self = other }
241+
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
242+
243+
init(xx: Double) {
244+
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
245+
}

test/SILOptimizer/definite_init_cross_module_swift4.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ extension Point {
1010
self.y = yy
1111
}
1212

13+
init(xx: Double) {
14+
self.x = xx // expected-warning {{initializer for struct 'Point' must use "self.init(...)" or "self = ..." because it is not in module 'OtherModule'}}
15+
} // expected-error {{return from initializer without initializing all stored properties}}
16+
1317
init(xxx: Double, yyy: Double) {
1418
// This is OK
1519
self.init(x: xxx, y: yyy)
@@ -234,8 +238,50 @@ extension PrivatePoint {
234238
self = other
235239
}
236240

241+
init(other: PrivatePoint, cond: Bool) {
242+
if cond { self = other }
243+
} // expected-error {{return from initializer without initializing all stored properties}}
244+
237245
// Ideally we wouldn't mention the names of non-public stored properties
238246
// across module boundaries, but this will go away in Swift 5 mode anyway.
239247
init() {
240248
} // expected-error {{return from initializer without initializing all stored properties}}
241249
}
250+
251+
extension Empty {
252+
init(x: Double) {
253+
// This is OK
254+
self.init()
255+
}
256+
257+
init(other: Empty) {
258+
// This is okay
259+
self = other
260+
}
261+
262+
init(other: Empty, cond: Bool) {
263+
if cond { self = other }
264+
} // expected-warning {{initializer for struct 'Empty' must use "self.init(...)" or "self = ..." on all paths because it is not in module 'OtherModule'}}
265+
266+
init(xx: Double) {
267+
} // expected-warning {{initializer for struct 'Empty' must use "self.init(...)" or "self = ..." because it is not in module 'OtherModule'}}
268+
}
269+
270+
extension GenericEmpty {
271+
init(x: Double) {
272+
// This is OK
273+
self.init()
274+
}
275+
276+
init(other: GenericEmpty<T>) {
277+
// This is okay
278+
self = other
279+
}
280+
281+
init(other: GenericEmpty<T>, cond: Bool) {
282+
if cond { self = other }
283+
} // expected-warning {{initializer for struct 'GenericEmpty<T>' must use "self.init(...)" or "self = ..." on all paths because it is not in module 'OtherModule'}}
284+
285+
init(xx: Double) {
286+
} // expected-warning {{initializer for struct 'GenericEmpty<T>' must use "self.init(...)" or "self = ..." because it is not in module 'OtherModule'}}
287+
}

0 commit comments

Comments
 (0)