Skip to content

Commit 3ef4a2d

Browse files
authored
Merge pull request #21849 from jrose-apple/5.0-collocating-convenience-initializers
[5.0] Restore initializing entry points for @objc convenience initializers
2 parents 7322676 + 1fc9f7e commit 3ef4a2d

21 files changed

+896
-78
lines changed

docs/SIL.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,6 +2325,7 @@ mark_uninitialized
23252325
mu_kind ::= 'derivedself'
23262326
mu_kind ::= 'derivedselfonly'
23272327
mu_kind ::= 'delegatingself'
2328+
mu_kind ::= 'delegatingselfallocated'
23282329

23292330
%2 = mark_uninitialized [var] %1 : $*T
23302331
// $T must be an address
@@ -2345,6 +2346,7 @@ the mark_uninitialized instruction refers to:
23452346
- ``derivedself``: designates ``self`` in a derived (non-root) class
23462347
- ``derivedselfonly``: designates ``self`` in a derived (non-root) class whose stored properties have already been initialized
23472348
- ``delegatingself``: designates ``self`` on a struct, enum, or class in a delegating constructor (one that calls self.init)
2349+
- ``delegatingselfallocated``: designates ``self`` on a class convenience initializer's initializing entry point
23482350

23492351
The purpose of the ``mark_uninitialized`` instruction is to enable
23502352
definitive initialization analysis for global variables (when marked as

include/swift/SIL/SILInstruction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3654,6 +3654,10 @@ class MarkUninitializedInst
36543654
/// DelegatingSelf designates "self" on a struct, enum, or class
36553655
/// in a delegating constructor (one that calls self.init).
36563656
DelegatingSelf,
3657+
3658+
/// DelegatingSelfAllocated designates "self" in a delegating class
3659+
/// initializer where memory has already been allocated.
3660+
DelegatingSelfAllocated,
36573661
};
36583662
private:
36593663
Kind ThisKind;
@@ -3683,6 +3687,9 @@ class MarkUninitializedInst
36833687
bool isDelegatingSelf() const {
36843688
return ThisKind == DelegatingSelf;
36853689
}
3690+
bool isDelegatingSelfAllocated() const {
3691+
return ThisKind == DelegatingSelfAllocated;
3692+
}
36863693
};
36873694

36883695
/// MarkUninitializedBehaviorInst - Indicates that a logical property

lib/ParseSIL/ParseSIL.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3355,10 +3355,12 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
33553355
Kind = MarkUninitializedInst::DerivedSelfOnly;
33563356
else if (KindId.str() == "delegatingself")
33573357
Kind = MarkUninitializedInst::DelegatingSelf;
3358+
else if (KindId.str() == "delegatingselfallocated")
3359+
Kind = MarkUninitializedInst::DelegatingSelfAllocated;
33583360
else {
33593361
P.diagnose(KindLoc, diag::expected_tok_in_sil_instr,
33603362
"var, rootself, crossmodulerootself, derivedself, "
3361-
"derivedselfonly, or delegatingself");
3363+
"derivedselfonly, delegatingself, or delegatingselfallocated");
33623364
return true;
33633365
}
33643366

lib/SIL/SILPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,9 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
12971297
*this << "[derivedselfonly] ";
12981298
break;
12991299
case MarkUninitializedInst::DelegatingSelf: *this << "[delegatingself] ";break;
1300+
case MarkUninitializedInst::DelegatingSelfAllocated:
1301+
*this << "[delegatingselfallocated] ";
1302+
break;
13001303
}
13011304

13021305
*this << getIDAndType(MU->getOperand());

