Skip to content

Commit 9ce73f5

Browse files
committed
[Sema] InitAccessors: Diagnose situations when memberwise init cannot be synthesized
If some of the properties with init accessors have out of order accesses diagnose that while checking whether memberwise init could be synthesized.
1 parent 8ec24c6 commit 9ce73f5

File tree

3 files changed

+106
-1
lines changed

3 files changed

+106
-1
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7304,6 +7304,13 @@ ERROR(init_accessor_invalid_member_ref,none,
73047304
"refer to instance properties listed in 'initializes' and "
73057305
"'accesses' attributes",
73067306
(DeclNameRef))
7307+
ERROR(cannot_synthesize_memberwise_due_to_property_init_order,none,
7308+
"cannot synthesize memberwise initializer",
7309+
())
7310+
NOTE(out_of_order_access_in_init_accessor,none,
7311+
"init accessor for %0 cannot access stored property %1 because it "
7312+
"is called before %1 is initialized",
7313+
(Identifier, Identifier))
73077314

73087315
#define UNDEFINE_DIAGNOSTIC_MACROS
73097316
#include "DefineDiagnosticMacros.h"

lib/Sema/CodeSynthesis.cpp

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1317,17 +1317,78 @@ HasMemberwiseInitRequest::evaluate(Evaluator &evaluator,
13171317
if (hasUserDefinedDesignatedInit(evaluator, decl))
13181318
return false;
13191319

1320+
std::multimap<VarDecl *, VarDecl *> initializedViaAccessor;
1321+
decl->collectPropertiesInitializableByInitAccessors(initializedViaAccessor);
1322+
1323+
llvm::SmallPtrSet<VarDecl *, 4> initializedProperties;
1324+
llvm::SmallVector<std::pair<VarDecl *, Identifier>> invalidOrderings;
1325+
13201326
for (auto *member : decl->getMembers()) {
13211327
if (auto *var = dyn_cast<VarDecl>(member)) {
13221328
// If this is a backing storage property for a property wrapper,
13231329
// skip it.
13241330
if (var->getOriginalWrappedProperty())
13251331
continue;
13261332

1327-
if (var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
1333+
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
1334+
continue;
1335+
1336+
// If init accessors are not involved, we are done.
1337+
if (initializedViaAccessor.empty())
13281338
return true;
1339+
1340+
if (!initializedViaAccessor.count(var)) {
1341+
initializedProperties.insert(var);
1342+
continue;
1343+
}
1344+
1345+
// Check whether use of init accessors results in access to uninitialized
1346+
// properties.
1347+
1348+
for (auto iter = initializedViaAccessor.find(var);
1349+
iter != initializedViaAccessor.end(); ++iter) {
1350+
auto *initializerProperty = iter->second;
1351+
auto *initAccessor =
1352+
initializerProperty->getAccessor(AccessorKind::Init);
1353+
1354+
// Make sure that all properties accessed by init accessor
1355+
// are previously initialized.
1356+
if (auto accessAttr =
1357+
initAccessor->getAttrs().getAttribute<AccessesAttr>()) {
1358+
for (auto *property : accessAttr->getPropertyDecls(initAccessor)) {
1359+
if (!initializedProperties.count(property))
1360+
invalidOrderings.push_back(
1361+
{initializerProperty, property->getName()});
1362+
}
1363+
}
1364+
1365+
// Record all of the properties initialized by calling init accessor.
1366+
if (auto initAttr =
1367+
initAccessor->getAttrs().getAttribute<InitializesAttr>()) {
1368+
auto properties = initAttr->getPropertyDecls(initAccessor);
1369+
initializedProperties.insert(properties.begin(), properties.end());
1370+
}
1371+
}
13291372
}
13301373
}
1374+
1375+
if (invalidOrderings.empty())
1376+
return !initializedProperties.empty();
1377+
1378+
{
1379+
auto &diags = decl->getASTContext().Diags;
1380+
1381+
diags.diagnose(
1382+
decl, diag::cannot_synthesize_memberwise_due_to_property_init_order);
1383+
1384+
for (const auto &invalid : invalidOrderings) {
1385+
auto *accessor = invalid.first->getAccessor(AccessorKind::Init);
1386+
diags.diagnose(accessor->getLoc(),
1387+
diag::out_of_order_access_in_init_accessor,
1388+
invalid.first->getName(), invalid.second);
1389+
}
1390+
}
1391+
13311392
return false;
13321393
}
13331394

test/decl/var/init_accessors.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func test_use_of_initializes_accesses_on_non_inits() {
5050
}
5151

5252
get { x }
53+
set { }
5354
}
5455

5556
var _y: String {
@@ -63,6 +64,11 @@ func test_use_of_initializes_accesses_on_non_inits() {
6364
set(initialValue) accesses(x) {}
6465
// expected-error@-1 {{accesses(...) attribute could only be used with init accessors}}
6566
}
67+
68+
init(x: Int, y: String) {
69+
self.y = y
70+
self._x = x
71+
}
6672
}
6773
}
6874

@@ -103,6 +109,13 @@ func test_assignment_to_let_properties() {
103109
self.x = initialValue.0 // Ok
104110
self.y = initialValue.1 // Ok
105111
}
112+
113+
get { (x, y) }
114+
set { }
115+
}
116+
117+
init(x: Int, y: Int) {
118+
self.point = (x, y)
106119
}
107120
}
108121
}
@@ -116,6 +129,12 @@ func test_duplicate_and_computed_lazy_properties() {
116129
init(initialValue) initializes(_b, _a) accesses(_a) {
117130
// expected-error@-1 {{property '_a' cannot be both initialized and accessed}}
118131
}
132+
133+
get { _a }
134+
set { }
135+
}
136+
137+
init() {
119138
}
120139
}
121140

@@ -219,6 +238,8 @@ func test_invalid_references() {
219238
}
220239

221240
func test() -> Int? { 42 }
241+
242+
init() {}
222243
}
223244
}
224245

@@ -335,3 +356,19 @@ func test_memberwise_with_overlaps() {
335356

336357
_ = Test3(a: "a", triple: ("b", 2, [1.0, 2.0]), b: 0, c: [1.0]) // Ok
337358
}
359+
360+
func test_invalid_memberwise() {
361+
struct Test1 { // expected-error {{cannot synthesize memberwise initializer}}
362+
var _a: Int
363+
var _b: Int
364+
365+
var a: Int {
366+
init(initialValue) initializes(_a) accesses(_b) {
367+
// expected-note@-1 {{init accessor for 'a' cannot access stored property '_b' because it is called before '_b' is initialized}}
368+
_a = initialValue
369+
}
370+
371+
get { _a }
372+
}
373+
}
374+
}

0 commit comments

Comments
 (0)