Skip to content

Commit 4dbaaf3

Browse files
committed
[AST] InitAccessors: Allow assignments to let properties
1 parent 2e8b0e7 commit 4dbaaf3

File tree

2 files changed

+48
-9
lines changed

2 files changed

+48
-9
lines changed

lib/AST/Decl.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6703,10 +6703,28 @@ bool VarDecl::isSettable(const DeclContext *UseDC,
67036703
// 'let's are only ever settable from a specific DeclContext.
67046704
if (UseDC == nullptr)
67056705
return false;
6706-
6706+
67076707
// 'let' properties in structs/classes are only ever settable in their
6708-
// designated initializer(s).
6708+
// designated initializer(s) or by init accessors.
67096709
if (isInstanceMember()) {
6710+
// Init accessors allow assignments to `let` properties if a
6711+
// property is part of `initializes(...)` list.
6712+
if (auto *accessor =
6713+
dyn_cast<AccessorDecl>(const_cast<DeclContext *>(UseDC))) {
6714+
// Check whether this property is part of `initializes(...)` list,
6715+
// and allow assignment/mutation if so. DI would be responsible
6716+
// for checking for re-assignment.
6717+
if (auto *initAttr =
6718+
accessor->getAttrs().getAttribute<InitializesAttr>()) {
6719+
return llvm::is_contained(initAttr->getPropertyDecls(accessor),
6720+
const_cast<VarDecl *>(this));
6721+
}
6722+
6723+
// If there is no `initializes` attribute, no referenced properties
6724+
// can be assignment to or mutated.
6725+
return false;
6726+
}
6727+
67106728
auto *CD = dyn_cast<ConstructorDecl>(UseDC);
67116729
if (!CD) return false;
67126730

test/decl/var/init_accessors.swift

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
func test_invalid_init_accessor_use() {
44
var other: String = "" // expected-warning {{}}
55
var x: Int {
6-
init(newValue) initializes(other) {}
6+
init(initialValue) initializes(other) {}
77
// expected-error@-1 {{init accessors could only be associated with properties}}
88

99
get { 42 }
1010
}
1111

1212
struct X {
1313
subscript(x: Int) -> Bool {
14-
init(newValue) {} // expected-error {{init accessors could only be associated with properties}}
14+
init(initialValue) {} // expected-error {{init accessors could only be associated with properties}}
1515

1616
get { false }
1717
}
@@ -24,21 +24,21 @@ func test_use_of_initializes_accesses_on_non_inits() {
2424
var y: String
2525

2626
var _x: Int {
27-
init(newValue) initializes(x) accesses(y) { // Ok
27+
init(initialValue) initializes(x) accesses(y) { // Ok
2828
}
2929

3030
get { x }
3131
}
3232

3333
var _y: String {
3434
get { y }
35-
set(newValue) initializes(y) {}
35+
set(initialValue) initializes(y) {}
3636
// expected-error@-1 {{initalizes(...) attribute could only be used with init accessors}}
3737
}
3838

3939
var _q: String {
4040
get { y }
41-
set(newValue) accesses(x) {}
41+
set(initialValue) accesses(x) {}
4242
// expected-error@-1 {{accesses(...) attribute could only be used with init accessors}}
4343
}
4444
}
@@ -48,18 +48,39 @@ func test_invalid_refs_in_init_attrs() {
4848
struct Test {
4949
var c: Int { get { 42 } }
5050
var x: Int {
51-
init(newValue) initializes(a) accesses(b c) {}
51+
init(initialValue) initializes(a) accesses(b c) {}
5252
// expected-error@-1 {{find type 'a' in scope}}
5353
// expected-error@-2 {{find type 'b' in scope}}
5454
// expected-error@-3 {{init accessor cannot refer to property 'c'; init accessors can refer only to stored properties}}
5555
}
5656

5757
var y: String {
58-
init(newValue) initializes(test) {}
58+
init(initialValue) initializes(test) {}
5959
// expected-error@-1 {{ambiguous reference to member 'test'}}
6060
}
6161

6262
func test(_: Int) {} // expected-note {{'test' declared here}}
6363
func test(_: String) -> Int { 42 } // expected-note {{'test' declared here}}
6464
}
6565
}
66+
67+
func test_assignment_to_let_properties() {
68+
struct Test {
69+
let x: Int
70+
let y: Int // expected-note {{change 'let' to 'var' to make it mutable}}
71+
72+
var pointX: Int {
73+
init(initialValue) initializes(x) accesses(y) {
74+
self.x = initialValue // Ok
75+
self.y = 42 // expected-error {{cannot assign to property: 'y' is a 'let' constant}}
76+
}
77+
}
78+
79+
var point: (Int, Int) {
80+
init(initialValue) initializes(x y) {
81+
self.x = initialValue.0 // Ok
82+
self.y = initialValue.1 // Ok
83+
}
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)