Skip to content

Latest commit

 

History

History
92 lines (68 loc) · 4.14 KB

LDM-2019-10-23.md

File metadata and controls

92 lines (68 loc) · 4.14 KB

C# Language Design Meeting for Oct. 23, 2019

Agenda

  1. New primitive types (nint and nuint)

Discussion

Native int

Proposal: #435

Static members

The current spec states that nint and nuint are reserved keywords in a type context but there are contexts, like qualified names, that allow types but are not type contexts. Since nint and nuint have static members, we want users to be able to access them, so they should be allowed in qualified names.

IntPtr members

A follow-up question is what to do about existing members on the underlying IntPtr type. There are some members, like Add and Subtract, which may be misleading because they are not equivalent to + and - due to over/underflow checking.

There are two views here. On the one hand, the proposal to use IntPtr means that some implementation details for IntPtr will inevitably leak through. Since users will sometimes want to use an IntPtr as an nuint to do pointer arithmetic, any methods on IntPtr may be useful and this would just be making barriers.

On the other hand, nint is being added as a type for conceptually different reasons. It shouldn't be assumed that everything that applies to IntPtr should be applicable to nint. There's a follow-up question as whether we would exclude everything on IntPtr except for an include list, or include everything except for an exclude list. These may seem very different, but after more thought it's likely they're very similar. Since the framework would probably very rarely add new members to IntPtr after the language change (like with System.Int32), what we pick now is probably what will be present for a very long time, and anything new would probably be done with consultation from the language team.

Our tentative conclusion is that we should exclude some members. The following are questionable:

  • Zero (use the 0 literal)
  • Size (use sizeof)
  • Add, Subtract (part of the point of this type is to use "proper" C# arithmetic)
  • ToInt32, ToInt64, ToPointer (use the language-defined conversions)

Signed bitwise operations

Unfortunately, IntPtr is "signed" but is often used to represent pointers, which are unsigned according to framework guidelines. In the current design, IntPtr has an identity conversion to nint, which could cause the unfortunate scenario

IntPtr M(IntPtr p)
{
    nint x = p;
    x >> 1;
    return x;
}

This would use signed >>, which is probably incorrect if the value is assumed to be an unsigned pointer. This is not currently a problem with IntPtr because IntPtr does not define any bitwise operators.

Unfortunately, keeping an identity conversion is an important part of compatibility. We would like to support users who are currently using IntPtr, but would like to use nint, to go from IntPtr[] to nint[]. Without an identity conversion, we don't see how this would be possible in the language today.

Adding an entirely new concept in the language to support this without identity conversions is probably not worth it. Unless we can find a simple improvement to the design, we will probably have to accept this behavior.

Overriding, hiding, and implementation (OHI)

Since IntPtr and nint are the same CLR underlying type, they will be regarded as having the same signature. The compiler is able to see the difference. Should we regard them as identical for the purposes of OHI? This is similar to scenarios like tuple names, which are represented as attributes and thus not visible as part of the CLR type. However, in OHI we provide an error if tuple names change in an override because this could be the source of a bug (e.g., if the names were accidentally swapped). In this case we think IntPtr and nint are similar enough that we should regard the OHI behavior similar to dynamic/object, where it is allowed.

For use as an enum underlying type, we think nint/nuint should be allowed as long as it's not too much implementation work. The legal values would be the same as the valid constants for the underlying.

For sizeof, we think it should be supported in unsafe code, just like sizeof(IntPtr), but with no special behavior in safe code.