https://github.com/kuilpd updated 
https://github.com/llvm/llvm-project/pull/208832

>From 6f77215d31c060774ed2b2c3127d49eedca3cc9f Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <[email protected]>
Date: Thu, 9 Jul 2026 16:46:53 +0500
Subject: [PATCH 1/3] [lldb] Add comparison operators to DIL

---
 lldb/docs/dil-expr-lang.ebnf                  |  12 +-
 lldb/include/lldb/ValueObject/DILAST.h        |  19 +-
 lldb/include/lldb/ValueObject/DILEval.h       |   7 +
 lldb/include/lldb/ValueObject/DILLexer.h      |   6 +
 lldb/include/lldb/ValueObject/DILParser.h     |   2 +
 lldb/source/ValueObject/DILAST.cpp            |  12 ++
 lldb/source/ValueObject/DILEval.cpp           | 202 ++++++++++++++++++
 lldb/source/ValueObject/DILLexer.cpp          |  42 ++--
 lldb/source/ValueObject/DILParser.cpp         |  51 ++++-
 .../frame/var-dil/expr/Comparison/Makefile    |   3 +
 .../TestFrameVarDILExprComparison.py          | 173 +++++++++++++++
 .../frame/var-dil/expr/Comparison/main.cpp    |  34 +++
 12 files changed, 538 insertions(+), 25 deletions(-)
 create mode 100644 
lldb/test/API/commands/frame/var-dil/expr/Comparison/Makefile
 create mode 100644 
lldb/test/API/commands/frame/var-dil/expr/Comparison/TestFrameVarDILExprComparison.py
 create mode 100644 
lldb/test/API/commands/frame/var-dil/expr/Comparison/main.cpp

diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index f3c465711e956..827dec34dd0a8 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -5,13 +5,21 @@
 
 expression = assignment_expression ;
 
-assignment_expression = shift_expression
-                      | shift_expression assignment_operator 
assignment_expression ;
+assignment_expression = equality_expression
+                      | equality_expression assignment_operator 
assignment_expression ;
 
 assignment_operator = "="
                     | "+="
                     | "-=" ;
 
+equality_expression = relational_expression {"==" relational_expression}
+                    | relational_expression {"!=" relational_expression} ;
+
+relational_expression = shift_expression {"<" shift_expression}
+                      | shift_expression {">" shift_expression}
+                      | shift_expression {"<=" shift_expression}
+                      | shift_expression {">=" shift_expression} ;
+
 shift_expression = additive_expression {"<<" additive_expression}
                  | additive_expression {">>" additive_expression} ;
 
