Skip to content

Commit af28ecb

Browse files
committed
Merge branch 'draft-v8' into draft-v9
2 parents 5429e2e + a0995e5 commit af28ecb

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
@@ -650,6 +650,8 @@
650650
- [§16.2.5](structs.md#1625-struct-interfaces) Struct interfaces
651651
- [§16.2.6](structs.md#1626-struct-body) Struct body
652652
- [§16.3](structs.md#163-struct-members) Struct members
653+
- [§16.3.1](structs.md#1631-general) General
654+
- [§16.3.2](structs.md#1632-readonly-members) Readonly members
653655
- [§16.4](structs.md#164-class-and-struct-differences) Class and struct differences
654656
- [§16.4.1](structs.md#1641-general) General
655657
- [§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
@@ -2100,7 +2100,7 @@ ref_method_modifier
21002100
| 'override'
21012101
| 'abstract'
21022102
| 'extern'
2103-
| 'readonly' // direct struct members only
2103+
| 'readonly' // struct members only
21042104
| unsafe_modifier // unsafe code support
21052105
;
21062106
@@ -3315,7 +3315,7 @@ property_modifier
33153315
| 'override'
33163316
| 'abstract'
33173317
| 'extern'
3318-
| 'readonly' // direct struct members only
3318+
| 'readonly' // struct members only
33193319
| unsafe_modifier // unsafe code support
33203320
;
33213321
@@ -3401,7 +3401,7 @@ accessor_modifier
34013401
| 'internal' 'protected'
34023402
| 'protected' 'private'
34033403
| 'private' 'protected'
3404-
| 'readonly' // direct struct members only
3404+
| 'readonly' // struct members only
34053405
;
34063406
34073407
accessor_body
@@ -4061,7 +4061,7 @@ event_modifier
40614061
| 'override'
40624062
| 'abstract'
40634063
| 'extern'
4064-
| 'readonly' // direct struct members only
4064+
| 'readonly' // struct members only
40654065
| unsafe_modifier // unsafe code support
40664066
;
40674067
@@ -4342,7 +4342,7 @@ indexer_modifier
43424342
| 'override'
43434343
| 'abstract'
43444344
| 'extern'
4345-
| 'readonly' // direct struct members only
4345+
| 'readonly' // struct members only
43464346
| unsafe_modifier // unsafe code support
43474347
;
43484348

standard/conversions.md

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -709,24 +709,99 @@ A user-defined explicit conversion from an expression `E` to a type `T` is pro
709709
- 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.
710710
- 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.
711711
- Find the most-specific source type, `Sₓ`, of the operators in `U`:
712-
- If S exists and any of the operators in `U` convert from `S`, then `Sₓ` is `S`.
712+
- If `S` exists and any of the operators in `U` convert from `S`, then `Sₓ` is `S`.
713713
- 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.
714714
- 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.
715715
- Find the most-specific target type, `Tₓ`, of the operators in `U`:
716716
- If any of the operators in `U` convert to `T`, then `Tₓ` is `T`.
717717
- 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.
718718
- 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.
719719
- Find the most-specific conversion operator:
720-
- If U contains exactly one user-defined conversion operator that converts from `Sₓ` to `Tₓ`, then this is the most-specific conversion operator.
720+
- If `U` contains exactly one user-defined conversion operator that converts from `Sₓ` to `Tₓ`, then this is the most-specific conversion operator.
721721
- Otherwise, if `U` contains exactly one lifted conversion operator that converts from `Sₓ` to `Tₓ`, then this is the most-specific conversion operator.
722722
- Otherwise, the conversion is ambiguous and a compile-time error occurs.
723-
- Finally, apply the conversion:
724-
- If `E` does not already have the type `Sₓ`, then a standard explicit conversion from E to `Sₓ` is performed.
725-
- The most-specific user-defined conversion operator is invoked to convert from `Sₓ` to `Tₓ`.
726-
- If `Tₓ` is not `T`, then a standard explicit conversion from `Tₓ` to `T` is performed.
723+
- Finally, determine the runtime conversions required, in order:
724+
- If `E` does not already have the type `Sₓ`, then a standard explicit conversion from `E` to `Sₓ` is required.
725+
- 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*
726+
- The most-specific user-defined conversion operator is required to convert from `Sₓ` to `Tₓ`.
727+
- If `Tₓ` is not `T`, then a standard explicit conversion from `Tₓ` to `T` is required.
727728
728729
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`.
729730
731+
> *Example*:
732+
>
733+
> The user-defined type `Dose` is used by the examples below.
734+
> Note that for doses greater than `MaxDosage` the user-defined type `LargeDose` is mentioned,
735+
> however it is not otherwise used and no source is given.
736+
>
737+
> ```csharp
738+
> public readonly struct Dose
739+
> {
740+
> private readonly ushort dose;
741+
>
742+
> public Dose(ushort dose)
743+
> {
744+
> const ushort MaxDosage = 1000; // mg
745+
>
746+
> if (dose > MaxDosage)
747+
> {
748+
> throw new ArgumentOutOfRangeException
749+
> ( $"Dosage too large (> {MaxDosage}mg)."
750+
> + " Consider LargeDose."
751+
> );
752+
> }
753+
> this.dose = dose;
754+
> }
755+
>
756+
> public static explicit operator Dose(ushort b) => new Dose(b);
757+
>
758+
> public override string ToString() => $"{dose}mg";
759+
>
760+
> // other methods
761+
> }
762+
> ```
763+
>
764+
>
765+
> The following examples illustrate the above rules for explicit casting involving user-defined conversions:
766+
>
767+
> ```csharp
768+
> ushort amt0 = 500; // ≤ MaxDosage
769+
> var dose0 = (Dose)amt0; // amt0 is ushort, no additional conversions added
770+
> Console.WriteLine(dose0); // outputs 500mg
771+
>
772+
> byte amt1 = 250; // ≤ MaxDosage
773+
> var dose1 = (Dose)amt1; // amt0 is byte, byte to ushort conversion added
774+
> // this does not risk information loss, no warning
775+
> Console.WriteLine(dose1); // outputs 250mg
776+
>
777+
> var amt3 = 500; // > MaxDosage, var types amt3 as int
778+
> var dose3 = (Dose)amt3; // amt3 is int, int to ushort conversion added
779+
> // warning as information loss may occur
780+
> Console.WriteLine(dose3); // outputs 500mg, no actual information loss
781+
>
782+
> ushort amt4 = 1500; // > MaxDosage, requires LargeDose
783+
> var dose4 = (Dose)amt4; // amt4 is ushort, no additional conversions added
784+
> // throws ArgumentOutOfRangeException
785+
>
786+
> var amt5 = 1500; // > MaxDosage, var types amt5 as int
787+
> var dose5 = (Dose)amt5; // amt5 is int, int to ushort conversion added
788+
> // warning as information loss may occur
789+
> // no actual information loss but out of range
790+
> // throws ArgumentOutOfRangeException
791+
>
792+
> var amt6 = 66036; // var types amt6 as int
793+
> var dose6 = (Dose)amt6; // amt3 is int, int to ushort conversion added
794+
> // warning as information loss may occur
795+
> Console.WriteLine(dose6); // outputs 500mg, not 66036mg, due to information loss
796+
>
797+
> // Using a constructor instead of user-defined conversion:
798+
>
799+
> var dose7 = new Dose(amt6); // amt6 is int, constructor requires ushort
800+
> // compile time type error
801+
> ```
802+
>
803+
> *end example*
804+
730805
## 10.6 Conversions involving nullable types
731806
732807
### 10.6.1 Nullable Conversions

standard/grammar.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,7 +2175,7 @@ ref_method_modifier
21752175
| 'override'
21762176
| 'abstract'
21772177
| 'extern'
2178-
| 'readonly' // direct struct members only
2178+
| 'readonly' // struct members only
21792179
| unsafe_modifier // unsafe code support
21802180
;
21812181
@@ -2259,7 +2259,7 @@ property_modifier
22592259
| 'override'
22602260
| 'abstract'
22612261
| 'extern'
2262-
| 'readonly' // direct struct members only
2262+
| 'readonly' // struct members only
22632263
| unsafe_modifier // unsafe code support
22642264
;
22652265
@@ -2299,7 +2299,7 @@ accessor_modifier
22992299
| 'internal' 'protected'
23002300
| 'protected' 'private'
23012301
| 'private' 'protected'
2302-
| 'readonly' // direct struct members only
2302+
| 'readonly' // struct members only
23032303
;
23042304
23052305
accessor_body
@@ -2337,7 +2337,7 @@ event_modifier
23372337
| 'override'
23382338
| 'abstract'
23392339
| 'extern'
2340-
| 'readonly' // direct struct members only
2340+
| 'readonly' // struct members only
23412341
| unsafe_modifier // unsafe code support
23422342
;
23432343
@@ -2371,7 +2371,7 @@ indexer_modifier
23712371
| 'override'
23722372
| 'abstract'
23732373
| 'extern'
2374-
| 'readonly' // direct struct members only
2374+
| 'readonly' // struct members only
23752375
| unsafe_modifier // unsafe code support
23762376
;
23772377
@@ -2535,7 +2535,7 @@ struct_body
25352535
: '{' struct_member_declaration* '}'
25362536
;
25372537
2538-
// Source: §16.3 Struct members
2538+
// Source: §16.3.1 General
25392539
struct_member_declaration
25402540
: constant_declaration
25412541
| 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)