[Lldb-commits] [lldb] [lldb] Add logical operators to DIL (PR #209742)
Ilia Kuklin via lldb-commits
lldb-commits at lists.llvm.org
Thu Aug 13 07:24:52 PDT 2026
https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/209742
>From 0ac9d2a3e4a53144a6cdef7e8093a49e5e3fa2d8 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 14 Jul 2026 22:38:56 +0500
Subject: [PATCH 1/5] [lldb] Add logical operators to DIL
---
lldb/docs/dil-expr-lang.ebnf | 10 ++-
lldb/include/lldb/ValueObject/DILAST.h | 3 +
lldb/include/lldb/ValueObject/DILEval.h | 1 +
lldb/include/lldb/ValueObject/DILLexer.h | 3 +
lldb/include/lldb/ValueObject/DILParser.h | 2 +
lldb/source/ValueObject/DILAST.cpp | 4 +
lldb/source/ValueObject/DILEval.cpp | 82 +++++++++++++++++++
lldb/source/ValueObject/DILLexer.cpp | 39 ++++-----
lldb/source/ValueObject/DILParser.cpp | 58 +++++++++++--
.../frame/var-dil/expr/Logical/Makefile | 3 +
.../expr/Logical/TestFrameVarDILLogical.py | 78 ++++++++++++++++++
.../frame/var-dil/expr/Logical/main.cpp | 20 +++++
12 files changed, 274 insertions(+), 29 deletions(-)
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Logical/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Logical/main.cpp
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 9c610107da98a..23d4f1894172b 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -5,13 +5,17 @@
expression = assignment_expression ;
-assignment_expression = inclusive_or_expression
- | inclusive_or_expression assignment_operator assignment_expression ;
+assignment_expression = logical_or_expression
+ | logical_or_expression assignment_operator assignment_expression ;
assignment_operator = "="
| "+="
| "-=" ;
+logical_or_expression = logical_and_expression {"||" logical_and_expression} ;
+
+logical_and_expression = inclusive_or_expression {"&&" inclusive_or_expression} ;
+
inclusive_or_expression = exclusive_or_expression {"|" exclusive_or_expression} ;
exclusive_or_expression = and_expression {"^" and_expression} ;
@@ -34,7 +38,7 @@ cast_expression = unary_expression
unary_expression = postfix_expression
| unary_operator cast_expression ;
-unary_operator = "*" | "&" | "+" | "-" | "~";
+unary_operator = "*" | "&" | "+" | "-" | "~" | "!" ;
postfix_expression = primary_expression
| postfix_expression "[" expression "]"
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index b48fa33e5529c..874c93b761242 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -40,6 +40,7 @@ enum class UnaryOpKind {
Minus, ///< "-"
Plus, ///< "+"
Not, ///< "~"
+ LNot, ///< "!"
};
/// The binary operators recognized by DIL.
@@ -57,6 +58,8 @@ enum class BinaryOpKind {
Shr, ///< ">>"
Sub, ///< "-"
SubAssign, ///< "-="
+ LAnd, ///< "&&"
+ LOr, ///< "||"
};
/// Translates DIL tokens to BinaryOpKind.
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 489dc801b20db..97fe72e448d65 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -130,6 +130,7 @@ class Interpreter : Visitor {
llvm::Expected<lldb::ValueObjectSP>
EvaluateBinaryBitwise(BinaryOpKind kind, lldb::ValueObjectSP lhs,
lldb::ValueObjectSP rhs, uint32_t location);
+ llvm::Expected<lldb::ValueObjectSP> EvaluateLogical(const BinaryOpNode &node);
llvm::Expected<CompilerType>
PickIntegerType(lldb::TypeSystemSP type_system, ExecutionContextScope &ctx,
const IntegerLiteralNode &literal);
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index 8b58bbcc84547..aef26908d2d0e 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -26,12 +26,14 @@ class Token {
public:
enum Kind {
amp,
+ ampamp,
arrow,
caret,
colon,
coloncolon,
eof,
equal,
+ exclaim,
float_constant,
greatergreater,
identifier,
@@ -46,6 +48,7 @@ class Token {
percent,
period,
pipe,
+ pipepipe,
plus,
plusequal,
r_paren,
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 3e58620875a6a..6c5d5ee8fc081 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 ParseLogicalOrExpression();
+ ASTNodeUP ParseLogicalAndExpression();
ASTNodeUP ParseInclusiveOrExpression();
ASTNodeUP ParseExclusiveOrExpression();
ASTNodeUP ParseAndExpression();
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index 275564916a840..d0f89601385f2 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -39,6 +39,10 @@ BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind) {
return BinaryOpKind::Shl;
case Token::greatergreater:
return BinaryOpKind::Shr;
+ case Token::ampamp:
+ return BinaryOpKind::LAnd;
+ case Token::pipepipe:
+ return BinaryOpKind::LOr;
default:
break;
}
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 0ef4e244410a4..b3db7a25e6e5b 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -606,6 +606,26 @@ Interpreter::Visit(const UnaryOpNode &node) {
if (flipped)
return ValueObject::CreateValueObjectFromScalar(
m_stack_frame, scalar, operand->GetCompilerType(), "result");
+ break;
+ }
+ case UnaryOpKind::LNot: {
+ CompilerType operand_type = operand->GetCompilerType();
+ if (!operand_type.IsContextuallyConvertibleToBool()) {
+ std::string errMsg =
+ llvm::formatv("invalid argument type '{0}' to unary expression",
+ operand_type.GetTypeName());
+ return llvm::make_error<DILDiagnosticError>(m_expr, errMsg,
+ node.GetLocation());
+ }
+ llvm::Expected<lldb::TypeSystemSP> type_system =
+ GetTypeSystemFromCU(m_stack_frame);
+ if (!type_system)
+ return type_system.takeError();
+ auto value_or_err = operand->GetValueAsBool();
+ if (!value_or_err)
+ return value_or_err.takeError();
+ return ValueObject::CreateValueObjectFromBool(m_stack_frame, *type_system,
+ !(*value_or_err), "result");
}
}
return llvm::make_error<DILDiagnosticError>(m_expr, "invalid unary operation",
@@ -1007,8 +1027,68 @@ Interpreter::EvaluateBinaryShift(BinaryOpKind kind, lldb::ValueObjectSP lhs,
return EvaluateScalarOp(kind, lhs, rhs, lhs_type, location);
}
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::EvaluateLogical(const BinaryOpNode &node) {
+ // Operations {'&&', '||'} work for:
+ // {IsContextuallyConvertibleToBool} <-> {IsContextuallyConvertibleToBool}
+ // Note: Unlike C++, these operators will not evaluate or check the type
+ // of RHS if the result is determined after evaluating LHS.
+ auto lhs_or_err = EvaluateAndDereference(node.GetLHS());
+ if (!lhs_or_err)
+ return lhs_or_err;
+ lldb::ValueObjectSP lhs = *lhs_or_err;
+ auto lhs_type = lhs->GetCompilerType();
+ if (!lhs_type.IsContextuallyConvertibleToBool()) {
+ std::string errMsg = llvm::formatv(
+ "value of type {0} is not contextually convertible to 'bool'",
+ lhs_type.TypeDescription());
+ return llvm::make_error<DILDiagnosticError>(m_expr, errMsg,
+ node.GetLocation());
+ }
+ llvm::Expected<lldb::TypeSystemSP> type_system =
+ GetTypeSystemFromCU(m_stack_frame);
+ if (!type_system)
+ return type_system.takeError();
+
+ // For "&&", exit early if LHS is "false"
+ // For "||", exit early if LHS is "true".
+ auto lvalue_or_err = lhs->GetValueAsBool();
+ if (!lvalue_or_err)
+ return lvalue_or_err.takeError();
+ bool lhs_val = *lvalue_or_err;
+ bool exit_early = node.GetKind() == BinaryOpKind::LAnd ? !lhs_val : lhs_val;
+ if (exit_early)
+ return ValueObject::CreateValueObjectFromBool(m_stack_frame, *type_system,
+ lhs_val, "result");
+
+ // If the result is to be determined, evaluate the RHS.
+ auto rhs_or_err = EvaluateAndDereference(node.GetRHS());
+ if (!rhs_or_err)
+ return rhs_or_err;
+ lldb::ValueObjectSP rhs = *rhs_or_err;
+ auto rhs_type = rhs->GetCompilerType();
+ if (!rhs_type.IsContextuallyConvertibleToBool()) {
+ std::string errMsg = llvm::formatv(
+ "value of type {0} is not contextually convertible to 'bool'",
+ rhs_type.TypeDescription());
+ return llvm::make_error<DILDiagnosticError>(m_expr, errMsg,
+ node.GetLocation());
+ }
+
+ auto rvalue_or_err = rhs->GetValueAsBool();
+ if (!rvalue_or_err)
+ return rvalue_or_err.takeError();
+ return ValueObject::CreateValueObjectFromBool(m_stack_frame, *type_system,
+ *rvalue_or_err, "result");
+}
+
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const BinaryOpNode &node) {
+ // Handle logical operators separately. They may or may not evaluate RHS.
+ if (node.GetKind() == BinaryOpKind::LAnd ||
+ node.GetKind() == BinaryOpKind::LOr)
+ return EvaluateLogical(node);
+
auto lhs_or_err = EvaluateAndDereference(node.GetLHS());
if (!lhs_or_err)
return lhs_or_err;
@@ -1060,6 +1140,8 @@ Interpreter::Visit(const BinaryOpNode &node) {
case BinaryOpKind::Shl:
case BinaryOpKind::Shr:
return EvaluateBinaryShift(node.GetKind(), lhs, rhs, node.GetLocation());
+ default:
+ break;
}
return llvm::make_error<DILDiagnosticError>(
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index 813ab3bbcab3e..8e9809e4ef66f 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -22,6 +22,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
switch (kind) {
case Kind::amp:
return "amp";
+ case Kind::ampamp:
+ return "ampamp";
case Kind::arrow:
return "arrow";
case Kind::caret:
@@ -32,6 +34,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
return "coloncolon";
case Kind::equal:
return "equal";
+ case Kind::exclaim:
+ return "exclaim";
case Kind::eof:
return "eof";
case Kind::float_constant:
@@ -62,6 +66,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
return "period";
case Kind::pipe:
return "pipe";
+ case Kind::pipepipe:
+ return "pipepipe";
case Kind::plus:
return "plus";
case Kind::plusequal:
@@ -204,27 +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::caret, "^"},
- {Token::colon, ":"},
- {Token::equal, "="},
- {Token::l_paren, "("},
- {Token::l_square, "["},
- {Token::minus, "-"},
- {Token::percent, "%"},
- {Token::period, "."},
- {Token::pipe, "|"},
- {Token::plus, "+"},
- {Token::r_paren, ")"},
- {Token::r_square, "]"},
- {Token::slash, "/"},
- {Token::star, "*"},
+ {Token::ampamp, "&&"}, {Token::arrow, "->"},
+ {Token::coloncolon, "::"}, {Token::greatergreater, ">>"},
+ {Token::lessless, "<<"}, {Token::minusequal, "-="},
+ {Token::pipepipe, "||"}, {Token::plusequal, "+="},
+ {Token::amp, "&"}, {Token::caret, "^"},
+ {Token::colon, ":"}, {Token::equal, "="},
+ {Token::exclaim, "!"}, {Token::l_paren, "("},
+ {Token::l_square, "["}, {Token::minus, "-"},
+ {Token::percent, "%"}, {Token::period, "."},
+ {Token::pipe, "|"}, {Token::plus, "+"},
+ {Token::r_paren, ")"}, {Token::r_square, "]"},
+ {Token::slash, "/"}, {Token::star, "*"},
{Token::tilde, "~"},
};
for (auto [kind, str] : operators) {
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 389ce870539e4..dd12906534973 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -134,8 +134,8 @@ ASTNodeUP DILParser::ParseExpression() { return ParseAssignmentExpression(); }
// Parse an assignment_expression
//
// assignment_expression
-// inclusive_or_expression
-// inclusive_or_expression assignment_operator assignment_expression
+// logical_or_expression
+// logical_or_expression assignment_operator assignment_expression
//
// assignment_operator:
// "="
@@ -143,7 +143,7 @@ ASTNodeUP DILParser::ParseExpression() { return ParseAssignmentExpression(); }
// "-="
//
ASTNodeUP DILParser::ParseAssignmentExpression() {
- auto lhs = ParseInclusiveOrExpression();
+ auto lhs = ParseLogicalOrExpression();
assert(lhs && "ASTNodeUP must not contain a nullptr");
// Check if it's an assignment expression.
@@ -160,6 +160,50 @@ ASTNodeUP DILParser::ParseAssignmentExpression() {
return lhs;
}
+// Parse a logical_or_expression.
+//
+// logical_or_expression:
+// logical_and_expression {"||" logical_and_expression}
+//
+ASTNodeUP DILParser::ParseLogicalOrExpression() {
+ auto lhs = ParseLogicalAndExpression();
+ assert(lhs && "ASTNodeUP must not contain a nullptr");
+
+ while (CurToken().Is(Token::pipepipe)) {
+ Token token = CurToken();
+ m_dil_lexer.Advance();
+ auto rhs = ParseLogicalAndExpression();
+ 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 logical_and_expression.
+//
+// logical_and_expression:
+// inclusive_or_expression {"&&" inclusive_or_expression}
+//
+ASTNodeUP DILParser::ParseLogicalAndExpression() {
+ auto lhs = ParseInclusiveOrExpression();
+ assert(lhs && "ASTNodeUP must not contain a nullptr");
+
+ while (CurToken().Is(Token::ampamp)) {
+ Token token = CurToken();
+ m_dil_lexer.Advance();
+ auto rhs = ParseInclusiveOrExpression();
+ 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 an inclusive_or_expression.
//
// inclusive_or_expression:
@@ -365,10 +409,11 @@ ASTNodeUP DILParser::ParseCastExpression() {
// "+"
// "-"
// "~"
+// "!"
//
ASTNodeUP DILParser::ParseUnaryExpression() {
- if (CurToken().IsOneOf(
- {Token::amp, Token::star, Token::minus, Token::plus, Token::tilde})) {
+ if (CurToken().IsOneOf({Token::amp, Token::star, Token::minus, Token::plus,
+ Token::tilde, Token::exclaim})) {
Token token = CurToken();
uint32_t loc = token.GetLocation();
m_dil_lexer.Advance();
@@ -390,6 +435,9 @@ ASTNodeUP DILParser::ParseUnaryExpression() {
case Token::tilde:
return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Not,
std::move(rhs));
+ case Token::exclaim:
+ return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::LNot,
+ std::move(rhs));
default:
llvm_unreachable("invalid token kind");
}
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Logical/Makefile b/lldb/test/API/commands/frame/var-dil/expr/Logical/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Logical/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py b/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py
new file mode 100644
index 0000000000000..452973092ffff
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py
@@ -0,0 +1,78 @@
+"""
+Test DIL logical operators.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarLogical(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_logical(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")
+
+ self.expect_var_path("1 && 2", value="true")
+ self.expect_var_path("0 && 1", value="false")
+ self.expect_var_path("0 || 1", value="true")
+ self.expect_var_path("0 || 0", value="false")
+
+ self.expect_var_path("!1", value="false")
+ self.expect_var_path("!!1", value="true")
+
+ self.expect_var_path("!trueVar", value="false")
+ self.expect_var_path("!!trueVar", value="true")
+ self.expect_var_path("!falseVar", value="true")
+ self.expect_var_path("!!falseVar", value="false")
+
+ self.expect_var_path("trueVar || true", value="true")
+ self.expect_var_path("trueVar && false", value="false")
+ self.expect_var_path("falseVar || true", value="true")
+ self.expect_var_path("falseVar && true", value="false")
+ self.expect_var_path("true || false && false", value="true")
+ self.expect_var_path("(true || false) && false", value="false")
+
+ self.expect_var_path("!p_ptr", value="false")
+ self.expect_var_path("!!p_ptr", value="true")
+ self.expect_var_path("p_ptr && true", value="true")
+ self.expect_var_path("p_ptr && false", value="false")
+ self.expect_var_path("!p_nullptr", value="true")
+ self.expect_var_path("!!p_nullptr", value="false")
+ self.expect_var_path("p_nullptr || true", value="true")
+ self.expect_var_path("p_nullptr || false", value="false")
+
+ self.expect_var_path("!array", value="false")
+ self.expect_var_path("!!array", value="true")
+ self.expect_var_path("array || true", value="true")
+ self.expect_var_path("false || array", value="true")
+ self.expect_var_path("array && true", value="true")
+ self.expect_var_path("array && false", value="false")
+
+ # DIL doesn't evaluate the right operand if the left one
+ # determines the result
+ self.expect_var_path("true || __doesnt_exist", value="true")
+ self.expect_var_path("false && __doesnt_exist", value="false")
+
+ # Check errors
+ self.expect(
+ "frame var -- 'false || !s'",
+ error=True,
+ substrs=["invalid argument type 'S' to unary expression"],
+ )
+ self.expect(
+ "frame var -- 's || false'",
+ error=True,
+ substrs=["value of type 'S' is not contextually convertible to 'bool'"],
+ )
+ self.expect(
+ "frame var -- 'true && s'",
+ error=True,
+ substrs=["value of type 'S' is not contextually convertible to 'bool'"],
+ )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Logical/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Logical/main.cpp
new file mode 100644
index 0000000000000..3258016157cac
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Logical/main.cpp
@@ -0,0 +1,20 @@
+#include <cstdint>
+#include <limits>
+
+void stop() {}
+
+int main(int argc, char **argv) {
+ bool trueVar = true;
+ bool falseVar = false;
+
+ const char *p_ptr = "str";
+ const char *p_nullptr = nullptr;
+
+ int array[2] = {1, 2};
+
+ struct S {
+ } s;
+
+ stop(); // Set a breakpoint here
+ return 0;
+}
>From 9351f577a057ee4f1b319ab45de02844735b3e47 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 15 Jul 2026 19:23:30 +0500
Subject: [PATCH 2/5] 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 dd12906534973..f819ae11d599e 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -173,7 +173,7 @@ ASTNodeUP DILParser::ParseLogicalOrExpression() {
Token token = CurToken();
m_dil_lexer.Advance();
auto rhs = ParseLogicalAndExpression();
- 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));
@@ -195,7 +195,7 @@ ASTNodeUP DILParser::ParseLogicalAndExpression() {
Token token = CurToken();
m_dil_lexer.Advance();
auto rhs = ParseInclusiveOrExpression();
- 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));
>From 53a24e095d030000d1d137acc5b13f8916b235eb Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Mon, 20 Jul 2026 18:17:41 +0500
Subject: [PATCH 3/5] Deferefence the reference type value
---
lldb/source/ValueObject/DILEval.cpp | 5 +++++
.../frame/var-dil/expr/Logical/TestFrameVarDILLogical.py | 2 ++
lldb/test/API/commands/frame/var-dil/expr/Logical/main.cpp | 1 +
3 files changed, 8 insertions(+)
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index b3db7a25e6e5b..89d554b59fb38 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -609,6 +609,11 @@ Interpreter::Visit(const UnaryOpNode &node) {
break;
}
case UnaryOpKind::LNot: {
+ if (operand->GetCompilerType().IsReferenceType()) {
+ operand = operand->Dereference(error);
+ if (error.Fail())
+ return error.ToError();
+ }
CompilerType operand_type = operand->GetCompilerType();
if (!operand_type.IsContextuallyConvertibleToBool()) {
std::string errMsg =
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py b/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py
index 452973092ffff..1071ef55b01ee 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py
@@ -31,6 +31,8 @@ def test_logical(self):
self.expect_var_path("!!trueVar", value="true")
self.expect_var_path("!falseVar", value="true")
self.expect_var_path("!!falseVar", value="false")
+ self.expect_var_path("!trueRef", value="false")
+ self.expect_var_path("!!trueRef", value="true")
self.expect_var_path("trueVar || true", value="true")
self.expect_var_path("trueVar && false", value="false")
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Logical/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Logical/main.cpp
index 3258016157cac..851c7538a2acf 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Logical/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Logical/main.cpp
@@ -6,6 +6,7 @@ void stop() {}
int main(int argc, char **argv) {
bool trueVar = true;
bool falseVar = false;
+ bool &trueRef = trueVar;
const char *p_ptr = "str";
const char *p_nullptr = nullptr;
>From 3c16bf56ed7fbab9744bcfc72ffb8c99385ce42b Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 22 Jul 2026 22:38:07 +0500
Subject: [PATCH 4/5] Fix converting nullptr to bool
---
lldb/source/ValueObject/ValueObject.cpp | 2 ++
.../frame/var-dil/expr/Logical/TestFrameVarDILLogical.py | 2 ++
2 files changed, 4 insertions(+)
diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp
index b36a020d262c4..7c681327d9268 100644
--- a/lldb/source/ValueObject/ValueObject.cpp
+++ b/lldb/source/ValueObject/ValueObject.cpp
@@ -1228,6 +1228,8 @@ llvm::Expected<bool> ValueObject::GetValueAsBool() {
}
if (val_type.IsArrayType())
return GetAddressOf().address != 0;
+ if (val_type.IsNullPtrType())
+ return false;
return llvm::createStringError("type cannot be converted to bool");
}
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py b/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py
index 1071ef55b01ee..69ddd03fd1082 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Logical/TestFrameVarDILLogical.py
@@ -49,6 +49,8 @@ def test_logical(self):
self.expect_var_path("!!p_nullptr", value="false")
self.expect_var_path("p_nullptr || true", value="true")
self.expect_var_path("p_nullptr || false", value="false")
+ self.expect_var_path("nullptr || true", value="true")
+ self.expect_var_path("nullptr || false", value="false")
self.expect_var_path("!array", value="false")
self.expect_var_path("!!array", value="true")
>From e8cccbd542036f5743e6571e71c7c1a376a9d1a1 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 13 Aug 2026 19:19:31 +0500
Subject: [PATCH 5/5] Remove C++ mention from comments
---
lldb/source/ValueObject/DILEval.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 89d554b59fb38..a06a68272b047 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -1036,8 +1036,8 @@ llvm::Expected<lldb::ValueObjectSP>
Interpreter::EvaluateLogical(const BinaryOpNode &node) {
// Operations {'&&', '||'} work for:
// {IsContextuallyConvertibleToBool} <-> {IsContextuallyConvertibleToBool}
- // Note: Unlike C++, these operators will not evaluate or check the type
- // of RHS if the result is determined after evaluating LHS.
+ // Note: These operators will not evaluate or check the type of RHS
+ // if the result is determined after evaluating LHS.
auto lhs_or_err = EvaluateAndDereference(node.GetLHS());
if (!lhs_or_err)
return lhs_or_err;
More information about the lldb-commits
mailing list