- Native ints
We would like to supply high-quality native-size integers. The best we have today is IntPtr
, which lacks most operators and have a few behavioral weaknesses, including a suboptimal ToString
implementation. Xamarin has introduced user-defined nint
and nuint
types for interop, but those can't completely do the trick; for instance, user-defined operators cannot distinguish between checked and unchecked contexts.
Options:
- Improve
IntPtr
with operators - Add
nint
andnuint
as a user defined struct (the Xamarin approach) - Add
nint
andnuint
to the language- compile down to
IntPtr
and add an attribute to persist the additional type info in metadata - project language-level types to new user defined structs
- compile down to
- A combo of 1 and 3
IntPtr
has a few operators today; for instance +
with int
(which is checked on 32 bit and unchecked on 64 bit!), and some conversions.
The new structs in 3.2 look something like this:
struct NativeInt
{
public IntPtr Value;
public override string ToString() { ... }
}
/// etc
But operators are implemented by the language, not as user-defined operators.
The difference between 3.1 and 3.2 is that with 3.2 at runtime we have different types, so we can have differentiated runtime behavior: ToString
and reflection can tell them apart.
A downside is that operations that take ref IntPtr
(like Interlocked.CompareExchange
) wouldn't automatically take ref nint
. Having the public mutable field would let things still work for people, and we could go through and add nint
overloads over time to make it better.
This should be in mscorlib, but that takes time. Is there anything we can do to mitigate in the meantime? We could ship a nuget package etc, but there's some cost to that, including indefinite maintenance. But some of the people who would benefit from this will be in a terrible spot if we don't provide something.
We also need to deal with native float. There is no option to do 3.1 for floats; there is no IntPtr
equivalent. So that one would need a framework type. However, we could probably live with that nfloat
struct moving into the frameworks over time - other than Xamarin, which would add it faster for its interop scenarios.
With 3.1, if you consume a nint
-attributed IntPtr
with an old compiler, would it treat it as an intPtr
? If that's the case then the code would subtly change behavior on compiler upgrade. Unfortunate! We could perhaps poison nint
with ModReq
so that they cannot be consumed by existing compilers, but now nint
really is a different type, and requires separate overloads of methods that take it as a parameter.
Another option is to obsolete the user-defined operators on IntPtr
, to drive people to use nint
instead.
- Adoption, where a separate struct would take a while to propagate (we feel we've mostly mitigated this)
- We'll emit slightly less efficient and more verbose IL in a couple of cases
- Needing new overloads for
nint
where there areIntPtr
overloads today (or at least a conversion, and new overloads where there areref IntPtr
parameters).
Objection to 3.1:
- No runtime distinction (reflection and
ToString
) ToString
happens all the time
We're torn, and evenly balanced on preferring 3.1 vs 3.2. Could maybe be convinced to 3.2 if we can solve how do existing users of IntPtr
migrate.