-
Notifications
You must be signed in to change notification settings - Fork 92
Allow arrays of nullable reference types #1386
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?
Conversation
| array_type | ||
| : non_array_type rank_specifier+ | ||
| : array_type nullable_type_annotation rank_specifier+ | ||
| | non_array_type rank_specifier+ |
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.
This has the property that when parsing string?[][,]?[,,][,,,]? we end up with two array_type nodes: one being string?[][,]?[,,][,,,], and one being the inner string?[][,].
@Nigel-Ecma It's not mutual left recursion! 😁
…gnored by compilers
Nigel-Ecma
left a 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.
Nothing wrong with being concise; however unfortunately some of the conciseness is in the wrong places, and there are concerns this isn’t complete coverage of the feature.
|
I have hit my time limit for the week but will try next week to understand the tools to set up grammar tests and run them. |
Nigel-Ecma
left a 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.
Unfortunately I think there is quite a way to go yet.
Overall reading this I get a sense that too much is being placed on the number of occurrences of array_type/the change in shape of the array – these are really illusory changes (whether we think they should be or not) due to the erasure of nullability – meaning is not changing, even though adding/removing nullable annotations has the very non-illusory impact of generating compile time errors!
Trying to think of an illustration the best I came up with is language translation:
The new road avoids the swamp.
In Irish is:
Seachnaíonn an bóthar nua an portach.
These have the same meaning even if the structure has changed – translate the Irish word-by-word and you get:
Avoids the road new the swamp.
Arrays of nullable arrays doesn’t change the meaning of the code, what it changes is how the code must be written – e.g. rearrange indices (avoids the road new the swamp) – to achieve the same meaning.
Is this PR getting this across? Should it?
| > *Note*: This is the sole exception to the general rule that the meaning of a program remains the same when nullable reference types annotations are removed. *end note* | ||
| Every reference type which contains nullable annotations has a corresponding unannotated type with no semantic difference (§8.9.1). The corresponding unannotated type for an array of nullable arrays is a single array type which recursively collects all the ranks of all the nested *array_type*s. |
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.
With array types there is not in general a single “corresponding nullable type” (§8.9.1) – there are multiple, e.g.:
static void ArrayEquivalentTypes(int[][,]?[,,]?[,,,]?[,,,,] a1)
{
int[,,][][,]?[,,,]?[,,,,] a2 = a1;
int[,,,][,,][][,]?[,,,,] a3_1 = a1;
int[,,,][,,][][,]?[,,,,] a3_2 = a2;
int[,,,,][,,,][,,][][,] a4_1 = a1;
int[,,,,][,,,][,,][][,] a4_2 = a2;
int[,,,,][,,,][,,][][,] a4_3_1 = a3_1;
int[,,,,][,,,][,,][][,] a4_3_2 = a3_2;
int[,,][][,]?[,,,]?[,,,,] b3_1 = a3_1;
int[,,][][,]?[,,,]?[,,,,] b3_2 = a3_2;
int[,,][][,]?[,,,]?[,,,,] b4_1 = a4_1;
int[,,][][,]?[,,,]?[,,,,] b4_2 = a4_2;
int[,,][][,]?[,,,]?[,,,,] b4_3_1 = a4_3_1;
int[,,][][,]?[,,,]?[,,,,] b4_3_2 = a4_3_2;
int[,,,][,,][][,]?[,,,,] c4_1 = a4_1;
int[,,,][,,][][,]?[,,,,] c4_2 = a4_2;
int[,,,][,,][][,]?[,,,,] c4_3_1 = a4_3_1;
int[,,,][,,][][,]?[,,,,] c4_3_2 = a4_3_2;
// etc...
}This bundle of joy ;-) has four array types that are semantically equivalent to each other, and that’s not the limit by a long shot.
It needs to be specified that any two types (which need not be distinct) selected from a semantically equivalent set are implicitly convertible to each other. (Depending on implementation some, but not all, of the conversions may elicit a nullable warning.)
And while we are here, consider:
static void ArrayDifferentTypesSameLiteral()
{
int[]?[,] a1 =
{
{ new int[] { 1, 2 },
new int[] { 3, 4 }
}
};
int[,][] a2 =
{
{ new int[] { 1, 2 },
new int[] { 3, 4 }
}
};Different array types which are semantically equivalent can be init’ed using the same array literal. That will need to be specified.
| > ``` | ||
| > | ||
| > *end example* | ||
| 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, with one exception: arrays of nullable arrays are not parsed as a single *array_type*, but rather as multiple nested *array_type*s. The corresponding *non-nullable reference type* of an array of nullable arrays is not the single array type that would be parsed if the nullable annotations were removed; see §arrays-of-nullable-arrays. |
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.
I will surprise everybody ;-) and say “compiler” -> ”implementation”.
Not changing the compile time behavior is no longer correct, add/remove a nullable annotation on an array type without changing all associated index operations will produce compile-time errors.
I’m not sure you can say changing the number of array_type productions in the parse is an exception per se – the annotations do not change the described array shape in anyway (one might argue that the existing description of arrays is less than clear on the shape, if so adding nullable arrays is the time to fix that).
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.
I don't know about taking on the change of compiler -> implementation. "Compiler" is used consistently 31 times in the surrounding context, and the sentence you're commenting on precedes this PR; the term is only shown with a green background in the diff because I tacked some text on to the end of the line.
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.
Not changing the compile time behavior is no longer correct, add/remove a nullable annotation on an array type without changing all associated index operations will produce compile-time errors.
The "with one exception" part already addresses this, doesn't it? It is correct that the compile time behavior does not change, with that one exception of adding or removing a nullable annotation on an array type... right?
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.
I’m not sure you can say changing the number of array_type productions in the parse is an exception per se – the annotations do not change the described array shape in anyway (one might argue that the existing description of arrays is less than clear on the shape, if so adding nullable arrays is the time to fix that).
Here is why I think that it is an exception per se. The general rule is that the presence or absence of nullable annotations does not change the compile time or runtime behavior. Thus, the removal of ? that is present in a C# program would generally not affect compile time or runtime behavior. However, the removal of ? from T[]?[,] does affect compile time and runtime behavior. The causal mechanism by which the presence of the ? has meaning is the parse of the grammar; with the ?, the parse has two array_types, and without the ?, the parse has only one array_type. The meaning of the resulting compile time behavior and runtime type are defined in terms of the nested structure (if any) of those array_type(s) produced by the parse.
|
Overall, I like the approach of #1297 better. I find it easier to follow the concepts. (But that's not a strong preference if others prefer this direction). |
|
@BillWagner – unfortunately #1297 has issues, this one starts with a better grammar which binds the nullable annotation the right way – arrays used to have elements of non-array type, now they can have elements of nullable array type (but still not array type), and the grammar models that. There is still some way to go here both on arrays themselves, conversions, and in removing the “nullable annotations do not effect semantics” guarantee (as @jnm2 does not wish to use the “conditionally normative” device as a way out of that). However its not worth pursuing #1297 as an alternative. |
|
Note to myself as much as anything else: I've read through the change, and don't feel competent to express an opinion on it. |
|
Applied the C# 8 milestone, but if it's not ready, we should at least consider #1287. |
Proposed replacement for @Nigel-Ecma's #1287 and @gafter's #1297
Will fix #1385
I was trying not to end up creating new names like
non_array_non_nullable_reference_typeandnon_array_nullable_reference_typewhich don't sound like core concepts we'd want to be referencing elsewhere. (Cf #1287)TODO:
string?[]andint[]?[]were disallowed prior to the grammar changes in this PR.)