-
Notifications
You must be signed in to change notification settings - Fork 90
Add support for readonly members in structs #673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: draft-v8
Are you sure you want to change the base?
Changes from all commits
6997fb0
8adf490
4042ebb
1fbab5d
9a4faea
e58cbcd
f18083f
62ff34d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2094,6 +2094,7 @@ ref_method_modifier | |||||||||
| 'override' | ||||||||||
| 'abstract' | ||||||||||
| 'extern' | ||||||||||
| 'readonly' // direct struct members only | ||||||||||
| unsafe_modifier // unsafe code support | ||||||||||
; | ||||||||||
|
||||||||||
|
@@ -2132,7 +2133,7 @@ Grammar notes: | |||||||||
|
||||||||||
> *Note*: The overlapping of, and priority between, alternatives here is solely for descriptive convenience; the grammar rules could be elaborated to remove the overlap. ANTLR, and other grammar systems, adopt the same convenience and so *method_body* has the specified semantics automatically. *end note* | ||||||||||
|
||||||||||
A *method_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), the `new` ([§15.3.5](classes.md#1535-the-new-modifier)), `static` ([§15.6.3](classes.md#1563-static-and-instance-methods)), `virtual` ([§15.6.4](classes.md#1564-virtual-methods)), `override` ([§15.6.5](classes.md#1565-override-methods)), `sealed` ([§15.6.6](classes.md#1566-sealed-methods)), `abstract` ([§15.6.7](classes.md#1567-abstract-methods)), `extern` ([§15.6.8](classes.md#1568-external-methods)) and `async` ([§15.15](classes.md#1515-async-functions)) modifiers. | ||||||||||
A *method_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), the `new` ([§15.3.5](classes.md#1535-the-new-modifier)), `static` ([§15.6.3](classes.md#1563-static-and-instance-methods)), `virtual` ([§15.6.4](classes.md#1564-virtual-methods)), `override` ([§15.6.5](classes.md#1565-override-methods)), `sealed` ([§15.6.6](classes.md#1566-sealed-methods)), `abstract` ([§15.6.7](classes.md#1567-abstract-methods)), `extern` ([§15.6.8](classes.md#1568-external-methods)) and `async` ([§15.15](classes.md#1515-async-functions)). Additionally a *method_declaration* that is contained directly by a *struct_declaration* may include the `readonly` modifier (§cands-diffs-methods). | ||||||||||
|
||||||||||
A declaration has a valid combination of modifiers if all of the following are true: | ||||||||||
|
||||||||||
|
@@ -3281,6 +3282,7 @@ property_modifier | |||||||||
| 'override' | ||||||||||
| 'abstract' | ||||||||||
| 'extern' | ||||||||||
| 'readonly' // direct struct members only | ||||||||||
| unsafe_modifier // unsafe code support | ||||||||||
; | ||||||||||
|
||||||||||
|
@@ -3301,7 +3303,7 @@ ref_property_body | |||||||||
|
||||||||||
*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). | ||||||||||
|
||||||||||
There are two kinds of *property_declaration*: | ||||||||||
A *property_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and any one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), the `new` ([§15.3.5](classes.md#1535-the-new-modifier)), `static` ([§15.7.2](classes.md#1572-static-and-instance-properties)), `virtual` ([§15.6.4](classes.md#1564-virtual-methods), [§15.7.6](classes.md#1576-virtual-sealed-override-and-abstract-accessors)), `override` ([§15.6.5](classes.md#1565-override-methods), [§15.7.6](classes.md#1576-virtual-sealed-override-and-abstract-accessors)), `sealed` ([§15.6.6](classes.md#1566-sealed-methods)), `abstract` ([§15.6.7](classes.md#1567-abstract-methods), [§15.7.6](classes.md#1576-virtual-sealed-override-and-abstract-accessors)), `extern` ([§15.6.8](classes.md#1568-external-methods)), and `readonly` modifiers. However, it is a compile-time error for the *property_modifier* `readonly` to be used in a *property_declaration* that is not contained directly by a *struct_declaration* (([§16.4.11](structs.md#16411-automatically-implemented-properties)). | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same suggestion as for methods above:
Suggested change
|
||||||||||
|
||||||||||
- The first declares a non-ref-valued property. Its value has type *type*. This kind of property may be readable and/or writeable. | ||||||||||
- The second declares a ref-valued property. Its value is a *variable_reference* ([§9.5](variables.md#95-variable-references)), that may be `readonly`, to a variable of type *type*. This kind of property is only readable. | ||||||||||
|
@@ -3366,6 +3368,7 @@ accessor_modifier | |||||||||
| 'internal' 'protected' | ||||||||||
| 'protected' 'private' | ||||||||||
| 'private' 'protected' | ||||||||||
| 'readonly' // direct struct members only | ||||||||||
; | ||||||||||
|
||||||||||
accessor_body | ||||||||||
|
@@ -3392,6 +3395,7 @@ For a ref-valued property the *ref_get_accessor_declaration* consists optional a | |||||||||
The use of *accessor_modifier*s is governed by the following restrictions: | ||||||||||
|
||||||||||
- An *accessor_modifier* shall not be used in an interface or in an explicit interface member implementation. | ||||||||||
- It is a compile-time error for the *accessor_modifier* `readonly` to be used in a *property_declaration* or *indexer_declaration* that is not contained directly by a *struct_declaration* (§16.4.11, §cands-diffs-indexers). | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd include accessors on this restriction. Either in this bullet, or as a separate bullet item. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The restriction in this bullet is only for accessors; it applies to accessor_modifier, rather than property_modifier or indexer_modifier. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Possibly "is permitted only" to match the following line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jskeet Good idea:
Suggested change
|
||||||||||
- For a property or indexer that has no `override` modifier, an *accessor_modifier* is permitted only if the property or indexer has both a get and set accessor, and then is permitted only on one of those accessors. | ||||||||||
- For a property or indexer that includes an `override` modifier, an accessor shall match the *accessor_modifier*, if any, of the accessor being overridden. | ||||||||||
- The *accessor_modifier* shall declare an accessibility that is strictly more restrictive than the declared accessibility of the property or indexer itself. To be precise: | ||||||||||
|
@@ -4024,6 +4028,7 @@ event_modifier | |||||||||
| 'override' | ||||||||||
| 'abstract' | ||||||||||
| 'extern' | ||||||||||
| 'readonly' // direct struct members only | ||||||||||
| unsafe_modifier // unsafe code support | ||||||||||
; | ||||||||||
|
||||||||||
|
@@ -4043,7 +4048,7 @@ remove_accessor_declaration | |||||||||
|
||||||||||
*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). | ||||||||||
|
||||||||||
An *event_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and any one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), the `new` ([§15.3.5](classes.md#1535-the-new-modifier)), `static` ([§15.6.3](classes.md#1563-static-and-instance-methods), [§15.8.4](classes.md#1584-static-and-instance-events)), `virtual` ([§15.6.4](classes.md#1564-virtual-methods), [§15.8.5](classes.md#1585-virtual-sealed-override-and-abstract-accessors)), `override` ([§15.6.5](classes.md#1565-override-methods), [§15.8.5](classes.md#1585-virtual-sealed-override-and-abstract-accessors)), `sealed` ([§15.6.6](classes.md#1566-sealed-methods)), `abstract` ([§15.6.7](classes.md#1567-abstract-methods), [§15.8.5](classes.md#1585-virtual-sealed-override-and-abstract-accessors)), and `extern` ([§15.6.8](classes.md#1568-external-methods)) modifiers. | ||||||||||
An *event_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and any one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), the `new` ([§15.3.5](classes.md#1535-the-new-modifier)), `static` ([§15.6.3](classes.md#1563-static-and-instance-methods), [§15.8.4](classes.md#1584-static-and-instance-events)), `virtual` ([§15.6.4](classes.md#1564-virtual-methods), [§15.8.5](classes.md#1585-virtual-sealed-override-and-abstract-accessors)), `override` ([§15.6.5](classes.md#1565-override-methods), [§15.8.5](classes.md#1585-virtual-sealed-override-and-abstract-accessors)), `sealed` ([§15.6.6](classes.md#1566-sealed-methods)), `abstract` ([§15.6.7](classes.md#1567-abstract-methods), [§15.8.5](classes.md#1585-virtual-sealed-override-and-abstract-accessors)), `extern` ([§15.6.8](classes.md#1568-external-methods)), and `readonly` modifiers. However, it is a compile-time error for the *event_modifier* `readonly` to be used in an *event_declaration* that is not contained directly by a *struct_declaration* (§cands-diffs-events). | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Following my previous suggestions:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the way, an |
||||||||||
|
||||||||||
Event declarations are subject to the same rules as method declarations ([§15.6](classes.md#156-methods)) with regard to valid combinations of modifiers. | ||||||||||
|
||||||||||
|
@@ -4304,6 +4309,7 @@ indexer_modifier | |||||||||
| 'override' | ||||||||||
| 'abstract' | ||||||||||
| 'extern' | ||||||||||
| 'readonly' // direct struct members only | ||||||||||
| unsafe_modifier // unsafe code support | ||||||||||
; | ||||||||||
|
||||||||||
|
@@ -4325,7 +4331,7 @@ ref_indexer_body | |||||||||
|
||||||||||
*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). | ||||||||||
|
||||||||||
There are two kinds of *indexer_declaration*: | ||||||||||
An *indexer_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and any one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), the `new` ([§15.3.5](classes.md#1535-the-new-modifier)), `virtual` ([§15.6.4](classes.md#1564-virtual-methods)), `override` ([§15.6.5](classes.md#1565-override-methods)), `sealed` ([§15.6.6](classes.md#1566-sealed-methods)), `abstract` ([§15.6.7](classes.md#1567-abstract-methods)), `extern` ([§15.6.8](classes.md#1568-external-methods)), and `readonly` modifiers. However, it is a compile-time error for the *indexer_modifier* `readonly` to be used in an *indexer_declaration* that is not contained directly by a *struct_declaration* (§cands-diffs-indexers). | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above:
Suggested change
|
||||||||||
|
||||||||||
- The first declares a non-ref-valued indexer. Its value has type *type*. This kind of indexer may be readable and/or writeable. | ||||||||||
- The second declares a ref-valued indexer. Its value is a *variable_reference* ([§9.5](variables.md#95-variable-references)), that may be `readonly`, to a variable of type *type*. This kind of indexer is only readable. | ||||||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -57,7 +57,6 @@ The `readonly` modifier indicates that the *struct_declaration* declares a type | |||||
A readonly struct has the following constraints: | ||||||
|
||||||
- Each of its instance fields shall also be declared `readonly`. | ||||||
- None of its instance properties shall have a *set_accessor_declaration* ([§15.7.3](classes.md#1573-accessors)). | ||||||
- It shall not declare any field-like events ([§15.8.2](classes.md#1582-field-like-events)). | ||||||
|
||||||
When an instance of a readonly struct is passed to a method, its `this` is treated like an input argument/parameter, which disallows write access to any instance fields (except by constructors). | ||||||
|
@@ -154,6 +153,7 @@ Structs differ from classes in several important ways: | |||||
- Instance field declarations for a struct are not permitted to include variable initializers ([§16.4.8](structs.md#1648-field-initializers)). | ||||||
- A struct is not permitted to declare a parameterless instance constructor ([§16.4.9](structs.md#1649-constructors)). | ||||||
- A struct is not permitted to declare a finalizer. | ||||||
- Some kinds of members in structs are permitted to have the modifier `readonly` while that is not generally permitted for those same member kinds in classes. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While I can appreciate the desire not to repeat things, I suspect it's worth being more specific about which members can have |
||||||
|
||||||
### 16.4.2 Value semantics | ||||||
|
||||||
|
@@ -402,6 +402,8 @@ As described in [§16.4.5](structs.md#1645-default-values), the default value of | |||||
> | ||||||
> *end example* | ||||||
|
||||||
A *field_declaration* declared directly inside a *struct_declaration* having the *struct_modifier* `readonly` shall have the *field_modifier* `readonly`. | ||||||
|
||||||
### 16.4.9 Constructors | ||||||
|
||||||
Unlike a class, a struct is not permitted to declare a parameterless instance constructor. Instead, every struct implicitly has a parameterless instance constructor, which always returns the value that results from setting all value type fields to their default value and all reference type fields to `null` ([§8.3.3](types.md#833-default-constructors)). A struct can declare instance constructors having parameters. | ||||||
|
@@ -502,6 +504,50 @@ Automatically implemented properties ([§15.7.4](classes.md#1574-automatically-i | |||||
|
||||||
> *Note*: This access restriction means that constructors in structs containing automatically implemented properties often need an explicit constructor initializer where they would not otherwise need one, to satisfy the requirement of all fields being definitely assigned before any function member is invoked or the constructor returns. *end note* | ||||||
|
||||||
A *property_declaration* ([§15.7.1](classes.md#1571-general)) for an instance property in a *struct_declaration* may contain the *property_modifier* `readonly`. However, a static property shall not contain that modifier. | ||||||
|
||||||
It is a compile-time error to attempt to modify the state of an instance struct variable via a readonly property declared in that struct. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "attempt" feels more human than spec-like to me. What rule are we stating here, and can we do so more precisely? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree with @jskeet and additionally we have some repetition here which I think should be factored out as it is for classes: under classes modifiers are generally described in subclauses under §15.3 Class members. That same pattern can be repeated here with the current content of §16.3 Struct members becoming §16.3.1 General and adding §16.3.2 The readonly modifier. In §16.3.2 the rules for readonly members can be given (not proposed text):
This would provide a single place for defining what a readonly member. The additional per-member differences could follow in §16.3 subclauses or remain as they are in §16.4 subclauses. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From MS Learn:
This example demonstrates a subtly that might easily be missed from the statement on line 589:
An example like (maybe exactly) this one should be included in the Standard with explanatory text to cover:
Unless there are such explanations already here somewhere… There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the idea of an example. The "read only set that changes the state of a member" is a clear extension of what C# already permits: public struct S
{
private List<int> sequence;
public IList<int> Sequence
{
get
{
sequence ??= new List<int>();
return sequence;
}
}
} Callers can modify the returned sequence. The property above can't be marked |
||||||
|
||||||
It is a compile-time error for an automatically implemented property having a `readonly` modifier, to also have a `set` accessor. | ||||||
|
||||||
It is a compile-time error for an automatically implemented property in a `readonly` struct to have a `set` accessor. | ||||||
|
||||||
An automatically implemented property declared inside a `readonly` struct need not have a `readonly` modifier, as its `get` accessor is implicitly assumed to be readonly. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Roslyn also treats auto property getter of non-readonly structs as implicitly readonly. Should this be intended in spec? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This behaviour of Roslyn avoids copying the non- struct S {
int I { get; set; }
int J => 42;
static int M(in S s) {
int i = s.I; // does not copy s
int j = s.J; // defensive copy, as in ((S)s).J
return i + j;
}
} However, I don't think a conforming C# program can detect whether this copying happens, because the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the standard can remain quiet on this behavior. A conforming compiler shouldn't be required to add the readonly modifier in this instance. |
||||||
|
||||||
It is a compile-time error for a property to have a `readonly` modifier on both the `get` and the `set` accessors. | ||||||
|
||||||
It is a compile-time error to have a `readonly` modifier on a property itself as well as on either of its `get` and `set` accessors. | ||||||
|
||||||
If the `get` accessor has a `readonly` modifier, the `set` shall exist and shall not have that modifier. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should disallow this as well: struct S {
int P { // error CS8664: 'S.P': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor
readonly set { }
}
} Perhaps:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or merge with the earlier rule
resulting in:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, assuming CS8664 is the intended semantics and not a Roslyn quirk. IIRC conformance allows additional members other than
Suggested change
Could use “corresponding” rather than “sibling” There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assuming the implementation allows a property to have a nonstandard struct S {
private int i;
public int I {
readonly get => i;
increment => ++i;
}
} I think "all of its accessors" would be a good rule here. If the developer wants all accessors to be readonly, then they should make the whole property readonly, regardless of whether the accessors are standard or nonstandard. And if the developer wants the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, the grammar currently only allows |
||||||
|
||||||
### §cands-diffs-methods Methods | ||||||
|
||||||
A *method_declaration* ([§15.6.1](classes.md#1561-general)) for an instance method in a *struct_declaration* may contain the *method_modifier* `readonly`. However, a static method shall not contain that modifier. | ||||||
|
||||||
It is a compile-time error to attempt to modify the state of an instance struct variable via a readonly method declared in that struct. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another case of "attempt". |
||||||
|
||||||
Although a readonly method may call a sibling, non-readonly method, or property or indexer get accessor, doing so results in the creation of an implicit copy of `this` as a defensive measure. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, demo: struct S {
int P => 0; // not readonly
int this[string s] => 0; // not readonly
readonly void M() {
_ = P; // warning CS8656: Call to non-readonly member 'S.P.get' from a 'readonly' member results in an implicit copy of 'this'.
_ = this[""]; // warning CS8656: Call to non-readonly member 'S.this[string].get' from a 'readonly' member results in an implicit copy of 'this'.
N(); // warning CS8656: Call to non-readonly member 'S.N()' from a 'readonly' member results in an implicit copy of 'this'.
}
void N() {}
} The standard does not require these warnings and I think that's OK. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another demo, also OK: struct S {
private static int r;
public ref int P {
get => ref r; // not readonly
}
readonly void M() {
this.P = 0; // warning CS8656: Call to non-readonly member 'S.P.get' from a 'readonly' member results in an implicit copy of 'this'.
}
}
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree in both cases, these warnings are up to a compiler. The first example in particular does however raise another potential issue – see #1053. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made a note in #1053: I think we should defer this until we spec out There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We haven't defined "sibling" anywhere in the spec. I suggest replacing it with "may call a ... declared in the same There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The wording "declared in the same struct S {
void M(in S s) {}
readonly void N(S s) {
s.M(this); // must not make an implicit copy of `this`
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While I wrote a short while ago in #1053 that I wasn’t intending to get into the defensive copy issue at this time re-reading comments like the above makes me think we have little choice. Last year I suggested the common aspects of readonly need to be defined in single place and that including covering that readonly members may call mutating members but any mutations must not be visible afterwards – i.e. specify what not how. Do this and we should find wording things like the above easier. And I think we'll need to define sibling, at least for this topic if not for the wider Standard. |
||||||
|
||||||
A readonly method may call a sibling property or indexer set accessor that is readonly. If a sibling member’s accessor is not explicitly or implicitly readonly, a compile-error occurs. | ||||||
Comment on lines
+529
to
+531
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does "sibling" specifically mean an instance member of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another thing that could be defined in §16.3.x as per suggestion on line 509 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there already a rule against struct S {
public int P {
set {} // not readonly
}
public int this[string s] {
set {} // not readonly
}
readonly void M() {
this.P = 0; // error CS1604: Cannot assign to 'this.P' because it is read-only
this[""] = 0; // error CS1604: Cannot assign to 'this[""]' because it is read-only
}
}
class C {
static void N(in S that) {
that.P = 0; // error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable
that[""] = 0; // error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable
}
} Not clear to me why these are separate diagnostic codes. Can the C# standard cover both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The separate diagnostic codes (and text) are not mandated, that is a compiler choice – these messages are just (in the view of the compiler writer at least, YMMV) better than “error: LHS is read-only” |
||||||
|
||||||
All *method_declaration*s of a partial method shall have a `readonly` modifier, or none of them shall have it. | ||||||
|
||||||
### §cands-diffs-indexers Indexers | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So just to check, that means an indexer can have a I guess if it's modifying the state of something referenced via the struct, that's reasonable. Odd though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. That's exactly why. @jnm2 has a proposal to allow this in more places: https://github.com/dotnet/csharplang/blob/main/proposals/readonly-setter-calls-on-non-variables.md (Note: not currently in the working set) |
||||||
|
||||||
An *indexer_declaration* ([§15.9](classes.md#159-indexers)) for an instance indexer in a *struct_declaration* may contain the *indexer_modifier* `readonly`. | ||||||
|
||||||
It is a compile-time error to attempt to modify the state of an instance struct variable via a readonly indexer declared in that struct. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another case of "attempt" |
||||||
|
||||||
It is a compile-time error to have a `readonly` modifier on both the `get` and the `set` accessors. | ||||||
|
||||||
It is a compile-time error to have a `readonly` modifier on an indexer itself as well as on either of its `get` or `set` accessors. | ||||||
|
||||||
If the `get` accessor has a `readonly` modifier, the `set` shall exist and shall not have that modifier. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The resolution here should follow the same pattern chosen for the similar restriction on line 509 regarding properties. |
||||||
|
||||||
### §cands-diffs-events Events | ||||||
|
||||||
An *event_declaration* ([§15.8.1](classes.md#1581-general)) for an instance, non-field-like event in a *struct_declaration* may contain the *event_modifier* `readonly`. However, a static event shall not contain that modifier. | ||||||
|
||||||
### 16.4.12 Safe context constraint | ||||||
|
||||||
#### 16.4.12.1 General | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should specify that the
readonly
modifier can appear on an individual accessor (typicallyget
) as well. The most common forms would be:Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
~~Also, in this and several related bullets, I'm curious why the adverb "directly" is needed? ~~
Kalle is right. Ignore this comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The property_declaration of P is contained in the struct_declaration of S, but not directly, and
readonly
should not be allowed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meeting of 2025-05-14 - we think we probably need to add this, yes - we can't see where else it's specified.