[!INCLUDESpecletdisclaimer]
Champion issue: #6065
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.
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 |
[...]
C# supports eleven integral types: sbyte
, byte
, short
, ushort
, int
, uint
, nint
, nuint
, long
, ulong
, and char
. [...]
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.
The implicit numeric conversions are:
- From
sbyte
toshort
,int
,nint
,long
,float
,double
, ordecimal
. - From
byte
toshort
,ushort
,int
,uint
,nint
,nuint
,long
,ulong
,float
,double
, ordecimal
. - From
short
toint
,nint
,long
,float
,double
, ordecimal
. - From
ushort
toint
,uint
,nint
,nuint
,long
,ulong
,float
,double
, ordecimal
. - From
int
tonint
,long
,float
,double
, ordecimal
. - From
uint
tonuint
,long
,ulong
,float
,double
, ordecimal
. - From
nint
tolong
,float
,double
, ordecimal
. - From
nuint
toulong
,float
,double
, ordecimal
. - From
long
tofloat
,double
, ordecimal
. - From
ulong
tofloat
,double
, ordecimal
. - From
char
toushort
,int
,uint
,nint
,nuint
,long
,ulong
,float
,double
, ordecimal
. - From
float
todouble
.
[...]
An implicit constant expression conversion permits the following conversions:
- A constant_expression of type
int
can 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. [...]
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
sbyte
tobyte
,ushort
,uint
,nuint
,ulong
, orchar
. - From
byte
tosbyte
orchar
. - From
short
tosbyte
,byte
,ushort
,uint
,nuint
,ulong
, orchar
. - From
ushort
tosbyte
,byte
,short
, orchar
. - From
int
tosbyte
,byte
,short
,ushort
,uint
,nuint
,ulong
, orchar
. - From
uint
tosbyte
,byte
,short
,ushort
,int
,nint
, orchar
. - From
long
tosbyte
,byte
,short
,ushort
,int
,uint
,nint
,nuint
,ulong
, orchar
. - From
nint
tosbyte
,byte
,short
,ushort
,int
,uint
,nuint
,ulong
, orchar
. - From
nuint
tosbyte
,byte
,short
,ushort
,int
,uint
,nint
,long
, orchar
. - From
ulong
tosbyte
,byte
,short
,ushort
,int
,uint
,nint
,nuint
,long
, orchar
. - From
char
tosbyte
,byte
, orshort
. - From
float
tosbyte
,byte
,short
,ushort
,int
,uint
,nint
,nuint
,long
,ulong
,char
, ordecimal
. - From
double
tosbyte
,byte
,short
,ushort
,int
,uint
,nint
,nuint
,long
,ulong
,char
,float
, ordecimal
. - From
decimal
tosbyte
,byte
,short
,ushort
,int
,uint
,nint
,nuint
,long
,ulong
,char
,float
, ordouble
.
[...]
The explicit enumeration conversions are:
- From
sbyte
,byte
,short
,ushort
,int
,uint
,nint
,nuint
,long
,ulong
,char
,float
,double
, ordecimal
to 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.
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: [...]
[...] 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 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. [...]
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 unary plus operators are:
...
nint operator +(nint x);
nuint operator +(nuint x);
The predefined unary minus operators are:
-
Integer negation:
... nint operator –(nint x);
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.
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.
The predefined bitwise complement operators are:
...
nint operator ~(nint x);
nuint operator ~(nuint x);
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 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);
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);
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);
-
Integer addition:
... nint operator +(nint x, nint y); nuint operator +(nuint x, nuint y);
-
Integer subtraction:
... nint operator –(nint x, nint y); nuint operator –(nuint x, nuint y);
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 shiftsx
right by a number of bits computed as described below.When
x
is of typeint
,nint
orlong
, the low-order bits ofx
are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero ifx
is non-negative and set to one ifx
is negative.When
x
is of typeuint
,nuint
orulong
, the low-order bits ofx
are 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
x
isnint
ornuint
, the shift count is given by the low-order five bits ofcount
on a 32 bit platform, or the lower-order six bits ofcount
on a 64 bit platform.
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);
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);
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.
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.
[...]
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
, orulong
to any pointer_type. - From any pointer_type to
sbyte
,byte
,short
,ushort
,int
,uint
,nint
,nuint
,long
, orulong
.
[...]
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
.
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
.
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 + int
IntPtr - int
IntPtr -> int
long -> IntPtr
void* -> IntPtr
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.