Skip to content

Commit

Permalink
CSharp: More default parameter fixes (#1747)
Browse files Browse the repository at this point in the history
- Expression generation for `ConstructorReference` now also recursively
calls `VisitExpression` for the argument if only one argument is
detected. This allows correct overload generation for functions taking
a variable as the default parameter value.
- Default parameters of pointer-to-enumeration types are now correctly
generated similar to primitive types.
  • Loading branch information
trungnt2910 authored Jul 14, 2023
1 parent 1ce9cb7 commit add3aba
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 39 deletions.
19 changes: 19 additions & 0 deletions src/AST/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,25 @@ public static bool IsPointerToPrimitiveType(this Type t, PrimitiveType primitive
return ptr.Pointee.IsPrimitiveType(primitive);
}

public static bool IsPointerToEnum(this Type t)
{
var ptr = t as PointerType;
if (ptr == null)
return false;
return ptr.Pointee.IsEnumType();
}

public static bool IsPointerToEnum(this Type t, out Enumeration @enum)
{
var ptr = t as PointerType;
if (ptr == null)
{
@enum = null;
return false;
}
return ptr.Pointee.TryGetEnum(out @enum);
}

public static bool IsPointerTo<T>(this Type t, out T type) where T : Type
{
var pointee = t.GetPointee();
Expand Down
4 changes: 2 additions & 2 deletions src/Generator/Generators/CSharp/CSharpExpressionPrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public string VisitExpression(ExpressionObsolete expr)
case StatementClass.ConstructorReference:
var constructorExpr = (CXXConstructExprObsolete)expr;
if (constructorExpr.Arguments.Count == 1 &&
constructorExpr.Arguments[0].Declaration is Enumeration.Item)
return constructorExpr.Arguments[0].Declaration.Visit(typePrinter).Type;
constructorExpr.Arguments[0].Class != StatementClass.Any)
return VisitExpression(constructorExpr.Arguments[0]);
goto default;
default:
return expr.String;
Expand Down
10 changes: 6 additions & 4 deletions src/Generator/Generators/CSharp/CSharpSources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2783,7 +2783,7 @@ private bool GenerateMethodBody(Class @class, Method method,

private string OverloadParamNameWithDefValue(Parameter p, ref int index)
{
return p.Type.IsPointerToPrimitiveType() && p.Usage == ParameterUsage.InOut && p.HasDefaultValue
return (p.Type.IsPointerToPrimitiveType() || p.Type.IsPointerToEnum()) && p.Usage == ParameterUsage.InOut && p.HasDefaultValue
? "ref param" + index++
: ExpressionPrinter.VisitParameter(p);
}
Expand All @@ -2802,13 +2802,15 @@ private void GenerateOverloadCall(Function function)
for (int i = 0, j = 0; i < function.Parameters.Count; i++)
{
var parameter = function.Parameters[i];
PrimitiveType primitiveType;
PrimitiveType primitiveType = PrimitiveType.Null;
Enumeration enumeration = null;
if (parameter.Kind == ParameterKind.Regular && parameter.Ignore &&
parameter.Type.IsPointerToPrimitiveType(out primitiveType) &&
(parameter.Type.IsPointerToPrimitiveType(out primitiveType) ||
parameter.Type.IsPointerToEnum(out enumeration)) &&
parameter.Usage == ParameterUsage.InOut && parameter.HasDefaultValue)
{
var pointeeType = ((PointerType)parameter.Type).Pointee.ToString();
WriteLine($@"{pointeeType} param{j++} = {(primitiveType == PrimitiveType.Bool ? "false" : "0")};");
WriteLine($@"{pointeeType} param{j++} = {(primitiveType == PrimitiveType.Bool ? "false" : $"({pointeeType})0")};");
}
}

Expand Down
4 changes: 4 additions & 0 deletions tests/dotnet/CSharp/CSharp.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,10 @@ public void TestDefaultArguments()
methodsWithDefaultValues.DefaultWithSpecialization();
methodsWithDefaultValues.DefaultOverloadedImplicitCtor();
methodsWithDefaultValues.DefaultWithParamNamedSameAsMethod(5);
Assert.That(methodsWithDefaultValues.DefaultIntAssignedAnEnumWithBinaryOperatorAndFlags(), Is.EqualTo((int)(Bar.Items.Item1 | Bar.Items.Item2)));
Assert.That(methodsWithDefaultValues.DefaultWithConstantFlags(), Is.EqualTo(CSharp.CSharp.ConstFlag1 | CSharp.CSharp.ConstFlag2 | CSharp.CSharp.ConstFlag3));
Assert.IsTrue(methodsWithDefaultValues.DefaultWithPointerToEnum());
Assert.AreEqual(CSharp.CSharp.DefaultSmallPODInstance.__Instance, methodsWithDefaultValues.DefaultWithNonPrimitiveType().__Instance);
}
}

