From b53199f858e392e110c3c124dbeb4eccd72f15fc Mon Sep 17 00:00:00 2001 From: Fabio Anderegg Date: Thu, 1 Sep 2022 14:32:38 +0100 Subject: [PATCH 1/6] generate and use copy assignment operator when setting struct/class value properties --- src/Generator/AST/Utils.cs | 3 -- .../Generators/CSharp/CSharpSources.cs | 31 +++++++++++++++++++ src/Generator/Passes/ValidateOperatorsPass.cs | 4 ++- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/Generator/AST/Utils.cs b/src/Generator/AST/Utils.cs index 986b558516..47836867a1 100644 --- a/src/Generator/AST/Utils.cs +++ b/src/Generator/AST/Utils.cs @@ -36,9 +36,6 @@ public static bool CheckIgnoreMethod(Method method) if (method.IsDestructor) return true; - if (method.OperatorKind == CXXOperatorKind.Equal) - return true; - if (method.Access == AccessSpecifier.Private && !method.IsOverride && !method.IsExplicitlyGenerated) return true; diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index 8cedbf44d9..e0764de881 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -1009,8 +1009,34 @@ private bool GenerateFunctionSetter(Class @class, Property property) private void GenerateFieldSetter(Field field, Class @class, QualifiedType fieldType) { + if (field.Type.IsClass() && !field.Type.IsPointer()) + { + if (field.Type.TryGetClass(out Class fieldClass)) + { + var caop = fieldClass.Methods.FirstOrDefault(m => m.OperatorKind == CXXOperatorKind.Equal); + if (caop != null) + { + var fieldName = ((Class)field.Namespace).Layout.Fields.First( + f => f.FieldPtr == field.OriginalPtr).Name; + WriteLine($"var dest = new __IntPtr(&((__Internal*)__Instance)->{fieldName});"); + WriteLine($"var src = value.{Helpers.InstanceIdentifier};"); + + var typeName = TypePrinter.PrintNative(fieldClass); + if (IsInternalClassNested(fieldClass)) + typeName.RemoveNamespace(); + + WriteLine($"{fieldClass}.__Internal.OperatorEqual(dest, src);"); + //UnindentAndWriteCloseBrace(); + + return; + } + } + } + + string returnVar; Type type = field.Type.Desugar(); + var arrayType = type as ArrayType; if (arrayType != null && @class.IsValueType) { @@ -1481,6 +1507,11 @@ public void GenerateClassMethods(IList methods) continue; } + // We only use the copy assignment operator internally, + // so do not generate a public method wrapper for it + if (method.OperatorKind == CXXOperatorKind.Equal) + continue; + GenerateMethod(method, @class); } diff --git a/src/Generator/Passes/ValidateOperatorsPass.cs b/src/Generator/Passes/ValidateOperatorsPass.cs index b1f86db1e6..bf1dbd9211 100644 --- a/src/Generator/Passes/ValidateOperatorsPass.cs +++ b/src/Generator/Passes/ValidateOperatorsPass.cs @@ -55,6 +55,9 @@ private bool IsValidOperatorOverload(Method @operator) // The conversion operators can be overloaded case CXXOperatorKind.Conversion: case CXXOperatorKind.ExplicitConversion: + + // Copy assignment operator is used internally + case CXXOperatorKind.Equal: return true; // The comparison operators can be overloaded if their return type is bool @@ -127,7 +130,6 @@ private bool IsValidOperatorOverload(Method @operator) case CXXOperatorKind.PipePipe: // These operators cannot be overloaded. - case CXXOperatorKind.Equal: case CXXOperatorKind.Comma: case CXXOperatorKind.ArrowStar: case CXXOperatorKind.Arrow: From b0778c3d03dd4e95c927792aab8e649905dbe08a Mon Sep 17 00:00:00 2001 From: Fabio Anderegg Date: Mon, 5 Sep 2022 18:21:03 +0100 Subject: [PATCH 2/6] ignore operator= in static classes --- src/Generator/AST/Utils.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Generator/AST/Utils.cs b/src/Generator/AST/Utils.cs index 47836867a1..ad6d1eebc2 100644 --- a/src/Generator/AST/Utils.cs +++ b/src/Generator/AST/Utils.cs @@ -39,6 +39,10 @@ public static bool CheckIgnoreMethod(Method method) if (method.Access == AccessSpecifier.Private && !method.IsOverride && !method.IsExplicitlyGenerated) return true; + // operator= does not make sense on static classes, but might be generated anyway, so ignore here + if (method.OperatorKind == CXXOperatorKind.Equal && @class != null && @class.IsStatic) + return true; + // Ignore copy constructor if a base class don't has or has a private copy constructor if (method.IsCopyConstructor) { From 828c7770201c01e98cb22d5eb45542dd805795f5 Mon Sep 17 00:00:00 2001 From: Fabio Anderegg Date: Mon, 5 Sep 2022 18:21:30 +0100 Subject: [PATCH 3/6] do not try to call operator= on templated classes --- src/Generator/Generators/CSharp/CSharpSources.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index e0764de881..02a1205bc9 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -1011,7 +1011,7 @@ private void GenerateFieldSetter(Field field, Class @class, QualifiedType fieldT { if (field.Type.IsClass() && !field.Type.IsPointer()) { - if (field.Type.TryGetClass(out Class fieldClass)) + if (field.Type.TryGetClass(out Class fieldClass) && !(fieldClass is ClassTemplateSpecialization)) { var caop = fieldClass.Methods.FirstOrDefault(m => m.OperatorKind == CXXOperatorKind.Equal); if (caop != null) From 8aeecb8535eb1a03b879ca0fc0da149ceb445519 Mon Sep 17 00:00:00 2001 From: Fabio Anderegg Date: Mon, 5 Sep 2022 18:21:44 +0100 Subject: [PATCH 4/6] make sure to only call operator= when it's actually generated --- src/Generator/Generators/CSharp/CSharpSources.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index 02a1205bc9..6e57d2f35f 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -1014,7 +1014,7 @@ private void GenerateFieldSetter(Field field, Class @class, QualifiedType fieldT if (field.Type.TryGetClass(out Class fieldClass) && !(fieldClass is ClassTemplateSpecialization)) { var caop = fieldClass.Methods.FirstOrDefault(m => m.OperatorKind == CXXOperatorKind.Equal); - if (caop != null) + if (caop != null && caop.IsGenerated) { var fieldName = ((Class)field.Namespace).Layout.Fields.First( f => f.FieldPtr == field.OriginalPtr).Name; @@ -1026,7 +1026,6 @@ private void GenerateFieldSetter(Field field, Class @class, QualifiedType fieldT typeName.RemoveNamespace(); WriteLine($"{fieldClass}.__Internal.OperatorEqual(dest, src);"); - //UnindentAndWriteCloseBrace(); return; } From 3250077ef6c5fe755a86f741b242bf8d2a24ea4e Mon Sep 17 00:00:00 2001 From: Fabio Anderegg Date: Mon, 5 Sep 2022 21:43:10 +0100 Subject: [PATCH 5/6] always ignore move assignment if there is both copy and move --- .../Passes/CheckAmbiguousFunctions.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/Generator/Passes/CheckAmbiguousFunctions.cs b/src/Generator/Passes/CheckAmbiguousFunctions.cs index 09f848bbfd..964a12c694 100644 --- a/src/Generator/Passes/CheckAmbiguousFunctions.cs +++ b/src/Generator/Passes/CheckAmbiguousFunctions.cs @@ -69,6 +69,43 @@ public override bool VisitFunctionDecl(AST.Function function) private bool CheckDefaultParametersForAmbiguity(Function function, Function overload) { + // detect if function and overload are copy assignment or move assignment operators + // if both are either one of those types, ignore move assignment operator + if (function.OperatorKind == CXXOperatorKind.Equal && overload.OperatorKind == CXXOperatorKind.Equal && + function.Parameters.Count == 1 && overload.Parameters.Count == 1) + { + var functionParamType = function.Parameters[0].Type; + var overloadParamType = overload.Parameters[0].Type; + + if (functionParamType is PointerType && overloadParamType is PointerType) + { + var functionParamPointerType = functionParamType as PointerType; + var overloadParamPointerType = overloadParamType as PointerType; + + var functionPointee = functionParamPointerType.GetPointee(); + var overloadPointee = overloadParamPointerType.GetPointee(); + + functionPointee.TryGetClass(out Class @functionPointeeClass); + overloadPointee.TryGetClass(out Class @overloadPointeeClass); + + if (functionPointeeClass == function.Namespace && @overloadPointeeClass == overload.Namespace) + { + if (functionParamPointerType.Modifier == PointerType.TypeModifier.RVReference && + overloadParamPointerType.Modifier == PointerType.TypeModifier.LVReference) + { + function.ExplicitlyIgnore(); + return true; + } + else if (functionParamPointerType.Modifier == PointerType.TypeModifier.LVReference && + overloadParamPointerType.Modifier == PointerType.TypeModifier.RVReference) + { + overload.ExplicitlyIgnore(); + return true; + } + } + } + } + List functionParams = RemoveOperatorParams(function); List overloadParams = RemoveOperatorParams(overload); From a5ac3ee0031f07a7992fc7a9e0c7c2168f0e68fd Mon Sep 17 00:00:00 2001 From: Fabio Anderegg Date: Tue, 6 Sep 2022 10:27:32 +0100 Subject: [PATCH 6/6] csharp field setter: fix generation on internal field reference --- src/Generator/Generators/CSharp/CSharpSources.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index 6e57d2f35f..077bff1640 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -1018,10 +1018,10 @@ private void GenerateFieldSetter(Field field, Class @class, QualifiedType fieldT { var fieldName = ((Class)field.Namespace).Layout.Fields.First( f => f.FieldPtr == field.OriginalPtr).Name; - WriteLine($"var dest = new __IntPtr(&((__Internal*)__Instance)->{fieldName});"); + var typeName = TypePrinter.PrintNative(@class); + WriteLine($"var dest = new __IntPtr(&(({typeName}*)__Instance)->{fieldName});"); WriteLine($"var src = value.{Helpers.InstanceIdentifier};"); - var typeName = TypePrinter.PrintNative(fieldClass); if (IsInternalClassNested(fieldClass)) typeName.RemoveNamespace();