Skip to content

Commit ebd249b

Browse files
committed
Merge branch 'draft-v9' into merge-updates
2 parents 6f8e826 + af28ecb commit ebd249b

6 files changed

Lines changed: 137 additions & 18 deletions

File tree

.github/workflows/do-not-merge-label-check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- 'do not merge'
2323
steps:
2424
- name: Harden Runner
25-
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
25+
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
2626
with:
2727
egress-policy: audit
2828

standard/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,8 @@
673673
- [§16.2.5](structs.md#1625-struct-interfaces) Struct interfaces
674674
- [§16.2.6](structs.md#1626-struct-body) Struct body
675675
- [§16.3](structs.md#163-struct-members) Struct members
676+
- [§16.3.1](structs.md#1631-general) General
677+
- [§16.3.2](structs.md#1632-readonly-members) Readonly members
676678
- [§16.4](structs.md#164-class-and-struct-differences) Class and struct differences
677679
- [§16.4.1](structs.md#1641-general) General
678680
- [§16.4.2](structs.md#1642-value-semantics) Value semantics

standard/classes.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2143,7 +2143,7 @@ ref_method_modifier
21432143
| 'override'
21442144
| 'abstract'
21452145
| 'extern'
2146-
| 'readonly' // direct struct members only
2146+
| 'readonly' // struct members only
21472147
| unsafe_modifier // unsafe code support
21482148
;
21492149
@@ -3398,7 +3398,7 @@ property_modifier
33983398
| 'override'
33993399
| 'abstract'
34003400
| 'extern'
3401-
| 'readonly' // direct struct members only
3401+
| 'readonly' // struct members only
34023402
| unsafe_modifier // unsafe code support
34033403
;
34043404
@@ -3492,7 +3492,7 @@ accessor_modifier
34923492
| 'internal' 'protected'
34933493
| 'protected' 'private'
34943494
| 'private' 'protected'
3495-
| 'readonly' // direct struct members only
3495+
| 'readonly' // struct members only
34963496
;
34973497
34983498
accessor_body
@@ -4397,7 +4397,7 @@ event_modifier
43974397
| 'override'
43984398
| 'abstract'
43994399
| 'extern'
4400-
| 'readonly' // direct struct members only
4400+
| 'readonly' // struct members only
44014401
| unsafe_modifier // unsafe code support
44024402
;
44034403
@@ -4678,7 +4678,7 @@ indexer_modifier
46784678
| 'override'
46794679
| 'abstract'
46804680
| 'extern'
4681-
| 'readonly' // direct struct members only
4681+
| 'readonly' // struct members only
46824682
| unsafe_modifier // unsafe code support
46834683
;
46844684

standard/conversions.md

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -732,24 +732,99 @@ A user-defined explicit conversion from an expression `E` to a type `T` is pro
732732
- Find the set of types, `D`, from which user-defined conversion operators will be considered. This set consists of `S₀` (if `S₀` exists and is a class or struct), the base classes of `S₀` (if `S₀` exists and is a class), `T₀` (if `T₀` is a class or struct), and the base classes of `T₀` (if `T₀` is a class). A type is added to the set `D` only if an identity conversion to another type already included in the set doesnt exist.
733733
- Find the set of applicable user-defined and lifted conversion operators, `U`. This set consists of the user-defined and lifted implicit or explicit conversion operators declared by the classes or structs in `D` that convert from a type encompassing `E` or encompassed by `S` (if it exists) to a type encompassing or encompassed by `T`. If `U` is empty, the conversion is undefined and a compile-time error occurs.
734734
- Find the most-specific source type, `Sₓ`, of the operators in `U`:
735-
- If S exists and any of the operators in `U` convert from `S`, then `Sₓ` is `S`.
735+
- If `S` exists and any of the operators in `U` convert from `S`, then `Sₓ` is `S`.
736736
- Otherwise, if any of the operators in `U` convert from types that encompass `E`, then `Sₓ` is the most-encompassed type in the combined set of source types of those operators. If no most-encompassed type can be found, then the conversion is ambiguous and a compile-time error occurs.
737737
- Otherwise, `Sₓ` is the most-encompassing type in the combined set of source types of the operators in `U`. If exactly one most-encompassing type cannot be found, then the conversion is ambiguous and a compile-time error occurs.
738738
- Find the most-specific target type, `Tₓ`, of the operators in `U`:
739739
- If any of the operators in `U` convert to `T`, then `Tₓ` is `T`.
740740
- Otherwise, if any of the operators in `U` convert to types that are encompassed by `T`, then `Tₓ` is the most-encompassing type in the combined set of target types of those operators. If exactly one most-encompassing type cannot be found, then the conversion is ambiguous and a compile-time error occurs.
741741
- Otherwise, `Tₓ` is the most-encompassed type in the combined set of target types of the operators in `U`. If no most-encompassed type can be found, then the conversion is ambiguous and a compile-time error occurs.
742742
- Find the most-specific conversion operator:
743-
- If U contains exactly one user-defined conversion operator that converts from `Sₓ` to `Tₓ`, then this is the most-specific conversion operator.
743+
- If `U` contains exactly one user-defined conversion operator that converts from `Sₓ` to `Tₓ`, then this is the most-specific conversion operator.
744744
- Otherwise, if `U` contains exactly one lifted conversion operator that converts from `Sₓ` to `Tₓ`, then this is the most-specific conversion operator.
745745
- Otherwise, the conversion is ambiguous and a compile-time error occurs.
746-
- Finally, apply the conversion:
747-
- If `E` does not already have the type `Sₓ`, then a standard explicit conversion from E to `Sₓ` is performed.
748-
- The most-specific user-defined conversion operator is invoked to convert from `Sₓ` to `Tₓ`.
749-
- If `Tₓ` is not `T`, then a standard explicit conversion from `Tₓ` to `T` is performed.
746+
- Finally, determine the runtime conversions required, in order:
747+
- If `E` does not already have the type `Sₓ`, then a standard explicit conversion from `E` to `Sₓ` is required.
748+
- If this added standard explicit conversion is an explicit numeric ([§10.3.2](conversions.md#1032-explicit-numeric-conversions)) or enumeration ([§10.3.3](conversions.md#1033-explicit-enumeration-conversions)) one which may result in information loss ([§10.3.2](conversions.md#1032-explicit-numeric-conversions)), and the context is `unchecked` ([§12.8.20](expressions.md#12820-the-checked-and-unchecked-operators), [§13.12](statements.md#1312-the-checked-and-unchecked-statements)), then a compile-time warning shall be issued.<br>*Note*: In a `checked` context information loss would result in a runtime exception, hence no warning is required. If information loss is intended adding an explicit cast to the source will document this and silence the warning. *end note*
749+
- The most-specific user-defined conversion operator is required to convert from `Sₓ` to `Tₓ`.
750+
- If `Tₓ` is not `T`, then a standard explicit conversion from `Tₓ` to `T` is required.
750751
751752
A user-defined explicit conversion from a type `S` to a type `T` exists if a user-defined explicit conversion exists from a variable of type `S` to `T`.
752753
754+
> *Example*:
755+
>
756+
> The user-defined type `Dose` is used by the examples below.
757+
> Note that for doses greater than `MaxDosage` the user-defined type `LargeDose` is mentioned,
758+
> however it is not otherwise used and no source is given.
759+
>
760+
> ```csharp
761+
> public readonly struct Dose
762+
> {
763+
> private readonly ushort dose;
764+
>
765+
> public Dose(ushort dose)
766+
> {
767+
> const ushort MaxDosage = 1000; // mg
768+
>
769+
> if (dose > MaxDosage)
770+
> {
771+
> throw new ArgumentOutOfRangeException
772+
> ( $"Dosage too large (> {MaxDosage}mg)."
773+
> + " Consider LargeDose."
774+
> );
775+
> }
776+
> this.dose = dose;
777+
> }
778+
>
779+
> public static explicit operator Dose(ushort b) => new Dose(b);
780+
>
781+
> public override string ToString() => $"{dose}mg";
782+
>
783+
> // other methods
784+
> }
785+
> ```
786+
>
787+
>
788+
> The following examples illustrate the above rules for explicit casting involving user-defined conversions:
789+
>
790+
> ```csharp
791+
> ushort amt0 = 500; // ≤ MaxDosage
792+
> var dose0 = (Dose)amt0; // amt0 is ushort, no additional conversions added
793+
> Console.WriteLine(dose0); // outputs 500mg
794+
>
795+
> byte amt1 = 250; // ≤ MaxDosage
796+
> var dose1 = (Dose)amt1; // amt0 is byte, byte to ushort conversion added
797+
> // this does not risk information loss, no warning
798+
> Console.WriteLine(dose1); // outputs 250mg
799+
>
800+
> var amt3 = 500; // > MaxDosage, var types amt3 as int
801+
> var dose3 = (Dose)amt3; // amt3 is int, int to ushort conversion added
802+
> // warning as information loss may occur
803+
> Console.WriteLine(dose3); // outputs 500mg, no actual information loss
804+
>
805+
> ushort amt4 = 1500; // > MaxDosage, requires LargeDose
806+
> var dose4 = (Dose)amt4; // amt4 is ushort, no additional conversions added
807+
> // throws ArgumentOutOfRangeException
808+
>
809+
> var amt5 = 1500; // > MaxDosage, var types amt5 as int
810+
> var dose5 = (Dose)amt5; // amt5 is int, int to ushort conversion added
811+
> // warning as information loss may occur
812+
> // no actual information loss but out of range
813+
> // throws ArgumentOutOfRangeException
814+
>
815+
> var amt6 = 66036; // var types amt6 as int
816+
> var dose6 = (Dose)amt6; // amt3 is int, int to ushort conversion added
817+
> // warning as information loss may occur
818+
> Console.WriteLine(dose6); // outputs 500mg, not 66036mg, due to information loss
819+
>
820+
> // Using a constructor instead of user-defined conversion:
821+
>
822+
> var dose7 = new Dose(amt6); // amt6 is int, constructor requires ushort
823+
> // compile time type error
824+
> ```
825+
>
826+
> *end example*
827+
753828
## 10.6 Conversions involving nullable types
754829
755830
### 10.6.1 Nullable Conversions

standard/grammar.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,7 +2247,7 @@ ref_method_modifier
22472247
| 'override'
22482248
| 'abstract'
22492249
| 'extern'
2250-
| 'readonly' // direct struct members only
2250+
| 'readonly' // struct members only
22512251
| unsafe_modifier // unsafe code support
22522252
;
22532253
@@ -2335,7 +2335,7 @@ property_modifier
23352335
| 'override'
23362336
| 'abstract'
23372337
| 'extern'
2338-
| 'readonly' // direct struct members only
2338+
| 'readonly' // struct members only
23392339
| unsafe_modifier // unsafe code support
23402340
;
23412341
@@ -2381,7 +2381,7 @@ accessor_modifier
23812381
| 'internal' 'protected'
23822382
| 'protected' 'private'
23832383
| 'private' 'protected'
2384-
| 'readonly' // direct struct members only
2384+
| 'readonly' // struct members only
23852385
;
23862386
23872387
accessor_body
@@ -2419,7 +2419,7 @@ event_modifier
24192419
| 'override'
24202420
| 'abstract'
24212421
| 'extern'
2422-
| 'readonly' // direct struct members only
2422+
| 'readonly' // struct members only
24232423
| unsafe_modifier // unsafe code support
24242424
;
24252425
@@ -2453,7 +2453,7 @@ indexer_modifier
24532453
| 'override'
24542454
| 'abstract'
24552455
| 'extern'
2456-
| 'readonly' // direct struct members only
2456+
| 'readonly' // struct members only
24572457
| unsafe_modifier // unsafe code support
24582458
;
24592459
@@ -2617,7 +2617,7 @@ struct_body
26172617
: '{' struct_member_declaration* '}'
26182618
;
26192619
2620-
// Source: §16.3 Struct members
2620+
// Source: §16.3.1 General
26212621
struct_member_declaration
26222622
: constant_declaration
26232623
| field_declaration

standard/structs.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ struct_body
114114

115115
## 16.3 Struct members
116116

117+
### 16.3.1 General
118+
117119
The members of a struct consist of the members introduced by its *struct_member_declaration*s and the members inherited from the type `System.ValueType`.
118120

119121
```ANTLR
@@ -138,6 +140,46 @@ struct_member_declaration
138140
139141
Except for the differences noted in [§16.4](structs.md#164-class-and-struct-differences), the descriptions of class members provided in [§15.3](classes.md#153-class-members) through [§15.12](classes.md#1512-static-constructors) apply to struct members as well.
140142

143+
### 16.3.2 Readonly members
144+
145+
An instance member definition or accessor of an instance property, indexer, or event that includes the `readonly` modifier has the following restrictions:
146+
147+
- The `this` parameter is a `ref readonly` reference.
148+
- The member shall not reassign the value of `this` or an instance field of the receiver.
149+
- The member shall not reassign the value of an instance field-like event ([§15.8.2](classes.md#1582-field-like-events)) of the receiver.
150+
- If a readonly member invokes a non-readonly member, the structure referred to by `this` must be copied to use a writable reference for the `this` argument.
151+
152+
> *Note:* Instance fields include the hidden backing field used for automatically implemented properties ([§15.7.4](classes.md#1574-automatically-implemented-properties)). *end note*
153+
<!-- markdownlint-disable MD028 -->
154+
155+
<!-- markdownlint-enable MD028 -->
156+
> *Example*: A readonly member can modify the state of an object referred to by an instance field, even though the readonly member can’t reassign that instance member. The following code demonstrates the reassigning and modifying an instance field:
157+
>
158+
> <!-- Example: {template:"standalone-lib-without-using", name:"ReadonlyMember" } -->
159+
> ```csharp
160+
> public struct S
161+
> {
162+
> private List<string> messages;
163+
>
164+
> public S(IEnumerable<string> messages) =>
165+
> this.messages = new List<string>(messages);
166+
>
167+
> public void InitializeMessages() =>
168+
> messages = new List<string>();
169+
>
170+
> public readonly void AddMessage(string message)
171+
> {
172+
> if (messages == null)
173+
> {
174+
> throw new InvalidOperationException("Messages collection is not initialized.");
175+
> }
176+
> messages.Add(message);
177+
> }
178+
> }
179+
> ```
180+
>
181+
> The `readonly` method `AddMessage` can change the state of a message list. The `InitializeMessages` member can clear and re-initialize the list of messages. In the case of `AddMessage`, the `readonly` modifier is valid. In the case of `InitializeMessages`, adding the `readonly` modifier is invalid. *end example*
182+
141183
## 16.4 Class and struct differences
142184
143185
### 16.4.1 General

0 commit comments

Comments
 (0)