diff --git a/standard/arrays.md b/standard/arrays.md index 7f0611678..c9dc4bce4 100644 --- a/standard/arrays.md +++ b/standard/arrays.md @@ -112,7 +112,7 @@ Elements of arrays created by *array_creation_expression*s are always initialize ## 17.4 Array element access -Array elements are accessed using the *array access* variant of *element_access* expressions ([§12.8.12.2](expressions.md#128122-array-access)) of the form `A[I₁, I₂, ..., Iₓ]`, where `A` is an expression of an array type and each `Iₑ` is an expression of type `int`, `uint`, `long`, `ulong`, or can be implicitly converted to one or more of these types. The result of an array access is a variable reference ([§9.5](variables.md#95-variable-references)) to the array element selected by the indices. +Array elements are accessed using the *array access* variant of *element_access* expressions ([§12.8.12.2](expressions.md#128122-array-access)) of the form `A[I₁, I₂, ..., Iₓ]`, where `A` is an expression of an array type and each `Iₑ` is an expression of type `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, or can be implicitly converted to one or more of these types. The result of an array access is a variable reference ([§9.5](variables.md#95-variable-references)) to the array element selected by the indices. Array elements of single-dimensional arrays can also be accessed using an array access expression where the sole index, `I₁`, is an expression of type `Index`, `Range`, or can be implicitly converted to one or both of these types. If `I₁` is of type `Index`, or has been implicitly converted to that type, then the result of the array access is a variable reference to the array element selected by the index value. If `I₁` is of type `Range`, or has been implicitly converted to that type, then the result of the element access is a new array formed from a shallow copy of the array elements with indices in the `Range`, maintaining the element order. diff --git a/standard/classes.md b/standard/classes.md index a0113760d..165aca53e 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1556,7 +1556,7 @@ A *constant_declaration* may include a set of *attributes* ([§23](attributes.md The *type* of a *constant_declaration* specifies the type of the members introduced by the declaration. The type is followed by a list of *constant_declarator*s ([§13.6.3](statements.md#1363-local-constant-declarations)), each of which introduces a new member. A *constant_declarator* consists of an *identifier* that names the member, followed by an “`=`” token, followed by a *constant_expression* ([§12.25](expressions.md#1225-constant-expressions)) that gives the value of the member. -The *type* specified in a constant declaration shall be `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool`, `string`, an *enum_type*, or a *reference_type*. Each *constant_expression* shall yield a value of the target type or of a type that can be converted to the target type by an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)). +The *type* specified in a constant declaration shall be `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool`, `string`, an *enum_type*, or a *reference_type*. Each *constant_expression* shall yield a value of the target type or of a type that can be converted to the target type by an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)). The *type* of a constant shall be at least as accessible as the constant itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). @@ -1790,7 +1790,7 @@ These restrictions ensure that all threads will observe volatile writes performe - A *reference_type*. - A *type_parameter* that is known to be a reference type ([§15.2.5](classes.md#1525-type-parameter-constraints)). -- The type `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `char`, `float`, `bool`, `System.IntPtr`, or `System.UIntPtr`. +- The type `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `char`, `float`, `bool`, `System.IntPtr`, or `System.UIntPtr`. - An *enum_type* having an *enum_base* type of `byte`, `sbyte`, `short`, `ushort`, `int`, or `uint`. > *Example*: The example diff --git a/standard/conversions.md b/standard/conversions.md index 5a68a8177..8a357c2dc 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -83,6 +83,7 @@ An identity conversion converts from any type to the same type or a type that is - Between `object` and `dynamic`. - Between all tuple types with the same arity, and the corresponding constructed `ValueTuple<...>` type, when an identity conversion exists between each pair of corresponding element types. - Between types constructed from the same generic type where there exists an identity conversion between each corresponding type argument. +- Between array types containing elements of type `T` and `S`, such as `T[]` and `S[]`, where the rank of the two arrays is the same and there is an identity conversion between `T` and `S`. > *Example*: The following illustrates the recursive nature of the third rule: > @@ -115,22 +116,26 @@ All identity conversions are symmetric. If an identity conversion exists from `T In most cases, an identity conversion has no effect at runtime. However, since floating point operations may be performed at higher precision than prescribed by their type ([§8.3.7](types.md#837-floating-point-types)), assignment of their results may result in a loss of precision, and explicit casts are guaranteed to reduce precision to what is prescribed by the type ([§12.9.8](expressions.md#1298-cast-expressions)). +There is an identity conversion between `nint` and `System.IntPtr`, and between `nuint` and `System.UIntPtr`. + ### 10.2.3 Implicit numeric conversions The implicit numeric conversions are: -- From `sbyte` to `short`, `int`, `long`, `float`, `double`, or `decimal`. -- From `byte` to `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`, or `decimal`. -- From `short` to `int`, `long`, `float`, `double`, or `decimal`. -- From `ushort` to `int`, `uint`, `long`, `ulong`, `float`, `double`, or `decimal`. -- From `int` to `long`, `float`, `double`, or `decimal`. -- From `uint` to `long`, `ulong`, `float`, `double`, or `decimal`. +- From `sbyte` to `short`, `int`, `nint`, `long`, `float`, `double`, or `decimal`. +- From `byte` to `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `float`, `double`, or `decimal`. +- From `short` to `int`, `nint`, `long`, `float`, `double`, or `decimal`. +- From `ushort` to `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `float`, `double`, or `decimal`. +- From `int` to `nint`, `long`, `float`, `double`, or `decimal`. +- From `uint` to `nuint`, `long`, `ulong`, `float`, `double`, or `decimal`. +- From `nint` to `long`, `float`, `double`, or `decimal`. +- From `nuint` to `ulong`, `float`, `double`, or `decimal`. - From `long` to `float`, `double`, or `decimal`. - From `ulong` to `float`, `double`, or `decimal`. -- From `char` to `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`, or `decimal`. +- From `char` to `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `float`, `double`, or `decimal`. - From `float` to `double`. -Conversions from `int`, `uint`, `long` or `ulong` to `float` and from `long` or `ulong` to `double` may cause a loss of precision, but will never cause a loss of magnitude. The other implicit numeric conversions never lose any information. +Conversions from `int`, `uint`, `nint`, `nuint`, `long` or `ulong` to `float` and from `nint`, `nuint`, `long` or `ulong` to `double` may cause a loss of precision, but will never cause a loss of magnitude. The other implicit numeric conversions never lose any information. There are no predefined implicit conversions to the `char` type, so values of the other integral types do not automatically convert to the `char` type. @@ -315,9 +320,13 @@ This implicit conversion seemingly violates the advice in the beginning of [§10 An implicit constant expression conversion permits the following conversions: -- A *constant_expression* ([§12.25](expressions.md#1225-constant-expressions)) of type `int` can be converted to type `sbyte`, `byte`, `short`, `ushort`, `uint`, or `ulong`, provided the value of the *constant_expression* is within the range of the destination type. +- A *constant_expression* ([§12.25](expressions.md#1225-constant-expressions)) of type `int` can be converted to type `sbyte`, `byte`, `short`, `ushort`, `uint`, `nuint`, or `ulong`, provided the value of the *constant_expression* is within the range of the destination type. - A *constant_expression* of type `long` can be converted to type `ulong`, provided the value of the *constant_expression* is not negative. +The range for constants of type `nint` is the same range as `int`, and the range for constants of type `nuint` is the same range as `uint` ([§12.25](expressions.md#1225-constant-expressions)). + +> *Note*: This is a consequence of `nint`/`nuint` being the same size as, or larger than, `int`/`uint` ([§8.3.6](types.md#836-integral-types)). *end note* + ### 10.2.12 Implicit conversions involving type parameters For a *type_parameter* `T` that is known to be a reference type ([§15.2.5](classes.md#1525-type-parameter-constraints)), the following implicit reference conversions ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) exist: @@ -413,18 +422,20 @@ The explicit conversions that are not implicit conversions are conversions that The explicit numeric conversions are the conversions from a *numeric_type* to another *numeric_type* for which an implicit numeric conversion ([§10.2.3](conversions.md#1023-implicit-numeric-conversions)) does not already exist: -- From `sbyte` to `byte`, `ushort`, `uint`, `ulong`, or `char`. +- From `sbyte` to `byte`, `ushort`, `uint`, `ulong`, `nuint`, or `char`. - From `byte` to `sbyte` or `char`. -- From `short` to `sbyte`, `byte`, `ushort`, `uint`, `ulong`, or `char`. +- From `short` to `sbyte`, `byte`, `ushort`, `uint`, `nuint`, `ulong`, or `char`. - From `ushort` to `sbyte`, `byte`, `short`, or `char`. -- From `int` to `sbyte`, `byte`, `short`, `ushort`, `uint`, `ulong`, or `char`. -- From `uint` to `sbyte`, `byte`, `short`, `ushort`, `int`, or `char`. -- From `long` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `ulong`, or `char`. -- From `ulong` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, or `char`. +- From `int` to `sbyte`, `byte`, `short`, `ushort`, `uint`, `nuint`, `ulong`, or `char`. +- From `uint` to `sbyte`, `byte`, `short`, `ushort`, `int`, `nint`, or `char`. +- From `nint` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nuint`, `ulong`, or `char`. +- From `nuint` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `long`, or `char`. +- From `long` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `ulong`, or `char`. +- From `ulong` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, or `char`. - From `char` to `sbyte`, `byte`, or `short`. -- From `float` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, or `decimal`. -- From `double` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, or `decimal`. -- From `decimal` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, or `double`. +- From `float` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, or `decimal`. +- From `double` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, or `decimal`. +- From `decimal` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, or `double`. Because the explicit conversions include all implicit and explicit numeric conversions, it is always possible to convert from any *numeric_type* to any other *numeric_type* using a cast expression ([§12.9.8](expressions.md#1298-cast-expressions)). @@ -458,8 +469,8 @@ The explicit numeric conversions possibly lose information or possibly cause exc The explicit enumeration conversions are: -- From `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, or `decimal` to any *enum_type*. -- From any *enum_type* to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, or `decimal`. +- From `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, `double`, or `decimal` to any *enum_type*. +- From any *enum_type* to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, `double`, or `decimal`. - From any *enum_type* to any other *enum_type*. An explicit enumeration conversion between two types is processed by treating any participating *enum_type* as the underlying type of that *enum_type*, and then performing an implicit or explicit numeric conversion between the resulting types. diff --git a/standard/enums.md b/standard/enums.md index 12e8c745c..bda2202ba 100644 --- a/standard/enums.md +++ b/standard/enums.md @@ -44,9 +44,7 @@ enum_body ; ``` -Each enum type has a corresponding integral type called the ***underlying type*** of the enum type. This underlying type shall be able to represent all the enumerator values defined in the enumeration. If the *enum_base* is present, it explicitly declares the underlying type. The underlying type shall be one of the *integral types* ([§8.3.6](types.md#836-integral-types)) other than `char`. The underlying type may be specified either by an `integral_type` ([§8.3.5](types.md#835-simple-types)), or an `integral_type_name`. The `integral_type_name` is resolved in the same way as `type_name` ([§7.8.1](basic-concepts.md#781-general)), including taking any using directives ([§14.5](namespaces.md#145-using-directives)) into account. - -> *Note*: The `char` type cannot be used as an underlying type, either by keyword or via an `integral_type_name`. *end note* +Each enum type has a corresponding integral type called the ***underlying type*** of the enum type. This underlying type shall be able to represent all the enumerator values defined in the enumeration. If the *enum_base* is present, it explicitly declares the underlying type. The underlying type shall be one of the *integral types* ([§8.3.6](types.md#836-integral-types)) other than `nint`, `nuint`, and `char`. The underlying type may be specified either by an `integral_type` ([§8.3.5](types.md#835-simple-types)), or an `integral_type_name`. The `integral_type_name` is resolved in the same way as `type_name` ([§7.8.1](basic-concepts.md#781-general)), including taking any using directives ([§14.5](namespaces.md#145-using-directives)) into account. An enum declaration that does not explicitly declare an underlying type has an underlying type of `int`. diff --git a/standard/expressions.md b/standard/expressions.md index ce868655b..50ce53739 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -297,7 +297,7 @@ When overload resolution rules ([§12.6.4](expressions.md#1264-overload-resoluti **This subclause is informative.** -Unary numeric promotion occurs for the operands of the predefined `+`, `-`, and `~` unary operators. Unary numeric promotion simply consists of converting operands of type `sbyte`, `byte`, `short`, `ushort`, or `char` to type `int`. Additionally, for the unary – operator, unary numeric promotion converts operands of type `uint` to type `long`. +Unary numeric promotion occurs for the operands of the predefined `+`, `-`, and `~` unary operators. Unary numeric promotion simply consists of converting operands of type `sbyte`, `byte`, `short`, `ushort`, or `char` to type `int`. Additionally, for the unary `-` operator, unary numeric promotion converts operands of type `uint` to type `long`. **End of informative text.** @@ -310,10 +310,12 @@ Binary numeric promotion occurs for the operands of the predefined `+`, `-`, `*` - If either operand is of type `decimal`, the other operand is converted to type `decimal`, or a binding-time error occurs if the other operand is of type `float` or `double`. - Otherwise, if either operand is of type `double`, the other operand is converted to type `double`. - Otherwise, if either operand is of type `float`, the other operand is converted to type `float`. -- Otherwise, if either operand is of type `ulong`, the other operand is converted to type `ulong`, or a binding-time error occurs if the other operand is of `type sbyte`, `short`, `int`, or `long`. +- Otherwise, if either operand is of type `ulong`, the other operand is converted to type `ulong`, or a binding-time error occurs if the other operand is of type `sbyte`, `short`, `int`, `nint`, or `long`. +- Otherwise, if either operand is of type `nuint`, the other operand is converted to type `nuint`, or a binding-time error occurs if the other operand is of type `sbyte`, `short`, `int`, `nint`, or `long`. - Otherwise, if either operand is of type `long`, the other operand is converted to type `long`. -- Otherwise, if either operand is of type `uint` and the other operand is of type `sbyte`, `short`, or `int`, both operands are converted to type `long`. +- Otherwise, if either operand is of type `uint` and the other operand is of type `sbyte`, `short`, `int`, or `nint`, both operands are converted to type `long`. - Otherwise, if either operand is of type `uint`, the other operand is converted to type `uint`. +- Otherwise, if either operand is of type `nint`, the other operand is converted to type `nint`. - Otherwise, both operands are converted to type `int`. > *Note*: The first rule disallows any operations that mix the `decimal` type with the `double` and `float` types. The rule follows from the fact that there are no implicit conversions between the `decimal` type and the `double` and `float` types. *end note* @@ -1136,10 +1138,11 @@ Given two types `T₁` and `T₂`, `T₁` is a ***better conversion target*** th - `T₁` is `«TaskType»`([§15.14.1](classes.md#15141-general)), `T₂` is `«TaskType»`, and `S₁` is a better conversion target than `S₂` - `T₁` is `«TaskType»`([§15.14.1](classes.md#15141-general)), `T₂` is `«TaskType»`, and `T₁` is more specialized than `T₂` - `T₁` is `S₁` or `S₁?` where `S₁` is a signed integral type, and `T₂` is `S₂` or `S₂?` where `S₂` is an unsigned integral type. Specifically: - - `S₁` is `sbyte` and `S₂` is `byte`, `ushort`, `uint`, or `ulong` - - `S₁` is `short` and `S₂` is `ushort`, `uint`, or `ulong` - - `S₁` is `int` and `S₂` is `uint`, or `ulong` - - `S₁` is `long` and `S₂` is `ulong` + - `S₁` is `sbyte` and `S₂` is `byte`, `ushort`, `uint`, `nuint`, or `ulong` + - `S₁` is `short` and `S₂` is `ushort`, `uint`, `nuint`, or `ulong` + - `S₁` is `int` and `S₂` is `uint`, `nuint`, or `ulong` + - `S₁` is `nint` and `S₂` is `nuint` or `ulong` + - `S₁` is `long` and `S₂` is `nuint` or `ulong` #### 12.6.4.8 Overloading in generic classes @@ -2284,9 +2287,9 @@ For an array access the *argument_list* shall not contain named arguments or by- The number of expressions in the *argument_list* shall be the same as the rank of the *array_type*, and each expression shall be: -- of type `int`, `uint`, `long`, or `ulong`; or +- of type `int`, `uint`, `nint`, `nuint`, `long`, or `ulong`; or - for single rank array access only, of type `Index` or `Range`; or -- be implicitly convertible to one or more of the above types. +- implicitly convertible to one or more of the above types. The run-time processing of an array access of the form `P[A]`, where `P` is a *primary_expression* of an *array_type* and `A` is an *argument_list* of index expressions, consists of the following steps: @@ -2499,7 +2502,7 @@ If the *primary_expression* has the compile-time type `dynamic` then the operato If the operand of a postfix increment or decrement operation is a property or indexer access, the property or indexer shall have both a get and a set accessor. If this is not the case, a binding-time error occurs. -Unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. Predefined `++` and `--` operators exist for the following types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, and any enum type. The predefined `++` operators return the value produced by adding `1` to the operand, and the predefined `--` operators return the value produced by subtracting `1` from the operand. In a checked context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a `System.OverflowException` is thrown. +Unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. Predefined `++` and `--` operators exist for the following types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, and any enum type. The predefined `++` operators return the value produced by adding `1` to the operand, and the predefined `--` operators return the value produced by subtracting `1` from the operand. In a checked context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a `System.OverflowException` is thrown. There shall be an implicit conversion from the return type of the selected unary operator to the type of the *primary_expression*, otherwise a compile-time error occurs. @@ -3393,7 +3396,7 @@ A *default_value_expression* is a constant expression ([§12.25](expressions.md# - a reference type - a type parameter that is known to be a reference type ([§8.2](types.md#82-reference-types)); -- one of the following value types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool,`; or +- one of the following value types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool`; or - any enumeration type. ### 12.8.22 Stack allocation @@ -3611,6 +3614,8 @@ For an operation of the form `+x`, unary operator overload resolution ([§12.4. ```csharp int operator +(int x); uint operator +(uint x); +nint operator +(nint x); +nuint operator +(nuint x); long operator +(long x); ulong operator +(ulong x); float operator +(float x); @@ -3624,32 +3629,35 @@ Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted ### 12.9.3 Unary minus operator -For an operation of the form `–x`, unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined unary minus operators are: +For an operation of the form `-x`, unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined unary minus operators are: - Integer negation: ```csharp - int operator –(int x); - long operator –(long x); + int operator -(int x); + nint operator -(nint x); + long operator -(long x); ``` - The result is computed by subtracting `X` from zero. If the value of `X` is the smallest representable value of the operand type (−2³¹ for `int` or −2⁶³ for `long`), then the mathematical negation of `X` is not representable within the operand type. If this occurs within a `checked` context, a `System.OverflowException` is thrown; if it occurs within an `unchecked` context, the result is the value of the operand and the overflow is not reported. + The result is computed by subtracting `X` from zero. If the value of `X` is the smallest representable value of the operand type (−2³¹ for `int`, the corresponding value for `nint`, or −2⁶³ for `long`), then the mathematical negation of `X` is not representable within the operand type. If this occurs within a `checked` context, a `System.OverflowException` is thrown; if it occurs within an `unchecked` context, the result is the value of the operand and the overflow is not reported. If the operand of the negation operator is of type `uint`, it is converted to type `long`, and the type of the result is `long`. An exception is the rule that permits the `int` value `−2147483648` (−2³¹) to be written as a decimal integer literal ([§6.4.5.3](lexical-structure.md#6453-integer-literals)). - + + If the operand of the negation operator is of type `nuint`, a compile-time error occurs. + If the operand of the negation operator is of type `ulong`, a compile-time error occurs. An exception is the rule that permits the `long` value `−9223372036854775808` (−2⁶³) to be written as a decimal integer literal ([§6.4.5.3](lexical-structure.md#6453-integer-literals)) - Floating-point negation: ```csharp - float operator –(float x); - double operator –(double x); + float operator -(float x); + double operator -(double x); ``` The result is the value of `X` with its sign inverted. If `x` is `NaN`, the result is also `NaN`. - Decimal negation: ```csharp - decimal operator –(decimal x); + decimal operator -(decimal x); ``` The result is computed by subtracting `X` from zero. Decimal negation is equivalent to using the unary minus operator of type `System.Decimal`. @@ -3677,11 +3685,13 @@ For an operation of the form `~x`, unary operator overload resolution ([§12.4.4 ```csharp int operator ~(int x); uint operator ~(uint x); +nint operator ~(nint x); +nuint operator ~(nuint x); long operator ~(long x); ulong operator ~(ulong x); ``` -For each of these operators, the result of the operation is the bitwise complement of `x`. +For each of these operators, the result of the operation is the bitwise complement of `x`. Every enumeration type `E` implicitly provides the following bitwise complement operator: @@ -3727,7 +3737,7 @@ The operand of a prefix increment or decrement operation shall be an expression If the operand of a prefix increment or decrement operation is a property or indexer access, the property or indexer shall have both a get and a set accessor. If this is not the case, a binding-time error occurs. -Unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. Predefined `++` and `--` operators exist for the following types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, and any enum type. The predefined `++` operators return the value produced by adding `1` to the operand, and the predefined `--` operators return the value produced by subtracting `1` from the operand. In a `checked` context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a `System.OverflowException` is thrown. +Unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. Predefined `++` and `--` operators exist for the following types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, and any enum type. The predefined `++` operators return the value produced by adding `1` to the operand, and the predefined `--` operators return the value produced by subtracting `1` from the operand. In a `checked` context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a `System.OverflowException` is thrown. There shall be an implicit conversion from the return type of the selected unary operator to the type of the *unary_expression*, otherwise a compile-time error occurs. @@ -3980,6 +3990,8 @@ The predefined multiplication operators are listed below. The operators all comp ```csharp int operator *(int x, int y); uint operator *(uint x, uint y); + nint operator *(nint x, nint y); + nuint operator *(nuint x, nuint y); long operator *(long x, long y); ulong operator *(ulong x, ulong y); ``` @@ -4027,6 +4039,8 @@ The predefined division operators are listed below. The operators all compute th ```csharp int operator /(int x, int y); uint operator /(uint x, uint y); + nint operator /(nint x, nint y); + nuint operator /(nuint x, nuint y); long operator /(long x, long y); ulong operator /(ulong x, ulong y); ``` @@ -4035,7 +4049,7 @@ The predefined division operators are listed below. The operators all compute th The division rounds the result towards zero. Thus the absolute value of the result is the largest possible integer that is less than or equal to the absolute value of the quotient of the two operands. The result is zero or positive when the two operands have the same sign and zero or negative when the two operands have opposite signs. - If the left operand is the smallest representable `int` or `long` value and the right operand is `–1`, an overflow occurs. In a `checked` context, this causes a `System.ArithmeticException` (or a subclass thereof) to be thrown. In an `unchecked` context, it is implementation-defined as to whether a `System.ArithmeticException` (or a subclass thereof) is thrown or the overflow goes unreported with the resulting value being that of the left operand. + If the left operand is the smallest representable `int`, `nint`, or `long` value and the right operand is `–1`, an overflow occurs. In a `checked` context, this causes a `System.ArithmeticException` (or a subclass thereof) to be thrown. In an `unchecked` context, it is implementation-defined as to whether a `System.ArithmeticException` (or a subclass thereof) is thrown or the overflow goes unreported with the resulting value being that of the left operand. - Floating-point division: ```csharp @@ -4078,13 +4092,15 @@ The predefined remainder operators are listed below. The operators all compute t ```csharp int operator %(int x, int y); uint operator %(uint x, uint y); + nint operator %(nint x, nint y); + nuint operator %(nuint x, nuint y); long operator %(long x, long y); ulong operator %(ulong x, ulong y); ``` The result of `x % y` is the value produced by `x – (x / y) * y`. If `y` is zero, a `System.DivideByZeroException` is thrown. - If the left operand is the smallest `int` or `long` value and the right operand is `–1`, a `System.OverflowException` is thrown if and only if `x / y` would throw an exception. + If the left operand is the smallest `int`, `nint`, or `long` value and the right operand is `–1`, a `System.OverflowException` is thrown if and only if `x / y` would throw an exception. - Floating-point remainder: ```csharp @@ -4128,6 +4144,8 @@ The predefined addition operators are listed below. For numeric and enumeration ```csharp int operator +(int x, int y); uint operator +(uint x, uint y); + nint operator +(nint x, nint y); + nuint operator +(nuint x, nuint y); long operator +(long x, long y); ulong operator +(ulong x, ulong y); ``` @@ -4228,18 +4246,20 @@ The predefined subtraction operators are listed below. The operators all subtrac - Integer subtraction: ```csharp - int operator –(int x, int y); - uint operator –(uint x, uint y); - long operator –(long x, long y); - ulong operator –(ulong x, ulong y + int operator -(int x, int y); + uint operator -(uint x, uint y); + nint operator -(nint x, nint y); + nuint operator -(nuint x, nuint y); + long operator -(long x, long y); + ulong operator -(ulong x, ulong y); ``` In a `checked` context, if the difference is outside the range of the result type, a `System.OverflowException` is thrown. In an `unchecked` context, overflows are not reported and any significant high-order bits outside the range of the result type are discarded. - Floating-point subtraction: ```csharp - float operator –(float x, float y); - double operator –(double x, double y); + float operator -(float x, float y); + double operator -(double x, double y); ``` The difference is computed according to the rules of IEC 60559 arithmetic. The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaNs. In the table, `x` and `y` are nonzero finite values, and `z` is the result of `x – y`. If `x` and `y` are equal, `z` is positive zero. If `x – y` is too large to represent in the destination type, `z` is an infinity with the same sign as `x – y`. @@ -4257,7 +4277,7 @@ The predefined subtraction operators are listed below. The operators all subtrac - Decimal subtraction: ```csharp - decimal operator –(decimal x, decimal y); + decimal operator -(decimal x, decimal y); ``` If the magnitude of the resulting value is too large to represent in the decimal format, a `System.OverflowException` is thrown. The scale of the result, before any rounding, is the larger of the scales of the two operands. @@ -4267,13 +4287,13 @@ The predefined subtraction operators are listed below. The operators all subtrac - Enumeration subtraction. Every enumeration type implicitly provides the following predefined operator, where `E` is the enum type, and `U` is the underlying type of `E`: ```csharp - U operator –(E x, E y); + U operator -(E x, E y); ``` This operator is evaluated exactly as `(U)((U)x – (U)y)`. In other words, the operator computes the difference between the ordinal values of `x` and `y`, and the type of the result is the underlying type of the enumeration. ```csharp - E operator –(E x, U y); + E operator -(E x, U y); ``` This operator is evaluated exactly as `(E)((U)x – y)`. In other words, the operator subtracts a value from the underlying type of the enumeration, yielding a value of the enumeration. @@ -4281,7 +4301,7 @@ The predefined subtraction operators are listed below. The operators all subtrac - Delegate removal. Every delegate type implicitly provides the following predefined operator, where `D` is the delegate type: ```csharp - D operator –(D x, D y); + D operator -(D x, D y); ``` The semantics are as follows: @@ -4355,6 +4375,8 @@ The predefined shift operators are listed below. ```csharp int operator <<(int x, int count); uint operator <<(uint x, int count); + nint operator <<(nint x, int count); + nuint operator <<(nuint x, int count); long operator <<(long x, int count); ulong operator <<(ulong x, int count); ``` @@ -4367,20 +4389,22 @@ The predefined shift operators are listed below. ```csharp int operator >>(int x, int count); uint operator >>(uint x, int count); + nint operator >>(nint x, int count); + nuint operator >>(nuint x, int count); long operator >>(long x, int count); ulong operator >>(ulong x, int count); ``` The `>>` operator shifts `x` right by a number of bits computed as described below. - When `x` is of type `int` or `long`, the low-order bits of `x` are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero if `x` is non-negative and set to one if `x` is negative. + When `x` is of type `int`, `nint`, or `long`, the low-order bits of `x` are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero if `x` is non-negative and set to one if `x` is negative. - When `x` is of type `uint` or `ulong`, the low-order bits of `x` are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero. + When `x` is of type `uint`, `nuint`, or `ulong`, the low-order bits of `x` are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero. For the predefined operators, the number of bits to shift is computed as follows: -- When the type of `x` is `int` or `uint`, the shift count is given by the low-order five bits of `count`. In other words, the shift count is computed from `count & 0x1F`. -- When the type of `x` is `long` or `ulong`, the shift count is given by the low-order six bits of `count`. In other words, the shift count is computed from `count & 0x3F`. +- When the type of `x` is `int` or `uint`, the shift count is given by the low-order five bits of `count`. In other words, the shift count is computed from `count & 0x1F`. This also applies when the type of `x` is `nint` or `nuint`, and those types have the same size and representation as `int` and `uint`, respectively. +- When the type of `x` is `long` or `ulong`, the shift count is given by the low-order six bits of `count`. In other words, the shift count is computed from `count & 0x3F`. This also applies when the type of `x` is `nint` or `nuint`, and those types have the same size and representation as `long` and `ulong`, respectively. If the resulting shift count is zero, the shift operators simply return the value of `x`. @@ -4453,31 +4477,43 @@ The predefined integer comparison operators are: ```csharp bool operator ==(int x, int y); bool operator ==(uint x, uint y); +bool operator ==(nint x, nint y); +bool operator ==(nuint x, nuint y); bool operator ==(long x, long y); bool operator ==(ulong x, ulong y); bool operator !=(int x, int y); bool operator !=(uint x, uint y); +bool operator !=(nint x, nint y); +bool operator !=(nuint x, nuint y); bool operator !=(long x, long y); bool operator !=(ulong x, ulong y); bool operator <(int x, int y); bool operator <(uint x, uint y); +bool operator <(nint x, nint y); +bool operator <(nuint x, nuint y); bool operator <(long x, long y); bool operator <(ulong x, ulong y); bool operator >(int x, int y); bool operator >(uint x, uint y); +bool operator >(nint x, nint y); +bool operator >(nuint x, nuint y); bool operator >(long x, long y); bool operator >(ulong x, ulong y); bool operator <=(int x, int y); bool operator <=(uint x, uint y); +bool operator <=(nint x, nint y); +bool operator <=(nuint x, nuint y); bool operator <=(long x, long y); bool operator <=(ulong x, ulong y); bool operator >=(int x, int y); bool operator >=(uint x, uint y); +bool operator >=(nint x, nint y); +bool operator >=(nuint x, nuint y); bool operator >=(long x, long y); bool operator >=(ulong x, ulong y); ``` @@ -4519,7 +4555,7 @@ If either operand is NaN, the result is `false` for all operators except `!=`, When neither operand is NaN, the operators compare the values of the two floating-point operands with respect to the ordering ```csharp -–∞ < –max < ... < –min < –0.0 == +0.0 < +min < ... < +max < +∞ +-∞ < -max < ... < -min < -0.0 == +0.0 < +min < ... < +max < +∞ ``` where `min` and `max` are the smallest and largest positive finite values that can be represented in the given floating-point format. Notable effects of this ordering are: @@ -4926,21 +4962,27 @@ The predefined integer logical operators are: ```csharp int operator &(int x, int y); uint operator &(uint x, uint y); +nint operator &(nint x, nint y); +nuint operator &(nuint x, nuint y); long operator &(long x, long y); ulong operator &(ulong x, ulong y); int operator |(int x, int y); uint operator |(uint x, uint y); +nint operator |(nint x, nint y); +nuint operator |(nuint x, nuint y); long operator |(long x, long y); ulong operator |(ulong x, ulong y); int operator ^(int x, int y); uint operator ^(uint x, uint y); +nint operator ^(nint x, nint y); +nuint operator ^(nuint x, nuint y); long operator ^(long x, long y); ulong operator ^(ulong x, ulong y); ``` -The `&` operator computes the bitwise logical AND of the two operands, the `|` operator computes the bitwise logical OR of the two operands, and the `^` operator computes the bitwise logical exclusive OR of the two operands. No overflows are possible from these operations. +The `&` operator computes the bitwise logical AND of the two operands, the `|` operator computes the bitwise logical OR of the two operands, and the `^` operator computes the bitwise logical exclusive OR of the two operands. No overflows are possible from these operations. Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined integer logical operators defined above are also predefined. @@ -7025,11 +7067,12 @@ constant_expression ; ``` -A constant expression shall either have the value `null` or one of the following types: +A constant expression shall have the value `null`, or be of a value type or a reference type. If a constant expression has a value type, that type shall be one of the following: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool,` or any enumeration type. If a constant expression has a reference type, the expression shall: -- `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool`, `string`; -- an enumeration type; or -- a default value expression ([§12.8.21](expressions.md#12821-default-value-expressions)) for a reference type. +- have a value of type `string`; or +- be a default value expression ([§12.8.21](expressions.md#12821-default-value-expressions)) of reference type. + +A *constant_expression* of type `nint` shall have a value in the range \[-2147483648, 2147483647\]. A *constant_expression* of type `nuint` shall have a value in the range \[0, 4294967295\]. Only the following constructs are permitted in constant expressions: @@ -7079,6 +7122,26 @@ Whenever an expression fulfills the requirements listed above, the expression is The compile-time evaluation of constant expressions uses the same rules as run-time evaluation of non-constant expressions, except that where run-time evaluation would have thrown an exception, compile-time evaluation causes a compile-time error to occur. +The compile-time evaluation of a *constant_expression* shall: + +- treat all values of type `nint` as `System.Int32`; +- treat all values of type `nuint` as `System.UInt32`; +- and otherwise use the same evaluation rules as for run-time non-constant expressions. + +If any `nint`/`nuint` values are not representable as `Int32`/`UInt32`, or the run-time evaluation of the whole expression would throw an exception, then a compile-time error shall be produced. + +> *Example*: These rules mean that an expression involving native integers which is superficially valid as a *constant_expression* may only be valid as an *expression* evaluated at runtime using the full implementation-defined native integer precision. +> +> +> ```csharp +> public class C +> { +> private const nint hugeNumber = int.MaxValue + 1; +> } +> ``` +> +> The preceding example emits an error because the result of the expression `int.MaxValue + 1` isn't representable as `Int32`. On machines where the size of a `nint` is greater than 32 bits, the expression would succeed at runtime. *end example* + Unless a constant expression is explicitly placed in an `unchecked` context, overflows that occur in integral-type arithmetic operations and conversions during the compile-time evaluation of the expression always cause compile-time errors ([§12.8.20](expressions.md#12820-the-checked-and-unchecked-operators)). Constant expressions are required in the contexts listed below and this is indicated in the grammar by using *constant_expression*. In these contexts, a compile-time error occurs if an expression cannot be fully evaluated at compile-time. @@ -7092,7 +7155,7 @@ Constant expressions are required in the contexts listed below and this is indic - Attributes ([§23](attributes.md#23-attributes)) - In a *constant_pattern* ([§11.2.3](patterns.md#1123-constant-pattern)) -An implicit constant expression conversion ([§10.2.11](conversions.md#10211-implicit-constant-expression-conversions)) permits a constant expression of type `int` to be converted to `sbyte`, `byte`, `short`, `ushort`, `uint`, or `ulong`, provided the value of the constant expression is within the range of the destination type. +An implicit constant expression conversion ([§10.2.11](conversions.md#10211-implicit-constant-expression-conversions)) permits a constant expression of type `int` to be converted to `sbyte`, `byte`, `short`, `ushort`, `uint`, `nint`, `nuint`, or `ulong`, provided the value of the constant expression is within the range of the destination type. ## 12.26 Boolean expressions diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index a86d23aed..94ac6bebf 100644 --- a/standard/lexical-structure.md +++ b/standard/lexical-structure.md @@ -606,12 +606,13 @@ A ***contextual keyword*** is an identifier-like sequence of characters that has ```ANTLR contextual_keyword - : 'add' | 'alias' | 'ascending' | 'async' | 'await' - | 'by' | 'descending' | 'dynamic' | 'equals' | 'from' - | 'get' | 'global' | 'group' | 'into' | 'join' - | 'let' | 'nameof' | 'notnull' | 'on' | 'orderby' - | 'partial' | 'remove' | 'select' | 'set' | 'unmanaged' - | 'value' | 'var' | 'when' | 'where' | 'yield' + : 'add' | 'alias' | 'ascending' | 'async' | 'await' + | 'by' | 'descending' | 'dynamic' | 'equals' | 'from' + | 'get' | 'global' | 'group' | 'into' | 'join' + | 'let' | 'nameof' | 'nint' | 'notnull' | 'nuint' + | 'on' | 'orderby' | 'partial' | 'remove' | 'select' + | 'set' | 'unmanaged' | 'value' | 'var' | 'when' + | 'where' | 'yield' ; ``` @@ -665,6 +666,8 @@ The type of a *boolean_literal* is `bool`. Integer literals are used to write values of types `int`, `uint`, `long`, and `ulong`. Integer literals have three possible forms: decimal, hexadecimal, and binary. +> *Note:* There is no way to write literal values of type `nint` and `nuint`. Instead, implicit or explicit casts of other integral constant values may be used. *end note* + ```ANTLR Integer_Literal : Decimal_Integer_Literal diff --git a/standard/portability-issues.md b/standard/portability-issues.md index 40f1f9eb8..486636934 100644 --- a/standard/portability-issues.md +++ b/standard/portability-issues.md @@ -35,6 +35,7 @@ A conforming implementation is required to document its choice of behavior in ea 1. Whether or not finalizers are run as part of application termination. ([§7.2](basic-concepts.md#72-application-termination)) 1. Whether APIs allow a finalizer to be run more than once. ([§7.9](basic-concepts.md#79-automatic-memory-management)) 1. Whether or not finalizers are run as part of application termination. ([§7.9](basic-concepts.md#79-automatic-memory-management)) +1. The size and value range of the types `nint` and `nuint`. ([§8.3.6](types.md#836-integral-types)) 1. The API surface provided by `Expression` beyond the requirement for a `Compile` method. ([§8.6](types.md#86-expression-tree-types)) 1. The precise structure of the expression tree, as well as the exact process for creating it, when an anonymous function is converted to an expression-tree. ([§10.7.3](conversions.md#1073-evaluation-of-lambda-expression-conversions-to-expression-tree-types)) 1. The reason a conversion to a compatible delegate type may fail at compile-time. ([§10.7.3](conversions.md#1073-evaluation-of-lambda-expression-conversions-to-expression-tree-types)) diff --git a/standard/statements.md b/standard/statements.md index 925564544..903b2bce1 100644 --- a/standard/statements.md +++ b/standard/statements.md @@ -744,8 +744,8 @@ A *switch_statement* consists of the keyword `switch`, followed by a *tuple_expr The ***governing type*** of a `switch` statement is established by the switch’s *selector_expression*. -- If the type of the switch’s *selector_expression* is `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `bool`, `string`, or an *enum_type*, or if it is the nullable value type corresponding to one of these types, then that is the governing type of the `switch` statement. -- Otherwise, if exactly one user-defined implicit conversion exists from the type of the switch’s *selector_expression* to one of the following possible governing types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `string`, or, a nullable value type corresponding to one of those types, then the converted type is the governing type of the `switch` statement. +- If the type of the switch’s *selector_expression* is `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `bool`, `string`, or an *enum_type*, or if it is the nullable value type corresponding to one of these types, then that is the governing type of the `switch` statement. +- Otherwise, if exactly one user-defined implicit conversion exists from the type of the switch’s *selector_expression* to one of the following possible governing types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `string`, or, a nullable value type corresponding to one of those types, then the converted type is the governing type of the `switch` statement. - Otherwise, the governing type of the `switch` statement is the type of the switch’s *selector_expression*. It is an error if no such type exists. There can be at most one `default` label in a `switch` statement. diff --git a/standard/types.md b/standard/types.md index 865f99dfb..4ca84c863 100644 --- a/standard/types.md +++ b/standard/types.md @@ -151,7 +151,7 @@ Delegate types are described in [§21](delegates.md#21-delegates). ### 8.3.1 General -A value type is either a struct type or an enumeration type. C# provides a set of predefined struct types called the ***simple type***s. The simple types are identified through keywords. +A value type is either a struct type or an enumeration type. C# provides a set of predefined struct types called the ***simple type***s. The simple types are identified through keywords and contextual keywords. ```ANTLR value_type @@ -188,6 +188,8 @@ integral_type | 'ushort' | 'int' | 'uint' + | 'nint' + | 'nuint' | 'long' | 'ulong' | 'char' @@ -215,6 +217,8 @@ nullable_value_type ; ``` +Because the names `nint` and `nuint` are not keywords there is syntactic ambiguity between recognising them as a *type_name* or a *value_type*. If type resolution (§7.8.1) on either of these names succeeds, that name shall be recognised as a *type_name*; otherwise it shall be recognised as a *value_type*. + Unlike a variable of a reference type, a variable of a value type can contain the value `null` only if the value type is a nullable value type ([§8.3.12](types.md#8312-nullable-value-types)). For every non-nullable value type there is a corresponding nullable value type denoting the same set of values plus the value `null`. Assignment to a variable of a value type creates a *copy* of the value being assigned. This differs from assignment to a variable of a reference type, which copies the reference but not the object identified by the reference. @@ -230,7 +234,7 @@ Note that `System.ValueType` is not itself a *value_type*. Rather, it is a *clas All value types implicitly declare a public parameterless instance constructor called the ***default constructor***. The default constructor returns a zero-initialized instance known as the ***default value*** for the value type: - For all *simple_type*s, the default value is the value produced by a bit pattern of all zeros: - - For `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, and `ulong`, the default value is `0`. + - For `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, and `ulong`, the default value is `0`. - For `char`, the default value is `'\x0000'`. - For `float`, the default value is `0.0f`. - For `double`, the default value is `0.0d`. @@ -271,7 +275,7 @@ A struct type is a value type that can declare constants, fields, methods, prope ### 8.3.5 Simple types -C# provides a set of predefined `struct` types called the simple types. The simple types are identified through keywords, but these keywords are simply aliases for predefined `struct` types in the `System` namespace, as described in the table below. +Except for `nint` and `nuint`, the simple types are aliases for predefined `struct` types in the `System` namespace, as described in the table below. **Keyword** | **Aliased type** ----------- | ------------------ @@ -281,6 +285,8 @@ C# provides a set of predefined `struct` types called the simple types. The simp `ushort` | `System.UInt16` `int` | `System.Int32` `uint` | `System.UInt32` + `nint` | none; see below + `nuint` | none; see below `long` | `System.Int64` `ulong` | `System.UInt64` `char` | `System.Char` @@ -289,9 +295,9 @@ C# provides a set of predefined `struct` types called the simple types. The simp `bool` | `System.Boolean` `decimal` | `System.Decimal` -Because a simple type aliases a struct type, every simple type has members. +Every simple type has members. Each simple type that is an alias for a predefined struct type, has that struct type’s members. -> *Example*: `int` has the members declared in `System.Int32` and the members inherited from `System.Object`, and the following statements are permitted: +> *Example*: `int` has any implementation-specific members declared in `System.Int32` and the members (required and implementation specific) inherited from `System.Object`, and the following statements are permitted: > > > ```csharp @@ -313,9 +319,17 @@ Because a simple type aliases a struct type, every simple type has members. > > *end note*. + + +The types `nint` and `nuint` are represented by the types `System.IntPtr` and `System.UIntPtr`, respectively, and are *not* aliases for these types. In this context being *represented by* means: + +- The only members directly accessible for `nint` and `nuint` are the required methods of `Object` ([§C.2](standard-library.md#c2-standard-library-types-defined-in-isoiec-23271)). Any other members of `System.IntPtr` and `System.UIntPtr` may be accessed via those types. +- Operations performed through `dynamic` binding on `System.IntPtr` and `System.UIntPtr` values do not have access to the `nint` and `nuint` operators. +- In all other respects `nint` and `nuint` behave as if they are aliases of `System.IntPtr` and `System.UIntPtr`. + ### 8.3.6 Integral types -C# supports nine integral types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, and `char`. The integral types have the following sizes and ranges of values: +C# supports the following integral types, with the sizes and value ranges, as shown: - The `sbyte` type represents signed 8-bit integers with values from `-128` to `127`, inclusive. - The `byte` type represents unsigned 8-bit integers with values from `0` to `255`, inclusive. @@ -323,6 +337,8 @@ C# supports nine integral types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uin - The `ushort` type represents unsigned 16-bit integers with values from `0` to `65535`, inclusive. - The `int` type represents signed 32-bit integers with values from `-2147483648` to `2147483647`, inclusive. - The `uint` type represents unsigned 32-bit integers with values from `0` to `4294967295`, inclusive. +- The `nint` type represents a ***native signed integer*** whose size and value range are implementation-defined, but which shall be either that of `int` or `long`. +- The `nuint` type represents a ***native unsigned integer*** whose size and value range are implementation-defined, but which shall be either that of `uint` or `ulong`. The size of a native unsigned integer shall be the same as that of a native signed integer. - The `long` type represents signed 64-bit integers with values from `-9223372036854775808` to `9223372036854775807`, inclusive. - The `ulong` type represents unsigned 64-bit integers with values from `0` to `18446744073709551615`, inclusive. - The `char` type represents unsigned 16-bit integers with values from `0` to `65535`, inclusive. The set of possible values for the `char` type corresponds to the Unicode character set. @@ -330,7 +346,9 @@ C# supports nine integral types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uin All signed integral types are represented using two’s complement format. -The *integral_type* unary and binary operators always operate with signed 32-bit precision, unsigned 32-bit precision, signed 64-bit precision, or unsigned 64-bit precision, as detailed in [§12.4.7](expressions.md#1247-numeric-promotions). +The *integral_type* unary and binary operators always operate with signed 32-bit precision, unsigned 32-bit precision, signed 64-bit precision, unsigned 64-bit precision, native signed precision, or native unsigned precision, as detailed in [§12.4.7](expressions.md#1247-numeric-promotions). + +> *Note*: Native precision means 32-bit on 32-bit platforms and 64-bit on 64-bit platforms. Operators on `nint` and `nuint` use native precision rather than being promoted to a larger type. *end note* The `char` type is classified as an integral type, but it differs from the other integral types in two ways: @@ -713,7 +731,7 @@ unmanaged_type An *unmanaged_type* is any type that is neither a *reference_type* nor a *type_parameter* that is not constrained to be unmanaged, and contains no instance fields whose type is not an *unmanaged_type*. In other words, an *unmanaged_type* is one of the following: -- `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, or `bool`. +- `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, or `bool`. - Any *enum_type*. - Any user-defined *struct_type* that contains instance fields of *unmanaged_type*s only. - Any type parameter which is constrained to be unmanaged. diff --git a/standard/unsafe-code.md b/standard/unsafe-code.md index 053cafdff..577a7ba70 100644 --- a/standard/unsafe-code.md +++ b/standard/unsafe-code.md @@ -281,8 +281,8 @@ In an unsafe context, the set of available implicit conversions ([§10.2](conver Additionally, in an unsafe context, the set of available explicit conversions ([§10.3](conversions.md#103-explicit-conversions)) is extended to include the following explicit pointer conversions: - From any *pointer_type* to any other *pointer_type*. -- From `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, or `ulong` to any *pointer_type*. -- From any *pointer_type* to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, or `ulong`. +- From `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, or `ulong` to any *pointer_type*. +- From any *pointer_type* to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, or `ulong`. Finally, in an unsafe context, the set of standard implicit conversions ([§10.4.2](conversions.md#1042-standard-implicit-conversions)) includes the following pointer conversions: @@ -486,7 +486,7 @@ pointer_element_access When recognising a *primary_expression* if both the *element_access* and *pointer_element_access* ([§24.6.4](unsafe-code.md#2464-pointer-element-access)) alternatives are applicable then the latter shall be chosen if the embedded *primary_expression* is of pointer type ([§24.3](unsafe-code.md#243-pointer-types)). -In a pointer element access of the form `P[E]`, `P` shall be an expression of a pointer type other than `void*`, and `E` shall be an expression that can be implicitly converted to `int`, `uint`, `long`, or `ulong`. +In a pointer element access of the form `P[E]`, `P` shall be an expression of a pointer type other than `void*`, and `E` shall be an expression that can be implicitly converted to `int`, `uint`, `nint`, `nuint`, `long`, or `ulong`. A pointer element access of the form `P[E]` is evaluated exactly as `*(P + E)`. For a description of the pointer indirection operator (`*`), see [§24.6.2](unsafe-code.md#2462-pointer-indirection). For a description of the pointer addition operator (`+`), see [§24.6.7](unsafe-code.md#2467-pointer-arithmetic). @@ -616,6 +616,8 @@ T* operator –(T* x, ulong y); long operator –(T* x, T* y); ``` +There are no predefined operators for pointer addition or subtraction with native integer ([§8.3.6](types.md#836-integral-types)) offsets. Instead, `nint` and `nuint` values shall be promoted to `long` and `ulong`, respectively, with pointer arithmetic using the predefined operators for those types. + Given an expression `P` of a pointer type `T*` and an expression `N` of type `int`, `uint`, `long`, or `ulong`, the expressions `P + N` and `N + P` compute the pointer value of type `T*` that results from adding `N * sizeof(T)` to the address given by `P`. Likewise, the expression `P – N` computes the pointer value of type `T*` that results from subtracting `N * sizeof(T)` from the address given by `P`. Given two expressions, `P` and `Q`, of a pointer type `T*`, the expression `P – Q` computes the difference between the addresses given by `P` and `Q` and then divides that difference by `sizeof(T)`. The type of the result is always `long`. In effect, `P - Q` is computed as `((long)(P) - (long)(Q)) / sizeof(T)`. @@ -949,7 +951,7 @@ A fixed-size buffer declaration may include a set of attributes ([§23](attribut A fixed-size buffer declaration is not permitted to include the `static` modifier. -The buffer element type of a fixed-size buffer declaration specifies the element type of the buffers introduced by the declaration. The buffer element type shall be one of the predefined types `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, or `bool`. +The buffer element type of a fixed-size buffer declaration specifies the element type of the buffers introduced by the declaration. The buffer element type shall be one of the predefined types `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, `double`, or `bool`. The buffer element type is followed by a list of fixed-size buffer declarators, each of which introduces a new member. A fixed-size buffer declarator consists of an identifier that names the member, followed by a constant expression enclosed in `[` and `]` tokens. The constant expression denotes the number of elements in the member introduced by that fixed-size buffer declarator. The type of the constant expression shall be implicitly convertible to type `int`, and the value shall be a non-zero positive integer. diff --git a/standard/variables.md b/standard/variables.md index e5b3d435f..c76b64d3e 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1012,7 +1012,7 @@ variable_reference ## 9.6 Atomicity of variable references -Reads and writes of the following data types shall be atomic: `bool`, `char`, `byte`, `sbyte`, `short`, `ushort`, `uint`, `int`, `float`, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including `long`, `ulong`, `double`, and `decimal`, as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement. +Reads and writes of the following data types shall be atomic: `bool`, `char`, `byte`, `sbyte`, `short`, `ushort`, `uint`, `int`, `nint`, `nuint`, `float`, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including `long`, `ulong`, `double`, and `decimal`, as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement. ## 9.7 Reference variables and returns