Skip to content

Commit c82559e

Browse files
committed
[DI/Lowering] InitAccessors: Implement lowering of property assignments
DI marks all of of the previously initialized properties and Raw SIL lowering emits `destroy_addr` before calling init accessor for such properties to destroy previously set value.
1 parent b22e8e6 commit c82559e

6 files changed

+94
-19
lines changed

Diff for: lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ class ElementUseCollector {
663663
llvm::SmallDenseMap<VarDecl *, unsigned> &EN);
664664

665665
void addElementUses(unsigned BaseEltNo, SILType UseTy, SILInstruction *User,
666-
DIUseKind Kind);
666+
DIUseKind Kind, NullablePtr<VarDecl> Field = 0);
667667
void collectTupleElementUses(TupleElementAddrInst *TEAI, unsigned BaseEltNo);
668668
void collectStructElementUses(StructElementAddrInst *SEAI,
669669
unsigned BaseEltNo);
@@ -675,7 +675,8 @@ class ElementUseCollector {
675675
/// of $*(Int,Int) is a use of both Int elements of the tuple. This is a helper
676676
/// to keep the Uses data structure up to date for aggregate uses.
677677
void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy,
678-
SILInstruction *User, DIUseKind Kind) {
678+
SILInstruction *User, DIUseKind Kind,
679+
NullablePtr<VarDecl> Field) {
679680
// If we're in a subelement of a struct or enum, just mark the struct, not
680681
// things that come after it in a parent tuple.
681682
unsigned NumElements = 1;
@@ -685,7 +686,7 @@ void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy,
685686
getElementCountRec(TypeExpansionContext(*User->getFunction()), Module,
686687
UseTy, IsSelfOfNonDelegatingInitializer);
687688

688-
trackUse(DIMemoryUse(User, Kind, BaseEltNo, NumElements));
689+
trackUse(DIMemoryUse(User, Kind, BaseEltNo, NumElements, Field));
689690
}
690691

691692
/// Given a tuple_element_addr or struct_element_addr, compute the new
@@ -1211,7 +1212,7 @@ ElementUseCollector::collectAssignOrInitUses(PartialApplyInst *pai,
12111212
auto expansionContext = TypeExpansionContext(*pai->getFunction());
12121213
auto type = selfTy.getFieldType(property, Module, expansionContext);
12131214
addElementUses(Module.getFieldIndex(typeDC, property), type, User,
1214-
useKind);
1215+
useKind, property);
12151216
};
12161217

12171218
auto initializedElts = inst->getInitializedProperties();

