From add3aba684c367091d67129d1a2b0ed196025ad2 Mon Sep 17 00:00:00 2001 From: Trung Nguyen <57174311+trungnt2910@users.noreply.github.com> Date: Fri, 14 Jul 2023 20:11:51 +1000 Subject: [PATCH] CSharp: More default parameter fixes (#1747) - 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. --- src/AST/TypeExtensions.cs | 19 +++++++++ .../CSharp/CSharpExpressionPrinter.cs | 4 +- .../Generators/CSharp/CSharpSources.cs | 10 +++-- tests/dotnet/CSharp/CSharp.Tests.cs | 4 ++ tests/dotnet/CSharp/CSharp.cpp | 40 ++++++++++++++----- tests/dotnet/CSharp/CSharp.h | 26 ++++++++---- tests/dotnet/Common/Common.cpp | 8 ---- tests/dotnet/Common/Common.h | 8 ---- 8 files changed, 80 insertions(+), 39 deletions(-) diff --git a/src/AST/TypeExtensions.cs b/src/AST/TypeExtensions.cs index eb447d09..32333d6e 100644 --- a/src/AST/TypeExtensions.cs +++ b/src/AST/TypeExtensions.cs @@ -92,6 +92,25 @@ 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(this Type t, out T type) where T : Type { var pointee = t.GetPointee(); diff --git a/src/Generator/Generators/CSharp/CSharpExpressionPrinter.cs b/src/Generator/Generators/CSharp/CSharpExpressionPrinter.cs index 4302b3c1..3884d169 100644 --- a/src/Generator/Generators/CSharp/CSharpExpressionPrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpExpressionPrinter.cs @@ -68,8 +68,8 @@ namespace CppSharp.Generators.CSharp 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; diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index 621a09b1..11ff37ca 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -2783,7 +2783,7 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty 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); } @@ -2802,13 +2802,15 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty 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")};"); } } diff --git a/tests/dotnet/CSharp/CSharp.Tests.cs b/tests/dotnet/CSharp/CSharp.Tests.cs index aa7a0532..39b57fdf 100644 --- a/tests/dotnet/CSharp/CSharp.Tests.cs +++ b/tests/dotnet/CSharp/CSharp.Tests.cs @@ -346,6 +346,10 @@ public unsafe class CSharpTests 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); } } diff --git a/tests/dotnet/CSharp/CSharp.cpp b/tests/dotnet/CSharp/CSharp.cpp index 8cb552ae..a763f12c 100644 --- a/tests/dotnet/CSharp/CSharp.cpp +++ b/tests/dotnet/CSharp/CSharp.cpp @@ -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; @@ -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; @@ -1299,7 +1321,7 @@ TestString::TestString() : unicodeConst(L"ქართული ენა"), uni { } -TestChar32String::TestChar32String() : +TestChar32String::TestChar32String() : thirtyTwoBitConst(U"ქართული ენა") { static std::u32string nonConst = U"Test String"; @@ -1307,8 +1329,8 @@ TestChar32String::TestChar32String() : } TestChar32String::~TestChar32String() {} -void TestChar32String::UpdateString(const char32_t* s) -{ +void TestChar32String::UpdateString(const char32_t* s) +{ static std::u32string nativeOwnedMemory = s; thirtyTwoBitConst = nativeOwnedMemory.data(); } @@ -1316,8 +1338,8 @@ void TestChar32String::UpdateString(const char32_t* s) 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]; @@ -1325,10 +1347,10 @@ TestChar16String::TestChar16String() : 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; } @@ -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), }; diff --git a/tests/dotnet/CSharp/CSharp.h b/tests/dotnet/CSharp/CSharp.h index bc959388..01d730ad 100644 --- a/tests/dotnet/CSharp/CSharp.h +++ b/tests/dotnet/CSharp/CSharp.h @@ -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: @@ -480,6 +486,10 @@ public: void defaultWithSpecialization(IndependentFields specialization = IndependentFields()); 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: @@ -1082,9 +1092,9 @@ public: 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#" }; @@ -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 @@ -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; @@ -1476,7 +1486,7 @@ struct DLL_API ClassCustomTypeAlignment bool boolean; Align16 align16; - Align1 align1; + Align1 align1; double dbl; Align8 align8; }; @@ -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; }; diff --git a/tests/dotnet/Common/Common.cpp b/tests/dotnet/Common/Common.cpp index 2bdeb3cd..47076a02 100644 --- a/tests/dotnet/Common/Common.cpp +++ b/tests/dotnet/Common/Common.cpp @@ -1274,11 +1274,3 @@ extern "C" return s; } } // extern "C" - -void DLL_API FunctionWithFlagsAsDefaultParameter(int defaultParam) -{ -} - -void DLL_API FunctionWithConstFlagsAsDefaultParameter(int defaultParam) -{ -} diff --git a/tests/dotnet/Common/Common.h b/tests/dotnet/Common/Common.h index 9b7e2360..c018c422 100644 --- a/tests/dotnet/Common/Common.h +++ b/tests/dotnet/Common/Common.h @@ -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);