From 227346b5fded148edf94be4f4d129112abedf0ce Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 15:14:07 -0400 Subject: [PATCH 01/21] Add support for native-sized integers --- standard/lexical-structure.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index b025b9120..d0083d77f 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' | '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 From 03eca7b2c895e79d3fa0812cbd03bca39cfefae2 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 15:24:19 -0400 Subject: [PATCH 02/21] Add support for native-sized integers --- standard/types.md | 79 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/standard/types.md b/standard/types.md index ebf597502..5034c5b51 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 types***. 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 types***. 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,70 @@ 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`, as 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)`. + +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 were `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 a constant. + ### 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 +388,8 @@ C# supports nine integral types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uin - The `ushort` type represents unsigned 16-bit integers with values from `0` to `65535`, inclusive. - The `int` type represents signed 32-bit integers with values from `-2147483648` to `2147483647`, inclusive. - The `uint` type represents unsigned 32-bit integers with values from `0` to `4294967295`, inclusive. +- The `nint` type represents a ***native signed integer*** whose size and value range are implementation-defined, but which shall be either that of `int` or `long`. +- The `nuint` type represents a ***native unsigned integer*** whose size and value range are implementation-defined, but which shall be either that of `uint` or `ulong`. The size of a native unsigned integer shall be the same as that of a native signed integer. - The `long` type represents signed 64-bit integers with values from `-9223372036854775808` to `9223372036854775807`, inclusive. - The `ulong` type represents unsigned 64-bit integers with values from `0` to `18446744073709551615`, inclusive. - The `char` type represents unsigned 16-bit integers with values from `0` to `65535`, inclusive. The set of possible values for the `char` type corresponds to the Unicode character set. @@ -713,7 +780,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. From af0ab4c0b279bcb94c2a72c6b4808531b6f38968 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 15:26:36 -0400 Subject: [PATCH 03/21] Add support for native-sized integers --- standard/variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 28ca74e2e20869c5038cbbdacff90adba96c91df Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 15:36:50 -0400 Subject: [PATCH 04/21] Add support for native-sized integers --- standard/conversions.md | 63 ++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/standard/conversions.md b/standard/conversions.md index e95b2a9f2..5c1055a21 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. @@ -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. @@ -748,6 +756,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. From 8f0525002047c8d88ead16ada894eb82f58d3701 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 15:53:58 -0400 Subject: [PATCH 05/21] Add support for native-sized integers --- standard/expressions.md | 42 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index ecd659836..f4000dbc8 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. @@ -3391,7 +3393,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 @@ -3634,7 +3636,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: @@ -4033,7 +4037,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 @@ -4082,7 +4086,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 @@ -4371,14 +4375,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`. @@ -7026,6 +7030,8 @@ A constant expression shall either have the value `null` or one of the following - 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). @@ -7087,7 +7093,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 From d6ac5b3ce6d3625f140450f2cd1cd6003c0a8a23 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 15:56:15 -0400 Subject: [PATCH 06/21] Add support for native-sized integers --- standard/statements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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. From c1f8e25a727f40c1bff73c31a2b4c2539ab256fc Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 15:58:42 -0400 Subject: [PATCH 07/21] Add support for native-sized integers --- standard/classes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index b02654945..4bd2dbf34 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1555,7 +1555,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)). @@ -1789,7 +1789,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 From 49ad0291cece22ff1f17e8dc978aaed148fe1d1c Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 16:00:11 -0400 Subject: [PATCH 08/21] Add support for native-sized integers --- standard/arrays.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/arrays.md b/standard/arrays.md index 86bbb44dd..9cc4bd2ee 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. From 648b51bb64a96329d1b56b7cdee5d28615bb0bfa Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 16:01:46 -0400 Subject: [PATCH 09/21] Add support for native-sized integers --- standard/enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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* From c3229e18c0c116379697a193827b7b87429564b9 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 16:05:40 -0400 Subject: [PATCH 10/21] Add support for native-sized integers --- standard/unsafe-code.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/standard/unsafe-code.md b/standard/unsafe-code.md index 9aef836ff..1914d02b9 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: @@ -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. From dbd8cfd2da5f2c0fb43d2c35234f039ef235649b Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 17 Mar 2024 16:30:19 -0400 Subject: [PATCH 11/21] fix example name --- standard/types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/types.md b/standard/types.md index 5034c5b51..6e04e4f9b 100644 --- a/standard/types.md +++ b/standard/types.md @@ -321,7 +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 From c20b21a3f588b7dc7a561695800518372af1aabb Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 11 May 2025 15:35:01 -0400 Subject: [PATCH 12/21] correct link --- standard/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index f4000dbc8..37ed1b6b5 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -7026,7 +7026,7 @@ 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. From 51ca281825d4ea7021b40057f2705c0b8b600a82 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 11 May 2025 15:46:51 -0400 Subject: [PATCH 13/21] fix md formatting --- standard/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index 37ed1b6b5..1a9cbef63 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -7030,7 +7030,7 @@ A constant expression shall either have the value `null` or one of the following - 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`]. +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: From a408f0ab491a047196068336b5e039a6fce3388f Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 11 May 2025 16:04:06 -0400 Subject: [PATCH 14/21] fix formatting --- standard/lexical-structure.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index d0083d77f..5968728b1 100644 --- a/standard/lexical-structure.md +++ b/standard/lexical-structure.md @@ -606,13 +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' | 'nint' | 'nuint' | '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' ; ``` From c68362eaa66c358b29ac89cd9459dff420b0cb5d Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 11 May 2025 16:06:08 -0400 Subject: [PATCH 15/21] fix md formatting --- standard/types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/types.md b/standard/types.md index 6e04e4f9b..4bca8acd0 100644 --- a/standard/types.md +++ b/standard/types.md @@ -366,7 +366,7 @@ 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`. +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. From 9fcc65731172b37a4bffd36dca3ad950dd450203 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 12 May 2025 12:27:57 -0400 Subject: [PATCH 16/21] made minor tweaks --- standard/types.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/standard/types.md b/standard/types.md index 4bca8acd0..89ae1614f 100644 --- a/standard/types.md +++ b/standard/types.md @@ -317,7 +317,7 @@ 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`, as 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 and operations for the types `System.IntPtr` and `System.UIntPtr` when used in the context of native integer types. Consider the following: @@ -376,8 +376,6 @@ Methods hide other methods that differ by `nint` and `System.IntPtr`, or `nuint` `typeof(nint)` is `typeof(System.IntPtr)`, and `typeof(nuint)` is `typeof(System.UIntPtr)`. -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 were `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 a constant. - ### 8.3.6 Integral types C# supports the following integral types, with the sizes and value ranges, as shown: @@ -390,6 +388,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* - 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 cd3c7b5866d94d908134af4b5708b177e428cd2e Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 12 May 2025 12:29:32 -0400 Subject: [PATCH 17/21] Add 2 new types --- standard/conversions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/conversions.md b/standard/conversions.md index 5c1055a21..3d64c8643 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -321,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 From cb1316696361dd00c42bed760749ff9818285d18 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 12 May 2025 12:31:35 -0400 Subject: [PATCH 18/21] make minor tweak --- standard/expressions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/standard/expressions.md b/standard/expressions.md index 1a9cbef63..95e3d5b13 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -7080,6 +7080,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. From ac6c5ef1828f16a2c50683c8e19d76bf392715b2 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Fri, 16 May 2025 16:54:18 -0400 Subject: [PATCH 19/21] Add native types to pointer indexing --- standard/unsafe-code.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/unsafe-code.md b/standard/unsafe-code.md index 1914d02b9..9444398d9 100644 --- a/standard/unsafe-code.md +++ b/standard/unsafe-code.md @@ -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). From 03dc57298568a21d7759ebceed5bff23f7a90492 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 21 May 2025 14:34:23 -0400 Subject: [PATCH 20/21] Update standard/expressions.md Co-authored-by: Kalle Olavi Niemitalo --- standard/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index 95e3d5b13..f2d57789e 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -312,7 +312,7 @@ 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`, `nint`, 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`, `nint`, or `int`, both operands are converted to type `long`. From a35cd0de2f4b9bb02fe8f9d2caff4d33afbf387c Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 21 May 2025 14:34:57 -0400 Subject: [PATCH 21/21] Update standard/expressions.md Co-authored-by: Kalle Olavi Niemitalo --- standard/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index f2d57789e..361b3d98c 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -3393,7 +3393,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`, `nint`, `nuint`, `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