Expand Down
40 changes: 31 additions & 9 deletions tests/dotnet/CSharp/CSharp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,8 @@ MethodsWithDefaultValues::QMargins::QMargins(int left, int top, int right, int b
{
}

struct SmallPOD DefaultSmallPODInstance;

const char* MethodsWithDefaultValues::stringConstant = "test";
int MethodsWithDefaultValues::intConstant = 5;

Expand Down Expand Up @@ -808,6 +810,26 @@ int MethodsWithDefaultValues::DefaultWithParamNamedSameAsMethod(int DefaultWithP
return 1;
}

int MethodsWithDefaultValues::defaultIntAssignedAnEnumWithBinaryOperatorAndFlags(int f)
{
return f;
}

int MethodsWithDefaultValues::defaultWithConstantFlags(int f)
{
return f;
}

bool MethodsWithDefaultValues::defaultWithPointerToEnum(UntypedFlags* f1, int* f2)
{
return (f1 == NULL || *f1 == (UntypedFlags)0) && (f2 == NULL || *f2 == 0);
}

SmallPOD* MethodsWithDefaultValues::defaultWithNonPrimitiveType(SmallPOD& pod)
{
return &pod;
}

int MethodsWithDefaultValues::getA()
{
return m_foo.A;
Expand Down Expand Up @@ -1299,36 +1321,36 @@ TestString::TestString() : unicodeConst(L"ქართული ენა"), uni
{
}

TestChar32String::TestChar32String() :
TestChar32String::TestChar32String() :
thirtyTwoBitConst(U"ქართული ენა")
{
static std::u32string nonConst = U"Test String";
thirtyTwoBitNonConst = &nonConst[0];
}

TestChar32String::~TestChar32String() {}
void TestChar32String::UpdateString(const char32_t* s)
{
void TestChar32String::UpdateString(const char32_t* s)
{
static std::u32string nativeOwnedMemory = s;
thirtyTwoBitConst = nativeOwnedMemory.data();
}

const char32_t* TestChar32String::RetrieveString() { return thirtyTwoBitConst; }
void TestChar32String::functionPointerUTF32(void(*ptr)(const char32_t*)) {}

TestChar16String::TestChar16String() :
sixteenBitConst(u"ქართული ენა")
TestChar16String::TestChar16String() :
sixteenBitConst(u"ქართული ენა")
{
static std::u16string nonConst = u"Test String";
sixteenBitNonConst = &nonConst[0];
}

TestChar16String::~TestChar16String() {}

void TestChar16String::UpdateString(const char16_t* s)
{
void TestChar16String::UpdateString(const char16_t* s)
{
static std::u16string nativeOwnedMemory = s;
sixteenBitConst = nativeOwnedMemory.data();
sixteenBitConst = nativeOwnedMemory.data();
}
const char16_t* TestChar16String::RetrieveString() { return sixteenBitConst; }

Expand Down Expand Up @@ -1667,7 +1689,7 @@ const unsigned ClassCustomTypeAlignmentOffsets[5]
offsetof(ClassCustomTypeAlignment, align8),
};

const unsigned ClassCustomObjectAlignmentOffsets[2] {
const unsigned ClassCustomObjectAlignmentOffsets[2] {
offsetof(ClassCustomObjectAlignment, boolean),
offsetof(ClassCustomObjectAlignment, charAligned8),
};
Expand Down
26 changes: 18 additions & 8 deletions tests/dotnet/CSharp/CSharp.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,12 @@ enum class Empty : unsigned long long int
class _ClassWithLeadingUnderscore {
};

const int ConstFlag1 = 1;
const int ConstFlag2 = 2;
const int ConstFlag3 = 4;

extern DLL_API struct SmallPOD DefaultSmallPODInstance;

class DLL_API MethodsWithDefaultValues : public Quux
{
public:
Expand Down Expand Up @@ -480,6 +486,10 @@ class DLL_API MethodsWithDefaultValues : public Quux
void defaultWithSpecialization(IndependentFields<int> specialization = IndependentFields<int>());
void defaultOverloadedImplicitCtor(P p);
void defaultOverloadedImplicitCtor(Qux q = Qux());
int defaultIntAssignedAnEnumWithBinaryOperatorAndFlags(int f = Bar::Item1 | Bar::Item2);
int defaultWithConstantFlags(int f = ConstFlag1 | ConstFlag2 | ConstFlag3);
bool defaultWithPointerToEnum(UntypedFlags* f1 = NULL, int* f2 = NULL);
SmallPOD* defaultWithNonPrimitiveType(SmallPOD& pod = DefaultSmallPODInstance);
int DefaultWithParamNamedSameAsMethod(int DefaultWithParamNamedSameAsMethod, const Foo& defaultArg = Foo());
int getA();
private:
Expand Down Expand Up @@ -1082,9 +1092,9 @@ class DLL_API VariablesWithInitializer {
static constexpr const char* StringArray1[1] { "Str" "F,\"or" };
static constexpr const char* StringArray3[3] { "Str" "F,\"or", "C#", String };
static constexpr const char* StringArray30[30] {
"Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str",
"Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str",
"Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str",
"Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str",
"Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str",
"Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str", "Str",
};
static constexpr const char* StringArray3EmptyInitList[3] { };
static constexpr const wchar_t* WideStringArray[2] { L"Str", L"C#" };
Expand Down Expand Up @@ -1278,7 +1288,7 @@ struct StructTestArrayTypeFromTypedef
#define MY_MACRO_TEST2_4 (1 << 3)
#define MY_MACRO_TEST2_ALL (1 << 4) - 1

#define SIGNED_MACRO_VALUES_TO_ENUM_TEST_1 1 << 5
#define SIGNED_MACRO_VALUES_TO_ENUM_TEST_1 1 << 5
#define SIGNED_MACRO_VALUES_TO_ENUM_TEST_2 1 << 22
#define SIGNED_MACRO_VALUES_TO_ENUM_TEST_3 1L << 32
#define SIGNED_MACRO_VALUES_TO_ENUM_TEST_4 -1
Expand Down Expand Up @@ -1465,9 +1475,9 @@ struct DLL_API ConversionFunctions
short field = 100;
};

struct DLL_API ClassCustomTypeAlignment
struct DLL_API ClassCustomTypeAlignment
{
struct alignas(1) Align1 { };
struct alignas(1) Align1 { };
struct alignas(8) Align8 { };
struct alignas(16) Align16 {
double a;
Expand All @@ -1476,7 +1486,7 @@ struct DLL_API ClassCustomTypeAlignment

bool boolean;
Align16 align16;
Align1 align1;
Align1 align1;
double dbl;
Align8 align8;
};
Expand Down Expand Up @@ -1524,7 +1534,7 @@ DLL_API extern const unsigned StructWithEmbeddedArrayOfStructObjectAlignmentOffs

DLL_API const char* TestCSharpString(const char* in, CS_OUT const char** out);
DLL_API const wchar_t* TestCSharpStringWide(const wchar_t* in, CS_OUT const wchar_t** out);
DLL_API const char16_t* TestCSharpString16(const char16_t* in, CS_OUT const char16_t** out);
DLL_API const char16_t* TestCSharpString16(const char16_t* in, CS_OUT const char16_t** out);
DLL_API const char32_t* TestCSharpString32(const char32_t* in, CS_OUT const char32_t** out);

struct DLL_API FTIStruct { int a; };
Expand Down
8 changes: 0 additions & 8 deletions tests/dotnet/Common/Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1274,11 +1274,3 @@ extern "C"
return s;
}
} // extern "C"

void DLL_API FunctionWithFlagsAsDefaultParameter(int defaultParam)
{
}

void DLL_API FunctionWithConstFlagsAsDefaultParameter(int defaultParam)
{
}
8 changes: 0 additions & 8 deletions tests/dotnet/Common/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -1567,11 +1567,3 @@ extern "C"
DLL_API void takeConflictName(struct system* self);
DLL_API struct system freeFunctionReturnByValue();
} // extern "C"

void DLL_API FunctionWithFlagsAsDefaultParameter(int defaultParam = A | B);

const int ConstFlag1 = 1;
const int ConstFlag2 = 2;
const int ConstFlag3 = 4;

void DLL_API FunctionWithConstFlagsAsDefaultParameter(int defaultParam = ConstFlag1 | ConstFlag2 | ConstFlag3);

0 comments on commit add3aba

Please sign in to comment.