Skip to content

Commit a7b4666

Browse files
committed
Fix crash when using a default argument from a base template
1 parent e1a4e13 commit a7b4666

File tree

20 files changed

+623
-11
lines changed

20 files changed

+623
-11
lines changed

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,30 +1253,31 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl)
12531253

12541254
var handledDefaultArg = false;
12551255
var isExprDefaultValue = false;
1256+
var defaultArg = (parmVarDecl.HasDefaultArg && !parmVarDecl.HasUnparsedDefaultArg) ?
1257+
(parmVarDecl.HasUninstantiatedDefaultArg ? parmVarDecl.UninstantiatedDefaultArg : parmVarDecl.DefaultArg) :
1258+
null;
12561259

1257-
if (parmVarDecl.HasDefaultArg)
1260+
if (defaultArg != null)
12581261
{
1259-
isExprDefaultValue = IsDefaultValue(parmVarDecl.DefaultArg);
1262+
isExprDefaultValue = IsDefaultValue(defaultArg);
12601263

12611264
if ((_outputBuilder is CSharpOutputBuilder csharpOutputBuilder) && (_config.WithTransparentStructs.ContainsKey(typeName) || parameters.Skip(index).Any((parmVarDecl) => {
12621265
var type = parmVarDecl.Type;
12631266
var typeName = GetTargetTypeName(parmVarDecl, out var nativeTypeName);
12641267
return _config.WithTransparentStructs.ContainsKey(typeName);
12651268
})))
12661269
{
1267-
desc.CustomAttrGeneratorData = (parmVarDecl, this, csharpOutputBuilder, isExprDefaultValue ? null : parmVarDecl.DefaultArg);
1270+
desc.CustomAttrGeneratorData = (parmVarDecl, this, csharpOutputBuilder, isExprDefaultValue ? null : defaultArg);
12681271
handledDefaultArg = true;
12691272
}
12701273
}
12711274

12721275
_outputBuilder.BeginParameter(in desc);
12731276

1274-
if (parmVarDecl.HasDefaultArg && !handledDefaultArg)
1277+
if (defaultArg != null && !handledDefaultArg)
12751278
{
12761279
_outputBuilder.BeginParameterDefault();
12771280

1278-
var defaultArg = parmVarDecl.DefaultArg;
1279-
12801281
if (IsTypePointerOrReference(parmVarDecl) && (defaultArg.Handle.Evaluate.Kind == CXEval_UnExposed))
12811282
{
12821283
if (!isExprDefaultValue)
@@ -1290,7 +1291,7 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl)
12901291
}
12911292
else
12921293
{
1293-
Visit(parmVarDecl.DefaultArg);
1294+
Visit(defaultArg);
12941295
}
12951296

12961297
_outputBuilder.EndParameterDefault();
@@ -1341,10 +1342,10 @@ void ForTypedefDecl(ParmVarDecl parmVarDecl, TypedefDecl typedefDecl)
13411342

13421343
_outputBuilder.BeginParameter(in desc);
13431344

1344-
if (parmVarDecl.HasDefaultArg)
1345+
if (parmVarDecl.HasDefaultArg && !parmVarDecl.HasUnparsedDefaultArg)
13451346
{
13461347
_outputBuilder.BeginParameterDefault();
1347-
Visit(parmVarDecl.DefaultArg);
1348+
Visit(parmVarDecl.HasUninstantiatedDefaultArg ? parmVarDecl.UninstantiatedDefaultArg : parmVarDecl.DefaultArg);
13481349
_outputBuilder.EndParameterDefault();
13491350
}
13501351

sources/ClangSharp/Cursors/Decls/ParmVarDecl.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ internal ParmVarDecl(CXCursor handle) : base(handle, CXCursor_ParmDecl, CX_DeclK
2828

2929
public bool HasDefaultArg => Handle.HasDefaultArg;
3030

31+
public bool HasUnparsedDefaultArg => Handle.HasUnparsedDefaultArg;
32+
33+
public bool HasUninstantiatedDefaultArg => Handle.HasUninstantiatedDefaultArg;
34+
3135
public bool HasInheritedDefaultArg => Handle.HasInheritedDefaultArg;
3236

3337
public Type OriginalType => _originalType.Value;

sources/ClangSharp/Cursors/Exprs/CXXDefaultArgExpr.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ internal CXXDefaultArgExpr(CXCursor handle) : base(handle, CXCursor_UnexposedExp
2121
_usedContext = new Lazy<IDeclContext?>(() => TranslationUnit.GetOrCreate<Decl>(Handle.UsedContext) as IDeclContext);
2222
}
2323

24-
public Expr Expr => Param.DefaultArg;
25-
2624
public ParmVarDecl Param => _param.Value;
2725

2826
public IDeclContext? UsedContext => _usedContext.Value;

tests/ClangSharp.PInvokeGenerator.UnitTests/Base/CXXMethodDeclarationTest.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ public abstract class CXXMethodDeclarationTest : PInvokeGeneratorTest
1616
[Test]
1717
public Task ConversionTest() => ConversionTestImpl();
1818

19+
[Test]
20+
public Task DefaultParameterInheritedFromTemplateTest() => DefaultParameterInheritedFromTemplateTestImpl();
21+
1922
[Test]
2023
public Task DestructorTest() => DestructorTestImpl();
2124

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

108111
protected abstract Task ConversionTestImpl();
109112

113+
protected abstract Task DefaultParameterInheritedFromTemplateTestImpl();
114+
110115
protected abstract Task DestructorTestImpl();
111116

112117
protected abstract Task InstanceTestImpl();

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleUnix/CXXMethodDeclarationTest.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,40 @@ public int ToInt32()
125125
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(inputContents, expectedOutputContents);
126126
}
127127

