Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2089,12 +2089,58 @@ private void UncheckStmt(string targetTypeName, Stmt stmt)

_outputBuilder.EndUnchecked();
}
else if (EnumConstantInitNeedsCast(targetTypeName, stmt))
{
_outputBuilder.BeginInnerValue();
_outputBuilder.BeginInnerCast();
_outputBuilder.WriteCastType(targetTypeName);
_outputBuilder.EndInnerCast();

ParenthesizeStmt(stmt);

_outputBuilder.EndInnerValue();
}
else
{
VisitStmt(stmt);
}
}

private bool EnumConstantInitNeedsCast(string targetTypeName, Stmt stmt)
{
// A C# enum member initializer must be implicitly convertible to the enum's underlying
// type. The generator forces most enums to `int`, so an unsigned initializer expression
// (e.g. `1U << 22`) is not implicitly convertible and needs an explicit cast to the
// underlying type to compile. Out-of-range values are handled by the unchecked path.

if (stmt.DeclContext is not EnumDecl)
{
return false;
}

// Only cast the top-level initializer, not each nested subexpression, to avoid
// recursing back into this path while visiting the parenthesized initializer.
if (PreviousContext.Cursor is not EnumConstantDecl)
{
return false;
}

var expr = stmt as Expr;

if (expr is ImplicitCastExpr implicitCastExpr)
{
expr = implicitCastExpr.SubExprAsWritten;
}

if (expr is null)
{
return false;
}

var sourceTypeName = GetRemappedTypeName(expr, context: null, expr.Type, out _);
return IsUnsigned(sourceTypeName) && !IsUnsigned(targetTypeName);
}

private void Visit(Cursor cursor)
{
var currentContext = _context.AddLast((cursor, null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace ClangSharp.Test
{
public enum MyEnum2
{
MyEnum2_Value1 = MyEnum1_Value1,
MyEnum2_Value1 = ((int)(MyEnum1_Value1)),
}

public static partial class Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace ClangSharp.Test
{
public enum MyEnum2
{
MyEnum2_Value1 = MyEnum1_Value1,
MyEnum2_Value1 = ((int)(MyEnum1_Value1)),
}

public static partial class Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace ClangSharp.Test
{
public enum MyEnum2
{
MyEnum2_Value1 = MyEnum1_Value1,
MyEnum2_Value1 = ((int)(MyEnum1_Value1)),
}

public static partial class Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace ClangSharp.Test
{
public enum MyEnum2
{
MyEnum2_Value1 = MyEnum1_Value1,
MyEnum2_Value1 = ((int)(MyEnum1_Value1)),
}

public static partial class Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
<enumerator name="MyEnum2_Value1" access="public">
<type primitive="False">int</type>
<value>
<code>MyEnum1_Value1</code>
<value>
<cast>int</cast>
<value>
<code>MyEnum1_Value1</code>
</value>
</value>
</value>
</enumerator>
</enumeration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
<enumerator name="MyEnum2_Value1" access="public">
<type primitive="False">int</type>
<value>
<code>MyEnum1_Value1</code>
<value>
<cast>int</cast>
<value>
<code>MyEnum1_Value1</code>
</value>
</value>
</value>
</enumerator>
</enumeration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
<enumerator name="MyEnum2_Value1" access="public">
<type primitive="False">int</type>
<value>
<code>MyEnum1_Value1</code>
<value>
<cast>int</cast>
<value>
<code>MyEnum1_Value1</code>
</value>
</value>
</value>
</enumerator>
</enumeration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
<enumerator name="MyEnum2_Value1" access="public">
<type primitive="False">int</type>
<value>
<code>MyEnum1_Value1</code>
<value>
<cast>int</cast>
<value>
<code>MyEnum1_Value1</code>
</value>
</value>
</value>
</enumerator>
</enumeration>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace ClangSharp.Test
{
public enum MyEnum
{
MyEnum_Value0,
MyEnum_Value1 = ((int)(1U << 22)),
MyEnum_Value2 = ((int)((1U << 22) | (1 << 12))),
MyEnum_Value3 = unchecked((int)(0x80000000)),
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<bindings>
<namespace name="ClangSharp.Test">
<enumeration name="MyEnum" access="public">
<type>int</type>
<enumerator name="MyEnum_Value0" access="public">
<type primitive="False">int</type>
</enumerator>
<enumerator name="MyEnum_Value1" access="public">
<type primitive="False">int</type>
<value>
<value>
<cast>int</cast>
<code>(1U &lt;&lt; 22)</code>
</value>
</value>
</enumerator>
<enumerator name="MyEnum_Value2" access="public">
<type primitive="False">int</type>
<value>
<value>
<cast>int</cast>
<value>
<code>(1U &lt;&lt; 22) | (1 &lt;&lt; 12)</code>
</value>
</value>
</value>
</enumerator>
<enumerator name="MyEnum_Value3" access="public">
<type primitive="False">int</type>
<value>
<unchecked>
<value>
<cast>int</cast>
<value>
<code>0x80000000</code>
</value>
</value>
</unchecked>
</value>
</enumerator>
</enumeration>
</namespace>
</bindings>
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,17 @@ public Task WithImplicitConversionTest()
MyEnum_Value1,
MyEnum_Value2 = 0x80000000,
};
");

[Test]
public Task WithUnsignedInitConversionTest()
=> ValidateAsync(nameof(WithUnsignedInitConversionTest), @"enum MyEnum : int
{
MyEnum_Value0,
MyEnum_Value1 = (1U << 22),
MyEnum_Value2 = (1U << 22) | (1 << 12),
MyEnum_Value3 = 0x80000000,
};
");

[Test]
Expand Down
Loading