
# C# Language Design Meeting for March 25, 2020

## Agenda

1. Questions around the new `nint` type

2. Target-typed new

## Discussion

Issue #3259

### LangVersion

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.

1. Make an exception for `nint`, allowing them to be seen and compiled like an
`IntPtr` in `langversion` C# 8.

2. 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.

3. 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` operators

We have two proposals:

1. Remove built-in identity conversions between native integers and underlying types and add explicit conversions.

2. Remove `nint` operators when using the `IntPtr` type

**Conclusion**

(1) is a little too harsh. Let's do (2).

### Behavior of constant folding

The concern is platform dependence.

In the following example

```C#
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.

### Interfaces on `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`.

## Target-typed `new`

https://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 `new`ing 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!