diff --git a/lldb/include/lldb/ValueObject/DILAST.h 
b/lldb/include/lldb/ValueObject/DILAST.h
index 93310a91a15bb..90c15a6352062 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -42,16 +42,22 @@ enum class UnaryOpKind {
 
 /// The binary operators recognized by DIL.
 enum class BinaryOpKind {
-  Add,       ///< "+"
-  AddAssign, ///< "+="
   Assign,    ///< "="
-  Div,       ///< "/"
+  Add,       ///< "+"
+  Sub,       ///< "-"
   Mul,       ///< "*"
+  Div,       ///< "/"
   Rem,       ///< "%"
   Shl,       ///< "<<"
   Shr,       ///< ">>"
-  Sub,       ///< "-"
+  AddAssign, ///< "+="
   SubAssign, ///< "-="
+  LT,        ///< "<"
+  GT,        ///< ">"
+  LE,        ///< "<="
+  GE,        ///< ">="
+  EQ,        ///< "=="
+  NE,        ///< "!="
 };
 
 /// Translates DIL tokens to BinaryOpKind.
@@ -87,6 +93,8 @@ class ASTNode {
 
   virtual llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const = 0;
 
+  virtual bool IsConstLiteral() const { return false; }
+
   uint32_t GetLocation() const { return m_location; }
   NodeKind GetKind() const { return m_kind; }
 
@@ -246,6 +254,7 @@ class IntegerLiteralNode : public ASTNode {
 
   llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
 
+  bool IsConstLiteral() const override { return true; }
   const llvm::APInt &GetValue() const { return m_value; }
   uint32_t GetRadix() const { return m_radix; }
   bool IsUnsigned() const { return m_is_unsigned; }
@@ -270,6 +279,7 @@ class FloatLiteralNode : public ASTNode {
 
   llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
 
+  bool IsConstLiteral() const override { return true; }
   const llvm::APFloat &GetValue() const { return m_value; }
 
   static bool classof(const ASTNode &node) {
@@ -287,6 +297,7 @@ class BooleanLiteralNode : public ASTNode {
 
   llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
 
+  bool IsConstLiteral() const override { return true; }
   bool GetValue() const & { return m_value; }
 
   static bool classof(const ASTNode &node) {
diff --git a/lldb/include/lldb/ValueObject/DILEval.h 
b/lldb/include/lldb/ValueObject/DILEval.h
index 35784ea9987f9..5b05073983855 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -106,6 +106,13 @@ class Interpreter : Visitor {
                                                        lldb::ValueObjectSP rhs,
                                                        CompilerType 
result_type,
                                                        uint32_t location);
+  llvm::Error ValidateComparison(BinaryOpKind kind, lldb::ValueObjectSP &lhs,
+                                 lldb::ValueObjectSP &rhs, bool lhs_is_literal,
+                                 bool rhs_is_literal, uint32_t location);
+  llvm::Expected<lldb::ValueObjectSP>
+  EvaluateComparison(BinaryOpKind kind, lldb::ValueObjectSP lhs,
+                     lldb::ValueObjectSP rhs, bool lhs_is_literal,
+                     bool rhs_is_literal, uint32_t location);
   llvm::Expected<lldb::ValueObjectSP>
   EvaluateBinaryShift(BinaryOpKind kind, lldb::ValueObjectSP lhs,
                       lldb::ValueObjectSP rhs, uint32_t location);
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h 
b/lldb/include/lldb/ValueObject/DILLexer.h
index f9f42dc59b311..263ef0353d601 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -31,7 +31,11 @@ class Token {
     coloncolon,
     eof,
     equal,
+    equalequal,
+    exclaimequal,
     float_constant,
+    greater,
+    greaterequal,
     greatergreater,
     identifier,
     integer_constant,
@@ -39,6 +43,8 @@ class Token {
     kw_true,
     l_paren,
     l_square,
+    less,
+    lessequal,
     lessless,
     minus,
     minusequal,
diff --git a/lldb/include/lldb/ValueObject/DILParser.h 
b/lldb/include/lldb/ValueObject/DILParser.h
index 9e2bbff4b6614..2cebb5c222fa7 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -84,6 +84,8 @@ class DILParser {
   ASTNodeUP ParseExpression();
 
   ASTNodeUP ParseAssignmentExpression();
+  ASTNodeUP ParseEqualityExpression();
+  ASTNodeUP ParseRelationalExpression();
   ASTNodeUP ParseShiftExpression();
   ASTNodeUP ParseAdditiveExpression();
   ASTNodeUP ParseMultiplicativeExpression();
diff --git a/lldb/source/ValueObject/DILAST.cpp 
b/lldb/source/ValueObject/DILAST.cpp
index 40bf07bdd5aab..02252d9345a5a 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -33,6 +33,18 @@ BinaryOpKind GetBinaryOpKindFromToken(Token::Kind 
token_kind) {
     return BinaryOpKind::Shl;
   case Token::greatergreater:
     return BinaryOpKind::Shr;
+  case Token::less:
+    return BinaryOpKind::LT;
+  case Token::greater:
+    return BinaryOpKind::GT;
+  case Token::lessequal:
+    return BinaryOpKind::LE;
+  case Token::greaterequal:
+    return BinaryOpKind::GE;
+  case Token::equalequal:
+    return BinaryOpKind::EQ;
+  case Token::exclaimequal:
+    return BinaryOpKind::NE;
   default:
     break;
   }
diff --git a/lldb/source/ValueObject/DILEval.cpp 
b/lldb/source/ValueObject/DILEval.cpp
index 4c5ac96dccf74..2947d59c80e37 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -653,6 +653,18 @@ Interpreter::EvaluateScalarOp(BinaryOpKind kind, 
lldb::ValueObjectSP lhs,
     return value_object(l << r);
   case BinaryOpKind::Shr:
     return value_object(l >> r);
+  case BinaryOpKind::LT:
+    return value_object(l < r);
+  case BinaryOpKind::GT:
+    return value_object(l > r);
+  case BinaryOpKind::LE:
+    return value_object(l <= r);
+  case BinaryOpKind::GE:
+    return value_object(l >= r);
+  case BinaryOpKind::EQ:
+    return value_object(l == r);
+  case BinaryOpKind::NE:
+    return value_object(l != r);
   default:
     break;
   }
@@ -906,6 +918,186 @@ Interpreter::EvaluateAssignment(lldb::ValueObjectSP lhs,
   return lhs;
 }
 
+static bool IsLiteralZero(lldb::ValueObjectSP &val, bool is_literal) {
+  bool is_zero = val->GetValueAsUnsigned(-1) == 0;
+  bool is_boolean = val->GetCompilerType().IsBoolean();
+  return is_zero && !is_boolean && is_literal;
+}
+
+llvm::Error
+Interpreter::ValidateComparison(BinaryOpKind kind, lldb::ValueObjectSP &lhs,
+                                lldb::ValueObjectSP &rhs, bool lhs_is_literal,
+                                bool rhs_is_literal, uint32_t location) {
+  auto orig_lhs_type = lhs->GetCompilerType();
+  auto orig_rhs_type = rhs->GetCompilerType();
+
+  if (orig_lhs_type == orig_rhs_type)
+    return llvm::Error::success();
+
+  bool is_ordered = (kind == BinaryOpKind::LT || kind == BinaryOpKind::LE ||
+                     kind == BinaryOpKind::GT || kind == BinaryOpKind::GE);
+  bool lhs_nullptr_or_zero =
+      orig_lhs_type.IsNullPtrType() || IsLiteralZero(lhs, lhs_is_literal);
+  bool rhs_nullptr_or_zero =
+      orig_rhs_type.IsNullPtrType() || IsLiteralZero(rhs, rhs_is_literal);
+
+  if (orig_lhs_type.IsArrayType())
+    lhs = ArrayToPointerConversion(*lhs, m_stack_frame, "result");
+  if (orig_rhs_type.IsArrayType())
+    rhs = ArrayToPointerConversion(*rhs, m_stack_frame, "result");
+
+  CompilerType lhs_type = lhs->GetCompilerType();
+  CompilerType rhs_type = rhs->GetCompilerType();
+  lldb::ValueObjectSP lhs_child;
+  lldb::ValueObjectSP rhs_child;
+  bool is_signed;
+
+  if (!lhs_nullptr_or_zero && !lhs_type.IsPointerType() &&
+      !lhs_type.IsIntegerOrEnumerationType(is_signed)) {
+    // lhs is not a nullptr, pointer, enum or integer. Check to see if its
+    // first child could be a pointer. If so, update lhs_type accordingly.
+    lhs_child = lhs->GetChildAtIndex(0);
+    if (lhs_child && (lhs_child->IsPointerType() ||
+                      lhs_child->GetCompilerType().IsNullPtrType()))
+      lhs_type = lhs_child->GetCompilerType();
+  }
+  if (!rhs_nullptr_or_zero && !rhs_type.IsPointerType() &&
+      !rhs_type.IsIntegerOrEnumerationType(is_signed)) {
+    // rhs is not a nullptr, pointer, enum or integer. Check to see if its
+    // first child could be a pointer. If so, update rhs_type accordingly.
+    rhs_child = rhs->GetChildAtIndex(0);
+    if (rhs_child && (rhs_child->IsPointerType() ||
+                      rhs_child->GetCompilerType().IsNullPtrType()))
+      rhs_type = rhs_child->GetCompilerType();
+  }
+
+  if ((lhs_type != orig_lhs_type) || (rhs_type != orig_rhs_type)) {
+    if (lhs_type.IsNullPtrType() || rhs_type.IsNullPtrType())
+      return llvm::Error::success();
+
+    // May be an integer or enum.
+    if (!lhs_type.IsPointerType() || !rhs_type.IsPointerType())
+      return llvm::Error::success();
+
+    CompilerType lhs_unqualified =
+        lhs_type.GetCanonicalType().GetFullyUnqualifiedType();
+    CompilerType rhs_unqualified =
+        rhs_type.GetCanonicalType().GetFullyUnqualifiedType();
+
+    if (lhs_unqualified.IsPointerToVoid() || rhs_unqualified.IsPointerToVoid())
+      return llvm::Error::success();
+
+    // We have two pointers, neither of which is nullptr or void *. Make
+    // sure their types are compatible.
+    bool comparable = lhs_unqualified.CompareTypes(rhs_unqualified);
+    if (comparable)
+      return llvm::Error::success();
+
+    std::string errMsg = llvm::formatv(
+        "comparison of distinct pointer types ({0} and {1})",
+        orig_lhs_type.TypeDescription(), orig_rhs_type.TypeDescription());
+    return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location);
+  }
+
+  if (!is_ordered && ((orig_lhs_type.IsNullPtrType() && rhs_nullptr_or_zero) ||
+                      (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
+  // rules for arithmetic operators.
+  auto type_or_err = ArithmeticConversion(lhs, rhs, location);
+  if (!type_or_err)
+    return type_or_err.takeError();
+
+  lhs_type = lhs->GetCompilerType();
+  rhs_type = rhs->GetCompilerType();
+  if (lhs_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))
+      return llvm::Error::success();
+    std::string errMsg = llvm::formatv(
+        "invalid operands to binary expression ({0} and {1})",
+        orig_lhs_type.TypeDescription(), orig_rhs_type.TypeDescription());
+    return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location);
+  }
+
+  // Check if the value can be compared to a pointer. We allow all pointers,
+  // integers, unscoped enumerations and a nullptr literal if it's an
+  // equality/inequality comparison. For "pointer <-> integer" C++ allows only
+  // equality/inequality comparison against literal zero and nullptr. However 
in
+  // the debugger context it's often useful to compare a pointer with an 
integer
+  // representing an address. That said, this also allows comparing nullptr and
+  // any integer, not just literal zero, e.g. "nullptr == 1 -> false". C++
+  // doesn't allow it, but we implement this for convenience.
+  auto comparable_to_pointer = [&](CompilerType t) {
+    return t.IsPointerType() || t.IsInteger() ||
+           t.IsUnscopedEnumerationType() || (!is_ordered && t.IsNullPtrType());
+  };
+
+  if ((lhs_type.IsPointerType() && comparable_to_pointer(rhs_type)) ||
+      (comparable_to_pointer(lhs_type) && rhs_type.IsPointerType())) {
+    // If both are pointers, check if they have comparable types.
+    if ((lhs_type.IsPointerType() && !lhs_type.IsPointerToVoid()) &&
+        (rhs_type.IsPointerType() && !rhs_type.IsPointerToVoid())) {
+      // Compare canonical unqualified pointer types.
+      CompilerType lhs_unqualified_type =
+          lhs_type.GetCanonicalType().GetFullyUnqualifiedType();
+      CompilerType rhs_unqualified_type =
+          rhs_type.GetCanonicalType().GetFullyUnqualifiedType();
+      bool comparable = 
lhs_unqualified_type.CompareTypes(rhs_unqualified_type);
+
+      if (!comparable) {
+
+        std::string errMsg = llvm::formatv(
+            "comparison of distinct pointer types ({0} and {1})",
+            orig_lhs_type.TypeDescription(), orig_rhs_type.TypeDescription());
+        return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location);
+      }
+    }
+    // Comparing pointers to void is always allowed.
+    return llvm::Error::success();
+  }
+
+  std::string errMsg = llvm::formatv(
+      "invalid operands to binary expression ({0} and {1})",
+      orig_lhs_type.TypeDescription(), orig_rhs_type.TypeDescription());
+  return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location);
+}
+
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::EvaluateComparison(BinaryOpKind kind, lldb::ValueObjectSP lhs,
+                                lldb::ValueObjectSP rhs, bool lhs_is_literal,
+                                bool rhs_is_literal, uint32_t location) {
+  // Comparison works for:
+  //  nullptr_t <-> {nullptr_t,integer} (if integer is literal zero)
+  //  {nullptr_t,integer} <-> nullptr_t (if integer is literal zero)
+  //  {scalar,unscoped_enum} <-> {scalar,unscoped_enum}
+  //  scoped_enum <-> scoped_enum (if the same type)
+  //  pointer <-> pointer (if pointee types are compatible)
+  //  pointer <-> {integer,unscoped_enum,nullptr_t}
+  //  {integer,unscoped_enum,nullptr_t} <-> pointer
+  if (auto error = ValidateComparison(kind, lhs, rhs, lhs_is_literal,
+                                      rhs_is_literal, location))
+    return error;
+
+  CompilerType lhs_type = lhs->GetCompilerType();
+  CompilerType rhs_type = rhs->GetCompilerType();
+
+  llvm::Expected<lldb::TypeSystemSP> type_system =
+      GetTypeSystemFromCU(m_stack_frame);
+  if (!type_system)
+    return type_system.takeError();
+  CompilerType boolean_type = GetBasicType(*type_system, lldb::eBasicTypeBool);
+
+  return EvaluateScalarOp(kind, lhs, rhs, boolean_type, location);
+}
+
 llvm::Expected<lldb::ValueObjectSP>
 Interpreter::EvaluateBinaryShift(BinaryOpKind kind, lldb::ValueObjectSP lhs,
                                  lldb::ValueObjectSP rhs, uint32_t location) {
@@ -957,6 +1149,8 @@ Interpreter::Visit(const BinaryOpNode &node) {
     return rhs_or_err;
   lldb::ValueObjectSP rhs = *rhs_or_err;
 
+  bool lhs_is_literal = node.GetLHS().IsConstLiteral();
+  bool rhs_is_literal = node.GetRHS().IsConstLiteral();
   lldb::TypeSystemSP lhs_system =
       lhs->GetCompilerType().GetTypeSystem().GetSharedPointer();
   lldb::TypeSystemSP rhs_system =
@@ -995,6 +1189,14 @@ Interpreter::Visit(const BinaryOpNode &node) {
   case BinaryOpKind::Shl:
   case BinaryOpKind::Shr:
     return EvaluateBinaryShift(node.GetKind(), lhs, rhs, node.GetLocation());
+  case BinaryOpKind::EQ:
+  case BinaryOpKind::NE:
+  case BinaryOpKind::LT:
+  case BinaryOpKind::LE:
+  case BinaryOpKind::GT:
+  case BinaryOpKind::GE:
+    return EvaluateComparison(node.GetKind(), lhs, rhs, lhs_is_literal,
+                              rhs_is_literal, node.GetLocation());
   }
 
   return llvm::make_error<DILDiagnosticError>(
diff --git a/lldb/source/ValueObject/DILLexer.cpp 
b/lldb/source/ValueObject/DILLexer.cpp
index 997ba6b09f872..0aed547ebb0aa 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -32,8 +32,16 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
     return "equal";
   case Kind::eof:
     return "eof";
+  case Kind::equalequal:
+    return "equalequal";
+  case Kind::exclaimequal:
+    return "exclaimequal";
   case Kind::float_constant:
     return "float_constant";
+  case Kind::greater:
+    return "greater";
+  case Kind::greaterequal:
+    return "greaterequal";
   case Kind::greatergreater:
     return "greatergreater";
   case Kind::identifier:
@@ -48,6 +56,10 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
     return "l_paren";
   case Kind::l_square:
     return "l_square";
+  case Kind::less:
+    return "less";
+  case Kind::lessequal:
+    return "lessequal";
   case Kind::lessless:
     return "lessless";
   case Kind::minus:
@@ -198,24 +210,18 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
   // be ordered longest-to-shortest in the list below. E.g. '::' must come
   // before ':', and '+=' must come before '+'.
   constexpr std::pair<Token::Kind, const char *> operators[] = {
-      {Token::arrow, "->"},
-      {Token::coloncolon, "::"},
-      {Token::greatergreater, ">>"},
-      {Token::lessless, "<<"},
-      {Token::minusequal, "-="},
-      {Token::plusequal, "+="},
-      {Token::amp, "&"},
-      {Token::colon, ":"},
-      {Token::equal, "="},
-      {Token::l_paren, "("},
-      {Token::l_square, "["},
-      {Token::minus, "-"},
-      {Token::percent, "%"},
-      {Token::period, "."},
-      {Token::plus, "+"},
-      {Token::r_paren, ")"},
-      {Token::r_square, "]"},
-      {Token::slash, "/"},
+      {Token::arrow, "->"},        {Token::coloncolon, "::"},
+      {Token::equalequal, "=="},   {Token::exclaimequal, "!="},
+      {Token::greaterequal, ">="}, {Token::greatergreater, ">>"},
+      {Token::lessequal, "<="},    {Token::lessless, "<<"},
+      {Token::minusequal, "-="},   {Token::plusequal, "+="},
+      {Token::amp, "&"},           {Token::colon, ":"},
+      {Token::equal, "="},         {Token::greater, ">"},
+      {Token::l_paren, "("},       {Token::l_square, "["},
+      {Token::less, "<"},          {Token::minus, "-"},
+      {Token::percent, "%"},       {Token::period, "."},
+      {Token::plus, "+"},          {Token::r_paren, ")"},
+      {Token::r_square, "]"},      {Token::slash, "/"},
       {Token::star, "*"},
   };
   for (auto [kind, str] : operators) {
diff --git a/lldb/source/ValueObject/DILParser.cpp 
b/lldb/source/ValueObject/DILParser.cpp
index b55b12a2bc42a..a6afff57734e8 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -143,7 +143,7 @@ ASTNodeUP DILParser::ParseExpression() { return 
ParseAssignmentExpression(); }
 //    "-="
 //
 ASTNodeUP DILParser::ParseAssignmentExpression() {
-  auto lhs = ParseShiftExpression();
+  auto lhs = ParseEqualityExpression();
   assert(lhs && "ASTNodeUP must not contain a nullptr");
 
   // Check if it's an assignment expression.
@@ -160,6 +160,55 @@ ASTNodeUP DILParser::ParseAssignmentExpression() {
   return lhs;
 }
 
+// Parse an equality_expression.
+//
+//  equality_expression:
+//    relational_expression {"==" relational_expression}
+//    relational_expression {"!=" relational_expression}
+//
+ASTNodeUP DILParser::ParseEqualityExpression() {
+  auto lhs = ParseRelationalExpression();
+  assert(lhs && "ASTNodeUP must not contain a nullptr");
+
+  while (CurToken().IsOneOf({Token::equalequal, Token::exclaimequal})) {
+    Token token = CurToken();
+    m_dil_lexer.Advance();
+    auto rhs = ParseRelationalExpression();
+    assert(lhs && "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 relational_expression.
+//
+//  relational_expression:
+//    shift_expression {"<" shift_expression}
+//    shift_expression {">" shift_expression}
+//    shift_expression {"<=" shift_expression}
+//    shift_expression {">=" shift_expression}
+//
+ASTNodeUP DILParser::ParseRelationalExpression() {
+  auto lhs = ParseShiftExpression();
+  assert(lhs && "ASTNodeUP must not contain a nullptr");
+
+  while (CurToken().IsOneOf(
+      {Token::less, Token::greater, Token::lessequal, Token::greaterequal})) {
+    Token token = CurToken();
+    m_dil_lexer.Advance();
+    auto rhs = ParseShiftExpression();
+    assert(lhs && "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 shift_expression.
 //
 //  shift_expression:
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Comparison/Makefile 
b/lldb/test/API/commands/frame/var-dil/expr/Comparison/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Comparison/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git 
a/lldb/test/API/commands/frame/var-dil/expr/Comparison/TestFrameVarDILExprComparison.py
 
b/lldb/test/API/commands/frame/var-dil/expr/Comparison/TestFrameVarDILExprComparison.py
new file mode 100644
index 0000000000000..f5aa61e15ecbf
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/expr/Comparison/TestFrameVarDILExprComparison.py
@@ -0,0 +1,173 @@
+"""
+Test DIL comparison operators.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarComparison(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test_comparison(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+        )
+
+        self.runCmd("settings set target.experimental.use-DIL true")
+
+        # Check arithmetic comparison
+        self.expect_var_path("1 == 1", value="true")
+        self.expect_var_path("1 == 1.0", value="true")
+        self.expect_var_path("i == 1", value="true")
+        self.expect_var_path("iref == i", value="true")
+        self.expect_var_path("array[0] == i", value="true")
+        self.expect_var_path("trueVar == true", value="true")
+        self.expect_var_path("1 == true", value="true")
+        self.expect_var_path("array[0] != array[1]", value="true")
+        self.expect_var_path("1 != 2 == true", value="true")
+        self.expect_var_path("true != 2 < 3", value="false")
+        self.expect_var_path("ScopedEnum::kZeroS < ScopedEnum::kOneS", 
value="true")
+        self.expect_var_path("1 > 2", value="false")
+        self.expect_var_path("1 > 0.1", value="true")
+        self.expect_var_path("1 >= 2", value="false")
+        self.expect_var_path("2 >= 2", value="true")
+        self.expect_var_path("1.0 <= 1.25", value="true")
+        self.expect_var_path("1.25f <= 1.0", value="false")
+        self.expect_var_path("1.0 <= 1.0", value="true")
+
+        self.expect(
+            "frame var -- 'ScopedEnum::kZeroS < ScopedEnumInt8::kOneS8'",
+            error=True,
+            substrs=[
+                "invalid operands to binary expression "
+                "('ScopedEnum' and 'ScopedEnumInt8')"
+            ],
+        )
+
+        self.expect(
+            "frame var -- 's < 4",
+            error=True,
+            substrs=["invalid operands to binary expression ('S' and 'int')"],
+        )
+
+        # Check pointer comparison
+        self.expect_var_path("p_void == p_void", value="true")
+        self.expect_var_path("p_void == p_char1", value="true")
+        self.expect_var_path("p_void != p_char1", value="false")
+        self.expect_var_path("p_void > p_char1", value="false")
+        self.expect_var_path("p_void >= p_char1", value="true")
+        self.expect_var_path("p_void < (p_char1 + 1)", value="true")
+        self.expect_var_path("pp_void0 + 1 == pp_void1", value="true")
+
+        self.expect_var_path("(void*)1 == (void*)1", value="true")
+        self.expect_var_path("(void*)1 != (void*)1", value="false")
+        self.expect_var_path("(void*)2 > (void*)1", value="true")
+        self.expect_var_path("(void*)2 < (void*)1", value="false")
+
+        self.expect_var_path("(void*)1 == (char*)1", value="true")
+        self.expect_var_path("(char*)1 != (void*)1", value="false")
+        self.expect_var_path("(void*)2 > (char*)1", value="true")
+        self.expect_var_path("(char*)2 < (void*)1", value="false")
+
+        self.expect_var_path("(void*)0 == 0", value="true")
+        self.expect_var_path("0 != (void*)0", value="false")
+
+        self.expect_var_path("(void*)0 == nullptr", value="true")
+        self.expect_var_path("(void*)0 != nullptr", value="false")
+        self.expect_var_path("nullptr == (void*)1", value="false")
+        self.expect_var_path("nullptr != (void*)1", value="true")
+
+        self.expect_var_path("nullptr == nullptr", value="true")
+        self.expect_var_path("nullptr != nullptr", value="false")
+
+        self.expect_var_path("nullptr == 0", value="true")
+        self.expect_var_path("0 != nullptr", value="false")
+        self.expect_var_path("nullptr == 0U", value="true")
+        self.expect_var_path("0L != nullptr", value="false")
+        self.expect_var_path("nullptr == 0UL", value="true")
+        self.expect_var_path("0ULL != nullptr", value="false")
+        self.expect_var_path("nullptr == 0x0", value="true")
+        self.expect_var_path("0b0 != nullptr", value="false")
+        self.expect_var_path("nullptr == 00", value="true")
+        self.expect_var_path("0x0LLU != nullptr", value="false")
+
+        self.expect_var_path("0 == std_nullptr_t", value="true")
+        self.expect_var_path("std_nullptr_t != 0", value="false")
+
+        self.expect_var_path("array == p_int0", value="true")
+        self.expect_var_path("p_int0 == array", value="true")
+        self.expect_var_path("array < p_int1", value="true")
+        self.expect_var_path("array == nullptr", value="false")
+
+        # These are not allowed by C++, but DIL supports these for convenience.
+        self.expect_var_path("(void*)1 == 1", value="true")
+        self.expect_var_path("(void*)1 == 0", value="false")
+        self.expect_var_path("(void*)1 > 0", value="true")
+        self.expect_var_path("(void*)1 < 0", value="false")
+        self.expect_var_path("1 > (void*)0", value="true")
+        self.expect_var_path("2 < (void*)3", value="true")
+
+        # Integer is converted to uintptr_t, so negative numbers because large
+        # positive numbers.
+        self.expect_var_path("(void*)-1 == -1", value="true")
+        self.expect_var_path("(void*)1 > -1", value="false")
+
+        self.expect(
+            "frame var -- '(void*)0 > nullptr'",
+            error=True,
+            substrs=[
+                "invalid operands to binary expression ('void *' and 
'std::nullptr_t')"
+            ],
+        )
+
+        self.expect(
+            "frame var -- 'nullptr > 0'",
+            error=True,
+            substrs=[
+                "invalid operands to binary expression ('std::nullptr_t' and 
'int')"
+            ],
+        )
+
+        self.expect(
+            "frame var -- '1 == nullptr'",
+            error=True,
+            substrs=[
+                "invalid operands to binary expression ('int' and 
'std::nullptr_t')"
+            ],
+        )
+
+        self.expect(
+            "frame var -- 'nullptr == (int)0'",
+            error=True,
+            substrs=[
+                "invalid operands to binary expression ('std::nullptr_t' and 
'int')"
+            ],
+        )
+
+        self.expect(
+            "frame var -- 'false == nullptr'",
+            error=True,
+            substrs=[
+                "invalid operands to binary expression ('bool' and 
'std::nullptr_t')"
+            ],
+        )
+
+        self.expect(
+            "frame var -- 'p_int0 > p_char1'",
+            error=True,
+            substrs=[
+                "comparison of distinct pointer types ('int *' and 'const char 
*')"
+            ],
+        )
+
+        self.expect(
+            "frame var -- 'pp_void0 == p_char1'",
+            error=True,
+            substrs=[
+                "comparison of distinct pointer types ('void **' and 'const 
char *')"
+            ],
+        )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Comparison/main.cpp 
b/lldb/test/API/commands/frame/var-dil/expr/Comparison/main.cpp
new file mode 100644
index 0000000000000..982df688a88ca
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Comparison/main.cpp
@@ -0,0 +1,34 @@
+#include <cstdint>
+#include <cstddef>
+
+enum class ScopedEnum { kZeroS, kOneS };
+enum class ScopedEnumInt8 : int8_t { kZeroS8, kOneS8 };
+
+void stop() {}
+
+int main(int argc, char **argv) {
+  auto enum_one = ScopedEnum::kOneS;
+  auto enum_one_8 = ScopedEnumInt8::kOneS8;
+
+  bool trueVar = true;
+
+  int i = 1;
+  int j = 2;
+  int &iref = i;
+  int array[2] = {1, 2};
+
+  struct S {
+  } s;
+
+  int *p_int0 = &array[0];
+  int *p_int1 = &array[1];
+  const char *p_char1 = "hello";
+  void *p_void = (void *)p_char1;
+  void **pp_void0 = &p_void;
+  void **pp_void1 = pp_void0 + 1;
+
+  std::nullptr_t std_nullptr_t = nullptr;
+
+  stop(); // Set a breakpoint here
+  return 0;
+}

>From 0a373725f76060ee9c2b05fb2bdb3cc144f3beae Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <[email protected]>
Date: Tue, 14 Jul 2026 21:05:25 +0500
Subject: [PATCH 2/3] Fix formatting

---
 lldb/test/API/commands/frame/var-dil/expr/Comparison/main.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/test/API/commands/frame/var-dil/expr/Comparison/main.cpp 
b/lldb/test/API/commands/frame/var-dil/expr/Comparison/main.cpp
index 982df688a88ca..4ff21bb8f982f 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Comparison/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Comparison/main.cpp
@@ -1,5 +1,5 @@
-#include <cstdint>
 #include <cstddef>
+#include <cstdint>
 
 enum class ScopedEnum { kZeroS, kOneS };
 enum class ScopedEnumInt8 : int8_t { kZeroS8, kOneS8 };

>From e3a2d8443528d2de640eb46e5b880940a0daafff Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <[email protected]>
Date: Wed, 15 Jul 2026 19:25:33 +0500
Subject: [PATCH 3/3] Fix asserts

---
 lldb/source/ValueObject/DILParser.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lldb/source/ValueObject/DILParser.cpp 
b/lldb/source/ValueObject/DILParser.cpp
index a6afff57734e8..d193e7d7a5f1f 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -174,7 +174,7 @@ ASTNodeUP DILParser::ParseEqualityExpression() {
     Token token = CurToken();
     m_dil_lexer.Advance();
     auto rhs = ParseRelationalExpression();
-    assert(lhs && "ASTNodeUP must not contain a nullptr");
+    assert(rhs && "ASTNodeUP must not contain a nullptr");
     lhs = std::make_unique<BinaryOpNode>(
         token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()),
         std::move(lhs), std::move(rhs));
@@ -200,7 +200,7 @@ ASTNodeUP DILParser::ParseRelationalExpression() {
     Token token = CurToken();
     m_dil_lexer.Advance();
     auto rhs = ParseShiftExpression();
-    assert(lhs && "ASTNodeUP must not contain a nullptr");
+    assert(rhs && "ASTNodeUP must not contain a nullptr");
     lhs = std::make_unique<BinaryOpNode>(
         token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()),
         std::move(lhs), std::move(rhs));

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to