128+
protected override Task DefaultParameterInheritedFromTemplateTestImpl()
129+
{
130+
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
131+
const string InputContents = @"template <typename T>
132+
struct MyTemplate
133+
{
134+
int* DoWork(int* value = nullptr)
135+
{
136+
return value;
137+
}
138+
};
139+
140+
struct MyStruct : public MyTemplate<int>
141+
{};
142+
";
143+
144+
var entryPoint = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "__ZN8$MyTemplateDoWorkEv" : "_ZN10MyTemplateIiE6DoWorkEPi";
145+
146+
var expectedOutputContents = $@"using System.Runtime.InteropServices;
147+
148+
namespace ClangSharp.Test
149+
{{
150+
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
151+
public unsafe partial struct MyStruct
152+
{{
153+
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
154+
public static extern int* DoWork(MyStruct* pThis, int* value = null);
155+
}}
156+
}}
157+
";
158+
159+
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(InputContents, expectedOutputContents);
160+
}
161+
128162
protected override Task DestructorTestImpl()
129163
{
130164
var inputContents = @"struct MyStruct

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleWindows/CXXMethodDeclarationTest.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,40 @@ public int ToInt32()
125125
return ValidateGeneratedCSharpCompatibleWindowsBindingsAsync(inputContents, expectedOutputContents);
126126
}
127127

128+
protected override Task DefaultParameterInheritedFromTemplateTestImpl()
129+
{
130+
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
131+
const string InputContents = @"template <typename T>
132+
struct MyTemplate
133+
{
134+
int* DoWork(int* value = nullptr)
135+
{
136+
return value;
137+
}
138+
};
139+
140+
struct MyStruct : public MyTemplate<int>
141+
{};
142+
";
143+
144+
var entryPoint = Environment.Is64BitProcess ? "?DoWork@?$MyTemplate@H@@QEAAPEAHPEAH@Z" : "?DoWork@?$MyTemplate@H@@QEAPEHPEH@Z";
145+
146+
var expectedOutputContents = $@"using System.Runtime.InteropServices;
147+
148+
namespace ClangSharp.Test
149+
{{
150+
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
151+
public unsafe partial struct MyStruct
152+
{{
153+
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
154+
public static extern int* DoWork(MyStruct* pThis, int* value = null);
155+
}}
156+
}}
157+
";
158+
159+
return ValidateGeneratedCSharpCompatibleWindowsBindingsAsync(InputContents, expectedOutputContents);
160+
}
161+
128162
protected override Task DestructorTestImpl()
129163
{
130164
var inputContents = @"struct MyStruct

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpDefaultUnix/CXXMethodDeclarationTest.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,40 @@ public int ToInt32()
125125
return ValidateGeneratedCSharpDefaultUnixBindingsAsync(inputContents, expectedOutputContents);
126126
}
127127

128+
protected override Task DefaultParameterInheritedFromTemplateTestImpl()
129+
{
130+
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
131+
const string InputContents = @"template <typename T>
132+
struct MyTemplate
133+
{
134+
int* DoWork(int* value = nullptr)
135+
{
136+
return value;
137+
}
138+
};
139+
140+
struct MyStruct : public MyTemplate<int>
141+
{};
142+
";
143+
144+
var entryPoint = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "__ZN8$MyTemplateDoWorkEv" : "_ZN10MyTemplateIiE6DoWorkEPi";
145+
146+
var expectedOutputContents = $@"using System.Runtime.InteropServices;
147+
148+
namespace ClangSharp.Test
149+
{{
150+
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
151+
public unsafe partial struct MyStruct
152+
{{
153+
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
154+
public static extern int* DoWork(MyStruct* pThis, int* value = null);
155+
}}
156+
}}
157+
";
158+
159+
return ValidateGeneratedCSharpDefaultUnixBindingsAsync(InputContents, expectedOutputContents);
160+
}
161+
128162
protected override Task DestructorTestImpl()
129163
{
130164
var inputContents = @"struct MyStruct

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpDefaultWindows/CXXMethodDeclarationTest.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,40 @@ public int ToInt32()
125125
return ValidateGeneratedCSharpDefaultWindowsBindingsAsync(inputContents, expectedOutputContents);
126126
}
127127

