runtime: System.IntPtr(long) constructor throws overflow

The current .NET Framework on Windows and CoreCLR have the following implementation for System.IntPtr(long):

public unsafe IntPtr(long value)
{
    #if WIN32
        m_value = (void *)checked((int)value);
    #else
        m_value = (void *)value;
    #endif
}

This is quite unfortunate on 32-bit platforms because if you get a pointer value as a long which has the higher bit set, it will always throw an overflow exception.

To reproduce:

IntPtr ptr = new IntPtr(0x80000000);

The code should have been:

public unsafe IntPtr(long value)
{
    #if WIN32
        m_value = (void *)checked((uint)value);
    #else
        m_value = (void *)value;
    #endif
}

since we only care that the higher 32-bits of the long parameter are all zeros.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 15 (11 by maintainers)

Commits related to this issue

Most upvoted comments

I think that change would be a bit unfortunate. Consider:

const long val1 = -1;
const long val2 = 0xffffffff;
bool b1 = val1 == val2; // false
bool b2 = new IntPtr(val1) == new IntPtr(val2); // true on 32-bit, false on 64-bit

I think it’s the naming of the type that is problematic – IntPtr really represents an integer of native size, but it is documented and named as if it represents a pointer. As a native integer the current behavior makes perfect sense. The problem then arises from shoehorning a pointer into a native integer and the native integer being called IntPtr. A better fit would be to use UIntPtr when representing pointers but that type is not CLS compatible and unused in all of the BCL.