Skip to content

Commit 34c8cf6

Browse files
committed
[Sema/SILGen] InitAccessors: Memberwise initializers with init accessors should follow field order
Skip stored properties that are initialized via init accessors and emit parameters/initializations in field order which allows us to cover more use-cases.
1 parent 6ca9728 commit 34c8cf6

File tree

4 files changed

+187
-59
lines changed

4 files changed

+187
-59
lines changed

lib/SILGen/SILGenConstructor.cpp

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -400,29 +400,39 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
400400

401401
// If we have an indirect return slot, initialize it in-place.
402402
if (resultSlot) {
403-
// Tracks all the init accessors we have emitted
404-
// because they can initialize more than one property.
405-
llvm::SmallPtrSet<AccessorDecl *, 2> emittedInitAccessors;
406403
auto elti = elements.begin(), eltEnd = elements.end();
407-
for (VarDecl *field : decl->getStoredProperties()) {
404+
405+
llvm::SmallPtrSet<VarDecl *, 4> storedProperties;
406+
{
407+
auto properties = decl->getStoredProperties();
408+
storedProperties.insert(properties.begin(), properties.end());
409+
}
410+
411+
for (auto *member : decl->getMembers()) {
412+
auto *field = dyn_cast<VarDecl>(member);
413+
if (!field)
414+
continue;
415+
416+
if (initializedViaAccessor.count(field))
417+
continue;
408418

409419
// Handle situations where this stored propery is initialized
410420
// via a call to an init accessor on some other property.
411-
if (initializedViaAccessor.count(field) == 1) {
412-
auto *initProperty = initializedViaAccessor.find(field)->second;
413-
auto *initAccessor = initProperty->getAccessor(AccessorKind::Init);
414-
415-
if (!emittedInitAccessors.insert(initAccessor).second)
421+
if (auto *initAccessor = field->getAccessor(AccessorKind::Init)) {
422+
if (field->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) {
423+
assert(elti != eltEnd &&
424+
"number of args does not match number of fields");
425+
426+
emitApplyOfInitAccessor(SGF, Loc, initAccessor, resultSlot, selfTy,
427+
std::move(*elti));
428+
++elti;
416429
continue;
430+
}
431+
}
417432

418-
assert(elti != eltEnd &&
419-
"number of args does not match number of fields");
420-
421-
emitApplyOfInitAccessor(SGF, Loc, initAccessor, resultSlot, selfTy,
422-
std::move(*elti));
423-
++elti;
433+
// If this is not one of the stored properties, let's move on.
434+
if (!storedProperties.count(field))
424435
continue;
425-
}
426436

427437
auto fieldTy =
428438
selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext());

lib/Sema/CodeSynthesis.cpp

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -303,14 +303,6 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
303303
std::multimap<VarDecl *, VarDecl *> initializedViaAccessor;
304304
decl->collectPropertiesInitializableByInitAccessors(initializedViaAccessor);
305305

306-
auto createParameter = [&](VarDecl *property) {
307-
accessLevel = std::min(accessLevel, property->getFormalAccess());
308-
params.push_back(createMemberwiseInitParameter(decl, Loc, property));
309-
};
310-
311-
// A single property could be used to initialize N other stored
312-
// properties via a call to its init accessor.
313-
llvm::SmallPtrSet<VarDecl *, 4> usedInitProperties;
314306
for (auto member : decl->getMembers()) {
315307
auto var = dyn_cast<VarDecl>(member);
316308
if (!var)
@@ -319,14 +311,11 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
319311
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
320312
continue;
321313

322-
// Check whether this property could be initialized via init accessor.
323-
if (initializedViaAccessor.count(var) == 1) {
324-
auto *initializerProperty = initializedViaAccessor.find(var)->second;
325-
if (usedInitProperties.insert(initializerProperty).second)
326-
createParameter(initializerProperty);
327-
} else {
328-
createParameter(var);
329-
}
314+
if (initializedViaAccessor.count(var))
315+
continue;
316+
317+
accessLevel = std::min(accessLevel, var->getFormalAccess());
318+
params.push_back(createMemberwiseInitParameter(decl, Loc, var));
330319
}
331320
} else if (ICK == ImplicitConstructorKind::DefaultDistributedActor) {
332321
auto classDecl = dyn_cast<ClassDecl>(decl);
@@ -1333,48 +1322,47 @@ HasMemberwiseInitRequest::evaluate(Evaluator &evaluator,
13331322
if (initializedViaAccessor.empty())
13341323
return true;
13351324

1336-
switch (initializedViaAccessor.count(var)) {
1337-
// Not covered by an init accessor.
1338-
case 0:
1339-
initializedProperties.insert(var);
1340-
continue;
1341-
1342-
// Covered by a single init accessor.
1343-
case 1:
1344-
break;
1345-
1346-
// Covered by one than one init accessor which means that we
1347-
// cannot synthesize memberwise initializer.
1348-
default:
1349-
return false;
1350-
}
1351-
13521325
// Check whether use of init accessors results in access to uninitialized
13531326
// properties.
13541327

1355-
for (auto iter = initializedViaAccessor.find(var);
1356-
iter != initializedViaAccessor.end(); ++iter) {
1357-
auto *initializerProperty = iter->second;
1358-
auto *initAccessor =
1359-
initializerProperty->getAccessor(AccessorKind::Init);
1360-
1328+
if (auto *initAccessor = var->getAccessor(AccessorKind::Init)) {
13611329
// Make sure that all properties accessed by init accessor
13621330
// are previously initialized.
13631331
if (auto accessAttr =
1364-
initAccessor->getAttrs().getAttribute<AccessesAttr>()) {
1332+
initAccessor->getAttrs().getAttribute<AccessesAttr>()) {
13651333
for (auto *property : accessAttr->getPropertyDecls(initAccessor)) {
13661334
if (!initializedProperties.count(property))
13671335
invalidOrderings.push_back(
1368-
{initializerProperty, property->getName()});
1336+
{var, property->getName()});
13691337
}
13701338
}
13711339

13721340
// Record all of the properties initialized by calling init accessor.
13731341
if (auto initAttr =
1374-
initAccessor->getAttrs().getAttribute<InitializesAttr>()) {
1342+
initAccessor->getAttrs().getAttribute<InitializesAttr>()) {
13751343
auto properties = initAttr->getPropertyDecls(initAccessor);
13761344
initializedProperties.insert(properties.begin(), properties.end());
13771345
}
1346+
1347+
continue;
1348+
}
1349+
1350+
switch (initializedViaAccessor.count(var)) {
1351+
// Not covered by an init accessor.
1352+
case 0:
1353+
initializedProperties.insert(var);
1354+
continue;
1355+
1356+
// Covered by a single init accessor, we'll handle that
1357+
// once we get to the property with init accessor.
1358+
case 1:
1359+
continue;
1360+
1361+
// Covered by more than one init accessor which means that we
1362+
// cannot synthesize memberwise initializer due to intersecting
1363+
// initializations.
1364+
default:
1365+
return false;
13781366
}
13791367
}
13801368
}

test/Interpreter/init_accessors.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,64 @@ test_assignments()
332332
// CHECK-NEXT: test-assignments-1: (3, 42)
333333
// CHECK-NEXT: a-init-accessor: 0
334334
// CHECK-NEXT: test-assignments-2: (0, 2)
335+
336+
func test_memberwise_ordering() {
337+
struct Test1 {
338+
var _a: Int
339+
var _b: Int
340+
341+
var a: Int {
342+
init(initialValue) initializes(_a) accesses(_b) {
343+
_a = initialValue
344+
}
345+
346+
get { _a }
347+
set { }
348+
}
349+
}
350+
351+
let test1 = Test1(_b: 42, a: 0)
352+
print("test-memberwise-ordering-1: \(test1)")
353+
354+
struct Test2 {
355+
var _a: Int
356+
357+
var pair: (Int, Int) {
358+
init(initialValue) initializes(_a, _b) {
359+
_a = initialValue.0
360+
_b = initialValue.1
361+
}
362+
363+
get { (_a, _b) }
364+
set { }
365+
}
366+
367+
var _b: Int
368+
}
369+
370+
let test2 = Test2(pair: (-1, -2))
371+
print("test-memberwise-ordering-2: \(test2)")
372+
373+
struct Test3 {
374+
var _a: Int
375+
var _b: Int
376+
377+
var pair: (Int, Int) {
378+
init(initialValue) accesses(_a, _b) {
379+
}
380+
381+
get { (_a, _b) }
382+
set { }
383+
}
384+
385+
var _c: Int
386+
}
387+
388+
let test3 = Test3(_a: 1, _b: 2, _c: 3)
389+
print("test-memberwise-ordering-3: \(test3)")
390+
}
391+
392+
test_memberwise_ordering()
393+
// CHECK: test-memberwise-ordering-1: Test1(_a: 0, _b: 42)
394+
// CHECK-NEXT: test-memberwise-ordering-2: Test2(_a: -1, _b: -2)
395+
// CHECK-NEXT: test-memberwise-ordering-3: Test3(_a: 1, _b: 2, _c: 3)

test/decl/var/init_accessors.swift

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,11 +360,26 @@ func test_memberwise_with_overlaps_dont_synthesize_inits() {
360360
// expected-error@-1 {{'Test3<String, [Double]>' cannot be constructed because it has no accessible initializers}}
361361
}
362362

363-
func test_invalid_memberwise() {
364-
struct Test1 { // expected-error {{cannot synthesize memberwise initializer}}
363+
func test_memberwise_ordering() {
364+
struct Test1 {
365365
var _a: Int
366366
var _b: Int
367367

368+
var a: Int {
369+
init(initialValue) initializes(_a) accesses(_b) {
370+
_a = initialValue
371+
}
372+
373+
get { _a }
374+
set { }
375+
}
376+
}
377+
378+
_ = Test1(_b: 42, a: 0) // Ok
379+
380+
struct Test2 { // expected-error {{cannot synthesize memberwise initializer}}
381+
var _a: Int
382+
368383
var a: Int {
369384
init(initialValue) initializes(_a) accesses(_b) {
370385
// expected-note@-1 {{init accessor for 'a' cannot access stored property '_b' because it is called before '_b' is initialized}}
@@ -373,5 +388,59 @@ func test_invalid_memberwise() {
373388

374389
get { _a }
375390
}
391+
392+
var _b: Int
376393
}
394+
395+
struct Test3 {
396+
var _a: Int
397+
398+
var pair: (Int, Int) {
399+
init(initialValue) initializes(_a, _b) {
400+
_a = initialValue.0
401+
_b = initialValue.1
402+
}
403+
404+
get { (_a, _b) }
405+
set { }
406+
}
407+
408+
var _b: Int
409+
}
410+
411+
_ = Test3(pair: (0, 1)) // Ok
412+
413+
struct Test4 {
414+
var _a: Int
415+
var _b: Int
416+
417+
var pair: (Int, Int) {
418+
init(initalValue) accesses(_a, _b) {
419+
}
420+
421+
get { (_a, _b) }
422+
set { }
423+
}
424+
425+
var _c: Int
426+
}
427+
428+
_ = Test4(_a: 0, _b: 1, _c: 2) // Ok
429+
430+
struct Test5 {
431+
var _a: Int
432+
var _b: Int
433+
434+
var c: Int {
435+
init(initalValue) initializes(_c) accesses(_a, _b) {
436+
}
437+
438+
get { _c }
439+
set { }
440+
}
441+
442+
var _c: Int
443+
}
444+
445+
_ = Test5(_a: 0, _b: 1, c: 2) // Ok
377446
}

0 commit comments

Comments
 (0)