From 9472624b9f74599db42157ea8434b7c384ce7cb2 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 19 Jul 2026 10:25:30 -0700 Subject: [PATCH 1/2] Strip the redundant global-scope qualifier from NativeTypeName clang 22 spells a name with a leading `::` when the unqualified name is shadowed by a type in an enclosing namespace. Strip the leading global-scope qualifier so `::boolean *` matches the ~874 other unqualified `boolean` sites instead of emitting spurious churn. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../PInvokeGenerator.Naming.cs | 63 ++++++++++++------- ...obalScopeQualifierIsStrippedTest.CSharp.cs | 13 ++++ ...GlobalScopeQualifierIsStrippedTest.Xml.xml | 15 +++++ .../Baseline/NamespaceNativeTypeNameTest.cs | 26 ++++++++ 4 files changed, 95 insertions(+), 22 deletions(-) create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/GlobalScopeQualifierIsStrippedTest.CSharp.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/GlobalScopeQualifierIsStrippedTest.Xml.xml diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs index 361f6366..6b6a8bc1 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs @@ -146,6 +146,16 @@ private void ResolveAnonymousCursorName(NamedDecl namedDecl, ReadOnlySpan private static string GetNamespaceQualifiedNativeTypeName(Type type, string nativeTypeName) { + // clang 22 can spell a global-scope name with a redundant leading `::` (e.g. `::boolean`) + // when the unqualified name is shadowed in an enclosing namespace. Drop the global-scope + // qualifier so the NativeTypeName stays consistent with every other unqualified spelling. + var globalScopeOffset = SkipLeadingTypeQualifiers(nativeTypeName); + + if (nativeTypeName.AsSpan(globalScopeOffset).StartsWith("::", StringComparison.Ordinal)) + { + nativeTypeName = nativeTypeName.Remove(globalScopeOffset, 2); + } + // Peel pointer/reference/array layers so a stripped namespace is restored even for // `Ns::Point *` and similar, where clang only ever drops the leading `Namespace::`. @@ -195,6 +205,36 @@ private static string GetNamespaceQualifiedNativeTypeName(Type type, string nati // qualifier belongs on the type name, after any leading run of these. private static readonly string[] s_leadingTypeQualifiers = ["const ", "volatile ", "struct ", "class ", "union ", "enum ", "__unaligned "]; + // Advances past any leading run of cv-qualifiers, elaborated tag keywords, or `__unaligned` to + // the offset where the type's nested-name-specifier begins. + private static int SkipLeadingTypeQualifiers(string nativeTypeName) + { + var offset = 0; + + while (true) + { + var rest = nativeTypeName.AsSpan(offset); + var matched = false; + + foreach (var prefix in s_leadingTypeQualifiers) + { + if (rest.StartsWith(prefix, StringComparison.Ordinal)) + { + offset += prefix.Length; + matched = true; + break; + } + } + + if (!matched) + { + break; + } + } + + return offset; + } + private static string GetNamespaceQualifiedNativeTypeName(Decl decl, string nativeTypeName) { // clang 22's type printer omits the enclosing C++ namespace from a reference when a @@ -220,28 +260,7 @@ private static string GetNamespaceQualifiedNativeTypeName(Decl decl, string nati // The qualifier belongs on the type name, after any leading cv-qualifiers, elaborated tag // keywords, or `__unaligned`, so e.g. a `const struct` pointer stays `const struct Ns::Point *` // rather than the malformed `Ns::const struct Point *`. - var offset = 0; - - while (true) - { - var rest = nativeTypeName.AsSpan(offset); - var matched = false; - - foreach (var prefix in s_leadingTypeQualifiers) - { - if (rest.StartsWith(prefix, StringComparison.Ordinal)) - { - offset += prefix.Length; - matched = true; - break; - } - } - - if (!matched) - { - break; - } - } + var offset = SkipLeadingTypeQualifiers(nativeTypeName); // The source spelling may already carry a trailing run of the namespace (a minimally // qualified reference, e.g. `Windows::Foundation::IPropertyValue` written inside diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/GlobalScopeQualifierIsStrippedTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/GlobalScopeQualifierIsStrippedTest.CSharp.cs new file mode 100644 index 00000000..e93ebd86 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/GlobalScopeQualifierIsStrippedTest.CSharp.cs @@ -0,0 +1,13 @@ +namespace ClangSharp.Test +{ + public partial struct @boolean + { + public int x; + } + + public unsafe partial struct Holder + { + [NativeTypeName("boolean *")] + public byte* globalPtr; + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/GlobalScopeQualifierIsStrippedTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/GlobalScopeQualifierIsStrippedTest.Xml.xml new file mode 100644 index 00000000..990c7e29 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/GlobalScopeQualifierIsStrippedTest.Xml.xml @@ -0,0 +1,15 @@ + + + + + + int + + + + + byte* + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/NamespaceNativeTypeNameTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/NamespaceNativeTypeNameTest.cs index 1fe1220f..0bf201d8 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/NamespaceNativeTypeNameTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/NamespaceNativeTypeNameTest.cs @@ -100,4 +100,30 @@ struct Interop return ValidateAsync(nameof(PartiallyQualifiedNamespaceIsNotDuplicatedTest), inputContents); } + + // clang 22 can spell a global-scope reference with a redundant leading `::` (e.g. `::boolean`) + // when the unqualified name is shadowed by a type in the enclosing namespace. The generator + // drops that global-scope qualifier so the `NativeTypeName` stays consistent with every other + // unqualified spelling rather than churning to `::boolean`. + [Test] + public Task GlobalScopeQualifierIsStrippedTest() + { + var inputContents = @"typedef unsigned char boolean; + +namespace Ns +{ + struct boolean + { + int x; + }; + + struct Holder + { + ::boolean* globalPtr; + }; +} +"; + + return ValidateAsync(nameof(GlobalScopeQualifierIsStrippedTest), inputContents); + } } From f638b15eeadd094d3d290eacb23c4a1ceca0c521 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 19 Jul 2026 10:25:37 -0700 Subject: [PATCH 2/2] Take the address of an inline-array field decayed to a pointer An array-to-pointer decay of an inline-array field was passed unaddressed, producing e.g. `NativeMemory.Fill(a->u.Byte, ...)` which does not compile because an `[InlineArray]` field has no implicit `void*` conversion. Emit the `&` when a `MemberExpr` of `ConstantArrayType` decays to a pointer, except in compatible mode where the `fixed` buffer decays naturally. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../PInvokeGenerator.VisitStmt.cs | 9 ++++++ ...nterArgumentTest.CSharp.Compatible.Unix.cs | 21 +++++++++++++ ...rArgumentTest.CSharp.Compatible.Windows.cs | 21 +++++++++++++ ...PointerArgumentTest.CSharp.Default.Unix.cs | 28 +++++++++++++++++ ...nterArgumentTest.CSharp.Default.Windows.cs | 28 +++++++++++++++++ ...oPointerArgumentTest.CSharp.Latest.Unix.cs | 28 +++++++++++++++++ ...interArgumentTest.CSharp.Latest.Windows.cs | 28 +++++++++++++++++ ...PointerArgumentTest.CSharp.Preview.Unix.cs | 28 +++++++++++++++++ ...nterArgumentTest.CSharp.Preview.Windows.cs | 28 +++++++++++++++++ ...ointerArgumentTest.Xml.Compatible.Unix.xml | 25 +++++++++++++++ ...terArgumentTest.Xml.Compatible.Windows.xml | 25 +++++++++++++++ ...ToPointerArgumentTest.Xml.Default.Unix.xml | 31 +++++++++++++++++++ ...ointerArgumentTest.Xml.Default.Windows.xml | 31 +++++++++++++++++++ ...dToPointerArgumentTest.Xml.Latest.Unix.xml | 31 +++++++++++++++++++ ...PointerArgumentTest.Xml.Latest.Windows.xml | 31 +++++++++++++++++++ ...ToPointerArgumentTest.Xml.Preview.Unix.xml | 31 +++++++++++++++++++ ...ointerArgumentTest.Xml.Preview.Windows.xml | 31 +++++++++++++++++++ .../FunctionDeclarationBodyImportTest.cs | 22 +++++++++++++ 18 files changed, 477 insertions(+) create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Compatible.Unix.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Compatible.Windows.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Default.Unix.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Default.Windows.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Latest.Unix.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Latest.Windows.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Preview.Unix.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Preview.Windows.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Compatible.Unix.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Compatible.Windows.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Default.Unix.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Default.Windows.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Latest.Unix.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Latest.Windows.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Preview.Unix.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Preview.Windows.xml diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index d2c71488..fa9dc7a3 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -1526,6 +1526,15 @@ private void VisitImplicitCastExpr(ImplicitCastExpr implicitCastExpr) } else { + // A constant-array field decayed to a pointer projects to an `[InlineArray]` + // value type, which has no implicit pointer conversion, so take its address to + // bind it to a pointer. A compatible-mode fixed buffer decays on its own, and + // array locals (managed arrays) or string literals must not be addressed here. + if (!Config.GenerateCompatibleCode && (subExpr is MemberExpr) && (subExpr.Type.CanonicalType is ConstantArrayType) && (implicitCastExpr.Type.CanonicalType is PointerType)) + { + outputBuilder.Write('&'); + } + VisitStmt(subExpr); } break; diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Compatible.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Compatible.Unix.cs new file mode 100644 index 00000000..9c40df1c --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Compatible.Unix.cs @@ -0,0 +1,21 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public unsafe partial struct MyStruct + { + [NativeTypeName("unsigned char[16]")] + public fixed byte Data[16]; + } + + public static unsafe partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, EntryPoint = "_Z15MyOtherFunctionPv", ExactSpelling = true)] + public static extern void MyOtherFunction(void* pData); + + public static void MyFunction([NativeTypeName("struct MyStruct *")] MyStruct* pStruct) + { + MyOtherFunction(pStruct->Data); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Compatible.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Compatible.Windows.cs new file mode 100644 index 00000000..2b2853e0 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Compatible.Windows.cs @@ -0,0 +1,21 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public unsafe partial struct MyStruct + { + [NativeTypeName("unsigned char[16]")] + public fixed byte Data[16]; + } + + public static unsafe partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?MyOtherFunction@@YAXPEAX@Z", ExactSpelling = true)] + public static extern void MyOtherFunction(void* pData); + + public static void MyFunction([NativeTypeName("struct MyStruct *")] MyStruct* pStruct) + { + MyOtherFunction(pStruct->Data); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Default.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Default.Unix.cs new file mode 100644 index 00000000..d6e31c8b --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Default.Unix.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + [NativeTypeName("unsigned char[16]")] + public _Data_e__FixedBuffer Data; + + [InlineArray(16)] + public partial struct _Data_e__FixedBuffer + { + public byte e0; + } + } + + public static unsafe partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, EntryPoint = "_Z15MyOtherFunctionPv", ExactSpelling = true)] + public static extern void MyOtherFunction(void* pData); + + public static void MyFunction([NativeTypeName("struct MyStruct *")] MyStruct* pStruct) + { + MyOtherFunction(&pStruct->Data); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Default.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Default.Windows.cs new file mode 100644 index 00000000..5b5f6d5f --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Default.Windows.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + [NativeTypeName("unsigned char[16]")] + public _Data_e__FixedBuffer Data; + + [InlineArray(16)] + public partial struct _Data_e__FixedBuffer + { + public byte e0; + } + } + + public static unsafe partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?MyOtherFunction@@YAXPEAX@Z", ExactSpelling = true)] + public static extern void MyOtherFunction(void* pData); + + public static void MyFunction([NativeTypeName("struct MyStruct *")] MyStruct* pStruct) + { + MyOtherFunction(&pStruct->Data); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Latest.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Latest.Unix.cs new file mode 100644 index 00000000..d6e31c8b --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Latest.Unix.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + [NativeTypeName("unsigned char[16]")] + public _Data_e__FixedBuffer Data; + + [InlineArray(16)] + public partial struct _Data_e__FixedBuffer + { + public byte e0; + } + } + + public static unsafe partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, EntryPoint = "_Z15MyOtherFunctionPv", ExactSpelling = true)] + public static extern void MyOtherFunction(void* pData); + + public static void MyFunction([NativeTypeName("struct MyStruct *")] MyStruct* pStruct) + { + MyOtherFunction(&pStruct->Data); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Latest.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Latest.Windows.cs new file mode 100644 index 00000000..5b5f6d5f --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Latest.Windows.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + [NativeTypeName("unsigned char[16]")] + public _Data_e__FixedBuffer Data; + + [InlineArray(16)] + public partial struct _Data_e__FixedBuffer + { + public byte e0; + } + } + + public static unsafe partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?MyOtherFunction@@YAXPEAX@Z", ExactSpelling = true)] + public static extern void MyOtherFunction(void* pData); + + public static void MyFunction([NativeTypeName("struct MyStruct *")] MyStruct* pStruct) + { + MyOtherFunction(&pStruct->Data); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Preview.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Preview.Unix.cs new file mode 100644 index 00000000..d6e31c8b --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Preview.Unix.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + [NativeTypeName("unsigned char[16]")] + public _Data_e__FixedBuffer Data; + + [InlineArray(16)] + public partial struct _Data_e__FixedBuffer + { + public byte e0; + } + } + + public static unsafe partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, EntryPoint = "_Z15MyOtherFunctionPv", ExactSpelling = true)] + public static extern void MyOtherFunction(void* pData); + + public static void MyFunction([NativeTypeName("struct MyStruct *")] MyStruct* pStruct) + { + MyOtherFunction(&pStruct->Data); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Preview.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Preview.Windows.cs new file mode 100644 index 00000000..5b5f6d5f --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.CSharp.Preview.Windows.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + [NativeTypeName("unsigned char[16]")] + public _Data_e__FixedBuffer Data; + + [InlineArray(16)] + public partial struct _Data_e__FixedBuffer + { + public byte e0; + } + } + + public static unsafe partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?MyOtherFunction@@YAXPEAX@Z", ExactSpelling = true)] + public static extern void MyOtherFunction(void* pData); + + public static void MyFunction([NativeTypeName("struct MyStruct *")] MyStruct* pStruct) + { + MyOtherFunction(&pStruct->Data); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Compatible.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Compatible.Unix.xml new file mode 100644 index 00000000..8cd5dace --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Compatible.Unix.xml @@ -0,0 +1,25 @@ + + + + + + byte + + + + + void + + void* + + + + void + + MyStruct* + + MyOtherFunction(pStruct->Data); + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Compatible.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Compatible.Windows.xml new file mode 100644 index 00000000..15dc30e5 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Compatible.Windows.xml @@ -0,0 +1,25 @@ + + + + + + byte + + + + + void + + void* + + + + void + + MyStruct* + + MyOtherFunction(pStruct->Data); + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Default.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Default.Unix.xml new file mode 100644 index 00000000..296fd096 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Default.Unix.xml @@ -0,0 +1,31 @@ + + + + + + byte + + + InlineArray(16) + + byte + + + + + + void + + void* + + + + void + + MyStruct* + + MyOtherFunction(&pStruct->Data); + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Default.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Default.Windows.xml new file mode 100644 index 00000000..37511c1b --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Default.Windows.xml @@ -0,0 +1,31 @@ + + + + + + byte + + + InlineArray(16) + + byte + + + + + + void + + void* + + + + void + + MyStruct* + + MyOtherFunction(&pStruct->Data); + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Latest.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Latest.Unix.xml new file mode 100644 index 00000000..296fd096 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Latest.Unix.xml @@ -0,0 +1,31 @@ + + + + + + byte + + + InlineArray(16) + + byte + + + + + + void + + void* + + + + void + + MyStruct* + + MyOtherFunction(&pStruct->Data); + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Latest.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Latest.Windows.xml new file mode 100644 index 00000000..37511c1b --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Latest.Windows.xml @@ -0,0 +1,31 @@ + + + + + + byte + + + InlineArray(16) + + byte + + + + + + void + + void* + + + + void + + MyStruct* + + MyOtherFunction(&pStruct->Data); + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Preview.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Preview.Unix.xml new file mode 100644 index 00000000..296fd096 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Preview.Unix.xml @@ -0,0 +1,31 @@ + + + + + + byte + + + InlineArray(16) + + byte + + + + + + void + + void* + + + + void + + MyStruct* + + MyOtherFunction(&pStruct->Data); + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Preview.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Preview.Windows.xml new file mode 100644 index 00000000..37511c1b --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/ArrayFieldToPointerArgumentTest.Xml.Preview.Windows.xml @@ -0,0 +1,31 @@ + + + + + + byte + + + InlineArray(16) + + byte + + + + + + void + + void* + + + + void + + MyStruct* + + MyOtherFunction(&pStruct->Data); + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs index 46881ef6..857ca648 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs @@ -44,6 +44,28 @@ public Task ArraySubscriptTest() return ValidateAsync(nameof(ArraySubscriptTest), inputContents); } + // A constant-array field projects to an `[InlineArray]` value type, which has no implicit + // pointer conversion, so a decay to a pointer argument must take its address (`&pStruct->Data`). + // A compatible-mode fixed buffer decays to a pointer on its own, so it stays unaddressed. + [Test] + public Task ArrayFieldToPointerArgumentTest() + { + var inputContents = @"void MyOtherFunction(void* pData); + +struct MyStruct +{ + unsigned char Data[16]; +}; + +void MyFunction(struct MyStruct* pStruct) +{ + MyOtherFunction(pStruct->Data); +} +"; + + return ValidateAsync(nameof(ArrayFieldToPointerArgumentTest), inputContents); + } + [Test] public Task BasicTest() {