From 3371daa69e54eca6b7ee037263e06c898a7b414f Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Thu, 3 Apr 2025 01:28:35 +0500 Subject: [PATCH] Move type checks for the ternary operator --- lldb/source/ValueObject/DILEval.cpp | 62 ++-- lldb/source/ValueObject/DILParser.cpp | 412 +------------------------- lldb/unittests/DIL/DILTests.cpp | 94 ++---- 3 files changed, 49 insertions(+), 519 deletions(-) diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index b1b3b361519c..74227b699770 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -1810,15 +1810,6 @@ Interpreter::Visit(const BinaryOpNode *node) { "result"); } - // Problem: cannot check the type of RHS without evaluating it anymore. - // If LHS is true, ignore rhs entirely without producing a potential error? - // Or evaluate both anyway to produce a correct error? - // LLDB produces error for RHS anyway: - // (lldb) expr true || s - // ˄ ˄ - // │ ╰─ 'Sx' is not contextually convertible to 'bool' - // ╰─ invalid operands to binary expression - // Breaking early didn't happen, evaluate the RHS and use it as a result. auto rhs_or_err = DILEvalNode(node->rhs()); if (!rhs_or_err) { @@ -1999,9 +1990,8 @@ Interpreter::Visit(const UnaryOpNode *node) { switch (node->kind()) { case UnaryOpKind::Deref: { - if (rhs_type.IsArrayType()) { + if (rhs_type.IsArrayType()) rhs = ArrayToPointerConversion(rhs, m_exe_ctx_scope); - } lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic); if (dynamic_rhs) @@ -2110,12 +2100,18 @@ Interpreter::Visit(const UnaryOpNode *node) { llvm::Expected Interpreter::Visit(const TernaryOpNode *node) { auto cond_or_err = DILEvalNode(node->cond()); - if (!cond_or_err) { + if (!cond_or_err) return cond_or_err; - } + lldb::ValueObjectSP cond = *cond_or_err; - assert(cond->GetCompilerType().IsContextuallyConvertibleToBool() && - "invalid ast: must be convertible to bool"); + // First check if the condition contextually converted to bool. + auto cond_type = cond->GetCompilerType(); + if (!cond_type.IsContextuallyConvertibleToBool()) { + return BailOut(ErrorCode::kInvalidOperandType, + llvm::formatv(kValueIsNotConvertibleToBool, + cond_type.TypeDescription()), + node->GetLocation()); + } // Pass down the flow analysis because the conditional operator is a "flow // control" construct -- LHS/RHS might be lvalues and eligible for some @@ -2220,9 +2216,8 @@ llvm::Error Interpreter::PrepareBinaryAddition(lldb::ValueObjectSP &lhs, auto result_type = ArithmeticConversions(lhs, rhs, m_exe_ctx_scope, is_comp_assign); - if (result_type.IsScalarType()) { + if (result_type.IsScalarType()) return llvm::Error::success(); - } auto lhs_type = lhs->GetCompilerType(); auto rhs_type = rhs->GetCompilerType(); @@ -2281,9 +2276,8 @@ llvm::Error Interpreter::PrepareBinarySubtraction(lldb::ValueObjectSP &lhs, auto result_type = ArithmeticConversions(lhs, rhs, m_exe_ctx_scope, is_comp_assign); - if (result_type.IsScalarType()) { + if (result_type.IsScalarType()) return llvm::Error::success(); - } auto lhs_type = lhs->GetCompilerType(); auto rhs_type = rhs->GetCompilerType(); @@ -2338,9 +2332,8 @@ llvm::Error Interpreter::PrepareBinaryOpScalar(lldb::ValueObjectSP &lhs, auto result_type = ArithmeticConversions(lhs, rhs, m_exe_ctx_scope, is_comp_assign); - if (result_type.IsScalarType()) { + if (result_type.IsScalarType()) return llvm::Error::success(); - } // Invalid operands. return BailOut(ErrorCode::kInvalidOperandType, @@ -2363,9 +2356,8 @@ llvm::Error Interpreter::PrepareBinaryOpInteger(lldb::ValueObjectSP &lhs, auto result_type = ArithmeticConversions(lhs, rhs, m_exe_ctx_scope, is_comp_assign); - if (result_type.IsInteger()) { + if (result_type.IsInteger()) return llvm::Error::success(); - } // Invalid operands. return BailOut(ErrorCode::kInvalidOperandType, @@ -2385,15 +2377,12 @@ llvm::Error Interpreter::PrepareBinaryShift(lldb::ValueObjectSP &lhs, auto orig_lhs_type = lhs->GetCompilerType(); auto orig_rhs_type = rhs->GetCompilerType(); - if (!is_comp_assign) { + if (!is_comp_assign) lhs = UnaryConversion(lhs, m_exe_ctx_scope); - } rhs = UnaryConversion(rhs, m_exe_ctx_scope); - if (lhs->GetCompilerType().IsInteger() && - rhs->GetCompilerType().IsInteger()) { + if (lhs->GetCompilerType().IsInteger() && rhs->GetCompilerType().IsInteger()) return llvm::Error::success(); - } // Invalid operands. return BailOut(ErrorCode::kInvalidOperandType, @@ -2487,9 +2476,8 @@ llvm::Error Interpreter::PrepareBinaryComparison(BinaryOpKind kind, } if (!is_ordered && ((orig_lhs_type.IsNullPtrType() && rhs_nullptr_or_zero) || - (lhs_nullptr_or_zero && orig_rhs_type.IsNullPtrType()))) { + (lhs_nullptr_or_zero && orig_rhs_type.IsNullPtrType()))) return llvm::Error::success(); - } // If the operands has arithmetic or enumeration type (scoped or unscoped), // usual arithmetic conversions are performed on both operands following the @@ -2502,16 +2490,15 @@ llvm::Error Interpreter::PrepareBinaryComparison(BinaryOpKind kind, auto boolean_ty = GetBasicType(m_exe_ctx_scope, lldb::eBasicTypeBool); if (lhs_type.IsScalarOrUnscopedEnumerationType() && - rhs_type.IsScalarOrUnscopedEnumerationType()) { + rhs_type.IsScalarOrUnscopedEnumerationType()) return llvm::Error::success(); - } // Scoped enums can be compared only to the instances of the same type. if (lhs_type.IsScopedEnumerationType() || rhs_type.IsScopedEnumerationType()) { - if (lhs_type.CompareTypes(rhs_type)) { + if (lhs_type.CompareTypes(rhs_type)) return llvm::Error::success(); - } + // Invalid operands. return BailOut(ErrorCode::kInvalidOperandType, llvm::formatv(kInvalidOperandsToBinaryExpression, @@ -2580,9 +2567,9 @@ static bool ImplicitConversionIsAllowed(CompilerType src, CompilerType dst, if (dst.IsPointerType()) { // Literal zero, `nullptr_t` and arrays can be implicitly converted to // pointers. - if (is_src_literal_zero || src.IsNullPtrType()) { + if (is_src_literal_zero || src.IsNullPtrType()) return true; - } + if (src.IsArrayType() && src.GetArrayElementType(nullptr).CompareTypes(dst.GetPointeeType())) { return true; @@ -2599,9 +2586,8 @@ llvm::Error Interpreter::PrepareAssignment(lldb::ValueObjectSP &lhs, auto to_type = lhs->GetCompilerType(); // If the expression already has the required type, nothing to do here. - if (from_type.CompareTypes(to_type)) { + if (from_type.CompareTypes(to_type)) return llvm::Error::success(); - } // Check if the implicit conversion is possible and insert a cast. if (ImplicitConversionIsAllowed(from_type, to_type, IsLiteralZero(rhs))) { diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 6c1701a29b6a..5a12ee01d395 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -34,9 +34,6 @@ namespace { -const char* kValueIsNotConvertibleToBool = - "value of type {0} is not contextually convertible to 'bool'"; - template constexpr unsigned type_width() { return static_cast(sizeof(T)) * CHAR_BIT; @@ -164,316 +161,6 @@ static ASTNodeUP InsertArrayToPointerConversion(ASTNodeUP expr) { std::move(expr), TypePromotionCastKind::ePointer); } -static CompilerType DoIntegralPromotion( - std::shared_ptr ctx, CompilerType from) { - assert((from.IsInteger() || from.IsUnscopedEnumerationType()) && - "Integral promotion works only for integers and unscoped enums."); - - // Don't do anything if the type doesn't need to be promoted. - if (!from.IsPromotableIntegerType()) { - return from; - } - - if (from.IsUnscopedEnumerationType()) { - // Get the enumeration underlying type and promote it. - return DoIntegralPromotion(ctx, from.GetEnumerationIntegerType()); - } - - // At this point the type should an integer. - assert(from.IsInteger() && "invalid type: must be an integer"); - - // Get the underlying builtin representation. - lldb::BasicType builtin_type = - from.GetCanonicalType().GetBasicTypeEnumeration(); - - uint64_t from_size = 0; - if (builtin_type == lldb::eBasicTypeWChar || - builtin_type == lldb::eBasicTypeSignedWChar || - builtin_type == lldb::eBasicTypeUnsignedWChar || - builtin_type == lldb::eBasicTypeChar16 || - builtin_type == lldb::eBasicTypeChar32) { - // Find the type that can hold the entire range of values for our type. - bool is_signed = from.IsSigned(); - if (auto temp = from.GetByteSize(ctx.get())) - from_size = temp.value(); - - CompilerType promote_types[] = { - GetBasicType(ctx, lldb::eBasicTypeInt), - GetBasicType(ctx, lldb::eBasicTypeUnsignedInt), - GetBasicType(ctx, lldb::eBasicTypeLong), - GetBasicType(ctx, lldb::eBasicTypeUnsignedLong), - GetBasicType(ctx, lldb::eBasicTypeLongLong), - GetBasicType(ctx, lldb::eBasicTypeUnsignedLongLong), - }; - for (auto& type : promote_types) { - uint64_t byte_size = 0; - if (auto temp = type.GetByteSize(ctx.get())) - byte_size = temp.value(); - if (from_size < byte_size || - (from_size == byte_size && - is_signed ==(bool)( - type.GetTypeInfo() & lldb::eTypeIsSigned))) - { - return type; - } - } - - llvm_unreachable("char type should fit into long long"); - } - - // Here we can promote only to "int" or "unsigned int". - CompilerType int_type = GetBasicType(ctx, lldb::eBasicTypeInt); - uint64_t int_byte_size = 0; - if (auto temp = int_type.GetByteSize(ctx.get())) - int_byte_size = temp.value(); - - // Signed integer types can be safely promoted to "int". - if (from.IsSigned()) { - return int_type; - } - // Unsigned integer types are promoted to "unsigned int" if "int" cannot hold - // their entire value range. - return (from_size == int_byte_size) - ? GetBasicType(ctx, lldb::eBasicTypeUnsignedInt) - : int_type; -} - -static ASTNodeUP -UsualUnaryConversions(std::shared_ptr ctx, - ASTNodeUP expr) { - // Perform usual conversions for unary operators. At the moment this includes - // array-to-pointer and the integral promotion for eligible types. - auto result_type = expr->GetDereferencedResultType(); - - if (expr->is_bitfield()) { - // Promote bitfields. If `int` can represent the bitfield value, it is - // converted to `int`. Otherwise, if `unsigned int` can represent it, it - // is converted to `unsigned int`. Otherwise, it is treated as its - // underlying type. - - uint32_t bitfield_size = expr->bitfield_size(); - // Some bitfields have undefined size (e.g. result of ternary operation). - // The AST's `bitfield_size` of those is 0, and no promotion takes place. - if (bitfield_size > 0 && result_type.IsInteger()) { - auto int_type = GetBasicType(ctx, lldb::eBasicTypeInt); - auto uint_type = GetBasicType(ctx, lldb::eBasicTypeUnsignedInt); - uint64_t int_byte_size = 0; - uint64_t uint_byte_size = 0; - if (auto temp = int_type.GetByteSize(ctx.get())) - int_byte_size = temp.value(); - if (auto temp = uint_type.GetByteSize(ctx.get())) - uint_byte_size = temp.value(); - uint32_t int_bit_size = int_byte_size * CHAR_BIT; - if (bitfield_size < int_bit_size || - (result_type.IsSigned() && bitfield_size == int_bit_size)) { - expr = std::make_unique(expr->GetLocation(), int_type, - std::move(expr), - TypePromotionCastKind::eArithmetic); - } else if (bitfield_size <= uint_byte_size * CHAR_BIT) { - expr = std::make_unique(expr->GetLocation(), uint_type, - std::move(expr), - TypePromotionCastKind::eArithmetic); - } - } - } - - if (result_type.IsArrayType()) { - expr = InsertArrayToPointerConversion(std::move(expr)); - } - - if (result_type.IsInteger() || result_type.IsUnscopedEnumerationType()) { - auto promoted_type = DoIntegralPromotion(ctx, result_type); - - // Insert a cast if the type promotion is happening. - // TODO: Make this an implicit static_cast. - if (!promoted_type.CompareTypes(result_type)) { - expr = std::make_unique(expr->GetLocation(), promoted_type, - std::move(expr), - TypePromotionCastKind::eArithmetic); - } - } - - return expr; -} - -static size_t ConversionRank(CompilerType type) { - // Get integer conversion rank - // https://eel.is/c++draft/conv.rank - switch (type.GetCanonicalType().GetBasicTypeEnumeration()) { - case lldb::eBasicTypeBool: - return 1; - case lldb::eBasicTypeChar: - case lldb::eBasicTypeSignedChar: - case lldb::eBasicTypeUnsignedChar: - return 2; - case lldb::eBasicTypeShort: - case lldb::eBasicTypeUnsignedShort: - return 3; - case lldb::eBasicTypeInt: - case lldb::eBasicTypeUnsignedInt: - return 4; - case lldb::eBasicTypeLong: - case lldb::eBasicTypeUnsignedLong: - return 5; - case lldb::eBasicTypeLongLong: - case lldb::eBasicTypeUnsignedLongLong: - return 6; - - // TODO: The ranks of char16_t, char32_t, and wchar_t are equal to the - // ranks of their underlying types. - case lldb::eBasicTypeWChar: - case lldb::eBasicTypeSignedWChar: - case lldb::eBasicTypeUnsignedWChar: - return 3; - case lldb::eBasicTypeChar16: - return 3; - case lldb::eBasicTypeChar32: - return 4; - - default: - break; - } - return 0; -} - -static lldb::BasicType BasicTypeToUnsigned(lldb::BasicType basic_type) { - switch (basic_type) { - case lldb::eBasicTypeInt: - return lldb::eBasicTypeUnsignedInt; - case lldb::eBasicTypeLong: - return lldb::eBasicTypeUnsignedLong; - case lldb::eBasicTypeLongLong: - return lldb::eBasicTypeUnsignedLongLong; - default: - return basic_type; - } -} - -static void -PerformIntegerConversions(std::shared_ptr ctx, - ASTNodeUP &l, ASTNodeUP &r, bool convert_lhs, - bool convert_rhs) { - // Assert that rank(l) < rank(r). - auto l_type = l->GetDereferencedResultType(); - auto r_type = r->GetDereferencedResultType(); - - // if `r` is signed and `l` is unsigned, check whether it can represent all - // of the values of the type of the `l`. If not, then promote `r` to the - // unsigned version of its type. - if (r_type.IsSigned() && !l_type.IsSigned()) { - uint64_t l_size = 0; - uint64_t r_size = 0; - if (auto temp = l_type.GetByteSize(ctx.get())) - l_size = temp.value();; - if (auto temp = r_type.GetByteSize(ctx.get())) - r_size = temp.value(); - - assert(l_size <= r_size && "left value must not be larger then the right!"); - - if (r_size == l_size) { - auto r_type_unsigned = GetBasicType( - ctx, - BasicTypeToUnsigned(r_type.GetCanonicalType() - .GetBasicTypeEnumeration())); - if (convert_rhs) { - r = std::make_unique(r->GetLocation(), r_type_unsigned, - std::move(r), - TypePromotionCastKind::eArithmetic); - } - } - } - - if (convert_lhs) { - l = std::make_unique(l->GetLocation(), r->result_type(), - std::move(l), - TypePromotionCastKind::eArithmetic); - } -} - -static CompilerType -UsualArithmeticConversions(std::shared_ptr ctx, - ASTNodeUP &lhs, ASTNodeUP &rhs, - bool is_comp_assign = false) { - // Apply unary conversions (e.g. intergal promotion) for both operands. - // In case of a composite assignment operator LHS shouldn't get promoted. - if (!is_comp_assign) { - lhs = UsualUnaryConversions(ctx, std::move(lhs)); - } - rhs = UsualUnaryConversions(ctx, std::move(rhs)); - - auto lhs_type = lhs->GetDereferencedResultType(); - auto rhs_type = rhs->GetDereferencedResultType(); - - if (lhs_type.CompareTypes(rhs_type)) { - return lhs_type; - } - - // If either of the operands is not arithmetic (e.g. pointer), we're done. - if (!lhs_type.IsScalarType() || !rhs_type.IsScalarType()) { - CompilerType bad_type; - return bad_type; - } - - // Handle conversions for floating types (float, double). - if (lhs_type.IsFloat() || rhs_type.IsFloat()) { - // If both are floats, convert the smaller operand to the bigger. - if (lhs_type.IsFloat() && rhs_type.IsFloat()) { - int order = lhs_type.GetBasicTypeEnumeration() - - rhs_type.GetBasicTypeEnumeration(); - if (order > 0) { - rhs = std::make_unique(rhs->GetLocation(), lhs_type, - std::move(rhs), - TypePromotionCastKind::eArithmetic); - return lhs_type; - } - assert(order < 0 && "illegal operands: must not be of the same type"); - if (!is_comp_assign) { - lhs = std::make_unique(lhs->GetLocation(), rhs_type, - std::move(lhs), - TypePromotionCastKind::eArithmetic); - } - return rhs_type; - } - - if (lhs_type.IsFloat()) { - assert(rhs_type.IsInteger() && "illegal operand: must be an integer"); - rhs = std::make_unique(rhs->GetLocation(), lhs_type, - std::move(rhs), - TypePromotionCastKind::eArithmetic); - return lhs_type; - } - assert(rhs_type.IsFloat() && "illegal operand: must be a float"); - if (!is_comp_assign) { - lhs = std::make_unique(lhs->GetLocation(), rhs_type, - std::move(lhs), - TypePromotionCastKind::eArithmetic); - } - return rhs_type; - } - - // Handle conversion for integer types. - assert((lhs_type.IsInteger() && rhs_type.IsInteger()) && - "illegal operands: must be both integers"); - - using Rank = std::tuple; - Rank l_rank = {ConversionRank(lhs_type), !lhs_type.IsSigned()}; - Rank r_rank = {ConversionRank(rhs_type), !rhs_type.IsSigned()}; - - if (l_rank < r_rank) { - PerformIntegerConversions(ctx, lhs, rhs, !is_comp_assign, true); - } else if (l_rank > r_rank) { - PerformIntegerConversions(ctx, rhs, lhs, true, !is_comp_assign); - } - - if (!is_comp_assign) { - assert(lhs->GetDereferencedResultType().GetCanonicalType().CompareTypes( - rhs->GetDereferencedResultType().GetCanonicalType()) && - "integral promotion error: operands result types must be the same"); - } - - return lhs->GetDereferencedResultType().GetCanonicalType(); -} - static TypeDeclaration::TypeSpecifier ToTypeSpecifier(Token::Kind kind) { using TypeSpecifier = TypeDeclaration::TypeSpecifier; switch (kind) { @@ -2967,103 +2654,8 @@ ASTNodeUP DILParser::BuildBinaryOp(BinaryOpKind kind, ASTNodeUP lhs, ASTNodeUP DILParser::BuildTernaryOp(ASTNodeUP cond, ASTNodeUP lhs, ASTNodeUP rhs, uint32_t location) { CompilerType bad_type; - // First check if the condition contextually converted to bool. - auto cond_type = cond->GetDereferencedResultType(); - if (!cond_type.IsContextuallyConvertibleToBool()) { - BailOut( - ErrorCode::kInvalidOperandType, - llvm::formatv(kValueIsNotConvertibleToBool, cond_type.TypeDescription()), - location); - return std::make_unique(); - } - - auto lhs_type = lhs->GetDereferencedResultType(); - auto rhs_type = rhs->GetDereferencedResultType(); - - // If operands have the same type, don't do any promotions. - if (lhs_type.CompareTypes(rhs_type)) { - return std::make_unique(location, lhs_type, std::move(cond), - std::move(lhs), std::move(rhs)); - } - // If operands have the same canonical type, use the canonical type. - if (lhs_type.GetCanonicalType().CompareTypes(rhs_type.GetCanonicalType())) { - return std::make_unique( - location, lhs_type.GetCanonicalType(), std::move(cond), std::move(lhs), - std::move(rhs)); - } - - // If both operands have arithmetic type, apply the usual arithmetic - // conversions to bring them to a common type. - if (lhs_type.IsScalarOrUnscopedEnumerationType() && - rhs_type.IsScalarOrUnscopedEnumerationType()) { - auto result_type = UsualArithmeticConversions(m_ctx_scope, lhs, rhs); - return std::make_unique( - location, result_type, std::move(cond), std::move(lhs), std::move(rhs)); - } - - // Apply array-to-pointer implicit conversions. - if (lhs_type.IsArrayType()) { - lhs = InsertArrayToPointerConversion(std::move(lhs)); - lhs_type = lhs->GetDereferencedResultType(); - } - if (rhs_type.IsArrayType()) { - rhs = InsertArrayToPointerConversion(std::move(rhs)); - rhs_type = rhs->GetDereferencedResultType(); - } - - // Check if operands have the same pointer type. - if (lhs_type.CompareTypes(rhs_type)) { - return std::make_unique(location, lhs_type, std::move(cond), - std::move(lhs), std::move(rhs)); - } - // Check if operands have the same canonical pointer type. - if (lhs_type.GetCanonicalType().CompareTypes(rhs_type.GetCanonicalType())) { - return std::make_unique( - location, lhs_type.GetCanonicalType(), std::move(cond), std::move(lhs), - std::move(rhs)); - } - - // If one operand is a pointer and the other is a nullptr or literal zero, - // convert the nullptr operand to pointer type. - if (lhs_type.IsPointerType() && - (rhs->is_literal_zero() || rhs_type.IsNullPtrType())) { - rhs = std::make_unique( - rhs->GetLocation(), lhs_type, std::move(rhs), TypePromotionCastKind::ePointer); - - return std::make_unique(location, lhs_type, std::move(cond), - std::move(lhs), std::move(rhs)); - } - if ((lhs->is_literal_zero() || lhs_type.IsNullPtrType()) && - rhs_type.IsPointerType()) { - lhs = std::make_unique( - lhs->GetLocation(), rhs_type, std::move(lhs), TypePromotionCastKind::ePointer); - - return std::make_unique(location, rhs_type, std::move(cond), - std::move(lhs), std::move(rhs)); - } - - // If one operand is nullptr and the other one is literal zero, convert - // the literal zero to a nullptr type. - if (lhs_type.IsNullPtrType() && rhs->is_literal_zero()) { - rhs = std::make_unique( - rhs->GetLocation(), lhs_type, std::move(rhs), CStyleCastKind::eNullptr); - - return std::make_unique(location, lhs_type, std::move(cond), - std::move(lhs), std::move(rhs)); - } - if (lhs->is_literal_zero() && rhs_type.IsNullPtrType()) { - lhs = std::make_unique( - lhs->GetLocation(), rhs_type, std::move(lhs), CStyleCastKind::eNullptr); - - return std::make_unique(location, rhs_type, std::move(cond), - std::move(lhs), std::move(rhs)); - } - - BailOut(ErrorCode::kInvalidOperandType, - llvm::formatv("incompatible operand types ({0} and {1})", - lhs_type.TypeDescription(), rhs_type.TypeDescription()), - location); - return std::make_unique(); + return std::make_unique(location, bad_type, std::move(cond), + std::move(lhs), std::move(rhs)); } ASTNodeUP DILParser::BuildBinarySubscript(ASTNodeUP lhs, ASTNodeUP rhs, diff --git a/lldb/unittests/DIL/DILTests.cpp b/lldb/unittests/DIL/DILTests.cpp index 8225c7a9a454..c3bdd6535f4f 100644 --- a/lldb/unittests/DIL/DILTests.cpp +++ b/lldb/unittests/DIL/DILTests.cpp @@ -962,14 +962,15 @@ TEST_F(EvalTest, TestLogicalOperators) { IsError("value of type 'S' is not contextually convertible to 'bool'\n" "s || false\n" "^")); - EXPECT_THAT( - Eval("true || s"), - IsError("value of type 'S' is not contextually convertible to 'bool'\n" - "true || s\n" - " ^")); EXPECT_THAT( Eval("s ? 1 : 2"), IsError("value of type 'S' is not contextually convertible to 'bool'")); + + // DIL doesn't check the second operand + // if the result is determined by the first + this->compare_with_lldb_ = false; + EXPECT_THAT(Eval("true || s"), IsEqual("true")); + EXPECT_THAT(Eval("false && s"), IsEqual("false")); } TEST_F(EvalTest, TestLocalVariables) { @@ -1178,7 +1179,7 @@ TEST_F(EvalTest, TestAddressOf) { EXPECT_THAT(Eval("&(true ? c : (char)1)"), IsError("cannot take the address of an rvalue of type 'char'")); EXPECT_THAT(Eval("&(true ? c : 1)"), - IsError("cannot take the address of an rvalue of type 'int'")); + IsError("cannot take the address of an rvalue of type 'char'")); } TEST_F(EvalTest, TestSubscript) { @@ -2567,85 +2568,35 @@ TEST_F(EvalTest, TestUnscopedEnumEmpty) { } TEST_F(EvalTest, TestTernaryOperator) { - EXPECT_THAT(Eval("true ? c : c"), IsEqual("'\\x02'")); - EXPECT_THAT(Eval("true ? c : (char)1"), IsEqual("'\\x02'")); - EXPECT_THAT(Eval("true ? c : 1"), IsEqual("2")); - EXPECT_THAT(Eval("false ? 1 : c"), IsEqual("2")); + // Ternary operator does not do type promotion for operands in DIL + this->compare_with_lldb_ = false; - bool compare_types = HAS_METHOD(lldb::SBType, GetEnumerationIntegerType()); + EXPECT_THAT(Eval("true ? 1 : 1"), IsEqual("1")); + EXPECT_THAT(Eval("false ? 1 : 2"), IsEqual("2")); EXPECT_THAT(Eval("false ? a_enum : EnumA::kOneA"), IsEqual("kOneA")); - EXPECT_THAT(Eval("false ? b_enum : a_enum"), IsEqual("2", compare_types)); - EXPECT_THAT(Eval("false ? c : b_enum"), IsEqual("1", compare_types)); - EXPECT_THAT(Eval("false ? b_enum : c"), IsEqual("2", compare_types)); - - EXPECT_THAT(Eval("true ? (int*)15 : 0"), - IsEqual(Is32Bit() ? "0x0000000f" : "0x000000000000000f")); - EXPECT_THAT(Eval("true ? 0 : (int*)15"), - IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); - EXPECT_THAT(Eval("true ? (int*)15 : nullptr"), - IsEqual(Is32Bit() ? "0x0000000f" : "0x000000000000000f")); - EXPECT_THAT(Eval("true ? nullptr : (int*)15"), - IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); - EXPECT_THAT(Eval("true ? 0 : nullptr"), - IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); - EXPECT_THAT(Eval("true ? nullptr : 0"), - IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); EXPECT_THAT(Eval("+(true ? arr2 : arr2)"), IsOk()); - EXPECT_THAT(Eval("true ? arr2 : arr3"), IsOk()); - EXPECT_THAT(Eval("true ? arr2 : 0"), IsOk()); - EXPECT_THAT(Eval("true ? 0 : arr2"), - IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); - EXPECT_THAT(Eval("true ? arr2 : nullptr"), IsOk()); - EXPECT_THAT(Eval("true ? nullptr : arr2"), - IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); - EXPECT_THAT(Eval("true ? arr2 : (int*)15"), IsOk()); - EXPECT_THAT(Eval("true ? (int*)15 : arr2"), - IsEqual(Is32Bit() ? "0x0000000f" : "0x000000000000000f")); - EXPECT_THAT(Eval("&(true ? arr2 : arr2)"), IsOk()); - EXPECT_THAT(Eval("true ? t : 1"), - IsError("incompatible operand types ('T' and 'int')\n" - "true ? t : 1\n" - " ^")); - EXPECT_THAT(Eval("true ? 1 : t"), - IsError("incompatible operand types ('int' and 'T')\n" - "true ? 1 : t\n" - " ^")); - EXPECT_THAT(Eval("true ? (int*)15 : 1"), - IsError("incompatible operand types ('int *' and 'int')\n" - "true ? (int*)15 : 1\n" - " ^")); - EXPECT_THAT(Eval("true ? arr2 : dbl_arr"), - IsError("incompatible operand types ('int *' and 'double *')\n" - "true ? arr2 : dbl_arr\n" - " ^")); - - EXPECT_THAT(Eval("&(true ? *pi : 0)"), + EXPECT_THAT(Eval("&(false ? *pi : 0)"), IsError("cannot take the address of an rvalue of type 'int'\n" - "&(true ? *pi : 0)\n" - "^")); - EXPECT_THAT(Eval("&(true ? arr2 : pi)"), - IsError("cannot take the address of an rvalue of type 'int *'\n" - "&(true ? arr2 : pi)\n" + "&(false ? *pi : 0)\n" "^")); - EXPECT_THAT(Eval("&(true ? arr2 : arr3)"), + EXPECT_THAT(Eval("&(false ? &arr2 : pi)"), IsError("cannot take the address of an rvalue of type 'int *'\n" - "&(true ? arr2 : arr3)\n" + "&(false ? &arr2 : pi)\n" "^")); - EXPECT_THAT(Eval("true ? nullptr : pi"), - IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); - EXPECT_THAT(Eval("true ? pi : nullptr"), IsOk()); - EXPECT_THAT(Eval("false ? nullptr : pi"), IsOk()); - EXPECT_THAT(Eval("false ? pi : nullptr"), - IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); - // Use pointers and arrays in bool context. EXPECT_THAT(Eval("pi ? 1 : 2"), IsEqual("1")); EXPECT_THAT(Eval("nullptr ? 1 : 2"), IsEqual("2")); EXPECT_THAT(Eval("arr2 ? 1 : 2"), IsEqual("1")); + EXPECT_THAT(Eval("EnumA::kOneA ? 1 : 2"), IsEqual("1")); + EXPECT_THAT( + Eval("t ? 1 : 2"), + IsError("value of type 'T' is not contextually convertible to 'bool'\n" + "t ? 1 : 2\n" + " ^")); } TEST_F(EvalTest, TestSizeOf) { @@ -3374,7 +3325,8 @@ TEST_F(EvalTest, DISABLED_TestSharedPtrCompare) { #endif } -TEST_F(EvalTest, TestTypeComparison) { +// TODO: Force these type comparisons withot a ternary operator +TEST_F(EvalTest, DISABLED_TestTypeComparison) { // This test is for border-case situations in the CompareTypes function. // Taking an address of ternary expression require operands of the same type.