meetings/2020/LDM-2020-03-25.md
Questions around the new nint type
Target-typed new
Issue #3259
THe question is what the behavior of the compiler should be when seeing an nint
type in langversion C# 8. Our convention is that the compiler never preserves
old behavior for older language versions. For instance, we do not preserve the
code for older code generation strategies and switch to that with the language
version flag. Instead, langversion is meant to be guard rails, providing
diagnostics when features are used that aren't available in older versions of the
language.
There are a few options we could take.
Make an exception for nint, allowing them to be seen and compiled like an
IntPtr in langversion C# 8.
Make a wider divergence between nint and IntPtr. Adding a modreq to
the emitted IntPtr type would make them effectively unusable by older language
versions and other languages.
Preserve the behavior, as long as no new semantics are used. For instance,
using the arithmetic operators on nint and on IntPtr have different semantics.
It would be an error to use any of these operators in older language versions.
Conclusion
We think (3) is the best balance.
IntPtr and nint operatorsWe have two proposals:
Remove built-in identity conversions between native integers and underlying types and add explicit conversions.
Remove nint operators when using the IntPtr type
Conclusion
(1) is a little too harsh. Let's do (2).
The concern is platform dependence.
In the following example
const nint m = int.MaxValue;
const nint u1 = unchecked(m + 1);
nint u2 = unchecked(m + 1);
if the machine is 32-bit, then the result overflows. If the machine is 64-bit, it does not.
While it's possible in the existing language to produce constant-folded values which are undefined, we don't think that behavior is desirable for nint.
The main contention is what to do in a checked context if we know the value will overflow
32-bits. We could either produce an error, saying that this will overflow on some platforms,
or produce a warning and push the calculation to runtime, warning that the calculation may
overflow at runtime (and produce an exception).
Conclusion
Whenever we can safely produce a constant value under 32-bits, we do constant folding. Otherwise,
the result is non-constant, and under checked, the code produces a warning and the result
is non-constant.
nint?Should interfaces on IntPtr and nint match? Or should nint only accept a certain set of
compiler-validated interfaces on IntPtr?
Conclusion
We trust that interfaces will only be added to IntPtr with recognition that those interfaces
also affect nint. We'll make all interfaces on IntPtr available on nint, with IntPtr
occurrences substituted for nint.
newhttps://github.com/dotnet/csharplang/blob/master/proposals/target-typed-new.md
Clarification about library evolution: if a user uses new(), adding a constructor to a type
can produce an ambiguity. Similarly, if a method is called with new() that can produce an
ambiguity if more overloads of that method is added. This is analogous with null or default,
which can convert to many different types and can produce ambiguity.
The spec currently specifies that there are a list of types where target-typed new is allowed. To
simplify, we propose that we specify that target-typed new should produce a fully-typed new and
the legality of that expression is defined elsewhere. This does make new() work on enums, which
is currently proposed as illegal because it may be confusing. However, new Enum() is legal
today, so we think that it should be allowed for target-typed new simply because of
consistency.
There's some debate on what it should do for nullable value types. On the one hand, the rule
"new() is just shorthand for writing out the type on the left," implies that the result should be
null. On the other hand, the nullable lifting rules would imply that the base type of the
target should be the underlying type, not the nullable type. Overall, we think that newing the
underlying type makes the most sense, both because it's the most useful (we already have a
shorthand for null) and because it's likely what the user intended.
For dynamic, we will not permit it simply because new dynamic() is also illegal.
Final thought: many thanks to @alrz for the great contribution!