128+
protected override Task DefaultParameterInheritedFromTemplateTestImpl()
129+
{
130+
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
131+
const string InputContents = @"template <typename T>
132+
struct MyTemplate
133+
{
134+
int* DoWork(int* value = nullptr)
135+
{
136+
return value;
137+
}
138+
};
139+
140+
struct MyStruct : public MyTemplate<int>
141+
{};
142+
";
143+
144+
var entryPoint = Environment.Is64BitProcess ? "?DoWork@?$MyTemplate@H@@QEAAPEAHPEAH@Z" : "?DoWork@?$MyTemplate@H@@QEAPEHPEH@Z";
145+
146+
var expectedOutputContents = $@"using System.Runtime.InteropServices;
147+
148+
namespace ClangSharp.Test
149+
{{
150+
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
151+
public unsafe partial struct MyStruct
152+
{{
153+
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
154+
public static extern int* DoWork(MyStruct* pThis, int* value = null);
155+
}}
156+
}}
157+
";
158+
159+
return ValidateGeneratedCSharpDefaultWindowsBindingsAsync(InputContents, expectedOutputContents);
160+
}
161+
128162
protected override Task DestructorTestImpl()
129163
{
130164
var inputContents = @"struct MyStruct

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpLatestUnix/CXXMethodDeclarationTest.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,40 @@ public int ToInt32()
125125
return ValidateGeneratedCSharpLatestUnixBindingsAsync(inputContents, expectedOutputContents);
126126
}
127127

128+
protected override Task DefaultParameterInheritedFromTemplateTestImpl()
129+
{
130+
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
131+
const string InputContents = @"template <typename T>
132+
struct MyTemplate
133+
{
134+
int* DoWork(int* value = nullptr)
135+
{
136+
return value;
137+
}
138+
};
139+
140+
struct MyStruct : public MyTemplate<int>
141+
{};
142+
";
143+
144+
var entryPoint = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "__ZN8$MyTemplateDoWorkEv" : "_ZN10MyTemplateIiE6DoWorkEPi";
145+
146+
var expectedOutputContents = $@"using System.Runtime.InteropServices;
147+
148+
namespace ClangSharp.Test
149+
{{
150+
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
151+
public unsafe partial struct MyStruct
152+
{{
153+
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
154+
public static extern int* DoWork(MyStruct* pThis, int* value = null);
155+
}}
156+
}}
157+
";
158+
159+
return ValidateGeneratedCSharpLatestUnixBindingsAsync(InputContents, expectedOutputContents);
160+
}
161+
128162
protected override Task DestructorTestImpl()
129163
{
130164
var inputContents = @"struct MyStruct

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpLatestWindows/CXXMethodDeclarationTest.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,40 @@ public int ToInt32()
125125
return ValidateGeneratedCSharpLatestWindowsBindingsAsync(inputContents, expectedOutputContents);
126126
}
127127

128+
protected override Task DefaultParameterInheritedFromTemplateTestImpl()
129+
{
130+
// NOTE: This is a regression test where a struct inherits a function from a template with a default parameter.
131+
const string InputContents = @"template <typename T>
132+
struct MyTemplate
133+
{
134+
int* DoWork(int* value = nullptr)
135+
{
136+
return value;
137+
}
138+
};
139+
140+
struct MyStruct : public MyTemplate<int>
141+
{};
142+
";
143+
144+
var entryPoint = Environment.Is64BitProcess ? "?DoWork@?$MyTemplate@H@@QEAAPEAHPEAH@Z" : "?DoWork@?$MyTemplate@H@@QEAPEHPEH@Z";
145+
146+
var expectedOutputContents = $@"using System.Runtime.InteropServices;
147+
148+
namespace ClangSharp.Test
149+
{{
150+
[NativeTypeName(""struct MyStruct : MyTemplate<int>"")]
151+
public unsafe partial struct MyStruct
152+
{{
153+
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
154+
public static extern int* DoWork(MyStruct* pThis, int* value = null);
155+
}}
156+
}}
157+
";
158+
159+
return ValidateGeneratedCSharpLatestWindowsBindingsAsync(InputContents, expectedOutputContents);
160+
}
161+
128162
protected override Task DestructorTestImpl()
129163
{
130164
var inputContents = @"struct MyStruct

0 commit comments

Comments
 (0)