lib/SILGen/SILGen.cpp

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -854,33 +854,18 @@ void SILGenModule::emitConstructor(ConstructorDecl *decl) {
854854

855855
bool ForCoverageMapping = doesASTRequireProfiling(M, decl);
856856

857-
auto emitClassAllocatorThunk = [&]{
858-
emitOrDelayFunction(
859-
*this, constant, [this, constant, decl](SILFunction *f) {
860-
preEmitFunction(constant, decl, f, decl);
861-
PrettyStackTraceSILFunction X("silgen emitConstructor", f);
862-
SILGenFunction(*this, *f, decl).emitClassConstructorAllocator(decl);
863-
postEmitFunction(constant, f);
864-
});
865-
};
866-
auto emitValueConstructorIfHasBody = [&]{
867-
if (decl->hasBody()) {
857+
if (declCtx->getSelfClassDecl()) {
858+
// Designated initializers for classes, as well as @objc convenience
859+
// initializers, have have separate entry points for allocation and
860+
// initialization.
861+
if (decl->isDesignatedInit() || decl->isObjC()) {
868862
emitOrDelayFunction(
869-
*this, constant, [this, constant, decl, declCtx](SILFunction *f) {
863+
*this, constant, [this, constant, decl](SILFunction *f) {
870864
preEmitFunction(constant, decl, f, decl);
871865
PrettyStackTraceSILFunction X("silgen emitConstructor", f);
872-
f->setProfiler(getOrCreateProfilerForConstructors(declCtx, decl));
873-
SILGenFunction(*this, *f, decl).emitValueConstructor(decl);
866+
SILGenFunction(*this, *f, decl).emitClassConstructorAllocator(decl);
874867
postEmitFunction(constant, f);
875868
});
876-
}
877-
};
878-
879-
if (declCtx->getSelfClassDecl()) {
880-
// Designated initializers for classes have have separate entry points for
881-
// allocation and initialization.
882-
if (decl->isDesignatedInit()) {
883-
emitClassAllocatorThunk();
884869

885870
// Constructors may not have bodies if they've been imported, or if they've
886871
// been parsed from a parseable interface.
@@ -900,21 +885,21 @@ void SILGenModule::emitConstructor(ConstructorDecl *decl) {
900885
},
901886
/*forceEmission=*/ForCoverageMapping);
902887
}
903-
// Convenience initializers for classes behave more like value constructors
904-
// in that there's only an allocating entry point that effectively
905-
// "constructs" the self reference by invoking another initializer.
906-
} else {
907-
emitValueConstructorIfHasBody();
908-
909-
// If the convenience initializer was imported from ObjC, we still have to
910-
// emit the allocator thunk.
911-
if (decl->hasClangNode()) {
912-
emitClassAllocatorThunk();
913-
}
888+
return;
914889
}
915-
} else {
916-
// Struct and enum constructors do everything in a single function.
917-
emitValueConstructorIfHasBody();
890+
}
891+
892+
// Struct and enum constructors do everything in a single function, as do
893+
// non-@objc convenience initializers for classes.
894+
if (decl->hasBody()) {
895+
emitOrDelayFunction(
896+
*this, constant, [this, constant, decl, declCtx](SILFunction *f) {
897+
preEmitFunction(constant, decl, f, decl);
898+
PrettyStackTraceSILFunction X("silgen emitConstructor", f);
899+
f->setProfiler(getOrCreateProfilerForConstructors(declCtx, decl));
900+
SILGenFunction(*this, *f, decl).emitValueConstructor(decl);
901+
postEmitFunction(constant, f);
902+
});
918903
}
919904
}
920905

lib/SILGen/SILGenApply.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,14 +1397,21 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
13971397
// protocols, which only witness initializing initializers.
13981398
else if (auto proto = dyn_cast<ProtocolDecl>(nominal)) {
13991399
useAllocatingCtor = !proto->isObjC();
1400-
// Similarly, class initializers self.init-delegate to each other via
1401-
// their allocating entry points, unless delegating to an ObjC-only,
1402-
// non-factory initializer.
1400+
// Factory initializers are effectively "allocating" initializers with no
1401+
// corresponding initializing entry point.
1402+
} else if (ctorRef->getDecl()->isFactoryInit()) {
1403+
useAllocatingCtor = true;
1404+
// If we're emitting a class initializer's non-allocating entry point and
1405+
// delegating to an initializer exposed to Objective-C, use the initializing
1406+
// entry point to avoid replacing an existing allocated object.
1407+
} else if (!SGF.AllocatorMetatype && ctorRef->getDecl()->isObjC()) {
1408+
useAllocatingCtor = false;
1409+
// In general, though, class initializers self.init-delegate to each other
1410+
// via their allocating entry points.
14031411
} else {
14041412
assert(isa<ClassDecl>(nominal)
14051413
&& "some new kind of init context we haven't implemented");
1406-
useAllocatingCtor = ctorRef->getDecl()->isFactoryInit()
1407-
|| !requiresForeignEntryPoint(ctorRef->getDecl());
1414+
useAllocatingCtor = !requiresForeignEntryPoint(ctorRef->getDecl());
14081415
}
14091416

14101417
// Load the 'self' argument.
@@ -1472,9 +1479,16 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
14721479
arg->isSelfExprOf(
14731480
cast<AbstractFunctionDecl>(SGF.FunctionDC->getAsDecl()), false);
14741481

1475-
constant =
1476-
constant.asForeign(!isObjCReplacementSelfCall &&
1477-
requiresForeignEntryPoint(ctorRef->getDecl()));
1482+
if (!isObjCReplacementSelfCall) {
1483+
if (useAllocatingCtor) {
1484+
constant =
1485+
constant.asForeign(requiresForeignEntryPoint(ctorRef->getDecl()));
1486+
} else {
1487+
// Note: if we ever implement delegating from one designated initializer
1488+
// to another, this won't be correct; that should do a direct dispatch.
1489+
constant = constant.asForeign(ctorRef->getDecl()->isObjC());
1490+
}
1491+
}
14781492

14791493
// Determine the callee. This is normally the allocating
14801494
// entry point, unless we're delegating to an ObjC initializer.
@@ -1485,7 +1499,8 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
14851499
constant, subs, expr));
14861500
} else if ((useAllocatingCtor || constant.isForeign) &&
14871501
!isSelfCallToReplacedInDynamicReplacement &&
1488-
getMethodDispatch(ctorRef->getDecl()) == MethodDispatch::Class) {
1502+
((constant.isForeign && !useAllocatingCtor) ||
1503+
getMethodDispatch(ctorRef->getDecl()) == MethodDispatch::Class)) {
14891504
// Dynamic dispatch to the initializer.
14901505
Scope S(SGF, expr);
14911506
setCallee(Callee::forClassMethod(

lib/SILGen/SILGenBridging.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1390,7 +1390,7 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
13901390
bool isInitializingToAllocatingInitThunk = false;
13911391
if (native.kind == SILDeclRef::Kind::Initializer) {
13921392
if (auto ctor = dyn_cast<ConstructorDecl>(native.getDecl())) {
1393-
if (!ctor->isDesignatedInit()) {
1393+
if (!ctor->isDesignatedInit() && !ctor->isObjC()) {
13941394
isInitializingToAllocatingInitThunk = true;
13951395
native = SILDeclRef(ctor, SILDeclRef::Kind::Allocator);
13961396
}

lib/SILGen/SILGenConstructor.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -481,9 +481,10 @@ void SILGenFunction::emitClassConstructorAllocator(ConstructorDecl *ctor) {
481481
// Allocate the 'self' value.
482482
bool useObjCAllocation = usesObjCAllocator(selfClassDecl);
483483

484-
if (ctor->hasClangNode()) {
485-
// For an allocator thunk synthesized for an Objective-C init method,
486-
// allocate using the metatype.
484+
if (ctor->hasClangNode() || ctor->isConvenienceInit()) {
485+
assert(ctor->hasClangNode() || ctor->isObjC());
486+
// For an allocator thunk synthesized for an @objc convenience initializer
487+
// or imported Objective-C init method, allocate using the metatype.
487488
SILValue allocArg = selfMetaValue;
488489

489490
// When using Objective-C allocation, convert the metatype
@@ -572,10 +573,13 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) {
572573
// enforce its DI properties on stored properties.
573574
MarkUninitializedInst::Kind MUKind;
574575

575-
if (isDelegating)
576-
MUKind = MarkUninitializedInst::DelegatingSelf;
577-
else if (selfClassDecl->requiresStoredPropertyInits() &&
578-
usesObjCAllocator) {
576+
if (isDelegating) {
577+
if (ctor->isObjC())
578+
MUKind = MarkUninitializedInst::DelegatingSelfAllocated;
579+
else
580+
MUKind = MarkUninitializedInst::DelegatingSelf;
581+
} else if (selfClassDecl->requiresStoredPropertyInits() &&
582+
usesObjCAllocator) {
579583
// Stored properties will be initialized in a separate
580584
// .cxx_construct method called by the Objective-C runtime.
581585
assert(selfClassDecl->hasSuperclass() &&

lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,7 +1810,8 @@ collectClassInitSelfLoadUses(MarkUninitializedInst *MUI,
18101810
//===----------------------------------------------------------------------===//
18111811

18121812
static bool shouldPerformClassInitSelf(const DIMemoryObjectInfo &MemoryInfo) {
1813-
assert(!MemoryInfo.isDelegatingInit());
1813+
if (MemoryInfo.MemoryInst->isDelegatingSelfAllocated())
1814+
return true;
18141815

18151816
return MemoryInfo.isNonDelegatingInit() &&
18161817
MemoryInfo.getType()->getClassOrBoundGenericClass() != nullptr &&
@@ -1824,6 +1825,12 @@ void swift::ownership::collectDIElementUsesFrom(
18241825
const DIMemoryObjectInfo &MemoryInfo, DIElementUseInfo &UseInfo,
18251826
bool isDIFinished, bool TreatAddressToPointerAsInout) {
18261827

1828+
if (shouldPerformClassInitSelf(MemoryInfo)) {
1829+
ClassInitElementUseCollector UseCollector(MemoryInfo, UseInfo);
1830+
UseCollector.collectClassInitSelfUses();
1831+
return;
1832+
}
1833+
18271834
if (MemoryInfo.isDelegatingInit()) {
18281835
// When we're analyzing a delegating constructor, we aren't field sensitive
18291836
// at all. Just treat all members of self as uses of the single
@@ -1833,12 +1840,6 @@ void swift::ownership::collectDIElementUsesFrom(
18331840
return;
18341841
}
18351842

1836-
if (shouldPerformClassInitSelf(MemoryInfo)) {
1837-
ClassInitElementUseCollector UseCollector(MemoryInfo, UseInfo);
1838-
UseCollector.collectClassInitSelfUses();
1839-
return;
1840-
}
1841-
18421843
ElementUseCollector(MemoryInfo, UseInfo, isDIFinished,
18431844
TreatAddressToPointerAsInout)
18441845
.collectFrom();

lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ class DIMemoryObjectInfo {
119119
/// True if the memory object is the 'self' argument of a class designated
120120
/// initializer.
121121
bool isClassInitSelf() const {
122-
if (isDelegatingInit())
122+
if (MemoryInst->isDelegatingSelf())
123123
return false;
124-
124+
125125
if (!MemoryInst->isVar()) {
126126
if (auto decl = getType()->getAnyNominal()) {
127127
if (isa<ClassDecl>(decl)) {
@@ -153,14 +153,13 @@ class DIMemoryObjectInfo {
153153
return isClassInitSelf() && !MemoryInst->isRootSelf();
154154
}
155155

156-
/// isDelegatingInit - True if this is a delegating initializer, one that
157-
/// calls 'self.init'.
156+
/// True if this is a delegating initializer, one that calls 'self.init'.
158157
bool isDelegatingInit() const {
159-
return MemoryInst->isDelegatingSelf();
158+
return MemoryInst->isDelegatingSelf() ||
159+
MemoryInst->isDelegatingSelfAllocated();
160160
}
161161

162-
/// isNonDelegatingInit - True if this is an initializer that initializes
163-
/// stored properties.
162+
/// True if this is an initializer that initializes stored properties.
164163
bool isNonDelegatingInit() const {
165164
switch (MemoryInst->getKind()) {
166165
case MarkUninitializedInst::Var:
@@ -171,6 +170,7 @@ class DIMemoryObjectInfo {
171170
case MarkUninitializedInst::DerivedSelfOnly:
172171
return true;
173172
case MarkUninitializedInst::DelegatingSelf:
173+
case MarkUninitializedInst::DelegatingSelfAllocated:
174174
return false;
175175
}
176176
return false;

0 commit comments

Comments
 (0)