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 @@ -347,6 +347,17 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type
result.nativeTypeName = $"{nativePointeeTypeName} &";
}
}
else if (type is MemberPointerType)
{
// C++ pointers-to-members have no C# equivalent. Their size and layout are ABI-specific
// (a data member pointer is pointer-sized on the Itanium ABI, where-as a member function
// pointer is generally larger), so we emit an opaque pointer-sized `void*` and preserve the
// original spelling via the NativeTypeName. The layout may be incorrect for member function
// pointers, which the diagnostic calls out so a consumer can remap the field if needed.

result.typeName = "void*";
AddDiagnostic(DiagnosticLevel.Warning, $"Unsupported type: '{type.TypeClass}'. Emitting an opaque 'void*'; the size and layout may be incorrect for member function pointers.", cursor);
}
else if (type is SubstTemplateTypeParmType substTemplateTypeParmType)
{
result.typeName = GetTypeName(cursor, context, rootType, substTemplateTypeParmType.ReplacementType, ignoreTransparentStructsWhereRequired, isTemplate, out _);
Expand Down Expand Up @@ -900,7 +911,7 @@ private void GetTypeSize(Cursor cursor, Type type, ref long alignment32, ref lon
{
GetTypeSize(cursor, enumType.Decl.IntegerType, ref alignment32, ref alignment64, ref has8BytePrimitiveField, out size32, out size64);
}
else if (type is FunctionType or PointerType or ReferenceType)
else if (type is FunctionType or PointerType or ReferenceType or MemberPointerType)
{
size32 = 4;
size64 = 8;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace ClangSharp.Test
{
public partial struct MyClass
{
}

public unsafe partial struct MyStruct
{
[NativeTypeName("int MyClass::*")]
public void* field;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<bindings>
<namespace name="ClangSharp.Test">
<struct name="MyClass" access="public"></struct>
<struct name="MyStruct" access="public" unsafe="true">
<field name="field" access="public">
<type native="int MyClass::*">void*</type>
</field>
</struct>
</namespace>
</bindings>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace ClangSharp.Test
{
public partial struct MyClass
{
}

public unsafe partial struct MyStruct
{
[NativeTypeName("void (MyClass::*)(int)")]
public void* callback;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<bindings>
<namespace name="ClangSharp.Test">
<struct name="MyClass" access="public"></struct>
<struct name="MyStruct" access="public" unsafe="true">
<field name="callback" access="public">
<type native="void (MyClass::*)(int)">void*</type>
</field>
</struct>
</namespace>
</bindings>
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.

using System.Collections.Generic;
using System.Threading.Tasks;
using NUnit.Framework;

namespace ClangSharp.UnitTests.Baseline;

[TestFixtureSource(nameof(Variants))]
public sealed class MemberPointerDeclarationTest : BaselineTest
{
public MemberPointerDeclarationTest(BaselineVariant variant) : base(variant)
{
}

protected override string Area => "MemberPointerDeclaration";

// C++ pointers-to-members have no C# equivalent, so the generator degrades to an opaque `void*` and a
// warning rather than hard-erroring the whole run (see dotnet/ClangSharp#511). Windows targets use the
// MSVC ABI, which additionally attaches an MSInheritance attribute to the referenced class.
private IReadOnlyList<Diagnostic> ExpectedDiagnostics(int column)
{
var memberPointer = new Diagnostic(DiagnosticLevel.Warning, "Unsupported type: 'CX_TypeClass_MemberPointer'. Emitting an opaque 'void*'; the size and layout may be incorrect for member function pointers.", $"Line 5, Column {column} in ClangUnsavedFile.h");

if (Variant.Os == BaselineOs.Windows)
{
var msInheritance = new Diagnostic(DiagnosticLevel.Warning, "Unsupported attribute: 'MSInheritance'. Generated bindings may be incomplete.", "Line 1, Column 8 in ClangUnsavedFile.h");
return [msInheritance, memberPointer];
}

return [memberPointer];
}

[Test]
public Task DataMemberPointerTest()
{
var inputContents = @"struct MyClass;

struct MyStruct
{
int MyClass::* field;
};
";

return ValidateAsync(nameof(DataMemberPointerTest), inputContents, expectedDiagnostics: ExpectedDiagnostics(column: 20));
}

[Test]
public Task FunctionMemberPointerTest()
{
var inputContents = @"struct MyClass;

struct MyStruct
{
void (MyClass::* callback)(int);
};
";

return ValidateAsync(nameof(FunctionMemberPointerTest), inputContents, expectedDiagnostics: ExpectedDiagnostics(column: 22));
}
}
Loading