Skip to content

Commit 112d0d4

Browse files
committed
[DI] InitAccessors: Start marking initializes(...) properties as "out"
Adjust DI to recognize that "out" location without uses is uninitialized.
1 parent 63e7561 commit 112d0d4

File tree

5 files changed

+147
-6
lines changed

5 files changed

+147
-6
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ ERROR(ivar_not_initialized_at_superinit,none,
189189
ERROR(ivar_not_initialized_at_implicit_superinit,none,
190190
"property '%0' not initialized at implicitly generated super.init call",
191191
(StringRef, bool))
192+
ERROR(ivar_not_initialized_by_init_accessor,none,
193+
"property %0 not initialized by init accessor",
194+
(DeclName))
192195

193196
ERROR(self_use_before_fully_init,none,
194197
"'self' used in %select{method call|property access}1 %0 before "

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ using namespace ownership;
3131
// Utility
3232
//===----------------------------------------------------------------------===//
3333

34+
static bool isVariableOrResult(MarkUninitializedInst *MUI) {
35+
return MUI->isVar() || MUI->isOut();
36+
}
37+
3438
static void gatherDestroysOfContainer(const DIMemoryObjectInfo &memoryInfo,
3539
DIElementUseInfo &useInfo) {
3640
auto *uninitMemory = memoryInfo.getUninitializedValue();
@@ -106,7 +110,7 @@ computeMemorySILType(MarkUninitializedInst *MUI, SILValue Address) {
106110

107111
// If this is a let variable we're initializing, remember this so we don't
108112
// allow reassignment.
109-
if (!MUI->isVar())
113+
if (!isVariableOrResult(MUI))
110114
return {MemorySILType, false};
111115

112116
auto *VDecl = MUI->getLoc().getAsASTNode<VarDecl>();
@@ -459,7 +463,7 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element,
459463

460464
// If we are analyzing a variable, we can generally get the decl associated
461465
// with it.
462-
if (MemoryInst->isVar())
466+
if (isVariableOrResult(MemoryInst))
463467
return MemoryInst->getLoc().getAsASTNode<VarDecl>();
464468

465469
// Otherwise, we can't.
@@ -516,7 +520,7 @@ SingleValueInstruction *DIMemoryObjectInfo::findUninitializedSelfValue() const {
516520
// If instruction is not a local variable, it could only
517521
// be some kind of `self` (root, delegating, derived etc.)
518522
// see \c MarkUninitializedInst::Kind for more details.
519-
if (!MUI->isVar())
523+
if (!isVariableOrResult(MUI))
520524
return ::getUninitializedValue(MUI);
521525
}
522526
}
@@ -525,7 +529,7 @@ SingleValueInstruction *DIMemoryObjectInfo::findUninitializedSelfValue() const {
525529

526530
ConstructorDecl *DIMemoryObjectInfo::getActorInitSelf() const {
527531
// is it 'self'?
528-
if (!MemoryInst->isVar())
532+
if (!isVariableOrResult(MemoryInst)) {
529533
if (auto decl =
530534
dyn_cast_or_null<ClassDecl>(getASTType()->getAnyNominal()))
531535
// is it for an actor?
@@ -535,6 +539,7 @@ ConstructorDecl *DIMemoryObjectInfo::getActorInitSelf() const {
535539
if (auto *ctor = dyn_cast_or_null<ConstructorDecl>(
536540
silFn->getDeclContext()->getAsDecl()))
537541
return ctor;
542+
}
538543

539544
return nullptr;
540545
}

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ class DIMemoryObjectInfo {
116116
unsigned getNumElements() const { return NumElements; }
117117

118118
/// Return true if this is 'self' in any kind of initializer.
119-
bool isAnyInitSelf() const { return !MemoryInst->isVar(); }
119+
bool isAnyInitSelf() const {
120+
return !MemoryInst->isVar() && !MemoryInst->isOut();
121+
}
120122

121123
/// Return uninitialized value of 'self' if current memory object
122124
/// is located in an initializer (of any kind).
@@ -150,7 +152,7 @@ class DIMemoryObjectInfo {
150152
if (MemoryInst->isDelegatingSelf())
151153
return false;
152154

153-
if (!MemoryInst->isVar()) {
155+
if (!MemoryInst->isVar() && !MemoryInst->isOut()) {
154156
if (auto decl = getASTType()->getAnyNominal()) {
155157
if (isa<ClassDecl>(decl)) {
156158
return true;
@@ -223,6 +225,8 @@ class DIMemoryObjectInfo {
223225
return MemoryInst->isDelegatingSelfAllocated();
224226
}
225227

228+
bool isOut() const { return MemoryInst->isOut(); }
229+
226230
enum class EndScopeKind { Borrow, Access };
227231

228232
/// Given an element number (in the flattened sense) return a pointer to a

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,18 @@ void LifetimeChecker::doIt() {
11521152
return;
11531153
}
11541154

1155+
// All of the indirect results marked as "out" have to be fully initialized
1156+
// before their lifetime ends.
1157+
if (TheMemory.isOut() && Uses.empty()) {
1158+
auto loc = TheMemory.getLoc();
1159+
1160+
std::string propertyName;
1161+
auto *property = TheMemory.getPathStringToElement(0, propertyName);
1162+
diagnose(Module, F.getLocation(),
1163+
diag::ivar_not_initialized_by_init_accessor, property->getName());
1164+
EmittedErrorLocs.push_back(loc);
1165+
}
1166+
11551167
// If the memory object has nontrivial type, then any destroy/release of the
11561168
// memory object will destruct the memory. If the memory (or some element
11571169
// thereof) is not initialized on some path, the bad things happen. Process
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// RUN: %target-swift-frontend -enable-experimental-feature InitAccessors -enable-copy-propagation=requested-passes-only -emit-sil -primary-file %s -o /dev/null -verify
2+
3+
// REQUIRES: asserts
4+
5+
struct Test1 {
6+
var x: Int // expected-note {{variable defined here}}
7+
var y: Int // expected-note {{variable defined here}}
8+
var full: (Int, Int)
9+
10+
var test1: (Int, Int) {
11+
init(initialValue) initializes(y full) accesses(x) {
12+
self.full = (self.x, self.y) // expected-error {{variable 'y' used before being initialized}}
13+
}
14+
15+
get { full }
16+
set { self.full = newValue }
17+
}
18+
19+
var pointY: Int {
20+
init(initialValue) initializes(y) {
21+
self.y = initialValue // Ok
22+
}
23+
}
24+
25+
var errorPoint1: (Int, Int) {
26+
init(initialValue) initializes(x y) {
27+
// expected-error@-1 {{property 'x' not initialized by init accessor}}
28+
// expected-error@-2 {{property 'y' not initialized by init accessor}}
29+
}
30+
}
31+
32+
var errorPoint2: (Int, Int) {
33+
init(initialValue) initializes(x y) {
34+
// expected-error@-1 {{property 'y' not initialized by init accessor}}
35+
self.x = initialValue.0
36+
}
37+
}
38+
39+
var errorPoint3: (Int, Int) {
40+
init(initialValue) initializes(x y) {
41+
self.y = initialValue.1
42+
print(y) // Ok
43+
print(x) // expected-error {{variable 'x' used before being initialized}}
44+
}
45+
}
46+
}
47+
48+
struct TestPartial {
49+
var x: Int
50+
var y: Int
51+
52+
var point: (Int, Int) {
53+
init(initialValue) initializes(x y) {
54+
self.x = initialValue.0
55+
self.y = initialValue.1
56+
}
57+
58+
get { (x, y) }
59+
set { }
60+
}
61+
62+
init(x: Int, y: Int) {
63+
self.x = x
64+
self.point = (x, y) // expected-error {{'self' used before all stored properties are initialized}}
65+
}
66+
67+
init(_ x: Int, _ y: Int) {
68+
self.x = x
69+
self.y = y
70+
self.point = (x, y) // Ok (calls a setter)
71+
}
72+
}
73+
74+
struct TestDoubleInit1 {
75+
let x: Int // expected-note {{change 'let' to 'var' to make it mutable}}
76+
77+
var invalidPointX: Int {
78+
init(initialValue) initializes(x) {
79+
self.x = initialValue
80+
self.x = 42 // expected-error {{immutable value 'x' may only be initialized once}}
81+
}
82+
}
83+
}
84+
85+
struct TestDoubleInit2 {
86+
let x: Int
87+
88+
var pointX: Int {
89+
init(initialValue) initializes(x) {
90+
self.x = initialValue
91+
}
92+
}
93+
94+
init(x: Int) {
95+
self.pointX = x
96+
self.x = 0 // FIXME: should be an error because `x` is a `let` property
97+
}
98+
}
99+
100+
struct TestAccessBeforeInit {
101+
var _x: Int
102+
var x: Int {
103+
init(initialValue) initializes(_x) accesses(y) {
104+
_x = initialValue
105+
}
106+
107+
get { _x }
108+
set {}
109+
}
110+
111+
var y: Int
112+
113+
init(x: Int, y: Int) {
114+
self.x = x // expected-error {{variable 'self.y' used before being initialized}}
115+
self.y = y
116+
}
117+
}

0 commit comments

Comments
 (0)