Skip to content

Commit 9ba90cb

Browse files
committed
Describe the impact of ? on arrays of arrays
Alternative to #1386. Fixes #1385.
1 parent dda6edd commit 9ba90cb

2 files changed

Lines changed: 74 additions & 8 deletions

File tree

standard/arrays.md

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,64 @@ Every array type is a reference type ([§8.2](types.md#82-reference-types)). The
3131
3232
The grammar productions for array types are provided in [§8.2.1](types.md#821-general).
3333
34-
An array type is written as a *non_array_type* followed by one or more *rank_specifier*s.
34+
An array type is written as a *non_array_type* followed by one or more *rank_specifier*s, or an *array_type* followed by a *nullable_type_annotation* followed by one or more *rank_specifier*s. The latter production is used to represent array types where the element type is includes a nullable array type.
3535
3636
A *non_array_type* is any *type* that is not itself an *array_type*.
3737
38-
The rank of an array type is given by the leftmost *rank_specifier* in the *array_type*: A *rank_specifier* indicates that the array is an array with a rank of one plus the number of “`,`” tokens in the *rank_specifier*.
38+
When determining the rank and element type of array type as specified below, only the *rank_specifier*s in the top-most production are considered, so in the production `array_type nullable_type_annotation rank_specifier+`, any `rank_specifier` within the `array_type` is ignored.
3939
40-
The element type of an array type is the type that results from deleting the leftmost *rank_specifier*:
40+
The rank of an array type is given by the leftmost *rank_specifier* in the *array_type*: A *rank_specifier* indicates that the array is an array with a rank of one plus the number of “`,`” tokens in the *rank_specifier*.
4141
42-
- An array type of the form `T[R]` is an array with rank `R` and a non-array element type `T`.
43-
- An array type of the form `T[R][R₁]...[Rₓ]` is an array with rank `R` and an element type `T[R₁]...[Rₓ]`.
42+
The element type of an array type is the type that results from deleting the leftmost *rank_specifier*.
4443
4544
In effect, the *rank_specifier*s are read from left to right *before* the final non-array element type.
4645
47-
> *Example*: The type in `T[][,,][,]` is a single-dimensional array of three-dimensional arrays of two-dimensional arrays of `int`. *end example*
46+
> *Example*: The following code shows several variable declarations, including a mixture of single-dimensional arrays, multi-dimensional arrays, and arrays of arrays, with some using nullable reference types. In each case, the rank and element type is described, and then demonstrated with a second variable declaration which is initialized using an element access expression.
47+
>
48+
> <!-- Example: {template:"standalone-console-without-using", name:"ArraysOfArrays", replaceEllipsis:true, customEllipsisReplacements:["default!","default!","default!","default!","default!","default!","default!","default!","default!", "default!"], expectedException:"NullReferenceException"} -->
49+
> ```csharp
50+
> // Rank 1, element type int
51+
> int[] array1 = ...;
52+
> int element1 = array1[0];
53+
>
54+
> // Rank 2, element type int
55+
> int[,] array2 = ...;
56+
> int element2 = array2[0, 1];
57+
>
58+
> // Rank 1, element type int? (Nullable<int>)
59+
> int?[] array3 = ...;
60+
> int? element3 = array3[0];
61+
>
62+
> // Rank 1, element type string? (nullable string)
63+
> string?[] array4 = ...;
64+
> string? element4 = array4[0];
65+
>
66+
> // Rank 1, element type string[,,][,]
67+
> string[][,,][,] array5 = ...;
68+
> string[,,][,] element5 = array5[0];
69+
>
70+
> // Rank 1, element type string; the array itself is nullable
71+
> string[]? array6 = ...;
72+
> string element6 = array6?[0] ?? "";
73+
>
74+
> // Rank 1, element type string[,]?
75+
> string[,]?[] array7 = ...;
76+
> string[,]? element7 = array7[0];
77+
>
78+
> // Rank 3, element type int[]?[,]
79+
> int[]?[,,][,] array8 = ...;
80+
> int[]?[,] element8 = array8[0, 1, 2];
81+
>
82+
> // Rank 1, element type string[,]?[]?[,,]
83+
> string[,]?[]?[][,,] array9 = ...;
84+
> string[,]?[]?[,,] element9 = array9[0];
85+
>
86+
> // Rank 2, element type string[][][,,]
87+
> // Note that this appears the same as the array9 example above other
88+
> // than for the use of ? but the rank and element type are significantly different.
89+
> string[,][][][,,] array10 = ...;
90+
> string[][][,,] element10 = array10[0, 1];
91+
> ```
4892
4993
At run-time, a value of an array type can be `null` or a reference to an instance of that array type.
5094

standard/types.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,22 @@ interface_type
5454
;
5555
5656
array_type
57+
: array_type nullable_type_annotation rank_specifier+
5758
: non_array_type rank_specifier+
5859
;
5960
6061
non_array_type
62+
: non_array_non_nullable_type nullable_type_annotation?
63+
| pointer_type // unsafe code support
64+
;
65+
66+
non_array_non_nullable_type
6167
: value_type
6268
| class_type
6369
| interface_type
6470
| delegate_type
6571
| 'dynamic'
6672
| type_parameter
67-
| pointer_type // unsafe code support
6873
;
6974
7075
rank_specifier
@@ -732,7 +737,9 @@ There are two forms of nullability for reference types:
732737

733738
> *Note:* The types `R` and `R?` are represented by the same underlying type, `R`. A variable of that underlying type can either contain a reference to an object or be the value `null`, which indicates “no reference.” *end note*
734739
735-
The syntactic distinction between a *nullable reference type* and its corresponding *non-nullable reference type* enables a compiler to generate diagnostics. A compiler must allow the *nullable_type_annotation* as defined in [§8.2.1](types.md#821-general). The diagnostics must be limited to warnings. Neither the presence or absence of nullable annotations, nor the state of the nullable context can change the compile time or runtime behavior of a program except for changes in any diagnostic messages generated at compile time.
740+
The syntactic distinction between a *nullable reference type* and its corresponding *non-nullable reference type* enables a compiler to generate diagnostics. A compiler must allow the *nullable_type_annotation* as defined in [§8.2.1](types.md#821-general). The diagnostics must be limited to warnings. Other than in the meaning of array types, neither the presence or absence of nullable annotations, nor the state of the nullable context can change the compile time or runtime behavior of a program except for changes in any diagnostic messages generated at compile time.
741+
742+
The meaning of array types is significantly impacted by the presence of *nullable_type_annotation* within an *array_type*, as described in [§17.2.1](arrays.md#1721-general).
736743

737744
### 8.9.2 Non-nullable reference types
738745

@@ -756,6 +763,8 @@ Throughout this specification, all C# code that does not contain nullable direct
756763

757764
> *Note:* A nullable context where both flags are disabled matches the previous standard behavior for reference types. *end note*
758765
766+
The rank and element of an array type declared using *nullable_type_annotation* is not affected by the nullable context.
767+
759768
#### 8.9.4.2 Nullable disable
760769

761770
When both the warning and annotations flags are disabled, the nullable context is *disabled*.
@@ -1106,6 +1115,8 @@ A compiler may issue a warning when nullability annotations differ between two t
11061115
11071116
A compiler may follow rules for interface variance ([§19.2.3.3](interfaces.md#19233-variance-conversion)), delegate variance ([§21.4](delegates.md#214-delegate-compatibility)), and array covariance ([§17.6](arrays.md#176-array-covariance)) in determining whether to issue a warning for type conversions.
11081117
1118+
(See17.2.1](arrays.md#1721-general) for the specification of the corresponding non-nullable array type used in `M7` and `M8`.)
1119+
11091120
> <!-- Example: {template:"code-in-class-lib", name:"NullVariance", ignoredWarnings:["CS8619"]} -->
11101121
> ```csharp
11111122
> #nullable enable
@@ -1143,6 +1154,17 @@ A compiler may follow rules for interface variance ([§19.2.3.3](interfaces.md#1
11431154
> string[] v1 = p; // Warning
11441155
> string[] v2 = p!; // No warning
11451156
> }
1157+
>
1158+
> public void M7(string[][,] p)
1159+
> {
1160+
> string[,]?[] v1 = p; // No warning
1161+
> }
1162+
>
1163+
> public void M8(string[]?[,] p)
1164+
> {
1165+
> string[,][] v1 = p; // Warning
1166+
> string[,][] v2 = p!; // No warning
1167+
> }
11461168
> }
11471169
> ```
11481170
>

0 commit comments

Comments
 (0)