Skip to content

Commit 8f8f000

Browse files
authored
Merge pull request #12834 from jrose-apple/restrict-cross-module-struct-initializers-2
Implementation of SE-0189 "Restrict cross-module struct initializers to be delegating" rdar://problem/34777878
2 parents e05c2cc + ec5ba41 commit 8f8f000

31 files changed

+1019
-217
lines changed

docs/SIL.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,7 @@ mark_uninitialized
23112311
sil-instruction ::= 'mark_uninitialized' '[' mu_kind ']' sil-operand
23122312
mu_kind ::= 'var'
23132313
mu_kind ::= 'rootself'
2314+
mu_kind ::= 'crossmodulerootself'
23142315
mu_kind ::= 'derivedself'
23152316
mu_kind ::= 'derivedselfonly'
23162317
mu_kind ::= 'delegatingself'
@@ -2328,6 +2329,9 @@ the mark_uninitialized instruction refers to:
23282329

23292330
- ``var``: designates the start of a normal variable live range
23302331
- ``rootself``: designates ``self`` in a struct, enum, or root class
2332+
- ``crossmodulerootself``: same as ``rootself``, but in a case where it's not
2333+
really safe to treat ``self`` as a root because the original module might add
2334+
more stored properties. This is only used for Swift 4 compatibility.
23312335
- ``derivedself``: designates ``self`` in a derived (non-root) class
23322336
- ``derivedselfonly``: designates ``self`` in a derived (non-root) class whose stored properties have already been initialized
23332337
- ``delegatingself``: designates ``self`` on a struct, enum, or class in a delegating constructor (one that calls self.init)

include/swift/AST/DiagnosticsSIL.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,14 @@ ERROR(mutating_protocol_witness_method_on_let_constant,none,
215215
"cannot perform mutating operation: '%0' is a 'let' constant",
216216
(StringRef))
217217

218+
WARNING(designated_init_in_cross_module_extension,none,
219+
"initializer for struct %0 must use \"self.init(...)\" or \"self = ...\""
220+
"%select{| on all paths}1 because "
221+
"%select{it is not in module %2|the struct was imported from C}3",
222+
(Type, bool, Identifier, bool))
223+
NOTE(designated_init_c_struct_fix,none,
224+
"use \"self.init()\" to initialize the struct with zero values", ())
225+
218226

219227
// Control flow diagnostics.
220228
ERROR(missing_return,none,

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2846,6 +2846,10 @@ NOTE(change_to_mutating,none,
28462846
"mark %select{method|accessor}0 'mutating' to make 'self' mutable",
28472847
(bool))
28482848

2849+
ERROR(assignment_let_property_delegating_init,none,
2850+
"'let' property %0 may not be initialized directly; use "
2851+
"\"self.init(...)\" or \"self = ...\" instead", (DeclName))
2852+
28492853
// ForEach Stmt
28502854
ERROR(sequence_protocol_broken,none,
28512855
"SequenceType protocol definition is broken", ())

include/swift/SIL/SILInstruction.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3386,6 +3386,14 @@ class MarkUninitializedInst
33863386
/// RootSelf designates "self" in a struct, enum, or root class.
33873387
RootSelf,
33883388

3389+
/// CrossModuleRootSelf is the same as "RootSelf", but in a case where
3390+
/// it's not really safe to treat 'self' as root because the original
3391+
/// module might add more stored properties.
3392+
///
3393+
/// This is only used for Swift 4 compatibility. In Swift 5, cross-module
3394+
/// initializers are always DelegatingSelf.
3395+
CrossModuleRootSelf,
3396+
33893397
/// DerivedSelf designates "self" in a derived (non-root) class.
33903398
DerivedSelf,
33913399

@@ -3412,6 +3420,9 @@ class MarkUninitializedInst
34123420
bool isRootSelf() const {
34133421
return ThisKind == RootSelf;
34143422
}
3423+
bool isCrossModuleRootSelf() const {
3424+
return ThisKind == CrossModuleRootSelf;
3425+
}
34153426
bool isDerivedClassSelf() const {
34163427
return ThisKind == DerivedSelf;
34173428
}

lib/AST/Decl.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5395,15 +5395,21 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags,
53955395
// Struct initializers that cannot see the layout of the struct type are
53965396
// always delegating. This occurs if the struct type is not fixed layout,
53975397
// and the constructor is either inlinable or defined in another module.
5398-
//
5399-
// FIXME: Figure out the right condition to use here that does not depend
5400-
// on the -enable-resilience flag, and make it conditional on
5401-
// -swift-version 5 instead, once the "disallow memberwise cross-module
5402-
// initializer" proposal lands.
5403-
if (Kind == BodyInitKind::None) {
5404-
if (isa<StructDecl>(NTD) &&
5405-
!NTD->hasFixedLayout(getParentModule(), getResilienceExpansion())) {
5398+
if (Kind == BodyInitKind::None && isa<StructDecl>(NTD)) {
5399+
if (getResilienceExpansion() == ResilienceExpansion::Minimal &&
5400+
!NTD->hasFixedLayout()) {
54065401
Kind = BodyInitKind::Delegating;
5402+
5403+
} else if (isa<ExtensionDecl>(getDeclContext())) {
5404+
const ModuleDecl *containingModule = getParentModule();
5405+
// Prior to Swift 5, cross-module initializers were permitted to be
5406+
// non-delegating. However, if the struct isn't fixed-layout, we have to
5407+
// be delegating because, well, we don't know the layout.
5408+
if (!NTD->hasFixedLayout() ||
5409+
containingModule->getASTContext().isSwiftVersionAtLeast(5)) {
5410+
if (containingModule != NTD->getParentModule())
5411+
Kind = BodyInitKind::Delegating;
5412+
}
54075413
}
54085414
}
54095415

lib/ParseSIL/ParseSIL.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3081,6 +3081,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
30813081
Kind = MarkUninitializedInst::Var;
30823082
else if (KindId.str() == "rootself")
30833083
Kind = MarkUninitializedInst::RootSelf;
3084+
else if (KindId.str() == "crossmodulerootself")
3085+
Kind = MarkUninitializedInst::CrossModuleRootSelf;
30843086
else if (KindId.str() == "derivedself")
30853087
Kind = MarkUninitializedInst::DerivedSelf;
30863088
else if (KindId.str() == "derivedselfonly")
@@ -3089,8 +3091,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
30893091
Kind = MarkUninitializedInst::DelegatingSelf;
30903092
else {
30913093
P.diagnose(KindLoc, diag::expected_tok_in_sil_instr,
3092-
"var, rootself, derivedself, derivedselfonly, "
3093-
"or delegatingself");
3094+
"var, rootself, crossmodulerootself, derivedself, "
3095+
"derivedselfonly, or delegatingself");
30943096
return true;
30953097
}
30963098

lib/SIL/SILPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,9 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
12901290
switch (MU->getKind()) {
12911291
case MarkUninitializedInst::Var: *this << "[var] "; break;
12921292
case MarkUninitializedInst::RootSelf: *this << "[rootself] "; break;
1293+
case MarkUninitializedInst::CrossModuleRootSelf:
1294+
*this << "[crossmodulerootself] ";
1295+
break;
12931296
case MarkUninitializedInst::DerivedSelf: *this << "[derivedself] "; break;
12941297
case MarkUninitializedInst::DerivedSelfOnly:
12951298
*this << "[derivedselfonly] ";

lib/SILGen/SILGenConstructor.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,25 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
200200
assert(!selfTy.getClassOrBoundGenericClass()
201201
&& "can't emit a class ctor here");
202202

203+
// Decide if we need to do extra work to warn on unsafe behavior in pre-Swift-5
204+
// modes.
205+
MarkUninitializedInst::Kind MUIKind;
206+
if (isDelegating) {
207+
MUIKind = MarkUninitializedInst::DelegatingSelf;
208+
} else if (getASTContext().isSwiftVersionAtLeast(5)) {
209+
MUIKind = MarkUninitializedInst::RootSelf;
210+
} else {
211+
auto *dc = ctor->getParent();
212+
if (isa<ExtensionDecl>(dc) &&
213+
dc->getAsStructOrStructExtensionContext()->getParentModule() !=
214+
dc->getParentModule()) {
215+
MUIKind = MarkUninitializedInst::CrossModuleRootSelf;
216+
} else {
217+
MUIKind = MarkUninitializedInst::RootSelf;
218+
}
219+
}
220+
203221
// Allocate the local variable for 'self'.
204-
auto MUIKind = isDelegating ? MarkUninitializedInst::DelegatingSelf
205-
: MarkUninitializedInst::RootSelf;
206222
emitLocalVariableWithCleanup(selfDecl, MUIKind)->finishInitialization(*this);
207223
SILValue selfLV = VarLocs[selfDecl].value;
208224

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: 34 additions & 12 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

@@ -108,13 +112,25 @@ class DIMemoryObjectInfo {
108112
/// True if the memory object is the 'self' argument of a struct initializer.
109113
bool isStructInitSelf() const {
110114
if (auto *MUI = dyn_cast<MarkUninitializedInst>(MemoryInst))
111-
if (MUI->isRootSelf())
115+
if (MUI->isRootSelf() || MUI->isCrossModuleRootSelf())
112116
if (auto decl = getType()->getAnyNominal())
113117
if (isa<StructDecl>(decl))
114118
return true;
115119
return false;
116120
}
117121

122+
/// True if the memory object is the 'self' argument of a non-delegating
123+
/// cross-module struct initializer.
124+
bool isCrossModuleStructInitSelf() const {
125+
if (auto *MUI = dyn_cast<MarkUninitializedInst>(MemoryInst)) {
126+
if (MUI->isCrossModuleRootSelf()) {
127+
assert(isStructInitSelf());
128+
return true;
129+
}
130+
}
131+
return false;
132+
}
133+
118134
/// True if the memory object is the 'self' argument of a class initializer.
119135
bool isClassInitSelf() const {
120136
if (auto *MUI = dyn_cast<MarkUninitializedInst>(MemoryInst))
@@ -125,17 +141,15 @@ class DIMemoryObjectInfo {
125141
return false;
126142
}
127143

128-
/// isDerivedClassSelf - Return true if this memory object is the 'self' of
129-
/// a derived class init method.
144+
/// True if this memory object is the 'self' of a derived class initializer.
130145
bool isDerivedClassSelf() const {
131146
if (auto MUI = dyn_cast<MarkUninitializedInst>(MemoryInst))
132147
return MUI->isDerivedClassSelf();
133148
return false;
134149
}
135150

136-
/// isDerivedClassSelfOnly - Return true if this memory object is the 'self'
137-
/// of a derived class init method for which we can assume that all ivars
138-
/// have been initialized.
151+
/// True if this memory object is the 'self' of a derived class initializer for
152+
/// which we can assume that all ivars have been initialized.
139153
bool isDerivedClassSelfOnly() const {
140154
if (auto MUI = dyn_cast<MarkUninitializedInst>(MemoryInst))
141155
return MUI->isDerivedClassSelfOnly();
@@ -171,9 +185,17 @@ class DIMemoryObjectInfo {
171185
/// stored properties.
172186
bool isNonDelegatingInit() const {
173187
if (auto *MUI = dyn_cast<MarkUninitializedInst>(MemoryInst)) {
174-
if (MUI->isDerivedClassSelf() || MUI->isDerivedClassSelfOnly() ||
175-
MUI->isRootSelf())
188+
switch (MUI->getKind()) {
189+
case MarkUninitializedInst::Var:
190+
return false;
191+
case MarkUninitializedInst::RootSelf:
192+
case MarkUninitializedInst::CrossModuleRootSelf:
193+
case MarkUninitializedInst::DerivedSelf:
194+
case MarkUninitializedInst::DerivedSelfOnly:
176195
return true;
196+
case MarkUninitializedInst::DelegatingSelf:
197+
return false;
198+
}
177199
}
178200
return false;
179201
}

0 commit comments

Comments
 (0)