The X# native type PTR compiles to a real unsafe pointer (void*). However, the NULL_PTR literal does not compile to a void* value — it compiles to System.IntPtr.Zero. Since IntPtr/nint and void* both have applicable (but different) equality operators, comparing a PTR variable to NULL_PTR now produces:
Operator '!=' is ambiguous on operands of type 'ptr' and 'nint'
(and the equivalent for ==).
This appears to be a long-standing inconsistency in the compiler that only surfaces as a hard error under newer .NET / Roslyn versions where pointer-vs-nint operator resolution is stricter.
Repro
LOCAL h AS PTR
h := NULL_PTR
IF h == NULL_PTR
? "null"
ENDIF
Compiling under .NET 10 gives:
Operator '==' is ambiguous on operands of type 'ptr' and 'nint'
Root cause
-
PTR as a declared type maps to a genuine C# pointer type (void*):
XSharpTreeTransformationCore.cs:10282
case XP.PTR:
context.Put(_syntaxFactory.PointerType(VoidType, SyntaxFactory.MakeToken(SyntaxKind.AsteriskToken)));
break;
-
The internal helper PtrType, used when generating the NULL_PTR literal, is defined as System.IntPtr, not void*:
XSharpTreeTransformationCore.cs:190
protected TypeSyntax PtrType => GenerateQualifiedName(SystemQualifiedNames.IntPtr);
-
NULL_PTR is generated as IntPtr.Zero using that helper:
XSharpTreeTransformationRT.cs:4189
case XP.NULL_PTR:
expr = MakeSimpleMemberAccess(PtrType, GenerateSimpleName("Zero"));
break;
(same pattern repeated at XSharpTreeTransformationRT.cs:4796)
Since nint is just the C# built-in alias for System.IntPtr, the emitted comparison is effectively void* == nint. Under Roslyn's pointer/native-int operator resolution rules this is ambiguous (CS0034), because both the pointer equality operator and the nint numeric equality operator are equally applicable via implicit conversion.
PtrType (IntPtr) is correctly used elsewhere for USUAL-boxing and PSZ/PCALL marshalling, where a real IntPtr is actually required — those usages are not affected and should not change:
The X# native type
PTRcompiles to a real unsafe pointer (void*). However, theNULL_PTRliteral does not compile to avoid*value — it compiles toSystem.IntPtr.Zero. SinceIntPtr/nintandvoid*both have applicable (but different) equality operators, comparing aPTRvariable toNULL_PTRnow produces:(and the equivalent for
==).This appears to be a long-standing inconsistency in the compiler that only surfaces as a hard error under newer .NET / Roslyn versions where pointer-vs-
nintoperator resolution is stricter.Repro
Compiling under .NET 10 gives:
Root cause
PTRas a declared type maps to a genuine C# pointer type (void*):XSharpTreeTransformationCore.cs:10282
The internal helper
PtrType, used when generating theNULL_PTRliteral, is defined asSystem.IntPtr, notvoid*:XSharpTreeTransformationCore.cs:190
NULL_PTRis generated asIntPtr.Zerousing that helper:XSharpTreeTransformationRT.cs:4189
(same pattern repeated at XSharpTreeTransformationRT.cs:4796)
Since
nintis just the C# built-in alias forSystem.IntPtr, the emitted comparison is effectivelyvoid* == nint. Under Roslyn's pointer/native-int operator resolution rules this is ambiguous (CS0034), because both the pointer equality operator and thenintnumeric equality operator are equally applicable via implicit conversion.PtrType(IntPtr) is correctly used elsewhere for USUAL-boxing and PSZ/PCALL marshalling, where a realIntPtris actually required — those usages are not affected and should not change: