Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Note
This article is a feature specification. The specification serves as the design document for the feature. It includes proposed specification changes, along with information needed during the design and development of the feature. These articles are published until the proposed spec changes are finalized and incorporated in the current ECMA specification.
There may be some discrepancies between the feature specification and the completed implementation. Those differences are captured in the pertinent language design meeting (LDM) notes.
You can learn more about the process for adopting feature speclets into the C# language standard in the article on the specifications.
Champion issue: https://github.com/dotnet/csharplang/issues/6065
Summary
This is a revision on the initial native integers feature (spec), where the nint/nuint types were distinct from the underlying types System.IntPtr/System.UIntPtr.
In short, we now treat nint/nuint as simple types aliasing System.IntPtr/System.UIntPtr, like we do for int in relation to System.Int32. The System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr runtime feature flag triggers this new behavior.
Design
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.
| Keyword | Aliased type |
|---|---|
sbyte |
System.SByte |
byte |
System.Byte |
short |
System.Int16 |
ushort |
System.UInt16 |
int |
System.Int32 |
uint |
System.UInt32 |
nint |
System.IntPtr |
nuint |
System.UIntPtr |
long |
System.Int64 |
ulong |
System.UInt64 |
char |
System.Char |
float |
System.Single |
double |
System.Double |
bool |
System.Boolean |
decimal |
System.Decimal |
[...]
8.3.6 Integral types
C# supports eleven integral types: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, and char. [...]
8.8 Unmanaged types
In other words, an unmanaged_type is one of the following:
sbyte,byte,short,ushort,int,uint,nint,nuint,long,ulong,char,float,double,decimal, orbool.- Any enum_type.
- Any user-defined struct_type that is not a constructed type and contains fields of unmanaged_types only.
- In unsafe code, any pointer_type.
10.2.3 Implicit numeric conversions
The implicit numeric conversions are:
- From
sbytetoshort,int,nint,long,float,double, ordecimal. - From
bytetoshort,ushort,int,uint,nint,nuint,long,ulong,float,double, ordecimal. - From
shorttoint,nint,long,float,double, ordecimal. - From
ushorttoint,uint,nint,nuint,long,ulong,float,double, ordecimal. - From
inttonint,long,float,double, ordecimal. - From
uinttonuint,long,ulong,float,double, ordecimal. - From
ninttolong,float,double, ordecimal. - From
nuinttoulong,float,double, ordecimal. - From
longtofloat,double, ordecimal. - From
ulongtofloat,double, ordecimal. - From
chartoushort,int,uint,nint,nuint,long,ulong,float,double, ordecimal. - From
floattodouble.
[...]
10.2.11 Implicit constant expression conversions
An implicit constant expression conversion permits the following conversions:
- A constant_expression of type
intcan be converted to typesbyte,byte,short,ushort,uint,nint,nuint, orulong, provided the value of the constant_expression is within the range of the destination type. [...]
10.3.2 Explicit numeric conversions
The explicit numeric conversions are the conversions from a numeric_type to another numeric_type for which an implicit numeric conversion does not already exist:
- From
sbytetobyte,ushort,uint,nuint,ulong, orchar. - From
bytetosbyteorchar. - From
shorttosbyte,byte,ushort,uint,nuint,ulong, orchar. - From
ushorttosbyte,byte,short, orchar. - From
inttosbyte,byte,short,ushort,uint,nuint,ulong, orchar. - From
uinttosbyte,byte,short,ushort,int,nint, orchar. - From
longtosbyte,byte,short,ushort,int,uint,nint,nuint,ulong, orchar. - From
ninttosbyte,byte,short,ushort,int,uint,nuint,ulong, orchar. - From
nuinttosbyte,byte,short,ushort,int,uint,nint,long, orchar. - From
ulongtosbyte,byte,short,ushort,int,uint,nint,nuint,long, orchar. - From
chartosbyte,byte, orshort. - From
floattosbyte,byte,short,ushort,int,uint,nint,nuint,long,ulong,char, ordecimal. - From
doubletosbyte,byte,short,ushort,int,uint,nint,nuint,long,ulong,char,float, ordecimal. - From
decimaltosbyte,byte,short,ushort,int,uint,nint,nuint,long,ulong,char,float, ordouble.
[...]
10.3.3 Explicit enumeration conversions
The explicit enumeration conversions are:
- From
sbyte,byte,short,ushort,int,uint,nint,nuint,long,ulong,char,float,double, ordecimalto any enum_type. - From any enum_type to
sbyte,byte,short,ushort,int,uint,nint,nuint,long,ulong,char,float,double, ordecimal. - From any enum_type to any other enum_type.
12.6.4.7 Better conversion target
Given two types T₁ and T₂, T₁ is a better conversion target than T₂ if one of the following holds:
- An implicit conversion from
T₁toT₂exists and no implicit conversion fromT₂toT₁exists T₁isTask<S₁>,T₂isTask<S₂>, andS₁is a better conversion target thanS₂T₁isS₁orS₁?whereS₁is a signed integral type, andT₂isS₂orS₂?whereS₂is an unsigned integral type. Specifically: [...]
12.8.12 Element access
[...] 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 shall be implicitly convertible to one or more of these types.
11.8.12.2 Array access
[...] 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 shall be implicitly convertible to one or more of these types.
[...] The run-time processing of an array access of the form P[A], where P is a primary_no_array_creation_expression of an array_type and A is an argument_list, consists of the following steps:
[...]
- The index expressions of the argument_list are evaluated in order, from left to right. Following evaluation of each index expression, an implicit conversion to one of the following types is performed:
int,uint,nint,nuint,long,ulong. The first type in this list for which an implicit conversion exists is chosen. [...]
12.8.16 Postfix increment and decrement operators
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.
12.9.2 Unary plus operator
The predefined unary plus operators are:
...
nint operator +(nint x);
nuint operator +(nuint x);
12.9.3 Unary minus operator
The predefined unary minus operators are:
Integer negation:
... nint operator –(nint x);
12.8.16 Postfix increment and decrement operators
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.
11.7.19 Default value expressions
In addition, a default_value_expression is a constant expression if the type is 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.9.5 Bitwise complement operator
The predefined bitwise complement operators are:
...
nint operator ~(nint x);
nuint operator ~(nuint x);
12.9.6 Prefix increment and decrement operators
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.
12.10 Arithmetic operators
12.10.2 Multiplication operator
The predefined multiplication operators are listed below. The operators all compute the product of x and y.
Integer multiplication:
... nint operator *(nint x, nint y); nuint operator *(nuint x, nuint y);
12.10.3 Division operator
The predefined division operators are listed below. The operators all compute the quotient of x and y.
Integer division:
... nint operator /(nint x, nint y); nuint operator /(nuint x, nuint y);
12.10.4 Remainder operator
The predefined remainder operators are listed below. The operators all compute the remainder of the division between x and y.
Integer remainder:
... nint operator %(nint x, nint y); nuint operator %(nuint x, nuint y);
12.10.5 Addition operator
Integer addition:
... nint operator +(nint x, nint y); nuint operator +(nuint x, nuint y);
12.10.6 Subtraction operator
Integer subtraction:
... nint operator –(nint x, nint y); nuint operator –(nuint x, nuint y);
12.11 Shift operators
The predefined shift operators are listed below.
Shift left:
... nint operator <<(nint x, int count); nuint operator <<(nuint x, int count);Shift right:
... nint operator >>(nint x, int count); nuint operator >>(nuint x, int count);The
>>operator shiftsxright by a number of bits computed as described below.When
xis of typeint,nintorlong, the low-order bits ofxare discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero ifxis non-negative and set to one ifxis negative.When
xis of typeuint,nuintorulong, the low-order bits ofxare discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero.Unsigned shift right:
... nint operator >>>(nint x, int count); nuint operator >>>(nuint x, int count);
For the predefined operators, the number of bits to shift is computed as follows: [...]
- When the type of
xisnintornuint, the shift count is given by the low-order five bits ofcounton a 32 bit platform, or the lower-order six bits ofcounton a 64 bit platform.
12.12 Relational and type-testing operators
12.12.2 Integer comparison operators
The predefined integer comparison operators are:
...
bool operator ==(nint x, nint y);
bool operator ==(nuint x, nuint y);
bool operator !=(nint x, nint y);
bool operator !=(nuint x, nuint y);
bool operator <(nint x, nint y);
bool operator <(nuint x, nuint y);
bool operator >(nint x, nint y);
bool operator >(nuint x, nuint y);
bool operator <=(nint x, nint y);
bool operator <=(nuint x, nuint y);
bool operator >=(nint x, nint y);
bool operator >=(nuint x, nuint y);
12.12 Logical operators
12.12.2 Integer logical operators
The predefined integer logical operators are:
...
nint operator &(nint x, nint y);
nuint operator &(nuint x, nuint y);
nint operator |(nint x, nint y);
nuint operator |(nuint x, nuint y);
nint operator ^(nint x, nint y);
nuint operator ^(nuint x, nuint y);
12.22 Constant expressions
A constant expression may be either a value type or a reference type. If a constant expression is a value type, it must be one of the following types: sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, or any enumeration type.
[...]
An implicit constant expression conversion 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.
17.4 Array element access
Array elements are accessed using element_access expressions 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 element access is a variable, namely the array element selected by the indices.
23.5 Pointer conversions
23.5.1 General
[...]
Additionally, in an unsafe context, the set of available 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,nint,nuint,long, orulongto any pointer_type. - From any pointer_type to
sbyte,byte,short,ushort,int,uint,nint,nuint,long, orulong.
23.6.4 Pointer element access
[...]
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.
23.6.7 Pointer arithmetic
In an unsafe context, the + operator and – operator can be applied to values of all pointer types except void*. Thus, for every pointer type T*, the following operators are implicitly defined:
[...]
T* operator +(T* x, nint y);
T* operator +(T* x, nuint y);
T* operator +(nint x, T* y);
T* operator +(nuint x, T* y);
T* operator -(T* x, nint y);
T* operator -(T* x, nuint y);
Given an expression P of a pointer type T* and an expression N of type int, uint, nint, nuint, 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.
Various considerations
Breaking changes
One of the main impacts of this design is that System.IntPtr and System.UIntPtr gain some built-in operators (conversions, unary and binary).
Those include checked operators, which means that the following operators on those types will now throw when overflowing:
IntPtr + intIntPtr - intIntPtr -> intlong -> IntPtrvoid* -> IntPtr
Metadata encoding
This design means that nint and nuint can simply be emitted as System.IntPtr and System.UIntPtr, without the use of System.Runtime.CompilerServices.NativeIntegerAttribute.
Similarly, when loading metadata NativeIntegerAttribute can be ignored.
C# feature specifications