From 2eaa27f2a8455a66084a25d0db6c8931293a3827 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 15:14:07 -0400 Subject: [PATCH 01/26] Add support for native-sized integers Add support for native-sized integers Add support for native-sized integers Add support for native-sized integers Add support for native-sized integers Add support for native-sized integers Add support for native-sized integers Add support for native-sized integers Add support for native-sized integers fix example name correct link fix md formatting fix formatting fix md formatting made minor tweaks Add 2 new types make minor tweak Add native types to pointer indexing Update standard/expressions.md Co-authored-by: Kalle Olavi Niemitalo Update standard/expressions.md Co-authored-by: Kalle Olavi Niemitalo Add support for native-sized integers fix missing commit --- standard/arrays.md | 2 +- standard/classes.md | 4 +- standard/conversions.md | 65 ++++++++++++++++++++--------- standard/enums.md | 2 +- standard/expressions.md | 46 ++++++++++++--------- standard/lexical-structure.md | 15 +++---- standard/statements.md | 4 +- standard/types.md | 78 ++++++++++++++++++++++++++++++++--- standard/unsafe-code.md | 10 +++-- standard/variables.md | 2 +- 10 files changed, 165 insertions(+), 63 deletions(-) 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..a90dd7a1f 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -115,22 +115,28 @@ 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`. + +For the compound types array, nullable type, constructed type, and tuple, there is an identity conversion between native integers ([§8.3.6]( types.md#836-integral-types)) and their underlying types. + ### 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 `long`, `nuint`, `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,7 +321,7 @@ 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`, `nint`, `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. ### 10.2.12 Implicit conversions involving type parameters @@ -413,18 +419,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`, `long`, `ulong`, or `char`. +- From `nuint` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, 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 +466,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. @@ -824,6 +832,23 @@ Evaluation of a nullable conversion based on an underlying conversion from `S` - If the nullable conversion is from `S` to `T?`, the conversion is evaluated as the underlying conversion from `S` to `T` followed by a wrapping from `T` to `T?`. - If the nullable conversion is from `S?` to `T`, the conversion is evaluated as an unwrapping from `S?` to `S` followed by the underlying conversion from `S` to `T`. +Conversion from `A` to `Nullable` is: + +- an implicit nullable conversion if there is an identity conversion or implicit conversion from `A` to `B`; +- an explicit nullable conversion if there is an explicit conversion from `A` to `B`; +- otherwise, invalid. + +Conversion from `Nullable` to `B` is: + +- an explicit nullable conversion if there is an identity conversion or implicit or explicit numeric conversion from `A` to `B`; +- otherwise, invalid. + +Conversion from `Nullable` to `Nullable` is: + +- an identity conversion if there is an identity conversion from `A` to `B`; +- an explicit nullable conversion if there is an implicit or explicit numeric conversion from `A` to `B`; +- otherwise, invalid. + ### 10.6.2 Lifted conversions Given a user-defined conversion operator that converts from a non-nullable value type `S` to a non-nullable value type `T`, a ***lifted conversion operator*** exists that converts from `S?` to `T?`. This lifted conversion operator performs an unwrapping from `S?` to `S` followed by the user-defined conversion from `S` to `T` followed by a wrapping from `T` to `T?`, except that a null valued `S?` converts directly to a null valued `T?`. A lifted conversion operator has the same implicit or explicit classification as its underlying user-defined conversion operator. diff --git a/standard/enums.md b/standard/enums.md index 12e8c745c..4928d103f 100644 --- a/standard/enums.md +++ b/standard/enums.md @@ -44,7 +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. +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. > *Note*: The `char` type cannot be used as an underlying type, either by keyword or via an `integral_type_name`. *end note* diff --git a/standard/expressions.md b/standard/expressions.md index ce868655b..3947dc11e 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -291,13 +291,15 @@ When overload resolution rules ([§12.6.4](expressions.md#1264-overload-resoluti > *Example*: For the operation `b * s`, where `b` is a `byte` and `s` is a `short`, overload resolution selects `operator *(int, int)` as the best operator. Thus, the effect is that `b` and `s` are converted to `int`, and the type of the result is `int`. Likewise, for the operation `i * d`, where `i` is an `int` and `d` is a `double`, `overload` resolution selects `operator *(double, double)` as the best operator. *end example* +There are no predefined operators for dealing with native integer ([§8.3.6]( types.md#836-integral-types)). Instead, `nint` and `nuint` values shall be promoted to `long` and `ulong`, respectively, and the resulting corresponding predefined operators used instead. + **End of informative text.** #### 12.4.7.2 Unary numeric promotions **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` or `nint` to type `long`. **End of informative text.** @@ -310,10 +312,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`, `nint`, or `int`, 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* @@ -2274,17 +2278,13 @@ In this case the compile-time type of the *element_access* depends on the compil If the *primary_expression* of an *element_access* is: -- a value of an array type, the *element_access* is an array access ([§12.8.12.2](expressions.md#128122-array-access)); -- a value of `string` type, the *element_access* is a string access ([§12.8.12.3](expressions.md#128123-string-access)); -- otherwise, the *primary_expression* shall be a variable or value of a class, struct, or interface type that has one or more indexer members, in which case the *element_access* is an indexer access ([§12.8.12.4](expressions.md#128124-indexer-access)). - #### 12.8.12.2 Array access For an array access the *argument_list* shall not contain named arguments or by-reference arguments ([§15.6.2.3](classes.md#15623-by-reference-parameters)). 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. @@ -2591,7 +2591,9 @@ The run-time processing of an *object_creation_expression* of the form new `T(A) - An instance of type `T` is created by allocating a temporary local variable. Since an instance constructor of a *struct_type* is required to definitely assign a value to each field of the instance being created, no initialization of the temporary variable is necessary. - The instance constructor is invoked according to the rules of function member invocation ([§12.6.6](expressions.md#1266-function-member-invocation)). A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this. -##### 12.8.17.2.2 Object initializers +`new nint()` is equivalent to `(nint)0`, and `new nuint()` is equivalent to `(nuint)0`. + +#### 12.8.17.2.2 Object initializers An ***object initializer*** specifies values for zero or more fields, properties, or indexed elements of an object. @@ -3393,7 +3395,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 @@ -3636,7 +3638,9 @@ For an operation of the form `–x`, unary operator overload resolution ([§12. 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. 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: @@ -4035,7 +4039,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 @@ -4084,7 +4088,7 @@ The predefined remainder operators are listed below. The operators all compute t 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 @@ -4373,14 +4377,14 @@ The predefined shift operators are listed below. 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`. @@ -7027,10 +7031,12 @@ constant_expression A constant expression shall either have the value `null` or one of the following types: -- `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool`, `string`; +- `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `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. +A *constant_expression* of type `nint` shall have a value in the range \[`int.MinValue`,`int.MaxValue`\]. A *constant_expression* of type `nuint` shall have a value in the range \[`uint.MinValue`,`uint.MaxValue`\]. + Only the following constructs are permitted in constant expressions: - Literals (including the `null` literal). @@ -7079,6 +7085,8 @@ 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. +Due to the implementation-defined nature of native integers ([§8.3.6](types.md#836-integral-types)), constant folding operations on `nint` and `nuint` operands shall be evaluated as if they had type `System.Int32` and `System.UInt32`, respectively. If the operation results in a constant value representable in 32 bits, constant folding may be performed at compile-time. Otherwise, the operation is executed at runtime and is not considered to be a constant. + 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 +7100,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..52f5fbdc9 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' ; ``` @@ -663,7 +664,7 @@ The type of a *boolean_literal* is `bool`. #### 6.4.5.3 Integer literals -Integer literals are used to write values of types `int`, `uint`, `long`, and `ulong`. Integer literals have three possible forms: decimal, hexadecimal, and binary. +Integer literals are used to write values of types `int`, `uint`, `long`, and `ulong`. (There is no way to write values of type `nint` and `nuint`. Instead, implicit or explicit casts of other integral constant values may be used.) Integer literals have three possible forms: decimal, hexadecimal, and binary. ```ANTLR Integer_Literal 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..c52b33562 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' @@ -230,7 +232,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 +273,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 +283,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,7 +293,7 @@ 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: > @@ -313,9 +317,68 @@ Because a simple type aliases a struct type, every simple type has members. > > *end note*. +Although `nint` and `nuint` shall be represented by the types `System.IntPtr` and `System.UIntPtr`, respectively, `nint` and `nuint` are *not* aliases for those types. As such, not all members of the corresponding `System` types are defined for `nint` and `nuint`. Instead, the compiler shall make available additional conversions and operations for the types `System.IntPtr` and `System.UIntPtr` when used in the context of native integer types. + +Consider the following: + + +```csharp +nint a1 = 1; // OK +System.IntPtr a2 = 1; // Error: no implicit conversion +``` + +While the implementation provides operations and conversions for `nint` and `nuint` that are appropriate for integer types, those operations and conversions are not available on the `System` type counterparts. Similarly, + + +```csharp +M((nint)1); + +static void M(dynamic d) +{ + var v = d >> 2; // RuntimeBinderException: '>>' cannot be applied to operands + // of type System.IntPtr/System.UIntPtr and int +} +``` + +The only constructor for `nint` or `nuint` is the parameter-less constructor. + +The following members of `System.IntPtr` and `System.UIntPtr` are explicitly excluded from `nint` or `nuint`: + +```csharp +// constructors +// arithmetic operators +// implicit and explicit conversions +public static readonly IntPtr Zero; // use 0 instead +public static int Size { get; } // use sizeof() instead +public static IntPtr Add(IntPtr pointer, int offset); +public static IntPtr Subtract(IntPtr pointer, int offset); +public int ToInt32(); +public long ToInt64(); +public void* ToPointer(); +``` + +The remaining members of `System.IntPtr` and `System.UIntPtr` are implicitly included in `nint` and `nuint`. These are: + +```csharp +public override bool Equals(object obj); +public override int GetHashCode(); +public override string ToString(); +public string ToString(string format); +``` + +Interfaces implemented by `System.IntPtr` and `System.UIntPtr` are implicitly included in `nint` and `nuint`, with occurrences of the underlying types replaced by the corresponding native integer types. For example, if `IntPtr` implements `ISerializable`, `IEquatable`, and `IComparable`, then `nint` implements `ISerializable`, `IEquatable`, and `IComparable`. + +`nint` and `System.IntPtr`, and `nuint` and `System.UIntPtr`, are considered equivalent for overriding, hiding, and implementing, however. + +Overloads cannot differ by `nint` and `System.IntPtr`, and `nuint` and `System.UIntPtr`, alone. However, overrides and implementations may differ by `nint` and `System.IntPtr`, or `nuint` and `System.UIntPtr`, alone. + +Methods hide other methods that differ by `nint` and `System.IntPtr`, or `nuint` and `System.UIntPtr`, alone. + +`typeof(nint)` is `typeof(System.IntPtr)`, and `typeof(nuint)` is `typeof(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 +386,9 @@ 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. + > *Note*: Unlike the other integral types `nint` and `nuint` do not have `const` fields called `MinValue` and `MaxValue`. *end note* - 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. @@ -713,7 +779,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..356df8815 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 From 8df9554384ed0d6cf80dddf102e2697111d72cd4 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 7 Nov 2025 15:38:31 -0500 Subject: [PATCH 02/26] missing back tick --- standard/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index 3947dc11e..b550cd56b 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2284,7 +2284,7 @@ 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`, `nint`, nuint`, `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. From 8393fd2132b553544c220117903a6f3635e565f1 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 9 Nov 2025 11:35:40 -0500 Subject: [PATCH 03/26] Add entry for nint/nuint --- standard/portability-issues.md | 1 + 1 file changed, 1 insertion(+) 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)) From 91861a99ba99d95cd1fef74976effb0e74bed33d Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 16 Nov 2025 09:30:11 -0500 Subject: [PATCH 04/26] Apply suggestions from code review --- standard/conversions.md | 2 +- standard/types.md | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/standard/conversions.md b/standard/conversions.md index a90dd7a1f..daedaee7d 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -117,7 +117,7 @@ In most cases, an identity conversion has no effect at runtime. However, since f There is an identity conversion between `nint` and `System.IntPtr`, and between `nuint` and `System.UIntPtr`. -For the compound types array, nullable type, constructed type, and tuple, there is an identity conversion between native integers ([§8.3.6]( types.md#836-integral-types)) and their underlying types. +For the compound types array, nullable type, constructed type, and tuple, there is an identity conversion between native integers ([§8.3.6](types.md#836-integral-types)) and their underlying types. ### 10.2.3 Implicit numeric conversions diff --git a/standard/types.md b/standard/types.md index c52b33562..568c82d9e 100644 --- a/standard/types.md +++ b/standard/types.md @@ -321,13 +321,7 @@ Although `nint` and `nuint` shall be represented by the types `System.IntPtr` an Consider the following: - -```csharp -nint a1 = 1; // OK -System.IntPtr a2 = 1; // Error: no implicit conversion -``` - -While the implementation provides operations and conversions for `nint` and `nuint` that are appropriate for integer types, those operations and conversions are not available on the `System` type counterparts. Similarly, +While the implementation provides operations and conversions for `nint` and `nuint` that are appropriate for integer types, those operations and conversions are not available on the `System` type counterparts. For example, ```csharp From ebf94dbaf4d553e22a79c5b9eb0dd9cdde9d4bdb Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 16 Nov 2025 09:32:40 -0500 Subject: [PATCH 05/26] Apply suggestions from code review --- standard/types.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/standard/types.md b/standard/types.md index 568c82d9e..3f67957cc 100644 --- a/standard/types.md +++ b/standard/types.md @@ -319,8 +319,6 @@ Every simple type has members. Each simple type that is an alias for a predefine Although `nint` and `nuint` shall be represented by the types `System.IntPtr` and `System.UIntPtr`, respectively, `nint` and `nuint` are *not* aliases for those types. As such, not all members of the corresponding `System` types are defined for `nint` and `nuint`. Instead, the compiler shall make available additional conversions and operations for the types `System.IntPtr` and `System.UIntPtr` when used in the context of native integer types. -Consider the following: - While the implementation provides operations and conversions for `nint` and `nuint` that are appropriate for integer types, those operations and conversions are not available on the `System` type counterparts. For example, From 4d7b6cb21cca744db8976344a78bbcfcb41bbd6f Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 9 Jan 2026 14:46:16 -0500 Subject: [PATCH 06/26] address open comments. - https://github.com/dotnet/csharpstandard/pull/1060#discussion_r2085211124: Take Nigel's suggestion, with minor wordsmithing. - https://github.com/dotnet/csharpstandard/pull/1060#discussion_r2085219962 No change in this PR. Addressed with Rex's work on Annex C. - For https://github.com/dotnet/csharpstandard/pull/1060#discussion_r2085239142: Use Nigel's suggestion. - For https://github.com/dotnet/csharpstandard/pull/1060#discussion_r2085614999: Took Nigel's suggestion. - https://github.com/dotnet/csharpstandard/pull/1060#discussion_r2101572606 No change. Deferred to when we address #729 --- standard/expressions.md | 18 +++++++++++++----- standard/types.md | 2 ++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index b550cd56b..babd606f0 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -7029,11 +7029,11 @@ constant_expression ; ``` -A constant expression shall either have the value `null` or one of the following types: +A constant expression may be either 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`, `nint`, `nuint`, `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`; +- have a value of `null`; 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 \[`int.MinValue`,`int.MaxValue`\]. A *constant_expression* of type `nuint` shall have a value in the range \[`uint.MinValue`,`uint.MaxValue`\]. @@ -7085,7 +7085,15 @@ 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. -Due to the implementation-defined nature of native integers ([§8.3.6](types.md#836-integral-types)), constant folding operations on `nint` and `nuint` operands shall be evaluated as if they had type `System.Int32` and `System.UInt32`, respectively. If the operation results in a constant value representable in 32 bits, constant folding may be performed at compile-time. Otherwise, the operation is executed at runtime and is not considered to be a constant. +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. + +> *Note*: 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. *end note* 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)). diff --git a/standard/types.md b/standard/types.md index 3f67957cc..69cb5e776 100644 --- a/standard/types.md +++ b/standard/types.md @@ -217,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*. In context if type resolution (§7.8.1) on either of these names succeeds then 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. From 6672808f206b7acd08f17af9259228c41e5aef09 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 9 Jan 2026 14:58:07 -0500 Subject: [PATCH 07/26] Address current comments. --- standard/expressions.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index babd606f0..73382df54 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -1140,10 +1140,10 @@ 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`, `nuint`, or `ulong` + - `S₁` is `long` and `S₂` is `nuint`, or `ulong` #### 12.6.4.8 Overloading in generic classes @@ -2278,6 +2278,10 @@ In this case the compile-time type of the *element_access* depends on the compil If the *primary_expression* of an *element_access* is: +- a value of an array type, the *element_access* is an array access ([§12.8.12.2](expressions.md#128122-array-access)); +- a value of `string` type, the *element_access* is a string access ([§12.8.12.3](expressions.md#128123-string-access)); +- otherwise, the *primary_expression* shall be a variable or value of a class, struct, or interface type that has one or more indexer members, in which case the *element_access* is an indexer access ([§12.8.12.4](expressions.md#128124-indexer-access)). + #### 12.8.12.2 Array access For an array access the *argument_list* shall not contain named arguments or by-reference arguments ([§15.6.2.3](classes.md#15623-by-reference-parameters)). From 0c4c32f281f9a6752cc986ae9a96dff021b48d6b Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 12 Jan 2026 14:41:08 -0500 Subject: [PATCH 08/26] Edit pass on `nint` Proofread and edit pass. --- standard/conversions.md | 13 ++----------- standard/expressions.md | 2 -- standard/lexical-structure.md | 4 +++- standard/types.md | 10 ++++++---- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/standard/conversions.md b/standard/conversions.md index daedaee7d..ab6430bff 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -324,6 +324,8 @@ 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`, `nint`, `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 `nint` is the same range as `int`. The range of constants for `nuint` is the same range as `uint`. + ### 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: @@ -838,17 +840,6 @@ Conversion from `A` to `Nullable` is: - an explicit nullable conversion if there is an explicit conversion from `A` to `B`; - otherwise, invalid. -Conversion from `Nullable` to `B` is: - -- an explicit nullable conversion if there is an identity conversion or implicit or explicit numeric conversion from `A` to `B`; -- otherwise, invalid. - -Conversion from `Nullable` to `Nullable` is: - -- an identity conversion if there is an identity conversion from `A` to `B`; -- an explicit nullable conversion if there is an implicit or explicit numeric conversion from `A` to `B`; -- otherwise, invalid. - ### 10.6.2 Lifted conversions Given a user-defined conversion operator that converts from a non-nullable value type `S` to a non-nullable value type `T`, a ***lifted conversion operator*** exists that converts from `S?` to `T?`. This lifted conversion operator performs an unwrapping from `S?` to `S` followed by the user-defined conversion from `S` to `T` followed by a wrapping from `T` to `T?`, except that a null valued `S?` converts directly to a null valued `T?`. A lifted conversion operator has the same implicit or explicit classification as its underlying user-defined conversion operator. diff --git a/standard/expressions.md b/standard/expressions.md index 73382df54..ff4d57180 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -291,8 +291,6 @@ When overload resolution rules ([§12.6.4](expressions.md#1264-overload-resoluti > *Example*: For the operation `b * s`, where `b` is a `byte` and `s` is a `short`, overload resolution selects `operator *(int, int)` as the best operator. Thus, the effect is that `b` and `s` are converted to `int`, and the type of the result is `int`. Likewise, for the operation `i * d`, where `i` is an `int` and `d` is a `double`, `overload` resolution selects `operator *(double, double)` as the best operator. *end example* -There are no predefined operators for dealing with native integer ([§8.3.6]( types.md#836-integral-types)). Instead, `nint` and `nuint` values shall be promoted to `long` and `ulong`, respectively, and the resulting corresponding predefined operators used instead. - **End of informative text.** #### 12.4.7.2 Unary numeric promotions diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index 52f5fbdc9..94ac6bebf 100644 --- a/standard/lexical-structure.md +++ b/standard/lexical-structure.md @@ -664,7 +664,9 @@ The type of a *boolean_literal* is `bool`. #### 6.4.5.3 Integer literals -Integer literals are used to write values of types `int`, `uint`, `long`, and `ulong`. (There is no way to write values of type `nint` and `nuint`. Instead, implicit or explicit casts of other integral constant values may be used.) Integer literals have three possible forms: decimal, hexadecimal, and binary. +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 diff --git a/standard/types.md b/standard/types.md index 69cb5e776..7602a7085 100644 --- a/standard/types.md +++ b/standard/types.md @@ -319,9 +319,9 @@ Every simple type has members. Each simple type that is an alias for a predefine > > *end note*. -Although `nint` and `nuint` shall be represented by the types `System.IntPtr` and `System.UIntPtr`, respectively, `nint` and `nuint` are *not* aliases for those types. As such, not all members of the corresponding `System` types are defined for `nint` and `nuint`. Instead, the compiler shall make available additional conversions and operations for the types `System.IntPtr` and `System.UIntPtr` when used in the context of native integer types. +Although `nint` and `nuint` shall be represented by the types `System.IntPtr` and `System.UIntPtr`, respectively, `nint` and `nuint` are *not* aliases for those types. As such, not all members of the corresponding `System` types are defined for `nint` and `nuint`. Instead, the compiler shall make available additional conversions, unary operators (§12.9) and arithmetic operators (§12.12) for the types `System.IntPtr` and `System.UIntPtr` when used in the context of native integer types. -While the implementation provides operations and conversions for `nint` and `nuint` that are appropriate for integer types, those operations and conversions are not available on the `System` type counterparts. For example, +While the language provides operations and conversions for `nint` and `nuint` that are appropriate for integer types, those operations and conversions are not available on the `System` type counterparts. For example, ```csharp @@ -362,14 +362,16 @@ public string ToString(string format); Interfaces implemented by `System.IntPtr` and `System.UIntPtr` are implicitly included in `nint` and `nuint`, with occurrences of the underlying types replaced by the corresponding native integer types. For example, if `IntPtr` implements `ISerializable`, `IEquatable`, and `IComparable`, then `nint` implements `ISerializable`, `IEquatable`, and `IComparable`. +`typeof(nint)` is `typeof(System.IntPtr)`, and `typeof(nuint)` is `typeof(System.UIntPtr)`. + + + `nint` and `System.IntPtr`, and `nuint` and `System.UIntPtr`, are considered equivalent for overriding, hiding, and implementing, however. Overloads cannot differ by `nint` and `System.IntPtr`, and `nuint` and `System.UIntPtr`, alone. However, overrides and implementations may differ by `nint` and `System.IntPtr`, or `nuint` and `System.UIntPtr`, alone. Methods hide other methods that differ by `nint` and `System.IntPtr`, or `nuint` and `System.UIntPtr`, alone. -`typeof(nint)` is `typeof(System.IntPtr)`, and `typeof(nuint)` is `typeof(System.UIntPtr)`. - ### 8.3.6 Integral types C# supports the following integral types, with the sizes and value ranges, as shown: From af72fb0a32ac3106c3760c8f3fec8fb370ef6c29 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 29 Jan 2026 14:39:14 -0500 Subject: [PATCH 09/26] Apply suggestions from code review Co-authored-by: Joseph Musser --- standard/conversions.md | 6 +++--- standard/expressions.md | 10 +++++----- standard/types.md | 2 +- standard/unsafe-code.md | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/standard/conversions.md b/standard/conversions.md index ab6430bff..fdb5aae86 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -128,7 +128,7 @@ The implicit numeric conversions are: - 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 `long`, `nuint`, `ulong`, `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`. @@ -427,8 +427,8 @@ The explicit numeric conversions are the conversions from a *numeric_type* to an - From `ushort` to `sbyte`, `byte`, `short`, 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`, `long`, `ulong`, or `char`. -- From `nuint` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `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`. diff --git a/standard/expressions.md b/standard/expressions.md index ff4d57180..c2cfb17b7 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -313,7 +313,7 @@ Binary numeric promotion occurs for the operands of the predefined `+`, `-`, `*` - 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`, `nint`, 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`. @@ -1140,8 +1140,8 @@ Given two types `T₁` and `T₂`, `T₁` is a ***better conversion target*** th - `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`, `nuint`, or `ulong` - `S₁` is `short` and `S₂` is `ushort`, `uint`, `nuint`, or `ulong` - - `S₁` is `int` and `S₂` is `uint`, `nuint`, `nuint`, or `ulong` - - `S₁` is `long` and `S₂` is `nuint`, or `ulong` + - `S₁` is `int` and `S₂` is `uint`, `nuint`, or `ulong` + - `S₁` is `long` and `S₂` is `nuint` or `ulong` #### 12.6.4.8 Overloading in generic classes @@ -2288,7 +2288,7 @@ The number of expressions in the *argument_list* shall be the same as the rank o - 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: @@ -2595,7 +2595,7 @@ The run-time processing of an *object_creation_expression* of the form new `T(A) `new nint()` is equivalent to `(nint)0`, and `new nuint()` is equivalent to `(nuint)0`. -#### 12.8.17.2.2 Object initializers +##### 12.8.17.2.2 Object initializers An ***object initializer*** specifies values for zero or more fields, properties, or indexed elements of an object. diff --git a/standard/types.md b/standard/types.md index 7602a7085..73de84c8a 100644 --- a/standard/types.md +++ b/standard/types.md @@ -364,7 +364,7 @@ Interfaces implemented by `System.IntPtr` and `System.UIntPtr` are implicitly in `typeof(nint)` is `typeof(System.IntPtr)`, and `typeof(nuint)` is `typeof(System.UIntPtr)`. - + `nint` and `System.IntPtr`, and `nuint` and `System.UIntPtr`, are considered equivalent for overriding, hiding, and implementing, however. diff --git a/standard/unsafe-code.md b/standard/unsafe-code.md index 356df8815..577a7ba70 100644 --- a/standard/unsafe-code.md +++ b/standard/unsafe-code.md @@ -616,7 +616,7 @@ 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. +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`. From 6ea9806d9e6220877745f009dfed2389a97533aa Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 29 Jan 2026 15:52:31 -0500 Subject: [PATCH 10/26] respond to first round feedback. --- standard/conversions.md | 3 ++- standard/expressions.md | 14 ++++++++++++-- standard/types.md | 9 +-------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/standard/conversions.md b/standard/conversions.md index fdb5aae86..61aabcc33 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 a 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: > @@ -324,7 +325,7 @@ 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`, `nint`, `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 `nint` is the same range as `int`. The range of constants for `nuint` is the same range as `uint`. +The range for constants of `nint` is the same range as `int`. The range of constants for `nuint` is the same range as `uint`. (§12.25) ### 10.2.12 Implicit conversions involving type parameters diff --git a/standard/expressions.md b/standard/expressions.md index c2cfb17b7..a403a9beb 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` or `nint` 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` or `nint` to type `long`. **End of informative text.** @@ -7095,7 +7095,17 @@ The compile-time evaluation of a *constant_expression* shall: 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. -> *Note*: 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. *end note* +> *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)). diff --git a/standard/types.md b/standard/types.md index 73de84c8a..34215fd27 100644 --- a/standard/types.md +++ b/standard/types.md @@ -351,14 +351,7 @@ public long ToInt64(); public void* ToPointer(); ``` -The remaining members of `System.IntPtr` and `System.UIntPtr` are implicitly included in `nint` and `nuint`. These are: - -```csharp -public override bool Equals(object obj); -public override int GetHashCode(); -public override string ToString(); -public string ToString(string format); -``` +The remaining members of `System.IntPtr` and `System.UIntPtr` are implicitly included in `nint` and `nuint`. Interfaces implemented by `System.IntPtr` and `System.UIntPtr` are implicitly included in `nint` and `nuint`, with occurrences of the underlying types replaced by the corresponding native integer types. For example, if `IntPtr` implements `ISerializable`, `IEquatable`, and `IComparable`, then `nint` implements `ISerializable`, `IEquatable`, and `IComparable`. From a0dfd6c6b9e05e7ec8df77e8df35d73724cfc522 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 2 Feb 2026 14:51:38 -0500 Subject: [PATCH 11/26] Rework Types.md for nint Rework the language in types with respect to `nint` and `nuint`. Focus on the language semantics, not the implementation based on `System.IntPtr` and `System.UIntPtr`. --- standard/types.md | 48 ++++++++++++----------------------------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/standard/types.md b/standard/types.md index 34215fd27..d1e8d9bc1 100644 --- a/standard/types.md +++ b/standard/types.md @@ -319,51 +319,27 @@ Every simple type has members. Each simple type that is an alias for a predefine > > *end note*. -Although `nint` and `nuint` shall be represented by the types `System.IntPtr` and `System.UIntPtr`, respectively, `nint` and `nuint` are *not* aliases for those types. As such, not all members of the corresponding `System` types are defined for `nint` and `nuint`. Instead, the compiler shall make available additional conversions, unary operators (§12.9) and arithmetic operators (§12.12) for the types `System.IntPtr` and `System.UIntPtr` when used in the context of native integer types. + -While the language provides operations and conversions for `nint` and `nuint` that are appropriate for integer types, those operations and conversions are not available on the `System` type counterparts. For example, +Although `nint` and `nuint` are represented by the types `System.IntPtr` and `System.UIntPtr`, respectively, `nint` and `nuint` are *not* aliases for those types. There is an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) between `nint` and `System.IntPtr`, and between `nuint` and `System.UIntPtr`. - -```csharp -M((nint)1); - -static void M(dynamic d) -{ - var v = d >> 2; // RuntimeBinderException: '>>' cannot be applied to operands - // of type System.IntPtr/System.UIntPtr and int -} -``` - -The only constructor for `nint` or `nuint` is the parameter-less constructor. - -The following members of `System.IntPtr` and `System.UIntPtr` are explicitly excluded from `nint` or `nuint`: - -```csharp -// constructors -// arithmetic operators -// implicit and explicit conversions -public static readonly IntPtr Zero; // use 0 instead -public static int Size { get; } // use sizeof() instead -public static IntPtr Add(IntPtr pointer, int offset); -public static IntPtr Subtract(IntPtr pointer, int offset); -public int ToInt32(); -public long ToInt64(); -public void* ToPointer(); -``` +The language provides conversions ([§10.2](conversions.md#102-implicit-conversions)), unary operators ([§12.9](expressions.md#129-unary-operators)), and binary operators ([§12.12](expressions.md#1212-arithmetic-operators)) for `nint` and `nuint` that are appropriate for integral types. These operators follow the same patterns as the corresponding operators for `int`/`long` and `uint`/`ulong`. -The remaining members of `System.IntPtr` and `System.UIntPtr` are implicitly included in `nint` and `nuint`. +> *Note*: Operations performed through `dynamic` binding on `System.IntPtr` and `System.UIntPtr` values do not have access to the `nint` and `nuint` operators. *end note* -Interfaces implemented by `System.IntPtr` and `System.UIntPtr` are implicitly included in `nint` and `nuint`, with occurrences of the underlying types replaced by the corresponding native integer types. For example, if `IntPtr` implements `ISerializable`, `IEquatable`, and `IComparable`, then `nint` implements `ISerializable`, `IEquatable`, and `IComparable`. +`sizeof(nint)` and `sizeof(nuint)` return the size of a native integer ([§23.6.9](unsafe-code.md#2369-the-sizeof-operator)). `typeof(nint)` is `typeof(System.IntPtr)`, and `typeof(nuint)` is `typeof(System.UIntPtr)`. - + -`nint` and `System.IntPtr`, and `nuint` and `System.UIntPtr`, are considered equivalent for overriding, hiding, and implementing, however. +`nint` and `System.IntPtr`, and `nuint` and `System.UIntPtr`, are considered equivalent for overriding, hiding, and implementing. -Overloads cannot differ by `nint` and `System.IntPtr`, and `nuint` and `System.UIntPtr`, alone. However, overrides and implementations may differ by `nint` and `System.IntPtr`, or `nuint` and `System.UIntPtr`, alone. +Overloads cannot differ by `nint` and `System.IntPtr`, or by `nuint` and `System.UIntPtr`, alone. However, overrides and implementations may differ by `nint` and `System.IntPtr`, or by `nuint` and `System.UIntPtr`, alone. -Methods hide other methods that differ by `nint` and `System.IntPtr`, or `nuint` and `System.UIntPtr`, alone. +Methods hide other methods that differ by `nint` and `System.IntPtr`, or by `nuint` and `System.UIntPtr`, alone. ### 8.3.6 Integral types @@ -377,7 +353,7 @@ C# supports the following integral types, with the sizes and value ranges, as sh - 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. - > *Note*: Unlike the other integral types `nint` and `nuint` do not have `const` fields called `MinValue` and `MaxValue`. *end note* + > *Note*: Unlike the other integral types `nint` and `nuint` do not have `const` fields called `MinValue` and `MaxValue`. As a practical consideration, implementations are expected to be at least 32-bit, so the value range of `nint` includes at least that of `int`, and the value range of `nuint` includes at least that of `uint`. *end note* - 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. From 629000609b419cddf0bb3a95fe9b1bec074c3cf1 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 2 Feb 2026 15:10:36 -0500 Subject: [PATCH 12/26] Add predefined operators Add text to specify the predefined operators for the `nint` and `nuint` types. --- standard/expressions.md | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index a403a9beb..31f54428a 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2501,7 +2501,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. @@ -3615,6 +3615,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); @@ -3628,7 +3630,7 @@ 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: @@ -3683,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: @@ -3733,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. @@ -4459,31 +4463,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); ``` @@ -4932,21 +4948,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. From ba32264e0d2e6d80bdce724bfdb0aabdca5a8cc2 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 2 Feb 2026 15:19:00 -0500 Subject: [PATCH 13/26] Further edits. --- standard/conversions.md | 6 ++++-- standard/expressions.md | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/standard/conversions.md b/standard/conversions.md index 61aabcc33..9fe89a491 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -83,7 +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 a 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`. +- 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: > @@ -325,7 +325,9 @@ 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`, `nint`, `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 `nint` is the same range as `int`. The range of constants for `nuint` is the same range as `uint`. (§12.25) +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 most common implementations being at least 32-bit ([§8.3.6](types.md#836-integral-types)). *end note* ### 10.2.12 Implicit conversions involving type parameters diff --git a/standard/expressions.md b/standard/expressions.md index 31f54428a..9c12738c8 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2593,8 +2593,6 @@ The run-time processing of an *object_creation_expression* of the form new `T(A) - An instance of type `T` is created by allocating a temporary local variable. Since an instance constructor of a *struct_type* is required to definitely assign a value to each field of the instance being created, no initialization of the temporary variable is necessary. - The instance constructor is invoked according to the rules of function member invocation ([§12.6.6](expressions.md#1266-function-member-invocation)). A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this. -`new nint()` is equivalent to `(nint)0`, and `new nuint()` is equivalent to `(nuint)0`. - ##### 12.8.17.2.2 Object initializers An ***object initializer*** specifies values for zero or more fields, properties, or indexed elements of an object. From 2f92f939bca985caf5fb431ed5d476f3db1cc7ef Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 2 Feb 2026 15:30:19 -0500 Subject: [PATCH 14/26] one more edit --- standard/types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/types.md b/standard/types.md index d1e8d9bc1..aa2be728f 100644 --- a/standard/types.md +++ b/standard/types.md @@ -297,7 +297,7 @@ Except for `nint` and `nuint`, the simple types are aliases for predefined `stru 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 From 1b6c859fb4c4b4c916176d71ab6c63f40327cdff Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 26 Feb 2026 16:14:21 -0500 Subject: [PATCH 15/26] Apply suggestions from code review Co-authored-by: Joseph Musser Co-authored-by: Nigel-Ecma <6654683+Nigel-Ecma@users.noreply.github.com> --- standard/conversions.md | 2 +- standard/expressions.md | 4 ++-- standard/types.md | 24 +++++------------------- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/standard/conversions.md b/standard/conversions.md index 9fe89a491..7e30ca3e6 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -322,7 +322,7 @@ 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`, `nint`, `nuint`, 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)). diff --git a/standard/expressions.md b/standard/expressions.md index 9c12738c8..466ae19da 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` or `nint` 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` or `nint` to type `long`. **End of informative text.** @@ -7057,7 +7057,7 @@ A constant expression may be either a value type or a reference type. If a const - have a value of `null`; 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 \[`int.MinValue`,`int.MaxValue`\]. A *constant_expression* of type `nuint` shall have a value in the range \[`uint.MinValue`,`uint.MaxValue`\]. +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: diff --git a/standard/types.md b/standard/types.md index aa2be728f..485b7e4c4 100644 --- a/standard/types.md +++ b/standard/types.md @@ -319,27 +319,13 @@ Every simple type has members. Each simple type that is an alias for a predefine > > *end note*. - + -Although `nint` and `nuint` are represented by the types `System.IntPtr` and `System.UIntPtr`, respectively, `nint` and `nuint` are *not* aliases for those types. There is an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) between `nint` and `System.IntPtr`, and between `nuint` and `System.UIntPtr`. +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 language provides conversions ([§10.2](conversions.md#102-implicit-conversions)), unary operators ([§12.9](expressions.md#129-unary-operators)), and binary operators ([§12.12](expressions.md#1212-arithmetic-operators)) for `nint` and `nuint` that are appropriate for integral types. These operators follow the same patterns as the corresponding operators for `int`/`long` and `uint`/`ulong`. - -> *Note*: Operations performed through `dynamic` binding on `System.IntPtr` and `System.UIntPtr` values do not have access to the `nint` and `nuint` operators. *end note* - -`sizeof(nint)` and `sizeof(nuint)` return the size of a native integer ([§23.6.9](unsafe-code.md#2369-the-sizeof-operator)). - -`typeof(nint)` is `typeof(System.IntPtr)`, and `typeof(nuint)` is `typeof(System.UIntPtr)`. - - - -`nint` and `System.IntPtr`, and `nuint` and `System.UIntPtr`, are considered equivalent for overriding, hiding, and implementing. - -Overloads cannot differ by `nint` and `System.IntPtr`, or by `nuint` and `System.UIntPtr`, alone. However, overrides and implementations may differ by `nint` and `System.IntPtr`, or by `nuint` and `System.UIntPtr`, alone. - -Methods hide other methods that differ by `nint` and `System.IntPtr`, or by `nuint` and `System.UIntPtr`, alone. +- 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 From 54340e2ca277b5196bf6bc957da680432fdfa85f Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 26 Feb 2026 16:18:52 -0500 Subject: [PATCH 16/26] Update standard/conversions.md Co-authored-by: Nigel-Ecma <6654683+Nigel-Ecma@users.noreply.github.com> --- standard/conversions.md | 1 - 1 file changed, 1 deletion(-) diff --git a/standard/conversions.md b/standard/conversions.md index 7e30ca3e6..d33990f04 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -118,7 +118,6 @@ In most cases, an identity conversion has no effect at runtime. However, since f There is an identity conversion between `nint` and `System.IntPtr`, and between `nuint` and `System.UIntPtr`. -For the compound types array, nullable type, constructed type, and tuple, there is an identity conversion between native integers ([§8.3.6](types.md#836-integral-types)) and their underlying types. ### 10.2.3 Implicit numeric conversions From 4ef33ef0a5959d070abdd6656b1ce6a5b6e0d7a3 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 26 Feb 2026 16:21:05 -0500 Subject: [PATCH 17/26] lint --- standard/conversions.md | 1 - 1 file changed, 1 deletion(-) diff --git a/standard/conversions.md b/standard/conversions.md index d33990f04..5f729390c 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -118,7 +118,6 @@ In most cases, an identity conversion has no effect at runtime. However, since f 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 e82a3d5e38299bd968a7006d3ef2afbfd891d527 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 26 Feb 2026 16:47:26 -0500 Subject: [PATCH 18/26] Apply suggestions from code review Co-authored-by: Nigel-Ecma <6654683+Nigel-Ecma@users.noreply.github.com> --- standard/conversions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/conversions.md b/standard/conversions.md index 5f729390c..619370a46 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -325,7 +325,7 @@ An implicit constant expression conversion permits the following conversions: 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 most common implementations being at least 32-bit ([§8.3.6](types.md#836-integral-types)). *end note* +> *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 From a526a64afe9dc35ca04799a75bc03116719b79f2 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 26 Feb 2026 16:53:34 -0500 Subject: [PATCH 19/26] Apply suggestion from @Nigel-Ecma Co-authored-by: Nigel-Ecma <6654683+Nigel-Ecma@users.noreply.github.com> --- standard/types.md | 1 - 1 file changed, 1 deletion(-) diff --git a/standard/types.md b/standard/types.md index 485b7e4c4..0d548f97c 100644 --- a/standard/types.md +++ b/standard/types.md @@ -339,7 +339,6 @@ C# supports the following integral types, with the sizes and value ranges, as sh - 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. - > *Note*: Unlike the other integral types `nint` and `nuint` do not have `const` fields called `MinValue` and `MaxValue`. As a practical consideration, implementations are expected to be at least 32-bit, so the value range of `nint` includes at least that of `int`, and the value range of `nuint` includes at least that of `uint`. *end note* - 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. From 35e4d8b47ec2ecf14d96e52a26e4daf7baf9810f Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 26 Feb 2026 16:58:59 -0500 Subject: [PATCH 20/26] remove extra note. --- standard/enums.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/standard/enums.md b/standard/enums.md index 4928d103f..bda2202ba 100644 --- a/standard/enums.md +++ b/standard/enums.md @@ -46,8 +46,6 @@ 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 `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. -> *Note*: The `char` type cannot be used as an underlying type, either by keyword or via an `integral_type_name`. *end note* - An enum declaration that does not explicitly declare an underlying type has an underlying type of `int`. > *Example*: The example From c52e98e2af15b6eafe5039a9312cf9d37c16412f Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 27 Feb 2026 09:33:07 -0500 Subject: [PATCH 21/26] Address operator feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Arithmetic operator code blocks (all add nint/nuint between uint/ulong and long/ulong lines): - added nint operator *(nint x, nint y); and nuint operator *(nuint x, nuint y); - added nint/nuint division operators - added nint/nuint remainder operators - added nint/nuint addition operators - added nint/nuint subtraction operators (also fixed missing closing paren on ulong line) 2. Shift operator code blocks: - added nint operator <<(nint x, int count); and nuint operator <<(nuint x, int count); - added nint operator >>(nint x, int count); and nuint operator >>(nuint x, int count); 3. Unary minus operator: - added nint operator –(nint x); - updated to mention nint: "(−2³¹ for int, the corresponding value for nint, or −2⁶³ for long)" --- standard/expressions.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 466ae19da..0407a43c3 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -3634,10 +3634,11 @@ For an operation of the form `-x`, unary operator overload resolution ([§12.4. ```csharp 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)). @@ -3988,6 +3989,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); ``` @@ -4035,6 +4038,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); ``` @@ -4086,6 +4091,8 @@ 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); ``` @@ -4136,6 +4143,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); ``` @@ -4238,8 +4247,10 @@ The predefined subtraction operators are listed below. The operators all subtrac ```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 + 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. @@ -4363,6 +4374,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); ``` @@ -4375,6 +4388,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); ``` From 68e5d69cb6d4dd4872f5ebb0991ee9cc03dd2827 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 27 Feb 2026 09:38:11 -0500 Subject: [PATCH 22/26] Add additional promotions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Removed or \nint`from the unary numeric promotion rule in §12.4.7.2 Since there is now a predefinednint operator -(nint x), nintshould not be promoted tolong`. 1. Add the S₁ is nint and S₂ is nuint or ulong entry in the better conversion target list --- standard/expressions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index 0407a43c3..9ce21927d 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` or `nint` 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.** @@ -1141,6 +1141,7 @@ Given two types `T₁` and `T₂`, `T₁` is a ***better conversion target*** th - `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 From 3c2b6220fa991d864e10331812ade48d3f60dd25 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 27 Feb 2026 09:43:20 -0500 Subject: [PATCH 23/26] Constant expression fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed from "A constant expression may be either a value type or a reference type" (with null only under the reference type branch) to "A constant expression shall have the value null, or be of a value type or a reference type." This hoists null to the top level so it's valid regardless of target type — covering nullable value type defaults like void M(int? x = null). Removed the now-redundant null bullet from the reference type list. --- standard/expressions.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 9ce21927d..4e3420dc0 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -7067,10 +7067,9 @@ constant_expression ; ``` -A constant expression may be either 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: +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: -- have a value of type `string`; -- have a value of `null`; or +- 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\]. From f880b275202e76d4bfd68d335b3454892ed98a22 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 27 Feb 2026 09:48:08 -0500 Subject: [PATCH 24/26] misc formatting fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Fixed missing opening backtick on `System.IntPtr`. 2. Fixed grammar from "In context if type resolution (§7.8.1) on either of these names succeeds then that name shall be recognised" to "If type resolution (§7.8.1) on either of these names succeeds, that name shall be recognised". 3. Added "native signed precision, or native unsigned precision" to the operator precision list, and added a note clarifying that native precision means 32-bit or 64-bit depending on platform and that nint/nuint operators use native precision rather than being promoted. --- standard/types.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/standard/types.md b/standard/types.md index 0d548f97c..4ca84c863 100644 --- a/standard/types.md +++ b/standard/types.md @@ -217,7 +217,7 @@ 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*. In context if type resolution (§7.8.1) on either of these names succeeds then that name shall be recognised as a *type_name*; otherwise it shall be recognised as a *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`. @@ -323,7 +323,7 @@ Every simple type has members. Each simple type that is an alias for a predefine 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. +- 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`. @@ -346,7 +346,9 @@ C# supports the following integral types, with the sizes and value ranges, as sh 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: From 41fc22e6f4ab834a047b0e526a4de7ba53480fcb Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 27 Feb 2026 09:56:45 -0500 Subject: [PATCH 25/26] remove redundant conversions --- standard/conversions.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/standard/conversions.md b/standard/conversions.md index 619370a46..8a357c2dc 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -835,12 +835,6 @@ Evaluation of a nullable conversion based on an underlying conversion from `S` - If the nullable conversion is from `S` to `T?`, the conversion is evaluated as the underlying conversion from `S` to `T` followed by a wrapping from `T` to `T?`. - If the nullable conversion is from `S?` to `T`, the conversion is evaluated as an unwrapping from `S?` to `S` followed by the underlying conversion from `S` to `T`. -Conversion from `A` to `Nullable` is: - -- an implicit nullable conversion if there is an identity conversion or implicit conversion from `A` to `B`; -- an explicit nullable conversion if there is an explicit conversion from `A` to `B`; -- otherwise, invalid. - ### 10.6.2 Lifted conversions Given a user-defined conversion operator that converts from a non-nullable value type `S` to a non-nullable value type `T`, a ***lifted conversion operator*** exists that converts from `S?` to `T?`. This lifted conversion operator performs an unwrapping from `S?` to `S` followed by the user-defined conversion from `S` to `T` followed by a wrapping from `T` to `T?`, except that a null valued `S?` converts directly to a null valued `T?`. A lifted conversion operator has the same implicit or explicit classification as its underlying user-defined conversion operator. From 0bda10432244b0d38e8566847bbf9c2ae488426d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 27 Feb 2026 10:02:24 -0500 Subject: [PATCH 26/26] em-dashes to hyphen-minus This fixes an old bulk change. --- standard/expressions.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 4e3420dc0..50ce53739 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -3634,9 +3634,9 @@ For an operation of the form `-x`, unary operator overload resolution ([§12.4. - Integer negation: ```csharp - int operator –(int x); - nint operator –(nint 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`, 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. @@ -3649,15 +3649,15 @@ For an operation of the form `-x`, unary operator overload resolution ([§12.4. - 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`. @@ -4246,20 +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); - 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); ``` 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`. @@ -4277,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. @@ -4287,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. @@ -4301,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: @@ -4555,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: