Skip to content

Fix crash when using a default argument from a base template #547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 12, 2024
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
19 changes: 10 additions & 9 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1253,30 +1253,31 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl)

var handledDefaultArg = false;
var isExprDefaultValue = false;
var defaultArg = (parmVarDecl.HasDefaultArg && !parmVarDecl.HasUnparsedDefaultArg) ?
(parmVarDecl.HasUninstantiatedDefaultArg ? parmVarDecl.UninstantiatedDefaultArg : parmVarDecl.DefaultArg) :
null;

if (parmVarDecl.HasDefaultArg)
if (defaultArg != null)
{
isExprDefaultValue = IsDefaultValue(parmVarDecl.DefaultArg);
isExprDefaultValue = IsDefaultValue(defaultArg);

if ((_outputBuilder is CSharpOutputBuilder csharpOutputBuilder) && (_config.WithTransparentStructs.ContainsKey(typeName) || parameters.Skip(index).Any((parmVarDecl) => {
var type = parmVarDecl.Type;
var typeName = GetTargetTypeName(parmVarDecl, out var nativeTypeName);
return _config.WithTransparentStructs.ContainsKey(typeName);
})))
{
desc.CustomAttrGeneratorData = (parmVarDecl, this, csharpOutputBuilder, isExprDefaultValue ? null : parmVarDecl.DefaultArg);
desc.CustomAttrGeneratorData = (parmVarDecl, this, csharpOutputBuilder, isExprDefaultValue ? null : defaultArg);
handledDefaultArg = true;
}
}

_outputBuilder.BeginParameter(in desc);

if (parmVarDecl.HasDefaultArg && !handledDefaultArg)
if (defaultArg != null && !handledDefaultArg)
{
_outputBuilder.BeginParameterDefault();

var defaultArg = parmVarDecl.DefaultArg;

if (IsTypePointerOrReference(parmVarDecl) && (defaultArg.Handle.Evaluate.Kind == CXEval_UnExposed))
{
if (!isExprDefaultValue)
Expand All @@ -1290,7 +1291,7 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl)
}
else
{
Visit(parmVarDecl.DefaultArg);
Visit(defaultArg);
}

_outputBuilder.EndParameterDefault();
Expand Down Expand Up @@ -1341,10 +1342,10 @@ void ForTypedefDecl(ParmVarDecl parmVarDecl, TypedefDecl typedefDecl)

_outputBuilder.BeginParameter(in desc);

if (parmVarDecl.HasDefaultArg)
if (parmVarDecl.HasDefaultArg && !parmVarDecl.HasUnparsedDefaultArg)
{
_outputBuilder.BeginParameterDefault();
Visit(parmVarDecl.DefaultArg);
Visit(parmVarDecl.HasUninstantiatedDefaultArg ? parmVarDecl.UninstantiatedDefaultArg : parmVarDecl.DefaultArg);
_outputBuilder.EndParameterDefault();
}

Expand Down
4 changes: 4 additions & 0 deletions sources/ClangSharp/Cursors/Decls/ParmVarDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ internal ParmVarDecl(CXCursor handle) : base(handle, CXCursor_ParmDecl, CX_DeclK

public bool HasDefaultArg => Handle.HasDefaultArg;

public bool HasUnparsedDefaultArg => Handle.HasUnparsedDefaultArg;

public bool HasUninstantiatedDefaultArg => Handle.HasUninstantiatedDefaultArg;

public bool HasInheritedDefaultArg => Handle.HasInheritedDefaultArg;

public Type OriginalType => _originalType.Value;
Expand Down
2 changes: 0 additions & 2 deletions sources/ClangSharp/Cursors/Exprs/CXXDefaultArgExpr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ internal CXXDefaultArgExpr(CXCursor handle) : base(handle, CXCursor_UnexposedExp
_usedContext = new Lazy<IDeclContext?>(() => TranslationUnit.GetOrCreate<Decl>(Handle.UsedContext) as IDeclContext);
}

public Expr Expr => Param.DefaultArg;

public ParmVarDecl Param => _param.Value;

public IDeclContext? UsedContext => _usedContext.Value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public abstract class CXXMethodDeclarationTest : PInvokeGeneratorTest
[Test]
public Task ConversionTest() => ConversionTestImpl();

[Test]
public Task DefaultParameterInheritedFromTemplateTest() => DefaultParameterInheritedFromTemplateTestImpl();

[Test]
public Task DestructorTest() => DestructorTestImpl();

Expand Down Expand Up @@ -107,6 +110,8 @@ public static int buf_close(void* pContext)

protected abstract Task ConversionTestImpl();

protected abstract Task DefaultParameterInheritedFromTemplateTestImpl();

protected abstract Task DestructorTestImpl();

protected abstract Task InstanceTestImpl();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,40 @@ public int ToInt32()
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(inputContents, expectedOutputContents);
}

protected override Task DefaultParameterInheritedFromTemplateTestImpl()
{
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
const string InputContents = @"template <typename T>
struct MyTemplate
{
int* DoWork(int* value = nullptr)
{
return value;
}
};

struct MyStruct : public MyTemplate<int>
{};
";

var entryPoint = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "__ZN8$MyTemplateDoWorkEv" : "_ZN10MyTemplateIiE6DoWorkEPi";

var expectedOutputContents = $@"using System.Runtime.InteropServices;

namespace ClangSharp.Test
{{
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
public unsafe partial struct MyStruct
{{
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
public static extern int* DoWork(MyStruct* pThis, int* value = null);
}}
}}
";

return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(InputContents, expectedOutputContents);
}

protected override Task DestructorTestImpl()
{
var inputContents = @"struct MyStruct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,40 @@ public int ToInt32()
return ValidateGeneratedCSharpCompatibleWindowsBindingsAsync(inputContents, expectedOutputContents);
}

protected override Task DefaultParameterInheritedFromTemplateTestImpl()
{
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
const string InputContents = @"template <typename T>
struct MyTemplate
{
int* DoWork(int* value = nullptr)
{
return value;
}
};

struct MyStruct : public MyTemplate<int>
{};
";

var entryPoint = Environment.Is64BitProcess ? "?DoWork@?$MyTemplate@H@@QEAAPEAHPEAH@Z" : "?DoWork@?$MyTemplate@H@@QEAPEHPEH@Z";

var expectedOutputContents = $@"using System.Runtime.InteropServices;

namespace ClangSharp.Test
{{
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
public unsafe partial struct MyStruct
{{
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
public static extern int* DoWork(MyStruct* pThis, int* value = null);
}}
}}
";

return ValidateGeneratedCSharpCompatibleWindowsBindingsAsync(InputContents, expectedOutputContents);
}

protected override Task DestructorTestImpl()
{
var inputContents = @"struct MyStruct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,40 @@ public int ToInt32()
return ValidateGeneratedCSharpDefaultUnixBindingsAsync(inputContents, expectedOutputContents);
}

protected override Task DefaultParameterInheritedFromTemplateTestImpl()
{
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
const string InputContents = @"template <typename T>
struct MyTemplate
{
int* DoWork(int* value = nullptr)
{
return value;
}
};

struct MyStruct : public MyTemplate<int>
{};
";

var entryPoint = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "__ZN8$MyTemplateDoWorkEv" : "_ZN10MyTemplateIiE6DoWorkEPi";

var expectedOutputContents = $@"using System.Runtime.InteropServices;

namespace ClangSharp.Test
{{
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
public unsafe partial struct MyStruct
{{
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
public static extern int* DoWork(MyStruct* pThis, int* value = null);
}}
}}
";

return ValidateGeneratedCSharpDefaultUnixBindingsAsync(InputContents, expectedOutputContents);
}

protected override Task DestructorTestImpl()
{
var inputContents = @"struct MyStruct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,40 @@ public int ToInt32()
return ValidateGeneratedCSharpDefaultWindowsBindingsAsync(inputContents, expectedOutputContents);
}

protected override Task DefaultParameterInheritedFromTemplateTestImpl()
{
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
const string InputContents = @"template <typename T>
struct MyTemplate
{
int* DoWork(int* value = nullptr)
{
return value;
}
};

struct MyStruct : public MyTemplate<int>
{};
";

var entryPoint = Environment.Is64BitProcess ? "?DoWork@?$MyTemplate@H@@QEAAPEAHPEAH@Z" : "?DoWork@?$MyTemplate@H@@QEAPEHPEH@Z";

var expectedOutputContents = $@"using System.Runtime.InteropServices;

namespace ClangSharp.Test
{{
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
public unsafe partial struct MyStruct
{{
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
public static extern int* DoWork(MyStruct* pThis, int* value = null);
}}
}}
";

return ValidateGeneratedCSharpDefaultWindowsBindingsAsync(InputContents, expectedOutputContents);
}

protected override Task DestructorTestImpl()
{
var inputContents = @"struct MyStruct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,40 @@ public int ToInt32()
return ValidateGeneratedCSharpLatestUnixBindingsAsync(inputContents, expectedOutputContents);
}

protected override Task DefaultParameterInheritedFromTemplateTestImpl()
{
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
const string InputContents = @"template <typename T>
struct MyTemplate
{
int* DoWork(int* value = nullptr)
{
return value;
}
};

struct MyStruct : public MyTemplate<int>
{};
";

var entryPoint = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "__ZN8$MyTemplateDoWorkEv" : "_ZN10MyTemplateIiE6DoWorkEPi";

var expectedOutputContents = $@"using System.Runtime.InteropServices;

namespace ClangSharp.Test
{{
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
public unsafe partial struct MyStruct
{{
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
public static extern int* DoWork(MyStruct* pThis, int* value = null);
}}
}}
";

return ValidateGeneratedCSharpLatestUnixBindingsAsync(InputContents, expectedOutputContents);
}

protected override Task DestructorTestImpl()
{
var inputContents = @"struct MyStruct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,40 @@ public int ToInt32()
return ValidateGeneratedCSharpLatestWindowsBindingsAsync(inputContents, expectedOutputContents);
}

protected override Task DefaultParameterInheritedFromTemplateTestImpl()
{
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
const string InputContents = @"template <typename T>
struct MyTemplate
{
int* DoWork(int* value = nullptr)
{
return value;
}
};

struct MyStruct : public MyTemplate<int>
{};
";

var entryPoint = Environment.Is64BitProcess ? "?DoWork@?$MyTemplate@H@@QEAAPEAHPEAH@Z" : "?DoWork@?$MyTemplate@H@@QEAPEHPEH@Z";

var expectedOutputContents = $@"using System.Runtime.InteropServices;

namespace ClangSharp.Test
{{
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
public unsafe partial struct MyStruct
{{
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
public static extern int* DoWork(MyStruct* pThis, int* value = null);
}}
}}
";

return ValidateGeneratedCSharpLatestWindowsBindingsAsync(InputContents, expectedOutputContents);
}

protected override Task DestructorTestImpl()
{
var inputContents = @"struct MyStruct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,40 @@ public int ToInt32()
return ValidateGeneratedCSharpPreviewUnixBindingsAsync(inputContents, expectedOutputContents);
}

protected override Task DefaultParameterInheritedFromTemplateTestImpl()
{
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
const string InputContents = @"template <typename T>
struct MyTemplate
{
int* DoWork(int* value = nullptr)
{
return value;
}
};

struct MyStruct : public MyTemplate<int>
{};
";

var entryPoint = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "__ZN8$MyTemplateDoWorkEv" : "_ZN10MyTemplateIiE6DoWorkEPi";

var expectedOutputContents = $@"using System.Runtime.InteropServices;

namespace ClangSharp.Test
{{
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
public unsafe partial struct MyStruct
{{
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
public static extern int* DoWork(MyStruct* pThis, int* value = null);
}}
}}
";

return ValidateGeneratedCSharpPreviewUnixBindingsAsync(InputContents, expectedOutputContents);
}

protected override Task DestructorTestImpl()
{
var inputContents = @"struct MyStruct
Expand Down
Loading