Language support for a native-sized signed and unsigned integer types.
The motivation is for interop scenarios and for low-level libraries.
## Design
[design]: #design
The identifiers `nint` and `nuint` are new contextual keywords that represent native signed and unsigned integer types.
The identifiers are only treated as keywords when name lookup does not find a viable result at that program location.
```C#
nint x = 3;
string y = nameof(nuint);
_ = nint.Equals(x, 3);
```
The types `nint` and `nuint` are represented by the underlying types `System.IntPtr` and `System.UIntPtr` with compiler surfacing additional conversions and operations for those types as native ints.
### Constants
Constant expressions may be of type `nint` or `nuint`.
There is no direct syntax for native int literals. Implicit or explicit casts of other integral constant values can be used instead: `const nint i = (nint)42;`.
`nint` constants are in the range [ `int.MinValue`, `int.MaxValue` ].
`nuint` constants are in the range [ `uint.MinValue`, `uint.MaxValue` ].
There are no `MinValue` or `MaxValue` fields on `nint` or `nuint` because, other than `nuint.MinValue`, those values cannot be emitted as constants.
Constant folding is supported for all unary operators { `+`, `-`, `~` } and binary operators { `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `<<`, `>>` }.
Constant folding operations are evaluated with `Int32` and `UInt32` operands rather than native ints for consistent behavior regardless of compiler platform.
If the operation results in a constant value in 32-bits, constant folding is performed at compile-time.
Otherwise the operation is executed at runtime and not considered a constant.
### Conversions
There is an identity conversion between `nint` and `IntPtr`, and between `nuint` and `UIntPtr`.
There is an identity conversion between compound types that differ by native ints and underlying types only: arrays, `Nullable<>`, constructed types, and tuples.
The tables below cover the conversions between special types.
(The IL for each conversion includes the variants for `unchecked` and `checked` contexts if different.)
- 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<A>` 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<A>` to `Nullable<B>` 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.
### Operators
The predefined operators are as follows.
These operators are considered during overload resolution based on normal rules for implicit conversions _if at least one of the operands is of type `nint` or `nuint`_.
(The IL for each operator includes the variants for `unchecked` and `checked` contexts if different.)
For some binary operators, the IL operators support additional operand types
(see [ECMA-335](https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf) III.1.5 Operand type table).
But the set of operand types supported by C# is limited for simplicity and for consistency with existing operators in the language.
Lifted versions of the operators, where the arguments and return types are `nint?` and `nuint?`, are supported.
Compound assignment operations `x op= y` where `x` or `y` are native ints follow the same rules as with other primitive types with pre-defined operators.
Specifically the expression is bound as `x = (T)(x op y)` where `T` is the type of `x` and where `x` is only evaluated once.
The shift operators should mask the number of bits to shift - to 5 bits if `sizeof(nint)` is 4, and to 6 bits if `sizeof(nint)` is 8.
The conversions and operators are synthesized by the compiler and are not part of the underlying `IntPtr` and `UIntPtr` types.
As a result those conversions and operators _are not available_ from the runtime binder for `dynamic`.
```C#
nint x = 2;
nint y = x + x; // ok
dynamic d = x;
nint z = d + x; // RuntimeBinderException: '+' cannot be applied 'System.IntPtr' and 'System.IntPtr'
```
### Type members
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`:
```C#
// 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`. For .NET Framework 4.7.2:
```C#
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 instance if `IntPtr` implements `ISerializable, IEquatable<IntPtr>, IComparable<IntPtr>`,
then `nint` implements `ISerializable, IEquatable<nint>, IComparable<nint>`.
### Overriding, hiding, and implementing
`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.
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.
### Miscellaneous
`nint` and `nuint` expressions used as array indices are emitted without conversion.
```C#
static object GetItem(object[] array, nint index)
{
return array[index]; // ok
}
```
`nint` and `nuint` can be used as an `enum` base type.
```C#
enum E : nint // ok
{
}
```
Reads and writes are atomic for types `nint`, `nuint`, and `enum` with base type `nint` or `nuint`.
Fields may be marked `volatile` for types `nint` and `nuint`.
[ECMA-334](https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-334.pdf) 15.5.4 does not include `enum` with base type `System.IntPtr` or `System.UIntPtr` however.
`default(nint)` and `new nint()` are equivalent to `(nint)0`.
`typeof(nint)` is `typeof(IntPtr)`.
`sizeof(nint)` is supported but requires compiling in an unsafe context (as does `sizeof(IntPtr)`).
The value is not a compile-time constant.
`sizeof(nint)` is implemented as `sizeof(IntPtr)` rather than `IntPtr.Size`.
Compiler diagnostics for type references involving `nint` or `nuint` report `nint` or `nuint` rather than `IntPtr` or `UIntPtr`.
### Metadata
`nint` and `nuint` are represented in metadata as `System.IntPtr` and `System.UIntPtr`.
Type references that include `nint` or `nuint` are emitted with a `System.Runtime.CompilerServices.NativeIntegerAttribute` to indicate which parts of the type reference are native ints.
```C#
namespace System.Runtime.CompilerServices
{
[AttributeUsage(
AttributeTargets.Class |
AttributeTargets.Event |
AttributeTargets.Field |
AttributeTargets.GenericParameter |
AttributeTargets.Parameter |
AttributeTargets.Property |
AttributeTargets.ReturnValue,
AllowMultiple = false,
Inherited = false)]
public sealed class NativeIntegerAttribute : Attribute
The encoding of type references with `NativeIntegerAttribute` is covered in [NativeIntegerAttribute.md](https://github.com/dotnet/roslyn/blob/master/docs/features/NativeIntegerAttribute.md).