From 05db205050d0262d05037df6a15b64c36e27606b Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 19 Aug 2021 09:08:31 -0700 Subject: [PATCH 01/15] Configure ClangSharp for 12.0.0 release --- Directory.Build.props | 2 +- Directory.Build.targets | 2 +- scripts/azure-pipelines.yml | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3b291f70..55962641 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -43,7 +43,7 @@ $(BaseArtifactsPath)pkg/$(Configuration)/ ClangSharp 12.0.0 - beta2 + beta3 pr diff --git a/Directory.Build.targets b/Directory.Build.targets index b5df26ed..d9444a7d 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -21,7 +21,7 @@ - + $(NETCoreSdkRuntimeIdentifier) $(OVERRIDE_RUNTIME_IDENTIFIER) diff --git a/scripts/azure-pipelines.yml b/scripts/azure-pipelines.yml index 2be7c59d..5266557e 100644 --- a/scripts/azure-pipelines.yml +++ b/scripts/azure-pipelines.yml @@ -29,6 +29,14 @@ jobs: architecture: x64 testwin32metadata: false +- template: azure-windows.yml + parameters: + name: windows_release_x64 + pool: windows-latest + configuration: Release + architecture: x64 + testwin32metadata: false + - template: azure-windows.yml parameters: name: test_win32metadata From eda14e366eb3c1f4f047f0658f1f1c4b19711169 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sat, 4 Sep 2021 08:20:56 -0700 Subject: [PATCH 02/15] Ensure we look through parens when handling sizeof exprs --- .../ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index 8d12da68..3ee8d1bd 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -1901,7 +1901,7 @@ private void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr unaryExprOrT { var arg = args[i]; - if (IsStmtAsWritten(arg, unaryExprOrTypeTraitExpr)) + if (IsStmtAsWritten(arg, unaryExprOrTypeTraitExpr, removeParens: true)) { index = i; break; From add41186690359df40f98ab42cfcdf3c3c9f8037 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 5 Sep 2021 15:58:23 -0700 Subject: [PATCH 03/15] Ensure that string and character literals don't unecessarily escape --- .../PInvokeGenerator.VisitDecl.cs | 4 ++-- .../ClangSharp.PInvokeGenerator/PInvokeGenerator.cs | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 90c551d4..96120b29 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -2521,12 +2521,12 @@ void ForUnderlyingType(TypedefDecl typedefDecl, Type underlyingType) if (_config.LogPotentialTypedefRemappings) { var typedefName = typedefDecl.UnderlyingDecl.Name; - var possibleNamesToRemap = new string[] {"_" + typedefName, "_tag" + typedefName, "tag" + typedefName}; + var possibleNamesToRemap = new string[] {"_" + typedefName, "_tag" + typedefName, "tag" + typedefName, typedefName + "_tag" }; var underlyingName = underlyingTagType.AsString; foreach (var possibleNameToRemap in possibleNamesToRemap) { - if (!_config.RemappedNames.ContainsKey(possibleNameToRemap)) + if (!_config.RemappedNames.ContainsKey(possibleNameToRemap) && !_config.RemappedNames.ContainsKey(possibleNameToRemap + "*")) { if (possibleNameToRemap == underlyingName) { diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 7cfab004..77b3f998 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -662,14 +662,20 @@ private string EscapeAndStripName(string name) return EscapeName(name); } - internal static string EscapeCharacter(char value) => EscapeString(value.ToString()); + internal static string EscapeCharacter(char value) => value switch { + '\\' => "\\\\", + '\r' => "\\r", + '\n' => "\\n", + '\t' => "\\t", + '\'' => "\\'", + _ => value.ToString(), + }; internal static string EscapeString(string value) => value.Replace("\\", "\\\\") .Replace("\r", "\\r") .Replace("\n", "\\n") .Replace("\t", "\\t") - .Replace("\"", "\\\"") - .Replace("\'", "\\'"); + .Replace("\"", "\\\""); private AccessSpecifier GetAccessSpecifier(NamedDecl namedDecl) { From b57bb4dcbd0b07c0939eebaa2cbabb731ccaca46 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 5 Sep 2021 16:18:52 -0700 Subject: [PATCH 04/15] Ensure that implicit casts of enums to integers have explicit casts generated --- .../ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index 3ee8d1bd..af7c2ccc 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -832,7 +832,7 @@ private void VisitImplicitCastExpr(ImplicitCastExpr implicitCastExpr) default: { - if ((subExpr is DeclRefExpr declRefExpr) && (declRefExpr.Decl is EnumConstantDecl enumConstantDecl)) + if (IsStmtAsWritten(subExpr, out var declRefExpr, removeParens: true) && (declRefExpr.Decl is EnumConstantDecl enumConstantDecl)) { ForEnumConstantDecl(implicitCastExpr, enumConstantDecl); } From 30a452f56c7f94bd8421110dadf04ac365acd4dc Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 5 Sep 2021 16:42:17 -0700 Subject: [PATCH 05/15] Ensure we insert `unchecked` for logical not operations that deal with different signs --- .../PInvokeGenerator.cs | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 77b3f998..14277889 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -3236,9 +3236,38 @@ private bool IsUnchecked(string targetTypeName, Stmt stmt) case CX_StmtClass.CX_StmtClass_UnaryOperator: { var unaryOperator = (UnaryOperator)stmt; - return IsUnchecked(targetTypeName, unaryOperator.SubExpr) - || IsUnchecked(targetTypeName, unaryOperator.Handle.Evaluate) - || ((unaryOperator.Opcode == CX_UnaryOperatorKind.CX_UO_Minus) && IsUnsigned(targetTypeName)); + + if (IsUnchecked(targetTypeName, unaryOperator.SubExpr)) + { + return true; + } + + var evaluation = unaryOperator.Handle.Evaluate; + + if (IsUnchecked(targetTypeName, evaluation)) + { + return true; + } + + var sourceTypeName = GetTypeName(stmt, context: null, unaryOperator.SubExpr.Type, out _); + + switch (unaryOperator.Opcode) + { + case CX_UnaryOperatorKind.CX_UO_Minus: + { + return IsUnsigned(targetTypeName); + } + + case CX_UnaryOperatorKind.CX_UO_Not: + { + return IsUnsigned(targetTypeName) != IsUnsigned(sourceTypeName); + } + + default: + { + return false; + } + } } // case CX_StmtClass.CX_StmtClass_VAArgExpr: From cca86dc414f296e2e429c91244e2569bae3daab3 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 5 Sep 2021 16:56:53 -0700 Subject: [PATCH 06/15] Ensure unsupported default parameter values are partially handled. --- .../PInvokeGenerator.VisitDecl.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 96120b29..09d9d211 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -952,7 +952,22 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl) if (parmVarDecl.HasDefaultArg) { _outputBuilder.BeginParameterDefault(); - UncheckStmt(typeName, parmVarDecl.DefaultArg); + + var defaultArg = parmVarDecl.DefaultArg; + + if (parmVarDecl.Type.CanonicalType.IsPointerType && (defaultArg.Handle.Evaluate.Kind == CXEvalResultKind.CXEval_UnExposed)) + { + AddDiagnostic(DiagnosticLevel.Info, $"Unsupported default parameter: '{name}'. Generated bindings may be incomplete.", defaultArg); + + var outputBuilder = StartCSharpCode(); + outputBuilder.Write("null"); + StopCSharpCode(); + } + else + { + UncheckStmt(typeName, parmVarDecl.DefaultArg); + } + _outputBuilder.EndParameterDefault(); } From 83cb797829940dd3ede3be79abdde74c0978dd91 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 5 Sep 2021 16:59:45 -0700 Subject: [PATCH 07/15] Ensure ascii character literals are cast to sbyte --- .../ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index af7c2ccc..b2c4087a 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -192,7 +192,7 @@ private void VisitCharacterLiteral(CharacterLiteral characterLiteral) if (!isPreviousExplicitCast) { - outputBuilder.Write("(byte)("); + outputBuilder.Write("(sbyte)("); } outputBuilder.Write('\''); From fb55a3aae997b332fd618973ea627e51adb52d40 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 5 Sep 2021 17:04:41 -0700 Subject: [PATCH 08/15] Ensure that IntPtr and UIntPtr are properly handled when checking for unchecked conversions --- sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 14277889..551bfcee 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -3348,6 +3348,7 @@ private static bool IsUnchecked(string typeName, long signedValue, bool isNegati case "uint": case "UInt32": case "nuint": + case "UIntPtr": { return false; } @@ -3373,6 +3374,7 @@ private static bool IsUnchecked(string typeName, long signedValue, bool isNegati case "int": case "Int32": case "nint": + case "IntPtr": { return (signedValue < int.MinValue) || (int.MaxValue < signedValue) || (isNegative && isHex); } From b76c30d167261b873881e821dd963dcf88acef85 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 5 Sep 2021 17:17:09 -0700 Subject: [PATCH 09/15] Ensure a paren is inserted for implicit bool conversions involving binary operators --- .../PInvokeGenerator.VisitStmt.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index b2c4087a..296f0db5 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -2026,7 +2026,18 @@ private void VisitUnaryOperator(UnaryOperator unaryOperator) if (canonicalType.IsIntegerType && (canonicalType.Kind != CXTypeKind.CXType_Bool)) { + var needsParens = IsStmtAsWritten(subExpr, out _); + + if (needsParens) + { + outputBuilder.Write('('); + } Visit(subExpr); + + if (needsParens) + { + outputBuilder.Write(')'); + } outputBuilder.Write(" == 0"); } else if (canonicalType is PointerType or ReferenceType) From 6f380796788fc5ba3d9321e9b02861abf8f8bc31 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 6 Sep 2021 11:19:30 -0700 Subject: [PATCH 10/15] Ensure we don't print a warning for pointer parameter defaults that are already null --- .../PInvokeGenerator.VisitDecl.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 09d9d211..16c21c18 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -957,7 +957,12 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl) if (parmVarDecl.Type.CanonicalType.IsPointerType && (defaultArg.Handle.Evaluate.Kind == CXEvalResultKind.CXEval_UnExposed)) { - AddDiagnostic(DiagnosticLevel.Info, $"Unsupported default parameter: '{name}'. Generated bindings may be incomplete.", defaultArg); + if (!IsStmtAsWritten(defaultArg, out _, removeParens: true) && + (!IsStmtAsWritten(defaultArg, out var castExpr, removeParens: true) || (castExpr.CastKind != CX_CastKind.CX_CK_NullToPointer)) && + (!IsStmtAsWritten(defaultArg, out var integerLiteral, removeParens: true) || (integerLiteral.Value != 0))) + { + AddDiagnostic(DiagnosticLevel.Info, $"Unsupported default parameter: '{name}'. Generated bindings may be incomplete.", defaultArg); + } var outputBuilder = StartCSharpCode(); outputBuilder.Write("null"); From 316f4c2d569e22acdf0f25ddc128278347e01584 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 6 Sep 2021 11:49:38 -0700 Subject: [PATCH 11/15] Better handle checking for overflow on binary operators --- .../PInvokeGenerator.cs | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 551bfcee..06d0ca85 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -3284,9 +3284,42 @@ bool IsOverflow(BinaryOperator binaryOperator) var lhs = binaryOperator.LHS; var rhs = binaryOperator.RHS; - if (!IsStmtAsWritten(lhs, out var lhsIntegerLiteral, removeParens: true) || !IsStmtAsWritten(rhs, out var rhsIntegerLiteral, removeParens: true)) + long lhsValue, rhsValue; + + if (IsStmtAsWritten(lhs, out var lhsIntegerLiteral, removeParens: true)) { - return false; + lhsValue = lhsIntegerLiteral.Value; + } + else + { + var lhsEvaluation = lhs.Handle.Evaluate; + + if (lhsEvaluation.Kind == CXEvalResultKind.CXEval_Int) + { + lhsValue = lhsEvaluation.AsInt; + } + else + { + return false; + } + } + + if (IsStmtAsWritten(rhs, out var rhsIntegerLiteral, removeParens: true)) + { + rhsValue = rhsIntegerLiteral.Value; + } + else + { + var rhsEvaluation = rhs.Handle.Evaluate; + + if (rhsEvaluation.Kind == CXEvalResultKind.CXEval_Int) + { + rhsValue = rhsEvaluation.AsInt; + } + else + { + return false; + } } var targetTypeName = GetRemappedTypeName(binaryOperator, context: null, binaryOperator.Type, out _); @@ -3297,15 +3330,15 @@ bool IsOverflow(BinaryOperator binaryOperator) case CX_BinaryOperatorKind.CX_BO_Add: { return isUnsigned - ? (ulong)lhsIntegerLiteral.Value + (ulong)rhsIntegerLiteral.Value < (ulong)lhsIntegerLiteral.Value - : lhsIntegerLiteral.Value + rhsIntegerLiteral.Value < lhsIntegerLiteral.Value; + ? (ulong)lhsValue + (ulong)rhsValue < (ulong)lhsValue + : lhsValue + rhsValue < lhsValue; } case CX_BinaryOperatorKind.CX_BO_Sub: { return isUnsigned - ? (ulong)lhsIntegerLiteral.Value - (ulong)rhsIntegerLiteral.Value > (ulong)lhsIntegerLiteral.Value - : lhsIntegerLiteral.Value - rhsIntegerLiteral.Value > lhsIntegerLiteral.Value; + ? (ulong)lhsValue - (ulong)rhsValue > (ulong)lhsValue + : lhsValue - rhsValue > lhsValue; } default: From a811b1f14917a47b4ded3ca9db7e279fc4ae754f Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 6 Sep 2021 12:04:19 -0700 Subject: [PATCH 12/15] Use nint/nuint in casts for more efficient codegen --- .../PInvokeGenerator.VisitStmt.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index 296f0db5..a02140fb 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -634,6 +634,7 @@ private void VisitDoStmt(DoStmt doStmt) private void VisitExplicitCastExpr(ExplicitCastExpr explicitCastExpr) { var outputBuilder = StartCSharpCode(); + if (IsPrevContextDecl(out _) && explicitCastExpr.Type is EnumType enumType) { outputBuilder.Write('('); @@ -643,10 +644,17 @@ private void VisitExplicitCastExpr(ExplicitCastExpr explicitCastExpr) } var type = explicitCastExpr.Type; - - var typeName = GetRemappedTypeName(explicitCastExpr, context: null, type, out _); + if (typeName == "IntPtr") + { + typeName = "nint"; + } + else if (typeName == "UIntPtr") + { + typeName = "nuint"; + } + outputBuilder.Write('('); outputBuilder.Write(typeName); outputBuilder.Write(')'); From 4ce1bc02507038b8dd8721e73963bb911a7345ed Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 6 Sep 2021 12:19:44 -0700 Subject: [PATCH 13/15] Ensure negative literals aren't treated as native integer constants --- .../PInvokeGenerator.VisitDecl.cs | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 16c21c18..850a7cb4 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -2724,7 +2724,7 @@ private void VisitVarDecl(VarDecl varDecl) } } } - else if ((type.IsLocalConstQualified || isMacroDefinitionRecord) && CanBeConstant(type, varDecl.Init)) + else if ((type.IsLocalConstQualified || isMacroDefinitionRecord) && CanBeConstant(type, typeName, varDecl.Init)) { kind |= ConstantKind.PrimitiveConstant; } @@ -2840,15 +2840,15 @@ void ForDeclStmt(VarDecl varDecl, DeclStmt declStmt) StopCSharpCode(); } - bool CanBeConstant(Type type, Expr initExpr) + bool CanBeConstant(Type type, string targetTypeName, Expr initExpr) { if (type is AttributedType attributedType) { - return CanBeConstant(attributedType.ModifiedType, initExpr); + return CanBeConstant(attributedType.ModifiedType, targetTypeName, initExpr); } else if (type is AutoType autoType) { - return CanBeConstant(autoType.CanonicalType, initExpr); + return CanBeConstant(autoType.CanonicalType, targetTypeName, initExpr); } else if (type is BuiltinType builtinType) { @@ -2872,28 +2872,28 @@ bool CanBeConstant(Type type, Expr initExpr) case CXTypeKind.CXType_Float: case CXTypeKind.CXType_Double: { - return IsConstant(initExpr); + return IsConstant(targetTypeName, initExpr); } } } else if (type is ElaboratedType elaboratedType) { - return CanBeConstant(elaboratedType.NamedType, initExpr); + return CanBeConstant(elaboratedType.NamedType, targetTypeName, initExpr); } else if (type is EnumType enumType) { - return CanBeConstant(enumType.Decl.IntegerType, initExpr); + return CanBeConstant(enumType.Decl.IntegerType, targetTypeName, initExpr); } else if (type is TypedefType typedefType) { - return CanBeConstant(typedefType.Decl.UnderlyingType, initExpr); + return CanBeConstant(typedefType.Decl.UnderlyingType, targetTypeName, initExpr); } return false; } } - private bool IsConstant(Expr initExpr) + private bool IsConstant(string targetTypeName, Expr initExpr) { switch (initExpr.StmtClass) { @@ -2902,7 +2902,7 @@ private bool IsConstant(Expr initExpr) case CX_StmtClass.CX_StmtClass_ConditionalOperator: { var conditionalOperator = (ConditionalOperator)initExpr; - return IsConstant(conditionalOperator.Cond) && IsConstant(conditionalOperator.LHS) && IsConstant(conditionalOperator.RHS); + return IsConstant(targetTypeName, conditionalOperator.Cond) && IsConstant(targetTypeName, conditionalOperator.LHS) && IsConstant(targetTypeName, conditionalOperator.RHS); } // case CX_StmtClass.CX_StmtClass_AddrLabelExpr: @@ -2921,7 +2921,7 @@ private bool IsConstant(Expr initExpr) case CX_StmtClass.CX_StmtClass_BinaryOperator: { var binaryOperator = (BinaryOperator)initExpr; - return IsConstant(binaryOperator.LHS) && IsConstant(binaryOperator.RHS); + return IsConstant(targetTypeName, binaryOperator.LHS) && IsConstant(targetTypeName, binaryOperator.RHS); } // case CX_StmtClass.CX_StmtClass_CompoundAssignOperator: @@ -3002,7 +3002,7 @@ private bool IsConstant(Expr initExpr) case CX_StmtClass.CX_StmtClass_CXXFunctionalCastExpr: { var cxxFunctionalCastExpr = (ExplicitCastExpr)initExpr; - return IsConstant(cxxFunctionalCastExpr.SubExprAsWritten); + return IsConstant(targetTypeName, cxxFunctionalCastExpr.SubExprAsWritten); } // case CX_StmtClass.CX_StmtClass_CXXConstCastExpr: @@ -3013,7 +3013,7 @@ private bool IsConstant(Expr initExpr) case CX_StmtClass.CX_StmtClass_ImplicitCastExpr: { var implicitCastExpr = (ImplicitCastExpr)initExpr; - return IsConstant(implicitCastExpr.SubExprAsWritten); + return IsConstant(targetTypeName, implicitCastExpr.SubExprAsWritten); } case CX_StmtClass.CX_StmtClass_CharacterLiteral: @@ -3032,7 +3032,7 @@ private bool IsConstant(Expr initExpr) { var declRefExpr = (DeclRefExpr)initExpr; return (declRefExpr.Decl is EnumConstantDecl) || - ((declRefExpr.Decl is VarDecl varDecl) && varDecl.HasInit && IsConstant(varDecl.Init)); + ((declRefExpr.Decl is VarDecl varDecl) && varDecl.HasInit && IsConstant(targetTypeName, varDecl.Init)); } // case CX_StmtClass.CX_StmtClass_DependentCoawaitExpr: @@ -3053,7 +3053,7 @@ private bool IsConstant(Expr initExpr) case CX_StmtClass.CX_StmtClass_ExprWithCleanups: { var exprWithCleanups = (ExprWithCleanups)initExpr; - return IsConstant(exprWithCleanups.SubExpr); + return IsConstant(targetTypeName, exprWithCleanups.SubExpr); } // case CX_StmtClass.CX_StmtClass_FunctionParmPackExpr: @@ -3109,7 +3109,7 @@ private bool IsConstant(Expr initExpr) case CX_StmtClass.CX_StmtClass_ParenExpr: { var parenExpr = (ParenExpr)initExpr; - return IsConstant(parenExpr.SubExpr); + return IsConstant(targetTypeName, parenExpr.SubExpr); } case CX_StmtClass.CX_StmtClass_ParenListExpr: @@ -3118,7 +3118,7 @@ private bool IsConstant(Expr initExpr) foreach (var expr in parenListExpr.Exprs) { - if (IsConstant(expr)) + if (IsConstant(targetTypeName, expr)) { return true; } @@ -3179,7 +3179,18 @@ private bool IsConstant(Expr initExpr) case CX_StmtClass.CX_StmtClass_UnaryOperator: { var unaryOperator = (UnaryOperator)initExpr; - return IsConstant(unaryOperator.SubExpr); + + if (!IsConstant(targetTypeName, unaryOperator.SubExpr)) + { + return false; + } + + if (unaryOperator.Opcode != CX_UnaryOperatorKind.CX_UO_Minus) + { + return true; + } + + return targetTypeName is not "IntPtr" and not "nint" and not "nuint" and not "UIntPtr"; } // case CX_StmtClass.CX_StmtClass_VAArgExpr: From 6a55a5b49648991f3224965d3d20a3eb84b5bafd Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Wed, 22 Sep 2021 13:04:48 -0700 Subject: [PATCH 14/15] Update Directory.Build.props --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 55962641..c5231b74 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -43,7 +43,7 @@ $(BaseArtifactsPath)pkg/$(Configuration)/ ClangSharp 12.0.0 - beta3 + beta3 pr From f9c3c091e1542da836add9641eaac26ad2947a05 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 23 Sep 2021 18:56:52 -0700 Subject: [PATCH 15/15] Fixing some tests and simplify when casts are inserted for character literals --- .../PInvokeGenerator.VisitStmt.cs | 26 ++++++++++++++++--- .../PInvokeGenerator.cs | 2 +- .../Base/FunctionDeclarationDllImportTest.cs | 2 +- .../VarDeclarationTest.cs | 4 +-- .../XmlCompatibleUnix/VarDeclarationTest.cs | 8 ++++-- 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index a02140fb..f606a521 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -188,9 +188,29 @@ private void VisitCharacterLiteral(CharacterLiteral characterLiteral) } else { - var isPreviousExplicitCast = IsPrevContextStmt(out _); + var castType = ""; - if (!isPreviousExplicitCast) + if (IsPrevContextStmt(out var implicitCastExpr)) + { + // C# characters are effectively `ushort` while C defaults to "char" which is + // most typically `sbyte`. Due to this we need to insert a correct implicit + // cast to ensure things are correctly handled here. + + var castExprTypeName = GetRemappedTypeName(implicitCastExpr, context: null, implicitCastExpr.Type, out _, skipUsing: true); + + if (!IsUnsigned(castExprTypeName)) + { + castType = "sbyte"; + } + else if (implicitCastExpr.Type.Handle.NumBits < 16) + { + // Cast to byte if the target type is less + + castType = "byte"; + } + } + + if (castType != "") { outputBuilder.Write("(sbyte)("); } @@ -199,7 +219,7 @@ private void VisitCharacterLiteral(CharacterLiteral characterLiteral) outputBuilder.Write(EscapeCharacter((char)characterLiteral.Value)); outputBuilder.Write('\''); - if (!isPreviousExplicitCast) + if (castType != "") { outputBuilder.Write(')'); } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 06d0ca85..f3c033eb 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -1494,7 +1494,7 @@ private string GetTypeName(Cursor cursor, Cursor context, Type rootType, Type ty { if (_config.GenerateUnixTypes) { - goto case CXTypeKind.CXType_Int; + goto case CXTypeKind.CXType_UInt; } else { diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Base/FunctionDeclarationDllImportTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Base/FunctionDeclarationDllImportTest.cs index 4d586330..e32cf7ab 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Base/FunctionDeclarationDllImportTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Base/FunctionDeclarationDllImportTest.cs @@ -50,7 +50,7 @@ public abstract class FunctionDeclarationDllImportTest : PInvokeGeneratorTest [InlineData("unsigned short", "7", true, "ushort", "7")] [InlineData("unsigned int", "8", true, "uint", "8")] [InlineData("unsigned long long", "9", true, "ulong", "9")] - [InlineData("unsigned short", "'A'", true, "ushort", "(byte)('A')")] + [InlineData("unsigned short", "'A'", true, "ushort", "'A'")] public abstract Task OptionalParameterTest(string nativeType, string nativeInit, bool expectedNativeTypeNameAttr, string expectedManagedType, string expectedManagedInit); [Theory] diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleUnix/VarDeclarationTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleUnix/VarDeclarationTest.cs index a6681969..d4537cf1 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleUnix/VarDeclarationTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleUnix/VarDeclarationTest.cs @@ -186,7 +186,7 @@ namespace ClangSharp.Test public static partial class Methods {{ [NativeTypeName(""#define MyMacro1 (long)0x80000000L"")] - public const IntPtr MyMacro1 = (IntPtr)(0x80000000); + public const IntPtr MyMacro1 = unchecked((nint)(0x80000000)); [NativeTypeName(""#define MyMacro2 (int)0x80000000"")] public const int MyMacro2 = unchecked((int)(0x80000000)); @@ -227,7 +227,7 @@ namespace ClangSharp.Test public static partial class Methods {{ [NativeTypeName(""#define MyMacro3 MyMacro2(3)"")] - public const int MyMacro3 = unchecked((int)(((UIntPtr)(1) << 31) | ((UIntPtr)(2) << 16) | ((UIntPtr)(3)))); + public const int MyMacro3 = unchecked((int)(((nuint)(1) << 31) | ((nuint)(2) << 16) | ((nuint)(3)))); }} }} "; diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/XmlCompatibleUnix/VarDeclarationTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/XmlCompatibleUnix/VarDeclarationTest.cs index 9873d3fc..9f0b2ed9 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/XmlCompatibleUnix/VarDeclarationTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/XmlCompatibleUnix/VarDeclarationTest.cs @@ -224,7 +224,11 @@ public override Task UncheckedConversionMacroTest() IntPtr - (IntPtr)(0x80000000) + + + (nint)(0x80000000) + + @@ -285,7 +289,7 @@ public override Task UncheckedConversionMacroTest2() int - ((int)(((UIntPtr)(1) << 31) | ((UIntPtr)(2) << 16) | ((UIntPtr)(3)))) + ((int)(((nuint)(1) << 31) | ((nuint)(2) << 16) | ((nuint)(3))))