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
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,10 @@ private static bool IsTypeVoid(Cursor? cursor, Type type)
=> IsType<BuiltinType>(cursor, type, out var builtinType)
&& (builtinType.Kind == CXType_Void);

private static bool IsTypeVoidPointer(Cursor? cursor, Type type)
=> IsType<PointerType>(cursor, type, out var pointerType)
&& IsTypeVoid(cursor, pointerType.PointeeType);

internal bool IsSupportedFixedSizedBufferType(string typeName)
{
switch (typeName)
Expand Down
22 changes: 20 additions & 2 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ private void VisitArraySubscriptExpr(ArraySubscriptExpr arraySubscriptExpr)
private void VisitBinaryOperator(BinaryOperator binaryOperator)
{
var outputBuilder = StartCSharpCode();
Visit(binaryOperator.LHS);

// C/C++ allow pointer arithmetic on `void*` (treating it as byte-sized), but C# does
// not. Insert an explicit `(byte*)` cast on any `void*` operand of an additive operator
// so the arithmetic is well-defined; `byte*` implicitly converts back to `void*` where
// the surrounding context requires it.
var isAdditive = binaryOperator.Opcode is CXBinaryOperator_Add or CXBinaryOperator_Sub;

VisitBinaryOperatorOperand(binaryOperator.LHS, isAdditive);
outputBuilder.Write(' ');
outputBuilder.Write(binaryOperator.OpcodeStr);
outputBuilder.Write(' ');
Expand Down Expand Up @@ -92,12 +99,23 @@ private void VisitBinaryOperator(BinaryOperator binaryOperator)
}
else
{
Visit(binaryOperator.RHS);
VisitBinaryOperatorOperand(binaryOperator.RHS, isAdditive);
}

StopCSharpCode();
}

private void VisitBinaryOperatorOperand(Expr operand, bool isAdditive)
{
if (isAdditive && IsTypeVoidPointer(operand, operand.Type))
{
StartCSharpCode().Write("(byte*)");
StopCSharpCode();
}

Visit(operand);
}

private void VisitBreakStmt(BreakStmt breakStmt)
{
StartCSharpCode().Write("break");
Expand Down
25 changes: 25 additions & 0 deletions tests/ClangSharp.PInvokeGenerator.UnitTests/CTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1214,4 +1214,29 @@ public static partial class Methods
return ValidateGeneratedCSharpLatestUnixBindingsAsync(inputContents, expectedOutputContents, commandLineArgs: DefaultCClangCommandLineArgs, language: "c", languageStandard: DefaultCStandard);
}
}

[Test]
public Task VoidPointerArithmeticTest()
{
// C/C++ allow pointer arithmetic on `void*` (treating it as byte-sized), but C# does not,
// so the generator must insert an explicit `(byte*)` cast on the `void*` operand.
var inputContents = @"void* MyFunction(void *buf, unsigned long long size) {
return buf + size;
}
";

var expectedOutputContents = @"namespace ClangSharp.Test
{
public static unsafe partial class Methods
{
public static void* MyFunction(void* buf, [NativeTypeName(""unsigned long long"")] ulong size)
{
return (byte*)buf + size;
}
}
}
";

return ValidateGeneratedCSharpLatestWindowsBindingsAsync(inputContents, expectedOutputContents, commandLineArgs: DefaultCClangCommandLineArgs, language: "c", languageStandard: DefaultCStandard);
}
}
Loading