https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/177208
>From baf85a70b387461a3db522d999ad35b09c9f3e0f Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Tue, 20 Jan 2026 21:24:32 +0500 Subject: [PATCH 1/6] [lldb] Add binary addition to DIL --- lldb/docs/dil-expr-lang.ebnf | 4 +- lldb/include/lldb/ValueObject/DILAST.h | 35 ++++ lldb/include/lldb/ValueObject/DILEval.h | 16 ++ lldb/include/lldb/ValueObject/DILParser.h | 1 + lldb/source/ValueObject/DILAST.cpp | 14 ++ lldb/source/ValueObject/DILEval.cpp | 155 ++++++++++++++++++ lldb/source/ValueObject/DILParser.cpp | 24 ++- .../Arithmetic/TestFrameVarDILArithmetic.py | 35 +++- .../frame/var-dil/expr/Arithmetic/main.cpp | 17 ++ 9 files changed, 298 insertions(+), 3 deletions(-) diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index 99a8b0fcaa006..127c1d6337efa 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -3,7 +3,9 @@ (* This is currently a subset of the final DIL Language, matching the current DIL implementation. *) -expression = cast_expression; +expression = additive_expression ; + +additive_expression = cast_expression {"+" cast_expression} ; cast_expression = unary_expression | "(" type_id ")" cast_expression; diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index da7659959093a..226f80ac70c5e 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -9,6 +9,7 @@ #ifndef LLDB_VALUEOBJECT_DILAST_H #define LLDB_VALUEOBJECT_DILAST_H +#include "lldb/ValueObject/DILLexer.h" #include "lldb/ValueObject/ValueObject.h" #include "llvm/Support/Error.h" #include <cstdint> @@ -19,6 +20,7 @@ namespace lldb_private::dil { /// The various types DIL AST nodes (used by the DIL parser). enum class NodeKind { eArraySubscriptNode, + eBinaryOpNode, eBitExtractionNode, eBooleanLiteralNode, eCastNode, @@ -38,6 +40,14 @@ enum class UnaryOpKind { Plus, // "+" }; +/// The binary operators recognized by DIL. +enum class BinaryOpKind { + Add, // "+" +}; + +/// Translates DIL tokens to BinaryOpKind. +BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind); + /// The type casts allowed by DIL. enum class CastKind { eArithmetic, ///< Casting to a scalar. @@ -148,6 +158,29 @@ class UnaryOpNode : public ASTNode { ASTNodeUP m_operand; }; +class BinaryOpNode : public ASTNode { +public: + BinaryOpNode(uint32_t location, BinaryOpKind kind, ASTNodeUP lhs, + ASTNodeUP rhs) + : ASTNode(location, NodeKind::eBinaryOpNode), m_kind(kind), + m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) {} + + llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override; + + BinaryOpKind GetKind() const { return m_kind; } + ASTNode &GetLHS() const { return *m_lhs; } + ASTNode &GetRHS() const { return *m_rhs; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eBinaryOpNode; + } + +private: + BinaryOpKind m_kind; + ASTNodeUP m_lhs; + ASTNodeUP m_rhs; +}; + class ArraySubscriptNode : public ASTNode { public: ArraySubscriptNode(uint32_t location, ASTNodeUP base, ASTNodeUP index) @@ -292,6 +325,8 @@ class Visitor { virtual llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode &node) = 0; virtual llvm::Expected<lldb::ValueObjectSP> + Visit(const BinaryOpNode &node) = 0; + virtual llvm::Expected<lldb::ValueObjectSP> Visit(const ArraySubscriptNode &node) = 0; virtual llvm::Expected<lldb::ValueObjectSP> Visit(const BitFieldExtractionNode &node) = 0; diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 550f5083b1dc6..6807a6616b846 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -57,6 +57,7 @@ class Interpreter : Visitor { Visit(const IdentifierNode &node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const MemberOfNode &node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode &node) override; + llvm::Expected<lldb::ValueObjectSP> Visit(const BinaryOpNode &node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const ArraySubscriptNode &node) override; llvm::Expected<lldb::ValueObjectSP> @@ -73,6 +74,21 @@ class Interpreter : Visitor { /// includes array-to-pointer and integral promotion for eligible types. llvm::Expected<lldb::ValueObjectSP> UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location); + + /// Perform an arithmetic conversion on two values from an arithmetic + /// operation. + /// \returns The result type of an arithmetic operation. + llvm::Expected<CompilerType> ArithmeticConversion(lldb::ValueObjectSP &lhs, + lldb::ValueObjectSP &rhs, + uint32_t location); + llvm::Expected<lldb::ValueObjectSP> EvaluateScalarOp(BinaryOpKind kind, + lldb::ValueObjectSP lhs, + lldb::ValueObjectSP rhs, + CompilerType result_type, + uint32_t location); + llvm::Expected<lldb::ValueObjectSP> + EvaluateBinaryAddition(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, + uint32_t location); llvm::Expected<CompilerType> PickIntegerType(lldb::TypeSystemSP type_system, std::shared_ptr<ExecutionContextScope> ctx, diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index 3bac4b2bb4171..d093f95cde841 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -94,6 +94,7 @@ class DILParser { ASTNodeUP Run(); ASTNodeUP ParseExpression(); + ASTNodeUP ParseAdditiveExpression(); ASTNodeUP ParseUnaryExpression(); ASTNodeUP ParsePostfixExpression(); ASTNodeUP ParsePrimaryExpression(); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index d8a714d33712d..180708f79c269 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -11,6 +11,16 @@ namespace lldb_private::dil { +BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind) { + switch (token_kind) { + case Token::plus: + return BinaryOpKind::Add; + default: + break; + } + llvm_unreachable("Unknown binary operator kind."); +} + llvm::Expected<lldb::ValueObjectSP> ErrorNode::Accept(Visitor *v) const { llvm_unreachable("Attempting to Visit a DIL ErrorNode."); } @@ -27,6 +37,10 @@ llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const { return v->Visit(*this); } +llvm::Expected<lldb::ValueObjectSP> BinaryOpNode::Accept(Visitor *v) const { + return v->Visit(*this); +} + llvm::Expected<lldb::ValueObjectSP> ArraySubscriptNode::Accept(Visitor *v) const { return v->Visit(*this); diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 187cc9bb16180..b30632ca7800d 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -127,6 +127,85 @@ Interpreter::UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location) { return valobj; } +static size_t IntegerConversionRank(CompilerType type) { + 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; + case lldb::eBasicTypeInt128: + case lldb::eBasicTypeUnsignedInt128: + return 7; + default: + break; + } + return 0; +} + +llvm::Expected<CompilerType> +Interpreter::ArithmeticConversion(lldb::ValueObjectSP &lhs, + lldb::ValueObjectSP &rhs, uint32_t location) { + // Apply unary conversion for both operands. + auto lhs_or_err = UnaryConversion(lhs, location); + if (!lhs_or_err) + return lhs_or_err.takeError(); + lhs = *lhs_or_err; + auto rhs_or_err = UnaryConversion(rhs, location); + if (!rhs_or_err) + return rhs_or_err.takeError(); + rhs = *rhs_or_err; + + CompilerType lhs_type = lhs->GetCompilerType(); + CompilerType rhs_type = rhs->GetCompilerType(); + + 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()) + return CompilerType(); + + // 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()) { + if (lhs_type.GetBasicTypeEnumeration() > + rhs_type.GetBasicTypeEnumeration()) + return lhs_type; + return rhs_type; + } + if (lhs_type.IsFloat() && rhs_type.IsInteger()) + return lhs_type; + return rhs_type; + } + + if (lhs_type.IsInteger() && rhs_type.IsInteger()) { + using Rank = std::tuple<size_t, bool>; + Rank l_rank = {IntegerConversionRank(lhs_type), !lhs_type.IsSigned()}; + Rank r_rank = {IntegerConversionRank(rhs_type), !rhs_type.IsSigned()}; + + if (l_rank < r_rank) + return rhs_type; + if (l_rank > r_rank) + return lhs_type; + } + return rhs_type; +} + static lldb::VariableSP DILFindVariable(ConstString name, VariableList &variable_list) { lldb::VariableSP exact_match; @@ -393,6 +472,82 @@ Interpreter::Visit(const UnaryOpNode &node) { node.GetLocation()); } +llvm::Expected<lldb::ValueObjectSP> +Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs, + lldb::ValueObjectSP rhs, CompilerType result_type, + uint32_t location) { + Scalar l, r; + bool l_resolved = lhs->ResolveValue(l); + bool r_resolved = rhs->ResolveValue(r); + + if (!l_resolved || !r_resolved) + return llvm::make_error<DILDiagnosticError>(m_expr, "invalid scalar value", + location); + + auto value_object = [this, result_type](Scalar scalar) { + return ValueObject::CreateValueObjectFromScalar(m_target, scalar, + result_type, "result"); + }; + + switch (kind) { + case BinaryOpKind::Add: + return value_object(l + r); + } + return llvm::make_error<DILDiagnosticError>( + m_expr, "invalid arithmetic operation", location); +} + +llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition( + lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { + // Operation '+' works for: + // {scalar,unscoped_enum} <-> {scalar,unscoped_enum} + // TODO: Pointer arithmetics + auto orig_lhs_type = lhs->GetCompilerType(); + auto orig_rhs_type = rhs->GetCompilerType(); + auto type_or_err = ArithmeticConversion(lhs, rhs, location); + if (!type_or_err) + return type_or_err.takeError(); + CompilerType result_type = *type_or_err; + + if (result_type.IsScalarType()) + return EvaluateScalarOp(BinaryOpKind::Add, lhs, rhs, result_type, location); + + std::string errMsg = + llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", + orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); + return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const BinaryOpNode &node) { + auto lhs_or_err = EvaluateAndDereference(node.GetLHS()); + if (!lhs_or_err) + return lhs_or_err; + lldb::ValueObjectSP lhs = *lhs_or_err; + auto rhs_or_err = EvaluateAndDereference(node.GetRHS()); + if (!rhs_or_err) + return rhs_or_err; + lldb::ValueObjectSP rhs = *rhs_or_err; + + lldb::TypeSystemSP lhs_system = + lhs->GetCompilerType().GetTypeSystem().GetSharedPointer(); + lldb::TypeSystemSP rhs_system = + rhs->GetCompilerType().GetTypeSystem().GetSharedPointer(); + if (lhs_system->GetPluginName() != rhs_system->GetPluginName()) { + // TODO: Attempt to convert values to current CU's type system + return llvm::make_error<DILDiagnosticError>( + m_expr, "operands have different type systems", node.GetLocation()); + } + + switch (node.GetKind()) { + case BinaryOpKind::Add: + return EvaluateBinaryAddition(lhs, rhs, node.GetLocation()); + } + + return llvm::make_error<DILDiagnosticError>( + m_expr, "unimplemented binary operation", node.GetLocation()); +} + llvm::Expected<lldb::ValueObjectSP> Interpreter::Visit(const MemberOfNode &node) { auto base_or_err = Evaluate(node.GetBase()); diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index df8d4d271c410..f07607c93d927 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -127,7 +127,29 @@ ASTNodeUP DILParser::Run() { // expression: // cast_expression // -ASTNodeUP DILParser::ParseExpression() { return ParseCastExpression(); } +ASTNodeUP DILParser::ParseExpression() { return ParseAdditiveExpression(); } + +// Parse an additive_expression. +// +// additive_expression: +// cast_expression {"+" cast_expression} +// +ASTNodeUP DILParser::ParseAdditiveExpression() { + auto lhs = ParseCastExpression(); + assert(lhs && "ASTNodeUP must not contain a nullptr"); + + while (CurToken().Is(Token::plus)) { + Token token = CurToken(); + m_dil_lexer.Advance(); + auto rhs = ParseCastExpression(); + assert(rhs && "ASTNodeUP must not contain a nullptr"); + lhs = std::make_unique<BinaryOpNode>( + token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()), + std::move(lhs), std::move(rhs)); + } + + return lhs; +} // Parse a cast_expression. // diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py index 53a85fed303f4..fe8ee99835311 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py @@ -13,7 +13,7 @@ class TestFrameVarDILArithmetic(TestBase): def test_arithmetic(self): self.build() - lldbutil.run_to_source_breakpoint( + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") ) @@ -44,3 +44,36 @@ def test_arithmetic(self): self.expect_var_path("+bitfield.b", value="2", type="int") self.expect_var_path("+bitfield.c", value="3", type="unsigned int") self.expect_var_path("+bitfield.d", value="4", type="uint64_t") + + # Check basic math and resulting types + self.expect_var_path("1 + 2", value="3", type="int") + self.expect_var_path("1 + true", value="2", type="int") + self.expect_var_path("1L + wchar", value="2", type="long") + self.expect_var_path("1L + char16", value="3", type="long") + self.expect_var_path("1LL + char32", value="4", type="long long") + self.expect_var_path("1UL + 1L", value="2", type="unsigned long") + self.expect_var_path("s + x", value="12", type="int") + self.expect_var_path("s + l", value="15", type="long") + self.expect_var_path("1.0 + 2.5", value="3.5", type="double") + self.expect_var_path("1 + 2.5f", value="3.5", type="float") + self.expect_var_path("2. + .5", value="2.5", type="double") + self.expect_var_path("2.f + .5f", value="2.5", type="float") + self.expect_var_path("f + d", value="3.5", type="double") + + # Check limits and overflows + frame = thread.GetFrameAtIndex(0) + int_min = frame.GetValueForVariablePath("int_min").GetValue() + int_max = frame.GetValueForVariablePath("int_max").GetValue() + uint_max = frame.GetValueForVariablePath("uint_max").GetValue() + ll_max = frame.GetValueForVariablePath("ll_max").GetValue() + ll_min = frame.GetValueForVariablePath("ll_min").GetValue() + ull_max = frame.GetValueForVariablePath("ull_max").GetValue() + self.expect_var_path("int_max + 1", value=int_min) + self.expect_var_path("uint_max + 1", value="0") + self.expect_var_path("ll_max + 1", value=ll_min) + self.expect_var_path("ull_max + 1", value="0") + + # Check references and typedefs + self.expect_var_path("ref + 1", value="3") + self.expect_var_path("my_ref + 1", value="3") + self.expect_var_path("ref + my_ref", value="4") diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp index 2c70e93433f5f..129a4214a42c4 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp @@ -1,11 +1,19 @@ #include <cstdint> +#include <limits> int main(int argc, char **argv) { short s = 10; unsigned short us = 1; + long l = 5; + float f = 1.0f; + double d = 2.5; int x = 2; int &ref = x; + int *p = &x; + typedef int &myref; + myref my_ref = x; + enum Enum { kZero, kOne } enum_one = kOne; wchar_t wchar = 1; char16_t char16 = 2; @@ -19,5 +27,14 @@ int main(int argc, char **argv) { }; BitFieldStruct bitfield = {1, 2, 3, 4}; + int int_max = std::numeric_limits<int>::max(); + int int_min = std::numeric_limits<int>::min(); + unsigned int uint_max = std::numeric_limits<unsigned int>::max(); + unsigned int uint_zero = 0; + long long ll_max = std::numeric_limits<long long>::max(); + long long ll_min = std::numeric_limits<long long>::min(); + unsigned long long ull_max = std::numeric_limits<unsigned long long>::max(); + unsigned long long ull_zero = 0; + return 0; // Set a breakpoint here } >From 0eaffcd2e6c2599c95ab954d25ea68531b8d7c93 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Fri, 13 Feb 2026 19:08:09 +0500 Subject: [PATCH 2/6] Fix IsFloat() after rebase --- lldb/source/ValueObject/DILEval.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index b30632ca7800d..fdd27401b4078 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -180,15 +180,17 @@ Interpreter::ArithmeticConversion(lldb::ValueObjectSP &lhs, return CompilerType(); // Handle conversions for floating types (float, double). - if (lhs_type.IsFloat() || rhs_type.IsFloat()) { + if (lhs_type.IsRealFloatingPointType() || + rhs_type.IsRealFloatingPointType()) { // If both are floats, convert the smaller operand to the bigger. - if (lhs_type.IsFloat() && rhs_type.IsFloat()) { + if (lhs_type.IsRealFloatingPointType() && + rhs_type.IsRealFloatingPointType()) { if (lhs_type.GetBasicTypeEnumeration() > rhs_type.GetBasicTypeEnumeration()) return lhs_type; return rhs_type; } - if (lhs_type.IsFloat() && rhs_type.IsInteger()) + if (lhs_type.IsRealFloatingPointType() && rhs_type.IsInteger()) return lhs_type; return rhs_type; } >From ca9822741ee1e76b9e047bbaa862de41dd65e752 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Mon, 16 Feb 2026 17:41:53 +0500 Subject: [PATCH 3/6] Add more tests --- .../frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py index fe8ee99835311..75f13f79f83da 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py @@ -59,6 +59,9 @@ def test_arithmetic(self): self.expect_var_path("2. + .5", value="2.5", type="double") self.expect_var_path("2.f + .5f", value="2.5", type="float") self.expect_var_path("f + d", value="3.5", type="double") + self.expect_var_path("1 + s + (x + l)", value="18", type="long") + self.expect_var_path("+2 + (-1)", value="1", type="int") + self.expect_var_path("-2 + (+1)", value="-1", type="int") # Check limits and overflows frame = thread.GetFrameAtIndex(0) >From 3c5e3f4e0f7e8c8856bf8bbdde18384b4646a498 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Thu, 19 Feb 2026 00:48:27 +0500 Subject: [PATCH 4/6] Redo ConversionRank logic, add comments, adjust formatting --- lldb/source/ValueObject/DILEval.cpp | 46 ++++++++----------- .../Arithmetic/TestFrameVarDILArithmetic.py | 1 + .../frame/var-dil/expr/Arithmetic/main.cpp | 1 + 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index fdd27401b4078..12f69579b0908 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -127,7 +127,9 @@ Interpreter::UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location) { return valobj; } -static size_t IntegerConversionRank(CompilerType type) { +/// Basic types with a lower rank are converted to the basic type +/// with a higher rank. +static size_t ConversionRank(CompilerType type) { switch (type.GetCanonicalType().GetBasicTypeEnumeration()) { case lldb::eBasicTypeBool: return 1; @@ -150,6 +152,14 @@ static size_t IntegerConversionRank(CompilerType type) { case lldb::eBasicTypeInt128: case lldb::eBasicTypeUnsignedInt128: return 7; + case lldb::eBasicTypeHalf: + return 8; + case lldb::eBasicTypeFloat: + return 9; + case lldb::eBasicTypeDouble: + return 10; + case lldb::eBasicTypeLongDouble: + return 11; default: break; } @@ -172,6 +182,7 @@ Interpreter::ArithmeticConversion(lldb::ValueObjectSP &lhs, CompilerType lhs_type = lhs->GetCompilerType(); CompilerType rhs_type = rhs->GetCompilerType(); + // If types already match, no need for further conversions if (lhs_type.CompareTypes(rhs_type)) return lhs_type; @@ -179,32 +190,10 @@ Interpreter::ArithmeticConversion(lldb::ValueObjectSP &lhs, if (!lhs_type.IsScalarType() || !rhs_type.IsScalarType()) return CompilerType(); - // Handle conversions for floating types (float, double). - if (lhs_type.IsRealFloatingPointType() || - rhs_type.IsRealFloatingPointType()) { - // If both are floats, convert the smaller operand to the bigger. - if (lhs_type.IsRealFloatingPointType() && - rhs_type.IsRealFloatingPointType()) { - if (lhs_type.GetBasicTypeEnumeration() > - rhs_type.GetBasicTypeEnumeration()) - return lhs_type; - return rhs_type; - } - if (lhs_type.IsRealFloatingPointType() && rhs_type.IsInteger()) - return lhs_type; - return rhs_type; - } - - if (lhs_type.IsInteger() && rhs_type.IsInteger()) { - using Rank = std::tuple<size_t, bool>; - Rank l_rank = {IntegerConversionRank(lhs_type), !lhs_type.IsSigned()}; - Rank r_rank = {IntegerConversionRank(rhs_type), !rhs_type.IsSigned()}; - - if (l_rank < r_rank) - return rhs_type; - if (l_rank > r_rank) - return lhs_type; - } + using Rank = std::tuple<size_t, bool>; + if (Rank{ConversionRank(lhs_type), !lhs_type.IsSigned()} > + Rank{ConversionRank(rhs_type), !rhs_type.IsSigned()}) + return lhs_type; return rhs_type; } @@ -517,7 +506,8 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition( std::string errMsg = llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); - return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg), + location); } llvm::Expected<lldb::ValueObjectSP> diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py index 75f13f79f83da..22b22ecaf2e5a 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py @@ -54,6 +54,7 @@ def test_arithmetic(self): self.expect_var_path("1UL + 1L", value="2", type="unsigned long") self.expect_var_path("s + x", value="12", type="int") self.expect_var_path("s + l", value="15", type="long") + self.expect_var_path("l + ul", value="11", type="unsigned long") self.expect_var_path("1.0 + 2.5", value="3.5", type="double") self.expect_var_path("1 + 2.5f", value="3.5", type="float") self.expect_var_path("2. + .5", value="2.5", type="double") diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp index 129a4214a42c4..0961199add9cb 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp @@ -5,6 +5,7 @@ int main(int argc, char **argv) { short s = 10; unsigned short us = 1; long l = 5; + unsigned long ul = 6; float f = 1.0f; double d = 2.5; >From af3dfe34d988d283668e7c2e289026f0d53ef89a Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Thu, 19 Feb 2026 03:48:32 +0500 Subject: [PATCH 5/6] Add PromoteSignedInteger --- lldb/include/lldb/ValueObject/DILEval.h | 7 ++ lldb/source/ValueObject/DILEval.cpp | 81 +++++++++++++++++-- .../Arithmetic/TestFrameVarDILArithmetic.py | 16 +++- .../frame/var-dil/expr/Arithmetic/main.cpp | 4 +- 4 files changed, 97 insertions(+), 11 deletions(-) diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 6807a6616b846..1846035070a14 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -75,6 +75,13 @@ class Interpreter : Visitor { llvm::Expected<lldb::ValueObjectSP> UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location); + /// If `lhs_type` is unsigned and `rhs_type` is signed, check whether it + /// can represent all of the values of `lhs_type`. + /// If not, then promote `rhs_type` to the unsigned version of its type. + /// \returns Unchanged `rhs_type` or promoted unsigned version. + llvm::Expected<CompilerType> PromoteSignedInteger(CompilerType &lhs_type, + CompilerType &rhs_type); + /// Perform an arithmetic conversion on two values from an arithmetic /// operation. /// \returns The result type of an arithmetic operation. diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 12f69579b0908..c0db985005ae3 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -166,6 +166,55 @@ static size_t ConversionRank(CompilerType type) { return 0; } +static lldb::BasicType BasicTypeToUnsigned(lldb::BasicType basic_type) { + switch (basic_type) { + case lldb::eBasicTypeChar: + case lldb::eBasicTypeSignedChar: + return lldb::eBasicTypeUnsignedChar; + case lldb::eBasicTypeShort: + return lldb::eBasicTypeUnsignedShort; + case lldb::eBasicTypeInt: + return lldb::eBasicTypeUnsignedInt; + case lldb::eBasicTypeLong: + return lldb::eBasicTypeUnsignedLong; + case lldb::eBasicTypeLongLong: + return lldb::eBasicTypeUnsignedLongLong; + case lldb::eBasicTypeInt128: + return lldb::eBasicTypeUnsignedInt128; + default: + return basic_type; + } +} + +llvm::Expected<CompilerType> +Interpreter::PromoteSignedInteger(CompilerType &lhs_type, + CompilerType &rhs_type) { + // This expects that Rank(lhs_type) < Rank(rhs_type). + if (!lhs_type.IsSigned() && rhs_type.IsSigned()) { + llvm::Expected<uint64_t> lhs_size = + lhs_type.GetBitSize(m_exe_ctx_scope.get()); + if (!lhs_size) + return lhs_size.takeError(); + llvm::Expected<uint64_t> rhs_size = + rhs_type.GetBitSize(m_exe_ctx_scope.get()); + if (!rhs_size) + return rhs_size.takeError(); + + if (*rhs_size == *lhs_size) { + llvm::Expected<lldb::TypeSystemSP> type_system = + GetTypeSystemFromCU(m_exe_ctx_scope); + if (!type_system) + return type_system.takeError(); + CompilerType r_type_unsigned = GetBasicType( + *type_system, + BasicTypeToUnsigned( + rhs_type.GetCanonicalType().GetBasicTypeEnumeration())); + return r_type_unsigned; + } + } + return rhs_type; +} + llvm::Expected<CompilerType> Interpreter::ArithmeticConversion(lldb::ValueObjectSP &lhs, lldb::ValueObjectSP &rhs, uint32_t location) { @@ -182,7 +231,7 @@ Interpreter::ArithmeticConversion(lldb::ValueObjectSP &lhs, CompilerType lhs_type = lhs->GetCompilerType(); CompilerType rhs_type = rhs->GetCompilerType(); - // If types already match, no need for further conversions + // If types already match, no need for further conversions. if (lhs_type.CompareTypes(rhs_type)) return lhs_type; @@ -190,11 +239,33 @@ Interpreter::ArithmeticConversion(lldb::ValueObjectSP &lhs, if (!lhs_type.IsScalarType() || !rhs_type.IsScalarType()) return CompilerType(); - using Rank = std::tuple<size_t, bool>; - if (Rank{ConversionRank(lhs_type), !lhs_type.IsSigned()} > - Rank{ConversionRank(rhs_type), !rhs_type.IsSigned()}) + size_t l_rank = ConversionRank(lhs_type); + size_t r_rank = ConversionRank(rhs_type); + // If both operands are integer, check if we need to promote + // the higher ranked signed type. + if (lhs_type.IsInteger() && rhs_type.IsInteger()) { + using Rank = std::tuple<size_t, bool>; + Rank int_l_rank = {l_rank, !lhs_type.IsSigned()}; + Rank int_r_rank = {r_rank, !rhs_type.IsSigned()}; + if (int_l_rank < int_r_rank) { + auto type_or_err = PromoteSignedInteger(lhs_type, rhs_type); + if (!type_or_err) + return type_or_err.takeError(); + return *type_or_err; + } + if (int_l_rank > int_r_rank) { + auto type_or_err = PromoteSignedInteger(rhs_type, lhs_type); + if (!type_or_err) + return type_or_err.takeError(); + return *type_or_err; + } return lhs_type; - return rhs_type; + } + + // Handle other combinations of integer and floating point operands. + if (l_rank < r_rank) + return rhs_type; + return lhs_type; } static lldb::VariableSP DILFindVariable(ConstString name, diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py index 22b22ecaf2e5a..5b68c493ba3a5 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py @@ -67,16 +67,24 @@ def test_arithmetic(self): # Check limits and overflows frame = thread.GetFrameAtIndex(0) int_min = frame.GetValueForVariablePath("int_min").GetValue() - int_max = frame.GetValueForVariablePath("int_max").GetValue() - uint_max = frame.GetValueForVariablePath("uint_max").GetValue() - ll_max = frame.GetValueForVariablePath("ll_max").GetValue() ll_min = frame.GetValueForVariablePath("ll_min").GetValue() - ull_max = frame.GetValueForVariablePath("ull_max").GetValue() self.expect_var_path("int_max + 1", value=int_min) self.expect_var_path("uint_max + 1", value="0") self.expect_var_path("ll_max + 1", value=ll_min) self.expect_var_path("ull_max + 1", value="0") + # Check signed integer promotion when different types have the same size + uint = frame.GetValueForVariablePath("ui") + long = frame.GetValueForVariablePath("l") + if uint.GetByteSize() == long.GetByteSize(): + self.expect_var_path("ui + l", value="9", type="unsigned long") + self.expect_var_path("l + ui", value="9", type="unsigned long") + ulong = frame.GetValueForVariablePath("ul") + longlong = frame.GetValueForVariablePath("ll") + if ulong.GetByteSize() == longlong.GetByteSize(): + self.expect_var_path("ul + ll", value="13", type="unsigned long long") + self.expect_var_path("ll + ul", value="13", type="unsigned long long") + # Check references and typedefs self.expect_var_path("ref + 1", value="3") self.expect_var_path("my_ref + 1", value="3") diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp index 0961199add9cb..f41b89beb2bde 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp @@ -4,8 +4,10 @@ int main(int argc, char **argv) { short s = 10; unsigned short us = 1; + unsigned int ui = 4; long l = 5; unsigned long ul = 6; + long long ll = 7; float f = 1.0f; double d = 2.5; @@ -31,11 +33,9 @@ int main(int argc, char **argv) { int int_max = std::numeric_limits<int>::max(); int int_min = std::numeric_limits<int>::min(); unsigned int uint_max = std::numeric_limits<unsigned int>::max(); - unsigned int uint_zero = 0; long long ll_max = std::numeric_limits<long long>::max(); long long ll_min = std::numeric_limits<long long>::min(); unsigned long long ull_max = std::numeric_limits<unsigned long long>::max(); - unsigned long long ull_zero = 0; return 0; // Set a breakpoint here } >From 807bd01ceca359477c0d0ac8d03c0f8ffa8f4e3f Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <[email protected]> Date: Thu, 19 Feb 2026 03:50:36 +0500 Subject: [PATCH 6/6] Check if conversion rank is 0 --- lldb/source/ValueObject/DILEval.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index c0db985005ae3..b64e4723473d1 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -241,6 +241,10 @@ Interpreter::ArithmeticConversion(lldb::ValueObjectSP &lhs, size_t l_rank = ConversionRank(lhs_type); size_t r_rank = ConversionRank(rhs_type); + if (l_rank == 0 || r_rank == 0) + return llvm::make_error<DILDiagnosticError>( + m_expr, "unexpected basic type in arithmetic operation", location); + // If both operands are integer, check if we need to promote // the higher ranked signed type. if (lhs_type.IsInteger() && rhs_type.IsInteger()) { _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
