Skip to content

Commit c02ee11

Browse files
committed
Allow static class conversion for classes with implicitly defined constructor
1 parent 9621e88 commit c02ee11

File tree

4 files changed

+107
-17
lines changed

4 files changed

+107
-17
lines changed

src/Generator.Tests/Passes/TestPasses.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,29 @@ public void TestCheckEnumsPass()
6666
Assert.IsTrue(ucharClassEnum.BuiltinType.Type == PrimitiveType.UChar);
6767
}
6868

69+
[Test]
70+
public void TestCheckStaticClassPass()
71+
{
72+
var staticClass = AstContext.Class("TestCheckStaticClass");
73+
var staticStruct = AstContext.Class("TestCheckStaticStruct");
74+
var staticClassDeletedCtor = AstContext.Class("TestCheckStaticClassDeleted");
75+
var nonStaticClass = AstContext.Class("TestCheckNonStaticClass");
76+
77+
Assert.IsFalse(staticClass.IsStatic);
78+
Assert.IsFalse(staticStruct.IsStatic);
79+
Assert.IsFalse(staticClassDeletedCtor.IsStatic);
80+
Assert.IsFalse(nonStaticClass.IsStatic);
81+
82+
passBuilder.AddPass(new CheckStaticClassPass());
83+
passBuilder.RunPasses(pass => pass.VisitASTContext(AstContext));
84+
85+
Assert.IsTrue(staticClass.IsStatic, "`TestCheckStaticClass` should be static");
86+
Assert.IsTrue(staticStruct.IsStatic, "`TestCheckStaticStruct` should be static");
87+
Assert.IsTrue(staticClassDeletedCtor.IsStatic, "`TestCheckStaticClassDeleted` should be static");
88+
89+
Assert.IsFalse(nonStaticClass.IsStatic, "`TestCheckNonStaticClass` should NOT be static, since it has a private data field");
90+
}
91+
6992
[Test]
7093
public void TestFunctionToInstancePass()
7194
{

src/Generator/Driver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ public void SetupPasses(ILibrary library)
230230

231231
passes.AddPass(new FindSymbolsPass());
232232
passes.AddPass(new CheckMacroPass());
233-
passes.AddPass(new CheckStaticClass());
233+
passes.AddPass(new CheckStaticClassPass());
234234

235235
if (Options.IsCLIGenerator || Options.IsCSharpGenerator || Options.IsCppGenerator)
236236
{

src/Generator/Passes/CheckStaticClass.cs renamed to src/Generator/Passes/CheckStaticClassPass.cs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ namespace CppSharp.Passes
77
/// <summary>
88
/// Checks for classes that should be bound as static classes.
99
/// </summary>
10-
public class CheckStaticClass : TranslationUnitPass
10+
public class CheckStaticClassPass : TranslationUnitPass
1111
{
12-
public CheckStaticClass()
12+
public CheckStaticClassPass()
1313
=> VisitOptions.ResetFlags(VisitFlags.ClassMethods);
1414

1515
public override bool VisitDeclaration(Declaration decl)
@@ -69,13 +69,34 @@ static bool ReturnsClassInstance(Function function)
6969

7070
public override bool VisitClassDecl(Class @class)
7171
{
72-
// If the class has any non-private constructors then it cannot
73-
// be bound as a static class and we bail out early.
74-
if (@class.Constructors.Any(m =>
75-
!(m.IsCopyConstructor || m.IsMoveConstructor)
76-
&& m.Access != AccessSpecifier.Private))
72+
// If the class is to be used as an opaque type, then it cannot be
73+
// bound as static.
74+
if (@class.IsOpaque)
75+
return false;
76+
77+
if (@class.IsDependent)
7778
return false;
7879

80+
if (@class.Constructors.Any(m =>
81+
{
82+
// Implicit constructors are not user-defined, so assume this was unintentional.
83+
if (m.IsImplicit)
84+
return false;
85+
86+
// Ignore deleted constructors.
87+
if (m.IsDeleted)
88+
return false;
89+
90+
// If the class has a copy or move constructor, it cannot be static.
91+
if (m.IsCopyConstructor || m.IsMoveConstructor)
92+
return true;
93+
94+
// If the class has any (user defined) non-private constructors then it cannot be static
95+
return m.Access != AccessSpecifier.Private;
96+
}))
97+
{
98+
return false;
99+
}
79100
// Check for any non-static fields or methods, in which case we
80101
// assume the class is not meant to be static.
81102
// Note: Static fields are represented as variables in the AST.
@@ -86,20 +107,12 @@ public override bool VisitClassDecl(Class @class)
86107

87108
// Check for any static function that return a pointer to the class.
88109
// If one exists, we assume it's a factory function and the class is
89-
// not meant to be static. It's a simple heuristic but it should be
110+
// not meant to be static. It's a simple heuristic, but it should be
90111
// good enough for the time being.
91112
if (@class.Functions.Any(m => !m.IsOperator && ReturnsClassInstance(m)) ||
92113
@class.Methods.Any(m => !m.IsOperator && ReturnsClassInstance(m)))
93114
return false;
94115

95-
// If the class is to be used as an opaque type, then it cannot be
96-
// bound as static.
97-
if (@class.IsOpaque)
98-
return false;
99-
100-
if (@class.IsDependent)
101-
return false;
102-
103116
// TODO: We should take C++ friends into account here, they might allow
104117
// a class to be instantiated even it if's not possible to instantiate
105118
// it using just its regular members.

tests/dotnet/Native/Passes.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,60 @@ struct TestCheckAmbiguousFunctionsPass
130130
int Method(int x) const;
131131
};
132132

133+
class TestCheckStaticClass
134+
{
135+
public:
136+
static int Method();
137+
static int Method(int x);
138+
139+
constexpr static float ConstExprStatic = 3.0f;
140+
inline static float InlineStatic = 1.0f;
141+
142+
private:
143+
inline static float PrivateInlineStatic = 1.0f;
144+
};
145+
146+
struct TestCheckStaticStruct
147+
{
148+
static int Method();
149+
static int Method(int x);
150+
151+
constexpr static float ConstExprStatic = 3.0f;
152+
inline static float InlineStatic = 1.0f;
153+
};
154+
155+
class TestCheckStaticClassDeleted
156+
{
157+
public:
158+
TestCheckStaticClassDeleted() = delete;
159+
160+
static int Method();
161+
static int Method(int x);
162+
163+
constexpr static float ConstExprStatic = 3.0f;
164+
inline static float InlineStatic = 1.0f;
165+
166+
private:
167+
inline static float PrivateInlineStatic = 1.0f;
168+
};
169+
170+
class TestCheckNonStaticClass
171+
{
172+
public:
173+
TestCheckNonStaticClass() = default;
174+
175+
static int Method();
176+
static int Method(int x);
177+
178+
constexpr static float ConstExprStatic = 3.0f;
179+
inline static float InlineStatic = 1.0f;
180+
181+
private:
182+
inline static float PrivateInlineStatic = 1.0f;
183+
184+
float NonStatic = 1.0f;
185+
};
186+
133187
#define CS_INTERNAL
134188
struct TestMethodAsInternal
135189
{

0 commit comments

Comments
 (0)