Skip to content

Commit 0ba30fe

Browse files
committed
Incorporate some text from dotnet#700
Bring in the normative text from dotnet#700. Some text is removed because of the decision on normative language in our September meeting.
1 parent 2be6531 commit 0ba30fe

File tree

2 files changed

+53
-154
lines changed

2 files changed

+53
-154
lines changed

standard/classes.md

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -412,17 +412,18 @@ type_parameter_constraints
412412
;
413413
414414
primary_constraint
415-
: class_type
416-
| 'class'
415+
: class_type '?'?
416+
| 'class' '?'?
417417
| 'struct'
418+
| `notnull`
418419
| 'unmanaged'
419420
;
420421
421422
secondary_constraints
422-
: interface_type
423+
: interface_type '?'?
423424
| type_parameter
424-
| secondary_constraints ',' interface_type
425-
| secondary_constraints ',' type_parameter
425+
| secondary_constraints ',' interface_type '?'?
426+
| secondary_constraints ',' type_parameter '?'?
426427
;
427428
428429
constructor_constraint
@@ -434,14 +435,18 @@ Each *type_parameter_constraints_clause* consists of the token `where`, followed
434435

435436
The list of constraints given in a `where` clause can include any of the following components, in this order: a single primary constraint, one or more secondary constraints, and the constructor constraint, `new()`.
436437

437-
A primary constraint can be a class type, the ***reference type constraint*** `class`, the ***value type constraint*** `struct`, or the ***unmanaged type constraint*** `unmanaged`.
438+
A primary constraint can be a class type, the ***reference type constraint*** `class`, the ***nullable reference type constraint*** `class?`, the ***not null*** constraint `notnull`, the ***value type constraint*** `struct` or the ***unmanaged type constraint*** `unmanaged`.
438439

439-
A secondary constraint can be a *type_parameter* or *interface_type*.
440+
A secondary constraint can be a *type_parameter* or *interface_type*, either optionally followed by `?`.
440441

441-
The reference type constraint specifies that a type argument used for the type parameter shall be a reference type. All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint.
442+
The reference type constraint specifies that a type argument used for the type parameter shall be a non-nullable reference type. All non-nullable class types, non-nullable interface types, non-nullable delegate types, non-nullable array types, and type parameters known to be a non-nullable reference type (as defined below) satisfy this constraint.
443+
444+
The nullable reference type constraint specifies that a type argument shall be either a non-nullable reference type or a nullable reference type. All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint.
442445

443446
The value type constraint specifies that a type argument used for the type parameter shall be a non-nullable value type. All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Note that although classified as a value type, a nullable value type ([§8.3.12](types.md#8312-nullable-value-types)) does not satisfy the value type constraint. A type parameter having the value type constraint shall not also have the *constructor_constraint*, although it may be used as a type argument for another type parameter with a *constructor_constraint*.
444447

448+
The ***not null*** constraint specifies that a type argument used for the type parameter shall be a non-nullable value type or a non-nullable reference type. All non-nullable class types, interface types, delegate types, array types, struct types, enum types, and type parameters known to be a non-nullable value type or non-nullable reference type satisfy this constraint.
449+
445450
> *Note*: The `System.Nullable<T>` type specifies the non-nullable value type constraint for `T`. Thus, recursively constructed types of the forms `T??` and `Nullable<Nullable<T>>` are prohibited. *end note*
446451
447452
Because `unmanaged` is not a keyword, in *primary_constraint* the unmanaged constraint is always syntactically ambiguous with *class_type*. For compatibility reasons, if a name lookup ([§12.8.4](expressions.md#1284-simple-names)) of the name `unmanaged` succeeds it is treated as a `class_type`. Otherwise it is treated as the unmanaged constraint.
@@ -604,7 +609,9 @@ The ***effective interface set*** of a type parameter `T` is defined as follows
604609
- If `T` has no *interface_type* constraints but has *type_parameter* constraints, its effective interface set is the union of the effective interface sets of its *type_parameter* constraints.
605610
- If `T` has both *interface_type* constraints and *type_parameter* constraints, its effective interface set is the union of the set of dynamic erasures of its *interface_type* constraints and the effective interface sets of its *type_parameter* constraints.
606611
607-
A type parameter is *known to be a reference type* if it has the reference type constraint or its effective base class is not `object` or `System.ValueType`.
612+
A type parameter is *known to be a non-nullable reference type* if it has the non-nullable reference type constraint or its effective base class is not `object` or `System.ValueType`.
613+
614+
A type parameter is *known to be a reference type* if it has the non-nullable reference type constraint, reference type constraint or its effective base class is not `object` or `System.ValueType`.
608615
609616
Values of a constrained type parameter type can be used to access the instance members implied by the constraints.
610617
@@ -878,7 +885,7 @@ All members of a generic class can use type parameters from any enclosing class,
878885
> class C<V>
879886
> {
880887
> public V f1;
881-
> public C<V> f2 = null;
888+
> public C<V> f2 = null!;
882889
>
883890
> public C(V x)
884891
> {
@@ -1055,17 +1062,17 @@ Non-nested types can have `public` or `internal` declared accessibility and have
10551062
> private class Node
10561063
> {
10571064
> public object Data;
1058-
> public Node Next;
1065+
> public Node? Next;
10591066
>
1060-
> public Node(object data, Node next)
1067+
> public Node(object data, Node? next)
10611068
> {
10621069
> this.Data = data;
10631070
> this.Next = next;
10641071
> }
10651072
> }
10661073
>
1067-
> private Node first = null;
1068-
> private Node last = null;
1074+
> private Node? first = null;
1075+
> private Node? last = null;
10691076
>
10701077
> // Public interface
10711078
> public void AddToFront(object o) {...}

standard/types.md

Lines changed: 32 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -610,8 +610,20 @@ A type parameter is an identifier designating a value type or reference type tha
610610
type_parameter
611611
: identifier
612612
;
613+
614+
nullable_type_parameter
615+
: non_nullable_non_value_type_parameter '?'
616+
;
617+
618+
non_nullable_non_value_type_parameter
619+
: type_parameter
620+
;
613621
```
614622
623+
The *non_nullable_non_value_type_parameter* in *nullable_type_parameter* shall be a type parameter that isn’t constrained to be a value type.
624+
625+
In *nullable_type_parameter*, the annotation `?` indicates the intent that type arguments of this type are nullable. The absence of the annotation `?` indicates the intent that type arguments of this type are non-nullable.
626+
615627
Since a type parameter can be instantiated with many different type arguments, type parameters have slightly different operations and restrictions than other types.
616628

617629
> *Note*: These include:
@@ -714,157 +726,37 @@ An *unmanaged_type* is any type that isn’t a *reference_type*, a *type_paramet
714726
- `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, or `bool`.
715727
- Any *enum_type*.
716728
- Any user-defined *struct_type* that is not a constructed type and contains instance fields of *unmanaged_type*s only.
717-
- In unsafe code ([§23.2](unsafe-code.md#232-unsafe-contexts)), any *pointer_type* ([§23.3](unsafe-code.md#233-pointer-types)).
718-
719-
## 8.9 Reference Types and nullability
720-
721-
### 8.9.1 General
722-
723-
A *nullable reference type* is denoted by appending a `?` to a valid non-nullable reference type name. There is no semantic difference between a non-nullable reference type and its corresponding nullable type. Both a nullable reference and a non-nullable reference can contain either a reference to an object or `null`. The presence or absence of the `?` annotation declares whether an expression is intended to permit null values or not. A compiler can provide diagnostics when an expression is not used according to that intent. The null state of an expression is defined in [§8.9.5](types.md#895-nullabilities-and-null-states). An identity conversion exists among a nullable reference type and its corresponding non-nullable reference type ([§10.2.2](conversions.md#1022-identity-conversion)).
724-
725-
There are two forms of nullability for reference types:
726-
727-
- *nullable*: A *nullable-reference-type* can be assigned `null`. Its default null state is *maybe-null*.
728-
- *non-nullable*” A *non-nullable reference* should not be assigned a `null` value. Its default null state is *not-null*.
729-
730-
> *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*
731-
732-
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 `?` 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.
733-
734-
### 8.9.2 Non-nullable reference types
735-
736-
A ***non-nullable reference type*** is a reference type of the form `T`, where `T` is the name of the type. The default null-state of a non-nullable variable is *not-null*. Warnings may be generated when an expression that is *maybe-null* is used where a *not-null* value is required.
737-
738-
### 8.9.3 Nullable reference types
739-
740-
A reference type of the form `T?` (such as `string?`) is a ***nullable reference type***. The default null-state of a nullable variable is *maybe null*. The annotation `?` indicates the intent that variables of this type are nullable. The compiler can recognize these intents to issue warnings. When the nullable annotation context is disabled, using this annotation can generate a warning.
741-
742-
### 8.9.4 Nullable context
743-
744-
#### 8.9.4.1 General
745-
746-
Every line of source code has a ***nullable context***. The annotations and warnings flags for the nullable context control nullable annotations ([§8.9.4.3](types.md#8943-nullable-annotations)) and nullable warnings ([§8.9.4.4](types.md#8944-nullable-warnings)), respectively. Each flag can be *enabled* or *disabled*. The compiler can use static flow analysis to determine the null state of any reference variable. A reference variable’s null state ([§8.9.5](types.md#895-nullabilities-and-null-states)) is either *not null*, *maybe null*, or *maybe default*.
747-
748-
The nullable context may be specified within source code via nullable directives ([§6.5.9](lexical-structure.md#659-nullable-directive)) and/or via some implementation-specific mechanism external to the source code. If both approaches are used, nullable directives supersede the settings made via an external mechanism.
749-
750-
The default state of the nullable context is implementation defined.
751-
752-
Throughout this specification, all C# code that does not contain nullable directives, or about which no statement is made regarding the current nullable context state, shall be assumed to have been compiled using a nullable context where both annotations and warnings are enabled.
753-
754-
> *Note:* A nullable context where both flags are disabled matches the previous standard behavior for reference types. *end note*
755-
756-
#### 8.9.4.2 Nullable disable
757-
758-
When both the warning and annotations flags are disabled, the nullable context is *disabled*.
759-
760-
When the nullable context is ***disabled***:
761-
762-
- No warning shall be generated when a variable of an unannotated reference type is initialized with, or assigned a value of, `null`.
763-
- No warning shall be generated when a variable of a reference type that possibly has the null value.
764-
- For any reference type `T`, the annotation `?` in `T?` generates a message and the type `T?` is the same as `T`.
765-
> *Note*: This message is characterized as “informational” rather than “warning,” so as not to confuse it with the state of the nullable warning setting, which is unrelated. *end note*
766-
- The null-forgiving operator `!` ([§12.8.9](expressions.md#1289-null-forgiving-expressions)) has no effect.
767-
768-
> *Example*:
769-
>
770-
> <!-- Example: {template:"code-in-main-without-using", name:"NullableAnnotationContext1", ignoredWarnings:["CS0219","CS8632"], expectedException:"NullReferenceException"} -->
771-
> ```csharp
772-
> #nullable disable annotations
773-
> string? s1 = null; // Informational message; ? is ignored
774-
> string s2 = null; // OK; null initialization of a reference
775-
> s2 = null; // OK; null assignment to a reference
776-
> char c1 = s2[1]; // OK; no warning on dereference of a possible null; throws NullReferenceException
777-
> c1 = s2![1]; // OK; ! is ignored
778-
> ```
779-
>
780-
> *end example*
781-
782-
#### 8.9.4.3 Nullable annotations
783-
784-
When the warning flag is disabled and the annotations flag is enabled, the nullable context is *annotations*.
785-
786-
When the nullable context is ***annotations***:
787-
788-
- For any reference type `T`, the annotation `?` in `T?` indicates that `T?` a nullable type, whereas the unannotated `T` is non-nullable.
789-
- No diagnostic warnings related to nullability are generated.
790-
- The null-forgiving operator `!` ([§12.8.9](expressions.md#1289-null-forgiving-expressions)) sets the null state of its operand to *not null*.
791-
792-
> *Example*:
793-
>
794-
> <!-- Example: {template:"code-in-main-without-using", name:"NullableAnnotationContext2", ignoredWarnings:["CS0219"], expectedException:"NullReferenceException"} -->
795-
> ```csharp
796-
> #nullable disable warnings
797-
> #nullable enable annotations
798-
> string? s1 = null; // OK; ? makes s2 nullable
799-
> string s2 = null; // OK; warnings are disabled
800-
> s2 = null; // OK; warnings are disabled
801-
> char c1 = s2[1]; // OK; warnings are disabled; throws NullReferenceException
802-
> c1 = s2![1]; // No warnings
803-
> ```
804-
>
805-
> *end example*
806-
807-
#### 8.9.4.4 Nullable warnings
808729

809-
When the warning flag is enabled and the annotations flag is disabled, the nullable context is *warnings*.
730+
## §Generics-and-nullable-placeholder
810731

811-
When the nullable context is ***warnings***, a compiler can generate diagnostics in the following cases:
732+
This is a placeholder for text that will be added in the clause on "nullable context" described in `#1124`. I'll rebase and edit once that's done.
812733

813-
- A reference variable that has been determined to be *maybe null*, is dereferenced.
814-
- A reference variable of a non-nullable type is assigned to an expression that is *maybe null*.
815-
- The `?` is used to note a nullable reference type.
816-
- The null-forgiving operator `!` ([§12.8.9](expressions.md#1289-null-forgiving-expressions)) is used to set the null state of its operand to *not null*.
734+
The distinction between the null states “maybe null” and “maybe default” is subtle and applies to type parameters. A type parameter `T` that has the state “maybe null” means the value is in the domain of valid values for `T`; however, that valid value may include `null`. A “maybe default” state means that the value may be outside the valid domain of values for `T`.
817735

818736
> *Example*:
819737
>
820-
> <!-- Example: {template:"code-in-main-without-using", name:"NullableAnnotationContext3", ignoredWarnings:["CS0219"], expectedWarnings:["CS8632", "CS8602"], expectedException:"NullReferenceException"} -->
738+
> <!-- Example: {template:"code-in-class-lib-without-using", name:"TypeParameters", ignoredWarnings:["CS0219"]} -->
821739
> ```csharp
822-
> #nullable disable annotations
823-
> #nullable enable warnings
824-
> string? s1 = null; // OK; ? makes s2 nullable
825-
> string s2 = null; // OK; null-state of s2 is "maybe null"
826-
> s2 = null; // OK; null-state of s2 is "maybe null"
827-
> char c1 = s2[1]; // Warning; dereference of a possible null; throws NullReferenceException
828-
> c1 = s2![1]; // The warning is suppressed
740+
> #nullable enable
741+
> // The value t here has the state "maybe null". It's possible for T to be instantiated
742+
> // with string?, in which case null would be within the domain of valid values here. The
743+
> // assumption though is the value provided here is within the valid values of T. Hence
744+
> // if T is `string` then null will not be a value, just as we assume that null is not
745+
> // provided for a normal string parameter
746+
> void M<T>(T t)
747+
> {
748+
> // if `T` is a `string`, `t` is not null.
749+
> // There is no guarantee that default(T) is within the valid values for T hence the
750+
> // state *must* be "maybe default" and hence local must be T?
751+
> // For example, default(string) is null, which must be assigned to a string?
752+
> T? local = default(T);
753+
> }
829754
> ```
830755
>
831756
> *end example*
832757
833-
#### 8.9.4.5 Nullable enable
834-
835-
When both the warning flag and the annotations flag are enabled, the nullable context is *enabled*.
836-
837-
When the nullable context is ***enabled***:
758+
The `class?` constraint generates a warning when the `annotations` flag is false.
838759
839760
- For any reference type `T`, the annotation `?` in `T?` makes `T?` a nullable type, whereas the unannotated `T` is non-nullable.
840-
- The compiler can use static flow analysis to determine the null state of any reference variable. When nullable warnings are enabled, a reference variables null state ([§8.9.5](types.md#895-nullabilities-and-null-states)) is either *not null*, *maybe null*, or *maybe default* and
841-
- The null-forgiving operator `!` ([§12.8.9](expressions.md#1289-null-forgiving-expressions)) sets the null state of its operand to *not null*.
842-
843-
### 8.9.5 Nullabilities and null states
844-
845-
A compiler is not required to perform any static analysis nor is it required to generate any diagnostic messages related to nullability.
846-
847-
**The remainder of this subclause is conditionally normative.**
848-
849-
A compiler that generates diagnostic messages conforms to these rules.
850-
851-
Every expression has one of three ***null state***s:
852-
853-
- *maybe null*: The value of the expression may evaluate to null.
854-
- *maybe default*: The value of the expression may evaluate to the default value for that type.
855-
- *not null*: The value of the expression isn’t null.
856-
857-
The ***default null state*** of an expression is determined by its type, and the state of the annotations flag when it is declared:
858-
859-
- The default null state of a nullable reference type is:
860-
- Maybe null when its declaration is in text where the annotations flag is enabled.
861-
- Not null when its declaration is in text where the annotations flag is disabled.
862-
- The default null state of a non-nullable reference type is not null.
863-
864-
A diagnostic can be produced when a variable ([§9.2.1](variables.md#921-general)) of a non-nullable reference type is initialized or assigned to an expression that is maybe null when that variable is declared in text where the annotation flag is enabled.
865-
866-
The compiler can update the null state of a variable as part of its analysis.
867-
868-
> *Note*: The compiler can treat a property ([§15.7](classes.md#157-properties)) as either a variable with state, or as independent get and set accessors ([§15.7.3](classes.md#1573-accessors)). In other words, a compiler can choose if writing to a property changes the null state of reading the property.
869761
870-
***End of conditionally normative text***
762+
When the nullable annotation context is enabled, the compiler shall recognize these intents. When the nullable annotation context is disabled, the compiler shall ignore these intents, thereby treating `T` and `T?` in the same (nullable) manner, and generate a warning. (The latter scenario was the only one that existed prior to the addition of nullable reference types.)

0 commit comments

Comments
 (0)