Skip to content

Commit dced827

Browse files
committed
Merge branch 'draft-10' into merge-updates
2 parents 05c3b5c + 789326c commit dced827

8 files changed

Lines changed: 177 additions & 62 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

.github/workflows/update-on-merge.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
shell: bash
5757

5858
- name: Create pull request
59-
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
59+
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
6060
if: ${{ steps.renumber-sections.outputs.status }} == 'success' && ${{ steps.update-grammar.outputs.status }} == 'success'
6161
with:
6262
title: "Automated Section renumber and grammar extraction"

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
@@ -2145,7 +2145,7 @@ ref_method_modifier
21452145
| 'override'
21462146
| 'abstract'
21472147
| 'extern'
2148-
| 'readonly' // direct struct members only
2148+
| 'readonly' // struct members only
21492149
| unsafe_modifier // unsafe code support
21502150
;
21512151
@@ -3400,7 +3400,7 @@ property_modifier
34003400
| 'override'
34013401
| 'abstract'
34023402
| 'extern'
3403-
| 'readonly' // direct struct members only
3403+
| 'readonly' // struct members only
34043404
| unsafe_modifier // unsafe code support
34053405
;
34063406
@@ -3494,7 +3494,7 @@ accessor_modifier
34943494
| 'internal' 'protected'
34953495
| 'protected' 'private'
34963496
| 'private' 'protected'
3497-
| 'readonly' // direct struct members only
3497+
| 'readonly' // struct members only
34983498
;
34993499
35003500
accessor_body
@@ -4399,7 +4399,7 @@ event_modifier
43994399
| 'override'
44004400
| 'abstract'
44014401
| 'extern'
4402-
| 'readonly' // direct struct members only
4402+
| 'readonly' // struct members only
44034403
| unsafe_modifier // unsafe code support
44044404
;
44054405
@@ -4680,7 +4680,7 @@ indexer_modifier
46804680
| 'override'
46814681
| 'abstract'
46824682
| 'extern'
4683-
| 'readonly' // direct struct members only
4683+
| 'readonly' // struct members only
46844684
| unsafe_modifier // unsafe code support
46854685
;
46864686

