Skip to content

Commit ac56a9f

Browse files
committed
Turn expansion into an answered question
1 parent 3320bb9 commit ac56a9f

File tree

1 file changed

+44
-32
lines changed

1 file changed

+44
-32
lines changed

proposals/readonly-setter-calls-on-non-variables.md

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -126,27 +126,48 @@ The current v8 specification draft does not yet specify readonly members (<https
126126

127127
> When a property or indexer declared in a _struct_type_ is the target of an assignment, **either** the instance expression associated with the property or indexer access shall be classified as a variable, **or the set accessor of the property or indexer shall be a readonly member ([§16.2.2](https://github.com/dotnet/csharpstandard/blob/standard-v7/standard/structs.md#1622-struct-modifiers))**. If the instance expression is classified as a value **and the set accessor is not a readonly member**, a binding-time error occurs.
128128
129-
## Expansions
129+
## Downsides
130+
131+
If this proposal is taken, it becomes a source-breaking change to remove the `readonly` keyword from a struct or setter. Without the `readonly` keyword, the errors would then be relevant and would reappear.
130132

131-
There's another location where this kind of assignment is blocked, which is in object initializers:
133+
Due to what looks like an unintentional change in the compiler, this source-breaking change is already in effect when the setter is called on an invocation expression:
132134

133135
```cs
134-
// ❌ CS1918 Members of property 'C.ArraySegmentProp' of type 'ArraySegment<object>' cannot be assigned with an object
135-
// initializer because it is of a value type
136-
_ = new C { ArraySegmentProp = { [42] = new object() } };
137-
// ~~~~~~~~~~~~~~~~
136+
// Removing 'readonly' from S1 causes a CS1612 error.
137+
M().Prop = 1;
138138

139-
class C
139+
S1 M() => default;
140+
141+
public readonly struct S1
140142
{
141-
public ArraySegment<object> ArraySegmentProp { get; set; }
143+
public int Prop { get => 0; set { } }
144+
}
145+
```
146+
147+
```cs
148+
// Removing 'readonly' from S2.Prop.set causes a CS1612 error.
149+
M().Prop = 1;
150+
151+
S2 M() => default;
152+
153+
public struct S2
154+
{
155+
public int Prop { get => 0; readonly set { } }
142156
}
143157
```
144158

145-
For the same reasons as above, this error is unnecessary when the properties being initialized have readonly `set`/`init` accessors. The error could be made more granular, placed on each property initializer which calls a _non-readonly_ setter.
159+
## Answered LDM questions
160+
161+
### Should similar assignments be permitted in object initializers?
146162

147-
Even in the limited cases where a ref-returning indexer is applicable, it does not help with CS1918, where it still unnecessarily broad:
163+
There's a separate error, CS1918 that blocks assignments through readonly setters when the assignments appear in object initializers. In addition, this error even blocks assignments to ref-returning properties and indexers, and those assignments are not blocked when they appear outside of object initializers.
148164

149165
```cs
166+
// ❌ CS1918 Members of property 'C.ArraySegmentProp' of type 'ArraySegment<object>' cannot be assigned with an object
167+
// initializer because it is of a value type
168+
_ = new C { ArraySegmentProp = { [42] = new object() } };
169+
// ~~~~~~~~~~~~~~~~
170+
150171
// ❌ CS1918 Members of property 'C.StructWithRefReturningIndexer' of type 'Span<object>' cannot be assigned with an object
151172
// initializer because it is of a value type
152173
_ = new C { StructWithRefReturningIndexer = { [42] = new object() } };
@@ -159,32 +180,23 @@ class C
159180
}
160181
```
161182

162-
## Downsides
163-
164-
If this proposal is taken, it becomes a source-breaking change to remove the `readonly` keyword from a struct or setter. Without the `readonly` keyword, the errors would then be relevant and would reappear.
165-
166-
Due to what looks like an unintentional change in the compiler, this source-breaking change is already in effect when the setter is called on an invocation expression:
183+
Such assignments desugar to the following form, the same form in which the CS1612 warning is being removed:
167184

168185
```cs
169-
// Removing 'readonly' from S1 causes a CS1612 error.
170-
M().Prop = 1;
171-
172-
S1 M() => default;
173-
174-
public readonly struct S1
175-
{
176-
public int Prop { get => 0; set { } }
177-
}
186+
var temp = new C();
187+
// Warning being removed:
188+
// CS1612 Cannot modify the return value of 'C.ArraySegmentProp' because it is not a variable
189+
temp.ArraySegmentProp[42] = new object();
178190
```
179191

180192
```cs
181-
// Removing 'readonly' from S2.Prop.set causes a CS1612 error.
182-
M().Prop = 1;
193+
var temp = new C();
194+
// Permitted today
195+
temp.StructWithRefReturningIndexer[42] = new object();
196+
```
183197

184-
S2 M() => default;
198+
Should this check be made more granular, so that members of struct types may be assigned when they would be allowed to be assigned in the desugared form?
185199

186-
public struct S2
187-
{
188-
public int Prop { get => 0; readonly set { } }
189-
}
190-
```
200+
#### Answer
201+
202+
Yes. This expansion will be included. [(LDM 2025-04-02)](https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-04-02.md#expansions)

0 commit comments

Comments
 (0)