Skip to content

Commit 47a7cd2

Browse files
committed
Handle negative, out of int range, and within int range cases
1 parent ef65f7b commit 47a7cd2

File tree

2 files changed

+97
-13
lines changed

2 files changed

+97
-13
lines changed

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,49 @@ private void VisitBinaryOperator(BinaryOperator binaryOperator)
4545
if (binaryOperator.IsShiftOp || binaryOperator.IsShiftAssignOp)
4646
{
4747
// RHS of shift operation in C# must be an int
48-
if (binaryOperator.RHS.Type.Kind is CXType_Int || (binaryOperator.RHS.Type.Kind is CXType_Long && !_config.GenerateUnixTypes))
49-
{
50-
Visit(binaryOperator.RHS);
51-
}
52-
else if (binaryOperator.RHS is IntegerLiteral literal)
53-
{
54-
outputBuilder.Write(literal.Value);
55-
}
56-
else
48+
49+
// Negative shifts are undefined behavior in C/C++, but still needs to have a compilable output
50+
var isNegated = binaryOperator.RHS is UnaryOperator { Opcode: CXUnaryOperator_Minus };
51+
var sign = isNegated ? -1 : 1;
52+
53+
var rhs = isNegated ? ((UnaryOperator)binaryOperator.RHS).SubExpr : binaryOperator.RHS;
54+
switch (rhs)
5755
{
58-
outputBuilder.Write("(int)");
56+
case IntegerLiteral literal:
57+
{
58+
var value = sign * literal.Value;
59+
if (value is > int.MaxValue or < int.MinValue)
60+
{
61+
// Literal is in int32 range
62+
outputBuilder.Write("(int)(");
63+
outputBuilder.Write(sign * literal.Value);
64+
outputBuilder.Write(")");
65+
}
66+
else
67+
{
68+
// Literal is not in int32 range
69+
outputBuilder.Write(sign * literal.Value);
70+
}
5971

60-
outputBuilder.Write('(');
61-
Visit(binaryOperator.RHS);
62-
outputBuilder.Write(')');
72+
break;
73+
}
74+
// Already the correct type or implicitly castable
75+
case { Type.Kind: CXType_Int or CXType_UShort or CXType_Short or CXType_Char_U or CXType_Char_S or CXType_UChar or CXType_SChar }:
76+
case { Type.Kind: CXType_Long } when _config is { GenerateUnixTypes: false }:
77+
case { Type.Kind: CXType_Char16 } when _config is { GenerateDisableRuntimeMarshalling: false }:
78+
case { Type.Kind: CXType_WChar } when _config is { GenerateDisableRuntimeMarshalling: false, GenerateUnixTypes: false }:
79+
{
80+
Visit(binaryOperator.RHS);
81+
break;
82+
}
83+
// Fallback
84+
default:
85+
{
86+
outputBuilder.Write("(int)(");
87+
Visit(binaryOperator.RHS);
88+
outputBuilder.Write(")");
89+
break;
90+
}
6391
}
6492
}
6593
else

tests/ClangSharp.PInvokeGenerator.UnitTests/CTest.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,27 @@ public Task UnsignedIntBitshiftTest()
292292
const int ShiftSignedLong = 1 << SignedLong;
293293
const int ShiftUnsigned = 1 << Unsigned;
294294
295+
const int Char = 1 << (signed char)1;
296+
const int UChar = 1 << (unsigned char)1;
297+
295298
const int CInt = 1 << 1;
296299
const int CUint = 1 << 1U;
297300
301+
const int Negative = 1 << -1;
302+
303+
const int OutOfRangePos = 1 << 10000000000;
304+
const int OutOfRangeNeg = 1 << -10000000000;
305+
306+
const int IntMax = 1 << 2147483647;
307+
const int IntMin = 1 << -2147483648;
308+
309+
const int LongMax = 1 << 9223372036854775807;
310+
const int LongMin = 1 << -9223372036854775808;
311+
312+
const int ULongMax = 1 << 18446744073709551615;
313+
314+
const int Hexadecimal = 1 << 0x01;
315+
298316
#define Left 1 << 1U
299317
#define Right 1 >> 1U
300318
@@ -307,6 +325,11 @@ public Task UnsignedIntBitshiftTest()
307325
#define Complex ((((unsigned int)(0)) << 29U) | (((unsigned int)(1)) << 22U) | (((unsigned int)(0)) << 12U) | ((unsigned int)(0)))
308326
";
309327

328+
// Non-ideal cases:
329+
// UChar
330+
// IntMin
331+
// ULongMax
332+
// Hexadecimal
310333
var expectedOutputContents = @"namespace ClangSharp.Test
311334
{
312335
public static partial class Methods
@@ -329,12 +352,45 @@ public static partial class Methods
329352
[NativeTypeName(""const int"")]
330353
public const int ShiftUnsigned = 1 << (int)(Unsigned);
331354
355+
[NativeTypeName(""const int"")]
356+
public const int Char = 1 << (sbyte)(1);
357+
358+
[NativeTypeName(""const int"")]
359+
public const int UChar = unchecked(1 << (byte)(1));
360+
332361
[NativeTypeName(""const int"")]
333362
public const int CInt = 1 << 1;
334363
335364
[NativeTypeName(""const int"")]
336365
public const int CUint = 1 << 1;
337366
367+
[NativeTypeName(""const int"")]
368+
public const int Negative = 1 << -1;
369+
370+
[NativeTypeName(""const int"")]
371+
public const int OutOfRangePos = unchecked(1 << (int)(10000000000));
372+
373+
[NativeTypeName(""const int"")]
374+
public const int OutOfRangeNeg = unchecked(1 << (int)(-10000000000));
375+
376+
[NativeTypeName(""const int"")]
377+
public const int IntMax = 1 << 2147483647;
378+
379+
[NativeTypeName(""const int"")]
380+
public const int IntMin = unchecked(1 << -2147483648);
381+
382+
[NativeTypeName(""const int"")]
383+
public const int LongMax = unchecked(1 << (int)(9223372036854775807));
384+
385+
[NativeTypeName(""const int"")]
386+
public const int LongMin = unchecked(1 << (int)(-9223372036854775808));
387+
388+
[NativeTypeName(""const int"")]
389+
public const int ULongMax = 1 << -1;
390+
391+
[NativeTypeName(""const int"")]
392+
public const int Hexadecimal = 1 << 1;
393+
338394
[NativeTypeName(""#define Left 1 << 1U"")]
339395
public const int Left = 1 << 1;
340396

0 commit comments

Comments
 (0)