Skip to content

Commit

Permalink
Incorporate some text from dotnet#700
Browse files Browse the repository at this point in the history
Bring in the normative text from dotnet#700.

Some text is removed because of the decision on normative language in our September meeting.
  • Loading branch information
BillWagner committed Sep 18, 2024
1 parent 874bfc9 commit c2bb8dd
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 14 deletions.
35 changes: 21 additions & 14 deletions standard/classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,17 +412,18 @@ type_parameter_constraints
;
primary_constraint
: class_type
| 'class'
: class_type '?'?
| 'class' '?'?
| 'struct'
| `notnull`
| 'unmanaged'
;
secondary_constraints
: interface_type
: interface_type '?'?
| type_parameter
| secondary_constraints ',' interface_type
| secondary_constraints ',' type_parameter
| secondary_constraints ',' interface_type '?'?
| secondary_constraints ',' type_parameter '?'?
;
constructor_constraint
Expand All @@ -434,14 +435,18 @@ Each *type_parameter_constraints_clause* consists of the token `where`, followed

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()`.

A primary constraint can be a class type, the ***reference type constraint*** `class`, the ***value type constraint*** `struct`, or the ***unmanaged type constraint*** `unmanaged`.
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`.

A secondary constraint can be a *type_parameter* or *interface_type*.
A secondary constraint can be a *type_parameter* or *interface_type*, either optionally followed by `?`.

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.
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.

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.

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*.

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.

> *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*
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.
Expand Down Expand Up @@ -604,7 +609,9 @@ The ***effective interface set*** of a type parameter `T` is defined as follows
- 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.
- 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.
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`.
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`.
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`.
Values of a constrained type parameter type can be used to access the instance members implied by the constraints.
Expand Down Expand Up @@ -878,7 +885,7 @@ All members of a generic class can use type parameters from any enclosing class,
> class C<V>
> {
> public V f1;
> public C<V> f2 = null;
> public C<V> f2 = null!;
>
> public C(V x)
> {
Expand Down Expand Up @@ -1055,17 +1062,17 @@ Non-nested types can have `public` or `internal` declared accessibility and have
> private class Node
> {
> public object Data;
> public Node Next;
> public Node? Next;
>
> public Node(object data, Node next)
> public Node(object data, Node? next)
> {
> this.Data = data;
> this.Next = next;
> }
> }
>
> private Node first = null;
> private Node last = null;
> private Node? first = null;
> private Node? last = null;
>
> // Public interface
> public void AddToFront(object o) {...}
Expand Down
46 changes: 46 additions & 0 deletions standard/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -601,8 +601,20 @@ A type parameter is an identifier designating a value type or reference type tha
type_parameter
: identifier
;
nullable_type_parameter
: non_nullable_non_value_type_parameter '?'
;
non_nullable_non_value_type_parameter
: type_parameter
;
```
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.

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.

Since a type parameter can be instantiated with many different type arguments, type parameters have slightly different operations and restrictions than other types.

> *Note*: These include:
Expand Down Expand Up @@ -705,3 +717,37 @@ An *unmanaged_type* is any type that isn’t a *reference_type*, a *type_paramet
- `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, or `bool`.
- Any *enum_type*.
- Any user-defined *struct_type* that is not a constructed type and contains instance fields of *unmanaged_type*s only.

## §Generics-and-nullable-placeholder

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.

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`.

> *Example*:
>
> <!-- Example: {template:"code-in-class-lib-without-using", name:"TypeParameters", ignoredWarnings:["CS0219"]} -->
> ```csharp
> #nullable enable
> // The value t here has the state "maybe null". It's possible for T to be instantiated
> // with string?, in which case null would be within the domain of valid values here. The
> // assumption though is the value provided here is within the valid values of T. Hence
> // if T is `string` then null will not be a value, just as we assume that null is not
> // provided for a normal string parameter
> void M<T>(T t)
> {
> // if `T` is a `string`, `t` is not null.
> // There is no guarantee that default(T) is within the valid values for T hence the
> // state *must* be "maybe default" and hence local must be T?
> // For example, default(string) is null, which must be assigned to a string?
> T? local = default(T);
> }
> ```
>
> *end example*
The `class?` constraint generates a warning when the `annotations` flag is false.
- For any reference type `T`, the annotation `?` in `T?` makes `T?` a nullable type, whereas the unannotated `T` is non-nullable.
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 comments on commit c2bb8dd

Please sign in to comment.