Diff for: lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,12 @@ struct DIMemoryUse {
314314
/// track of which tuple elements are affected.
315315
unsigned FirstElement, NumElements;
316316

317-
DIMemoryUse(SILInstruction *Inst, DIUseKind Kind, unsigned FE, unsigned NE)
318-
: Inst(Inst), Kind(Kind), FirstElement(FE), NumElements(NE) {
319-
}
317+
NullablePtr<VarDecl> Field;
318+
319+
DIMemoryUse(SILInstruction *Inst, DIUseKind Kind, unsigned FE, unsigned NE,
320+
NullablePtr<VarDecl> Field = 0)
321+
: Inst(Inst), Kind(Kind), FirstElement(FE), NumElements(NE),
322+
Field(Field) {}
320323

321324
DIMemoryUse() : Inst(nullptr) {}
322325

Diff for: lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

+4-6
Original file line numberDiff line numberDiff line change
@@ -1471,12 +1471,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
14711471
auto allFieldsInitialized =
14721472
getAnyUninitializedMemberAtInst(Use.Inst, 0,
14731473
TheMemory.getNumElements()) == -1;
1474-
if (allFieldsInitialized) {
1475-
Use.Kind = DIUseKind::Set;
1476-
} else {
1477-
diagnose(Module, Use.Inst->getLoc(), diag::use_of_self_before_fully_init);
1478-
return;
1479-
}
1474+
Use.Kind = allFieldsInitialized ? DIUseKind::Set : DIUseKind::Assign;
14801475
} else if (isFullyInitialized) {
14811476
Use.Kind = DIUseKind::Assign;
14821477
} else {
@@ -2399,6 +2394,9 @@ void LifetimeChecker::updateInstructionForInitState(unsigned UseID) {
23992394
llvm::erase_if(NonLoadUses[Inst], [&](unsigned id) { return id == UseID; });
24002395

24012396
switch (Use.Kind) {
2397+
case DIUseKind::Assign:
2398+
AI->markAsInitialized(Use.Field.get());
2399+
LLVM_FALLTHROUGH;
24022400
case DIUseKind::Initialization:
24032401
AI->setMode(AssignOrInitInst::Init);
24042402
break;

Diff for: lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp

+19-5
Original file line numberDiff line numberDiff line change
@@ -300,19 +300,33 @@ lowerAssignOrInitInstruction(SILBuilderWithScope &b,
300300
/*fromBuiltin=*/false);
301301
}
302302

303-
auto emitFieldReference = [&](VarDecl *field) -> SILValue {
303+
auto emitFieldReference = [&](VarDecl *field,
304+
bool emitDestroy = false) -> SILValue {
305+
SILValue fieldRef;
304306
if (isRefSelf) {
305-
return b.createRefElementAddr(loc, selfRef, field);
307+
fieldRef = b.createRefElementAddr(loc, selfRef, field);
308+
} else {
309+
fieldRef = b.createStructElementAddr(loc, selfRef, field);
306310
}
307-
return b.createStructElementAddr(loc, selfRef, field);
311+
312+
if (emitDestroy)
313+
b.createDestroyAddr(loc, fieldRef);
314+
315+
return fieldRef;
308316
};
309317

310318
SmallVector<SILValue> arguments;
311319

312320
// First, emit all of the properties listed in `initializes(...)`. They
313321
// are passed as indirect results.
314-
for (auto *property : inst->getInitializedProperties())
315-
arguments.push_back(emitFieldReference(property));
322+
{
323+
auto toInitialize = inst->getInitializedProperties();
324+
for (unsigned index : indices(toInitialize)) {
325+
arguments.push_back(emitFieldReference(
326+
toInitialize[index],
327+
/*emitDestroy=*/inst->isPropertyAlreadyInitialized(index)));
328+
}
329+
}
316330

317331
// Now emit `initialValue` which is the only argument specified
318332
// by the user.

Diff for: test/SIL/init_accessors.swift

+59
Original file line numberDiff line numberDiff line change
@@ -379,3 +379,62 @@ func test_local_with_memberwise() {
379379

380380
_ = TestMemberwiseGeneric(a: 1, pair: ("a", [0]))
381381
}
382+
383+
func test_assignments() {
384+
struct Test {
385+
var _a: Int
386+
var _b: Int
387+
388+
var a: Int {
389+
init(initialValue) initializes(_a) {
390+
self._a = initialValue
391+
}
392+
get { _a }
393+
set { _a = newValue }
394+
}
395+
396+
var pair: (Int, Int) {
397+
init(initialValue) initializes(_a, _b) {
398+
_a = initialValue.0
399+
_b = initialValue.1
400+
}
401+
402+
get { (_a, _b) }
403+
set { }
404+
}
405+
406+
// CHECK-LABEL: sil private @$s14init_accessors16test_assignmentsyyF4TestL_V1aADSi_tcfC : $@convention(method) (Int, @thin Test.Type) -> Test
407+
// CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivi : $@convention(thin) (Int) -> @out Int
408+
// CHECK: [[A_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #<abstract function>Test._a
409+
// CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], %0) : $@convention(thin) (Int) -> @out Int
410+
// CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivi : $@convention(thin) (Int) -> @out Int
411+
// CHECK: [[A_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #<abstract function>Test._a
412+
// CHECK-NEXT: destroy_addr [[A_REF]] : $*Int
413+
// CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], %0) : $@convention(thin) (Int) -> @out Int
414+
// CHECK: [[B_REF:%.*]] = struct_element_addr %23 : $*Test, #<abstract function>Test._b
415+
// CHECK-NEXT: store {{.*}} to [[B_REF]] : $*Int
416+
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivs : $@convention(method) (Int, @inout Test) -> ()
417+
// CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_VALUE:%.*]]) : $@convention(method) (Int, @inout Test) -> ()
418+
// CHECK: {{.*}} = apply [[SETTER_CLOSURE]](%0) : $@callee_guaranteed (Int) -> ()
419+
init(a: Int) {
420+
self.a = a
421+
self.a = a
422+
self._b = 42
423+
self.a = a
424+
}
425+
426+
// CHECK-LABEL: sil private @$s14init_accessors16test_assignmentsyyF4TestL_V1a1bADSi_SitcfC : $@convention(method) (Int, Int, @thin Test.Type) -> Test
427+
// CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivi : $@convention(thin) (Int) -> @out Int
428+
// CHECK: [[A_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #<abstract function>Test._a
429+
// CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], %0) : $@convention(thin) (Int) -> @out Int
430+
// CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V4pairSi_Sitvi : $@convention(thin) (Int, Int) -> (@out Int, @out Int)
431+
// CHECK: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE:%.*]] : $*Test, #<abstract function>Test._a
432+
// CHECK-NEXT: destroy_addr [[A_REF]] : $*Int
433+
// CHECK-NEXT: [[B_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*Test, #<abstract function>Test._b
434+
// CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], [[B_REF]], {{.*}}) : $@convention(thin) (Int, Int) -> (@out Int, @out Int)
435+
init(a: Int, b: Int) {
436+
self.a = a
437+
self.pair = (0, b)
438+
}
439+
}
440+
}

Diff for: test/SILOptimizer/init_accessor_definite_init_diagnostics.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ struct TestPartial {
7979

8080
init(x: Int, y: Int) {
8181
self.x = x
82-
self.point = (x, y) // expected-error {{'self' used before all stored properties are initialized}}
82+
self.point = (x, y) // Ok (x is going to get `destroy_addr`)
8383
}
8484

8585
init(_ x: Int, _ y: Int) {

0 commit comments

Comments
 (0)