standard/conversions.md

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -772,24 +772,99 @@ A user-defined explicit conversion from an expression `E` to a type `T` is pro
772772
- 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.
773773
- 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.
774774
- Find the most-specific source type, `Sₓ`, of the operators in `U`:
775-
- If S exists and any of the operators in `U` convert from `S`, then `Sₓ` is `S`.
775+
- If `S` exists and any of the operators in `U` convert from `S`, then `Sₓ` is `S`.
776776
- 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.
777777
- 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.
778778
- Find the most-specific target type, `Tₓ`, of the operators in `U`:
779779
- If any of the operators in `U` convert to `T`, then `Tₓ` is `T`.
780780
- 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.
781781
- 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.
782782
- Find the most-specific conversion operator:
783-
- If U contains exactly one user-defined conversion operator that converts from `Sₓ` to `Tₓ`, then this is the most-specific conversion operator.
783+
- If `U` contains exactly one user-defined conversion operator that converts from `Sₓ` to `Tₓ`, then this is the most-specific conversion operator.
784784
- Otherwise, if `U` contains exactly one lifted conversion operator that converts from `Sₓ` to `Tₓ`, then this is the most-specific conversion operator.
785785
- Otherwise, the conversion is ambiguous and a compile-time error occurs.
786-
- Finally, apply the conversion:
787-
- If `E` does not already have the type `Sₓ`, then a standard explicit conversion from E to `Sₓ` is performed.
788-
- The most-specific user-defined conversion operator is invoked to convert from `Sₓ` to `Tₓ`.
789-
- If `Tₓ` is not `T`, then a standard explicit conversion from `Tₓ` to `T` is performed.
786+
- Finally, determine the runtime conversions required, in order:
787+
- If `E` does not already have the type `Sₓ`, then a standard explicit conversion from `E` to `Sₓ` is required.
788+
- 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*
789+
- The most-specific user-defined conversion operator is required to convert from `Sₓ` to `Tₓ`.
790+
- If `Tₓ` is not `T`, then a standard explicit conversion from `Tₓ` to `T` is required.
790791
791792
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`.
792793
794+
> *Example*:
795+
>
796+
> The user-defined type `Dose` is used by the examples below.
797+
> Note that for doses greater than `MaxDosage` the user-defined type `LargeDose` is mentioned,
798+
> however it is not otherwise used and no source is given.
799+
>
800+
> ```csharp
801+
> public readonly struct Dose
802+
> {
803+
> private readonly ushort dose;
804+
>
805+
> public Dose(ushort dose)
806+
> {
807+
> const ushort MaxDosage = 1000; // mg
808+
>
809+
> if (dose > MaxDosage)
810+
> {
811+
> throw new ArgumentOutOfRangeException
812+
> ( $"Dosage too large (> {MaxDosage}mg)."
813+
> + " Consider LargeDose."
814+
> );
815+
> }
816+
> this.dose = dose;
817+
> }
818+
>
819+
> public static explicit operator Dose(ushort b) => new Dose(b);
820+
>
821+
> public override string ToString() => $"{dose}mg";
822+
>
823+
> // other methods
824+
> }
825+
> ```
826+
>
827+
>
828+
> The following examples illustrate the above rules for explicit casting involving user-defined conversions:
829+
>
830+
> ```csharp
831+
> ushort amt0 = 500; // ≤ MaxDosage
832+
> var dose0 = (Dose)amt0; // amt0 is ushort, no additional conversions added
833+
> Console.WriteLine(dose0); // outputs 500mg
834+
>
835+
> byte amt1 = 250; // ≤ MaxDosage
836+
> var dose1 = (Dose)amt1; // amt0 is byte, byte to ushort conversion added
837+
> // this does not risk information loss, no warning
838+
> Console.WriteLine(dose1); // outputs 250mg
839+
>
840+
> var amt3 = 500; // > MaxDosage, var types amt3 as int
841+
> var dose3 = (Dose)amt3; // amt3 is int, int to ushort conversion added
842+
> // warning as information loss may occur
843+
> Console.WriteLine(dose3); // outputs 500mg, no actual information loss
844+
>
845+
> ushort amt4 = 1500; // > MaxDosage, requires LargeDose
846+
> var dose4 = (Dose)amt4; // amt4 is ushort, no additional conversions added
847+
> // throws ArgumentOutOfRangeException
848+
>
849+
> var amt5 = 1500; // > MaxDosage, var types amt5 as int
850+
> var dose5 = (Dose)amt5; // amt5 is int, int to ushort conversion added
851+
> // warning as information loss may occur
852+
> // no actual information loss but out of range
853+
> // throws ArgumentOutOfRangeException
854+
>
855+
> var amt6 = 66036; // var types amt6 as int
856+
> var dose6 = (Dose)amt6; // amt3 is int, int to ushort conversion added
857+
> // warning as information loss may occur
858+
> Console.WriteLine(dose6); // outputs 500mg, not 66036mg, due to information loss
859+
>
860+
> // Using a constructor instead of user-defined conversion:
861+
>
862+
> var dose7 = new Dose(amt6); // amt6 is int, constructor requires ushort
863+
> // compile time type error
864+
> ```
865+
>
866+
> *end example*
867+
793868
## 10.6 Conversions involving nullable types
794869
795870
### 10.6.1 Nullable Conversions

standard/expressions.md

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,12 +1734,12 @@ A *deconstruction_expression* `var (e1, ..., en)` is shorthand for the *tuple_ex
17341734
17351735
A tuple expression has a type if and only if each of its element expressions `Ei` has a type `Ti`. The type shall be a tuple type of the same arity as the tuple expression, where each element is given by the following:
17361736
1737-
- If the tuple element in the corresponding position has a name `Ni`, then the tuple type element shall be `Ti Ni`.
1738-
- Otherwise, if `Ei` is of the form `Ni` or `E.Ni` or `E?.Ni` then the tuple type element shall be `Ti Ni`, *unless* any of the following holds:
1739-
- Another element of the tuple expression has the name `Ni`, or
1740-
- Another tuple element without a name has a tuple element expression of the form `Ni` or `E.Ni` or `E?.Ni`, or
1741-
- `Ni` is of the form `ItemX`, where `X` is a sequence of non-`0`-initiated decimal digits that could represent the position of a tuple element, and `X` does not represent the position of the element.
1742-
- Otherwise, the tuple type element shall be `Ti`.
1737+
- If the tuple element in the corresponding position has a name `N`, then the tuple type element shall be `TN`.
1738+
- Otherwise, if `E` is of the form `N` or `E.N` or `E?.N` then the tuple type element shall be `TN`, *unless* any of the following holds:
1739+
- Another element of the tuple expression has the name `N`, or
1740+
- Another tuple element without a name has a tuple element expression of the form `N` or `E.N` or `E?.N`, or
1741+
- `N` is of the form `ItemX`, where `X` is a sequence of non-`0`-initiated decimal digits that could represent the position of a tuple element, and `X` does not represent the position of the element.
1742+
- Otherwise, the tuple type element shall be `T`.
17431743
17441744
A tuple expression is evaluated by evaluating each of its element expressions in order from left to right.
17451745
@@ -4588,7 +4588,7 @@ equality_expression
45884588
<!-- markdownlint-disable MD028 -->
45894589

45904590
<!-- markdownlint-enable MD028 -->
4591-
> *Note*: There is a grammar ambiguity between *type* and *constant_pattern* in a `relational_expression` on the right-hand-side of `is`; either might be a valid parse of a qualified identifier. In such a case, only if it fails to bind as a type (for compatibility with previous versions of the language), is it resolved to be the first thing found (which must be either a constant or a type). This ambiguity is only present on the right-hand side of such an expression.
4591+
> *Note*: There is a grammar ambiguity between *type* and *constant_pattern* in a `relational_expression` on the right-hand-side of `is`; either might be a valid parse of a qualified identifier. In such a case, only if it fails to bind as a type (for compatibility with previous versions of the language), is it resolved to be the first thing found (which must be either a constant or a type). This ambiguity is only present on the right-hand side of such an expression. *end note*
45924592
45934593
The `is` operator is described in [§12.15.12](expressions.md#121512-the-is-operator) and the `as` operator is described in [§12.15.13](expressions.md#121513-the-as-operator).
45944594

@@ -4923,16 +4923,16 @@ The tuple equality operators are applied pairwise to the elements of the tuple o
49234923

49244924
If each operand `x` and `y` of a `==` or `!=` operator is classified either as a tuple or as a value with a tuple type ([§8.3.11](types.md#8311-tuple-types)), the operator is a *tuple equality operator*.
49254925

4926-
If an operand `e` is classified as a tuple, the elements `e1...en` shall be the results of evaluating the element expressions of the tuple expression. Otherwise if `e` is a value of a tuple type, the elements shall be `t.Item1...t.Itemn` where `t` is the result of evaluating `e`.
4926+
If an operand `e` is classified as a tuple, the elements `e₁...eₙ` shall be the results of evaluating the element expressions of the tuple expression. Otherwise if `e` is a value of a tuple type, the elements shall be `t.Item1...t.Itemn` where `t` is the result of evaluating `e`.
49274927

4928-
The operands `x` and `y` of a tuple equality operator shall have the same arity, or a compile time error occurs. For each pair of elements `xi` and `yi`, the same equality operator shall apply, and shall yield a result of type `bool`, `dynamic`, a type that has an implicit conversion to `bool`, or a type that defines the `true` and `false` operators.
4928+
The operands `x` and `y` of a tuple equality operator shall have the same arity, or a compile time error occurs. For each pair of elements `xᵢ` and `yᵢ`, the same equality operator shall apply, and shall yield a result of type `bool`, `dynamic`, a type that has an implicit conversion to `bool`, or a type that defines the `true` and `false` operators.
49294929

49304930
The tuple equality operator `x == y` is evaluated as follows:
49314931

49324932
- The left side operand `x` is evaluated.
49334933
- The right side operand `y` is evaluated.
4934-
- For each pair of elements `xi` and `yi` in lexical order:
4935-
- The operator `xi == yi` is evaluated, and a result of type `bool` is obtained in the following way:
4934+
- For each pair of elements `xᵢ` and `yᵢ` in lexical order:
4935+
- The operator `xᵢ == yᵢ` is evaluated, and a result of type `bool` is obtained in the following way:
49364936
- If the comparison yielded a `bool` then that is the result.
49374937
- Otherwise if the comparison yielded a `dynamic` then the operator `false` is dynamically invoked on it, and the resulting `bool` value is negated with the logical negation operator (`!`).
49384938
- Otherwise, if the type of the comparison has an implicit conversion to `bool`, that conversion is applied.
@@ -4944,8 +4944,8 @@ The tuple equality operator `x != y` is evaluated as follows:
49444944

49454945
- The left side operand `x` is evaluated.
49464946
- The right side operand `y` is evaluated.
4947-
- For each pair of elements `xi` and `yi` in lexical order:
4948-
- The operator `xi != yi` is evaluated, and a result of type `bool` is obtained in the following way:
4947+
- For each pair of elements `xᵢ` and `yᵢ` in lexical order:
4948+
- The operator `xᵢ != yᵢ` is evaluated, and a result of type `bool` is obtained in the following way:
49494949
- If the comparison yielded a `bool` then that is the result.
49504950
- Otherwise if the comparison yielded a `dynamic` then the operator `true` is dynamically invoked on it, and the resulting `bool` value is the result.
49514951
- Otherwise, if the type of the comparison has an implicit conversion to `bool`, that conversion is applied.
@@ -7080,24 +7080,25 @@ When a property or indexer declared in a *struct_type* is the target of an assig
70807080
> ```csharp
70817081
> struct Point
70827082
> {
7083-
> int x, y;
7083+
> int x, y;
70847084
>
7085-
> public Point(int x, int y)
7086-
> {
7087-
> this.x = x;
7088-
> this.y = y;
7089-
> }
7085+
> public Point(int x, int y)
7086+
> {
7087+
> this.x = x;
7088+
> this.y = y;
7089+
> }
70907090
>
7091-
> public int X
7092-
> {
7093-
> get { return x; }
7094-
> set { x = value; }
7095-
> }
7091+
> public int X
7092+
> {
7093+
> get { return x; }
7094+
> set { x = value; }
7095+
> }
70967096
>
7097-
> public int Y {
7098-
> get { return y; }
7099-
> set { y = value; }
7100-
> }
7097+
> public int Y
7098+
> {
7099+
> get { return y; }
7100+
> set { y = value; }
7101+
> }
71017102
> }
71027103
>
71037104
> struct Rectangle
@@ -7181,17 +7182,18 @@ The ref assignment operator shall not read the storage location referenced by th
71817182
> public static ref readonly int M3() { ... }
71827183
> public static void Test()
71837184
> {
7184-
> int v = 42;
7185-
> ref int r1 = ref v; // OK, r1 refers to v, which has value 42
7186-
> r1 = ref M1(); // Error; M1 returns a value, not a reference
7187-
> r1 = ref M2(); // OK; makes an alias
7188-
> r1 = ref M2u(); // Error; lhs and rhs have different types
7189-
> r1 = ref M3(); // error; M3 returns a ref readonly, which r1 cannot honor
7190-
> ref readonly int r2 = ref v; // OK; make readonly alias to ref
7191-
> r2 = ref M2(); // OK; makes an alias, adding read-only protection
7192-
> r2 = ref M3(); // OK; makes an alias and honors the read-only
7193-
> r2 = ref (r1 = ref M2()); // OK; r1 is an alias to a writable variable,
7194-
> // r2 is an alias (with read-only access) to the same variable
7185+
> int v = 42;
7186+
> ref int r1 = ref v; // OK, r1 refers to v, which has value 42
7187+
> r1 = ref M1(); // Error; M1 returns a value, not a reference
7188+
> r1 = ref M2(); // OK; makes an alias
7189+
> r1 = ref M2u(); // Error; lhs and rhs have different types
7190+
> r1 = ref M3(); // error; M3 returns a ref readonly, which r1 cannot honor
7191+
> ref readonly int r2 = ref v; // OK; make readonly alias to ref
7192+
> r2 = ref M2(); // OK; makes an alias, adding read-only protection
7193+
> r2 = ref M3(); // OK; makes an alias and honors the read-only
7194+
> r2 = ref (r1 = ref M2()); // OK; r1 is an alias to a writable variable,
7195+
> // r2 is an alias (with read-only access)
7196+
> // to the same variable
71957197
> }
71967198
> ```
71977199
>

0 commit comments

Comments
 (0)