Skip to content

Fix generation of long-typed defines when on Linux#699

Draft
Exanite wants to merge 10 commits into
dotnet:mainfrom
Exanite:fix/long-defines
Draft

Fix generation of long-typed defines when on Linux#699
Exanite wants to merge 10 commits into
dotnet:mainfrom
Exanite:fix/long-defines

Conversation

@Exanite

@Exanite Exanite commented Jun 29, 2026

Copy link
Copy Markdown
Member

This is more an issue report than a PR since I want to discuss the issue first, but is opened as a PR because it involves some proposed test cases and I'm also interested in fixing it.

Please let me know if the proposed changes are reasonable.


For context, C long is output as C# nint on Linux.

This causes an issue where a 64-bit ulong value is used as the initializer for a const field of type nint, leading to 2 errors:

  1. There is a missing type cast from long to nint.
  2. Because C# doesn't know the bit width of nint until run time, this errors with Constant initializer must be compile-time constant after fixing the first error. Edit: Looks like this is specified here: https://github.com/dotnet/csharplang/blob/main/proposals/csharp-9.0/native-integers.md

Current test outputs (matching main branch)

// When running the CLongDefinesTestUnix test case on Linux
public static partial class Methods
{
    [NativeTypeName("#define SIZE_MAX (18446744073709551615UL)")]
    public const nuint SIZE_MAX = (18446744073709551615U);

    [NativeTypeName("#define CL_IMPORT_MEMORY_WHOLE_ALLOCATION_ARM SIZE_MAX")]
    public const nuint CL_IMPORT_MEMORY_WHOLE_ALLOCATION_ARM = (18446744073709551615U);

    [NativeTypeName("#define LONG_MAX __LONG_MAX__")]
    public const nint LONG_MAX = unchecked(9223372036854775807);

    [NativeTypeName("#define ULONG_MAX (__LONG_MAX__ *2UL+1UL)")]
    public const nuint ULONG_MAX = (9223372036854775807 * 2U + 1U);
}

The CLongDefinesTestWindows test case already passes and is there to ensure that no unintended changes occur to the Windows behavior.

Expected test outputs

The expected test outputs are my proposal for roughly what the expected bindings output should look like.

Notably, we need to add a type cast and change the field to be a static readonly field instead of a const field.

// This matches the expected output I defined in the CLongDefinesTestUnix test
public static partial class Methods
{
    [NativeTypeName(""#define SIZE_MAX (18446744073709551615UL)"")]
    public static readonly nuint SIZE_MAX = (nuint)(18446744073709551615U);

    [NativeTypeName(""#define CL_IMPORT_MEMORY_WHOLE_ALLOCATION_ARM SIZE_MAX"")]
    public static readonly nuint CL_IMPORT_MEMORY_WHOLE_ALLOCATION_ARM = (nuint)(18446744073709551615U);

    [NativeTypeName(""#define LONG_MAX __LONG_MAX__"")]
    public static readonly nint LONG_MAX = (nint)(9223372036854775807);

    [NativeTypeName(""#define ULONG_MAX (__LONG_MAX__ *2UL+1UL)"")]
    public static readonly nuint ULONG_MAX = unchecked((nuint)(9223372036854775807 * 2U + 1U));
}
  • Note to self: Add tests for the "similar" case in my comment below

@Exanite

Exanite commented Jun 29, 2026

Copy link
Copy Markdown
Member Author

There is also a similar case with nint typed const fields:

Also from OpenCL:

#if INTPTR_MAX == INT32_MAX
#define CL_ICD2_TAG_KHR ((intptr_t)0x434C3331)
#else
#define CL_ICD2_TAG_KHR ((intptr_t)0x4F50454E434C3331)
#endif

This generates as the following on both Windows and Linux:

[NativeTypeName("#define CL_ICD2_TAG_KHR ((intptr_t)0x4F50454E434C3331)")]
public const nint CL_ICD2_TAG_KHR = unchecked((nint)(0x4F50454E434C3331));

In this case, the only change needed to make this compile would be to replace const with static readonly.

Exanite added 4 commits July 2, 2026 06:10
Will keep investigating the conditions a bit more since I'm not quite convinced this is optimal.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant