[Lldb-commits] [lldb] [LLDB] Add assignment to DIL. (PR #190223)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Apr 9 10:05:15 PDT 2026
https://github.com/cmtice updated https://github.com/llvm/llvm-project/pull/190223
>From d95b11406396f536874af7a1f9b06c8c3be2df0e Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 2 Apr 2026 10:44:51 -0700
Subject: [PATCH 1/6] [LLDB] Add assignment to DIL.
Add the ability for DIL to recognize and process assignment, updating
program variables. Recognizes '=', '+=' and '-=' operators. Increment and
decrement ('++' and '--') will be added in a separate (future) PR.
"*=" and "/=" need to wait until DIL handles multiply and divide
operators.
---
lldb/docs/dil-expr-lang.ebnf | 11 +-
lldb/include/lldb/ValueObject/DILAST.h | 9 +-
lldb/include/lldb/ValueObject/DILEval.h | 19 ++-
lldb/include/lldb/ValueObject/DILLexer.h | 3 +
lldb/include/lldb/ValueObject/DILParser.h | 1 +
lldb/source/ValueObject/DILAST.cpp | 10 +-
lldb/source/ValueObject/DILEval.cpp | 62 ++++++++++
lldb/source/ValueObject/DILLexer.cpp | 21 +++-
lldb/source/ValueObject/DILParser.cpp | 34 +++++-
lldb/source/ValueObject/ValueObject.cpp | 24 ++--
.../frame/var-dil/expr/Assignment/Makefile | 3 +
.../Assignment/TestFrameVarDILAddAssign.py | 31 +++++
.../expr/Assignment/TestFrameVarDILAssign.py | 110 ++++++++++++++++++
.../Assignment/TestFrameVarDILSubAssign.py | 31 +++++
.../frame/var-dil/expr/Assignment/main.cpp | 17 +++
15 files changed, 362 insertions(+), 24 deletions(-)
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Assignment/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILSubAssign.py
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index cdf85d75fd8e2..a1f1e5adff672 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -3,10 +3,17 @@
(* This is currently a subset of the final DIL Language, matching the current
DIL implementation. *)
-expression = additive_expression ;
+expression = assignment_expression;
+
+assignment_expression = additive_expression
+ | additive_expression assignment_operator assignment_expression ;
+
+assignment_operator = "="
+ | "+="
+ | "-=" ;
additive_expression = cast_expression {"+" cast_expression}
- cast_expression {"-" cast_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 350722fa6f22d..a50f0e6829527 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -42,14 +42,17 @@ enum class UnaryOpKind {
/// The binary operators recognized by DIL.
enum class BinaryOpKind {
- Add, // "+"
- Sub, // "-"
+ Add, // "+"
+ AddAssign, // "+="
+ Assign, // "="
+ Sub, // "-"
+ SubAssign, // "-="
};
/// Translates DIL tokens to BinaryOpKind.
BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind);
-/// The type casts allowed by DIL.
+//// The type casts allowed by DIL.
enum class CastKind {
eArithmetic, ///< Casting to a scalar.
eEnumeration, ///< Casting from a scalar to an enumeration type
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 323ac5eace2ba..c43ac87b2b0e9 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -88,6 +88,7 @@ class Interpreter : Visitor {
llvm::Expected<CompilerType> ArithmeticConversion(lldb::ValueObjectSP &lhs,
lldb::ValueObjectSP &rhs,
uint32_t location);
+
/// Add or subtract the offset to the pointer according to the pointee type
/// byte size.
/// \returns A new `ValueObject` with a new pointer value.
@@ -95,6 +96,7 @@ class Interpreter : Visitor {
lldb::ValueObjectSP offset,
BinaryOpKind operation,
uint32_t location);
+
llvm::Expected<lldb::ValueObjectSP> EvaluateScalarOp(BinaryOpKind kind,
lldb::ValueObjectSP lhs,
lldb::ValueObjectSP rhs,
@@ -103,9 +105,23 @@ class Interpreter : Visitor {
llvm::Expected<lldb::ValueObjectSP>
EvaluateBinaryAddition(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
uint32_t location);
+
llvm::Expected<lldb::ValueObjectSP>
EvaluateBinarySubtraction(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
uint32_t location);
+
+ llvm::Expected<lldb::ValueObjectSP>
+ EvaluateAssignment(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
+ uint32_t location);
+
+ llvm::Expected<lldb::ValueObjectSP>
+ EvaluateBinaryAddAssign(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
+ uint32_t location);
+
+ llvm::Expected<lldb::ValueObjectSP>
+ EvaluateBinarySubAssign(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
+ uint32_t location);
+
llvm::Expected<CompilerType>
PickIntegerType(lldb::TypeSystemSP type_system,
std::shared_ptr<ExecutionContextScope> ctx,
@@ -136,8 +152,7 @@ class Interpreter : Visitor {
bool m_use_synthetic;
bool m_fragile_ivar;
bool m_check_ptr_vs_member;
- // TODO: Remove 'maybe_unused' when next PR, using this, gets submitted.
- [[maybe_unused]] bool m_allow_var_updates;
+ bool m_allow_var_updates;
};
} // namespace lldb_private::dil
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index a927aa236377e..97ebbd713673f 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -30,6 +30,7 @@ class Token {
colon,
coloncolon,
eof,
+ equal,
float_constant,
identifier,
integer_constant,
@@ -38,8 +39,10 @@ class Token {
l_paren,
l_square,
minus,
+ minusequal,
period,
plus,
+ plusequal,
r_paren,
r_square,
star,
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 4afedb735af86..edafbe7b2c181 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -93,6 +93,7 @@ class DILParser {
ASTNodeUP Run();
ASTNodeUP ParseExpression();
+ ASTNodeUP ParseAssignmentExpression();
ASTNodeUP ParseAdditiveExpression();
ASTNodeUP ParseUnaryExpression();
ASTNodeUP ParsePostfixExpression();
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index dadd4cd365c7e..121430036eefc 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -13,10 +13,16 @@ namespace lldb_private::dil {
BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind) {
switch (token_kind) {
- case Token::plus:
- return BinaryOpKind::Add;
+ case Token::equal:
+ return BinaryOpKind::Assign;
case Token::minus:
return BinaryOpKind::Sub;
+ case Token::minusequal:
+ return BinaryOpKind::SubAssign;
+ case Token::plus:
+ return BinaryOpKind::Add;
+ case Token::plusequal:
+ return BinaryOpKind::AddAssign;
default:
break;
}
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 80b31be8e7c23..479be4e09feff 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -609,6 +609,8 @@ Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs,
return value_object(l + r);
case BinaryOpKind::Sub:
return value_object(l - r);
+ default:
+ break;
}
return llvm::make_error<DILDiagnosticError>(
m_expr, "invalid arithmetic operation", location);
@@ -729,6 +731,60 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
location);
}
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::EvaluateAssignment(lldb::ValueObjectSP lhs,
+ lldb::ValueObjectSP rhs, uint32_t location) {
+
+ Status status;
+ lhs->SetValueFromInteger(rhs, status, m_allow_var_updates);
+ if (status.Success())
+ return lhs;
+
+ std::string errMsg = std::string(status.AsCString());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+ location);
+}
+
+llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddAssign(
+ lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) {
+ lldb::ValueObjectSP ret;
+
+ auto ret_or_err = EvaluateBinaryAddition(lhs, rhs, location);
+ if (!ret_or_err)
+ return ret_or_err;
+
+ ret = *ret_or_err;
+
+ Status status;
+ lhs->SetValueFromInteger(ret, status, m_allow_var_updates);
+ if (status.Success())
+ return lhs;
+
+ std::string errMsg = std::string(status.AsCString());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+ location);
+}
+
+llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubAssign(
+ lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) {
+ lldb::ValueObjectSP ret;
+
+ auto ret_or_err = EvaluateBinarySubtraction(lhs, rhs, location);
+ if (!ret_or_err)
+ return ret_or_err;
+
+ ret = *ret_or_err;
+
+ Status status;
+ lhs->SetValueFromInteger(ret, status, m_allow_var_updates);
+ if (status.Success())
+ return lhs;
+
+ std::string errMsg = std::string(status.AsCString());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+ location);
+}
+
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const BinaryOpNode &node) {
auto lhs_or_err = EvaluateAndDereference(node.GetLHS());
@@ -753,8 +809,14 @@ Interpreter::Visit(const BinaryOpNode &node) {
switch (node.GetKind()) {
case BinaryOpKind::Add:
return EvaluateBinaryAddition(lhs, rhs, node.GetLocation());
+ case BinaryOpKind::AddAssign:
+ return EvaluateBinaryAddAssign(lhs, rhs, node.GetLocation());
+ case BinaryOpKind::Assign:
+ return EvaluateAssignment(lhs, rhs, node.GetLocation());
case BinaryOpKind::Sub:
return EvaluateBinarySubtraction(lhs, rhs, node.GetLocation());
+ case BinaryOpKind::SubAssign:
+ return EvaluateBinarySubAssign(lhs, rhs, node.GetLocation());
}
return llvm::make_error<DILDiagnosticError>(
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index c1ad354502438..d92186ea26a12 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -28,6 +28,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
return "colon";
case Kind::coloncolon:
return "coloncolon";
+ case Kind::equal:
+ return "equal";
case Kind::eof:
return "eof";
case Kind::float_constant:
@@ -46,10 +48,14 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
return "l_square";
case Kind::minus:
return "minus";
+ case Kind::minusequal:
+ return "minusequal";
case Kind::period:
return "period";
case Kind::plus:
return "plus";
+ case Kind::plusequal:
+ return "plusequal";
case Kind::r_paren:
return "r_paren";
case Kind::r_square:
@@ -180,11 +186,18 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
return Token(kind, word.str(), position);
}
+ // IMPORTANT: If two or more tokens share the same prefix, the tokens need to
+ // 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::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"},
- {Token::colon, ":"}, {Token::l_paren, "("}, {Token::l_square, "["},
- {Token::minus, "-"}, {Token::period, "."}, {Token::plus, "+"},
- {Token::r_paren, ")"}, {Token::r_square, "]"}, {Token::star, "*"},
+ {Token::amp, "&"}, {Token::arrow, "->"},
+ {Token::coloncolon, "::"}, {Token::colon, ":"},
+ {Token::equal, "="}, {Token::l_paren, "("},
+ {Token::l_square, "["}, {Token::minusequal, "-="},
+ {Token::minus, "-"}, {Token::period, "."},
+ {Token::plusequal, "+="}, {Token::plus, "+"},
+ {Token::r_paren, ")"}, {Token::r_square, "]"},
+ {Token::star, "*"},
};
for (auto [kind, str] : operators) {
if (remainder.consume_front(str))
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 919acd4645f71..7a162277baef0 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -89,7 +89,6 @@ CompilerType ResolveTypeByName(const std::string &name,
llvm::Expected<ASTNodeUP> DILParser::Parse(llvm::StringRef dil_input_expr,
DILLexer lexer,
std::shared_ptr<StackFrame> frame_sp,
-
lldb::DynamicValueType use_dynamic,
uint32_t options) {
const bool check_ptr_vs_member =
@@ -103,7 +102,6 @@ llvm::Expected<ASTNodeUP> DILParser::Parse(llvm::StringRef dil_input_expr,
DILParser parser(dil_input_expr, lexer, frame_sp, use_dynamic,
!no_synth_child, !no_fragile_ivar, check_ptr_vs_member,
error);
-
ASTNodeUP node_up = parser.Run();
assert(node_up && "ASTNodeUP must not contain a nullptr");
@@ -134,9 +132,37 @@ ASTNodeUP DILParser::Run() {
// Parse an expression.
//
// expression:
-// cast_expression
+// assignment_expression
+//
+ASTNodeUP DILParser::ParseExpression() { return ParseAssignmentExpression(); }
+
+// Parse an assignment_expression
//
-ASTNodeUP DILParser::ParseExpression() { return ParseAdditiveExpression(); }
+// assignment_expression
+// additive_expression
+// additive_expression assignment_operator assignment_expression
+//
+// assignment_operator:
+// "="
+// "+="
+// "-="
+//
+ASTNodeUP DILParser::ParseAssignmentExpression() {
+ auto lhs = ParseAdditiveExpression();
+ assert(lhs && "ASTNodeUP must not contain a nullptr");
+
+ // Check if it's an assignment expression.
+ if (CurToken().IsOneOf({Token::equal, Token::plusequal, Token::minusequal})) {
+ // That's an assignment!
+ Token token = CurToken();
+ m_dil_lexer.Advance();
+ auto rhs = ParseAssignmentExpression();
+ lhs = std::make_unique<BinaryOpNode>(
+ token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()),
+ std::move(lhs), std::move(rhs));
+ }
+ return lhs;
+}
// Parse an additive_expression.
//
diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp
index edad5aa4d490d..b821d26070850 100644
--- a/lldb/source/ValueObject/ValueObject.cpp
+++ b/lldb/source/ValueObject/ValueObject.cpp
@@ -1223,16 +1223,26 @@ void ValueObject::SetValueFromInteger(const llvm::APInt &value, Status &error,
return;
}
+ // Make sure we're not trying to assign to a constant.
+ if (GetIsConstant()) {
+ error =
+ Status::FromErrorString("current value is not assignable (a constant)");
+ return;
+ }
+
// Verify the proposed new value is the right size.
lldb::TargetSP target = GetTargetSP();
uint64_t byte_size = 0;
- if (auto temp =
- llvm::expectedToOptional(GetCompilerType().GetByteSize(target.get())))
- byte_size = temp.value();
- if (value.getBitWidth() != byte_size * CHAR_BIT) {
- error = Status::FromErrorString(
- "illegal argument: new value should be of the same size");
- return;
+ // Exclude size check when assigning an integer 1 or 0 to a boolean.
+ if (!val_type.IsBoolean() || (!value.isOne() && !value.isZero())) {
+ if (auto temp = llvm::expectedToOptional(
+ GetCompilerType().GetByteSize(target.get())))
+ byte_size = temp.value();
+ if (value.getBitWidth() != byte_size * CHAR_BIT) {
+ error = Status::FromErrorString(
+ "illegal argument: new value should be of the same size");
+ return;
+ }
}
lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>(
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/Makefile b/lldb/test/API/commands/frame/var-dil/expr/Assignment/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py
new file mode 100644
index 0000000000000..8559f35b7011e
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py
@@ -0,0 +1,31 @@
+"""
+Test DIL basic assignment.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILAssignment(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_assignment(self):
+ self.build()
+ (target, process, thread, bkpt) = 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("frame variable 'i += 1'", substrs=["2"])
+ self.expect("frame variable 'i += 2'", substrs=["4"])
+ self.expect("frame variable 'i += -4'", substrs=["0"])
+ self.expect("frame variable 'i += eOne'", substrs=["0"])
+ self.expect("frame variable 'i += eTwo'", substrs=["1"])
+
+ self.expect("frame variable 'f += 1'", substrs=["2.5"])
+ self.expect("frame variable 'f += -2.0f'", substrs=["0.5"])
+ self.expect("frame variable 'f += 2.5f'", substrs=["3"])
+ self.expect("frame variable 'f += eTwo'", substrs=["4"])
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
new file mode 100644
index 0000000000000..8d3aa825abca5
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
@@ -0,0 +1,110 @@
+"""
+Test DIL basic assignment.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+#from lldbsuite.test import lldbutil
+from lldbsuite.test import *
+
+
+class TestFrameVarDILAssignment(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_assignment(self):
+ self.build()
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.runCmd("settings set target.experimental.use-DIL true")
+
+ Is32Bit = False
+ if self.target().GetAddressByteSize() == 4:
+ Is32Bit = True
+
+ self.expect(
+ "frame variable '1 = 1'",
+ error=True,
+ substrs=["current value is not assignable (a constant)"]
+ )
+
+ # Assigning to an int var
+ self.expect_var_path("i", value="1")
+ self.expect("frame variable 'i = 5'", substrs=["i = 5"])
+ self.expect_var_path("i", value="5")
+ self.expect_var_path("j", value="-4")
+ self.expect("frame variable 'i = j'", substrs=["i = -4"])
+ self.expect_var_path("i", value="-4")
+ self.expect("frame variable 'i = 2'", substrs=["i = 2"])
+ self.expect_var_path("i", value="2")
+ self.expect("frame variable 'i = -2'", substrs=["i = -2"])
+ self.expect_var_path("i", value="-2")
+ self.expect("frame variable 'i = (int)eOne'", substrs=["i = 0"])
+ self.expect_var_path("i", value="0")
+ self.expect("frame variable 'i = (int)eTwo'", substrs=["i = 1"])
+ self.expect_var_path("i", value="1")
+
+ # Assigning to a float var
+ self.expect_var_path("f", value="1.5")
+ self.expect("frame variable 'f = 17.823f'", substrs=["f = 17.823"])
+ self.expect_var_path("f", value="17.823")
+ self.expect_var_path("pi", value="3.14159012")
+ self.expect("frame variable 'f = pi'", substrs=["f = 3.14159012"])
+ self.expect_var_path("f", value="3.14159012")
+ self.expect("frame variable 'f = 2.5f'", substrs=["f = 2.5"])
+ self.expect_var_path("f", value="2.5")
+ self.expect("frame variable 'f = 3.5f'", substrs=["f = 3.5"])
+ self.expect_var_path("f", value="3.5")
+
+
+ # Assigning to an enum
+ self.expect(
+ "frame variable 'i = eOne'",
+ error=True,
+ substrs=["illegal argument: new value should be of the same size"]
+ )
+
+ self.expect("frame variable 'eOne = 1'", substrs=["eOne = TWO"])
+
+ # Assigning to a pointer
+ self.expect(
+ "frame variable 'p = 1'",
+ error=True,
+ substrs=["illegal argument: new value should be of the same size"]
+ )
+
+ if Is32Bit:
+ self.expect("frame variable 'p = (int*)12'", substrs=["p = 0x0000000c"])
+ self.expect_var_path("p", value="0x0000000c")
+ self.expect("frame variable 'p = 0'", substrs=["p = 0x00000000"])
+ else:
+ self.expect("frame variable 'p = (int*)12'", substrs=["p = 0x000000000000000c"])
+ self.expect_var_path("p", value="0x000000000000000c")
+ self.expect("frame variable 'p = (int *)0'", substrs=["p = 0x0000000000000000"])
+
+ self.expect(
+ "frame variable 'p = farr'",
+ error=True,
+ substrs=["illegal argument: new value should be of the same size"]
+ )
+
+ # Assigning to a bool
+ self.expect_var_path("b", value = "false")
+ self.expect("frame variable 'b = true'", substrs=["b = true"])
+ self.expect_var_path("b", value = "true")
+ self.expect_var_path("(int)b", value = "")
+ self.expect("frame variable 'b = (bool)0'", substrs=["b = false"])
+ self.expect_var_path("b", value = "false")
+
+
+ # Assigning to an array
+ self.expect("frame variable 'farr'", substrs=["([0] = 1, [1] = 2)"])
+ self.expect("frame variable 'farr[1] = f'", substrs=["farr[1] = f = 3.5"])
+ self.expect_var_path("farr[1]", value = "3.5")
+ self.expect("frame variable 'farr'", substrs = ["([0] = 1, [1] = 3.5)"])
+ self.expect("frame variable 'arr'", substrs = ["([0] = 1, [1] = 2)"])
+ self.expect("frame variable 'arr[0] = 37'", substrs=["arr[0] = 37"])
+ self.expect("frame variable 'arr[1] = j'", substrs=["arr[1] = j = -4"])
+ self.expect("frame variable 'arr'", substrs = ["([0] = 37, [1] = -4)"])
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILSubAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILSubAssign.py
new file mode 100644
index 0000000000000..d21dbb54d1680
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILSubAssign.py
@@ -0,0 +1,31 @@
+"""
+Test DIL basic assignment.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILAssignment(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_assignment(self):
+ self.build()
+ (target, process, thread, bkpt) = 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("frame variable 'i -= 1'", substrs=["0"])
+ self.expect("frame variable 'i -= 2'", substrs=["-2"])
+ self.expect("frame variable 'i -= -4'", substrs=["2"])
+ self.expect("frame variable 'i -= eOne'", substrs=["2"])
+ self.expect("frame variable 'i -= eTwo'", substrs=["1"])
+
+ self.expect("frame variable 'f -= 1'", substrs=["0.5"])
+ self.expect("frame variable 'f -= -2.0f'", substrs=["2.5"])
+ self.expect("frame variable 'f -= -2.5f'", substrs=["5"])
+ self.expect("frame variable 'f -= eTwo'", substrs=["4"])
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp
new file mode 100644
index 0000000000000..59ad9748f2bd3
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp
@@ -0,0 +1,17 @@
+
+int main(int argc, char **argv) {
+ int i = 1;
+ int j = -4;
+ float f = 1.5f;
+ float pi = 3.14159f;
+ int* p = (int *)10;
+ bool b = false;
+ float farr[2] = {1.0f, 2.0f};
+ int arr[2] = {1, 2};
+
+ enum Enum { ONE, TWO };
+ Enum eOne = ONE;
+ Enum eTwo = TWO;
+
+ return 0; // Set a breakpoint here
+}
>From 5f952f8aca5d267215afdc080cce538799d278a5 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 2 Apr 2026 12:07:34 -0700
Subject: [PATCH 2/6] Fix clang-format issues.
---
lldb/include/lldb/ValueObject/DILAST.h | 2 +-
.../expr/Assignment/TestFrameVarDILAssign.py | 45 +++++++++++--------
.../frame/var-dil/expr/Assignment/main.cpp | 2 +-
3 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index a50f0e6829527..a08345bf87ef3 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -52,7 +52,7 @@ enum class BinaryOpKind {
/// Translates DIL tokens to BinaryOpKind.
BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind);
-//// The type casts allowed by DIL.
+/// The type casts allowed by DIL.
enum class CastKind {
eArithmetic, ///< Casting to a scalar.
eEnumeration, ///< Casting from a scalar to an enumeration type
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
index 8d3aa825abca5..6ab1a7cc2ec7b 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
@@ -5,7 +5,6 @@
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
-#from lldbsuite.test import lldbutil
from lldbsuite.test import *
@@ -22,12 +21,12 @@ def test_assignment(self):
Is32Bit = False
if self.target().GetAddressByteSize() == 4:
- Is32Bit = True
+ Is32Bit = True
self.expect(
"frame variable '1 = 1'",
error=True,
- substrs=["current value is not assignable (a constant)"]
+ substrs=["current value is not assignable (a constant)"],
)
# Assigning to an int var
@@ -63,7 +62,7 @@ def test_assignment(self):
self.expect(
"frame variable 'i = eOne'",
error=True,
- substrs=["illegal argument: new value should be of the same size"]
+ substrs=["illegal argument: new value should be of the same size"],
)
self.expect("frame variable 'eOne = 1'", substrs=["eOne = TWO"])
@@ -72,17 +71,25 @@ def test_assignment(self):
self.expect(
"frame variable 'p = 1'",
error=True,
- substrs=["illegal argument: new value should be of the same size"]
+ substrs=["illegal argument: new value should be of the same size"],
)
if Is32Bit:
- self.expect("frame variable 'p = (int*)12'", substrs=["p = 0x0000000c"])
- self.expect_var_path("p", value="0x0000000c")
- self.expect("frame variable 'p = 0'", substrs=["p = 0x00000000"])
+ self.expect(
+ "frame variable 'p = (int*)12'", substrs=["p = 0x0000000c"]
+ )
+ self.expect_var_path("p", value="0x0000000c")
+ self.expect(
+ "frame variable 'p = 0'", substrs=["p = 0x00000000"]
+ )
else:
- self.expect("frame variable 'p = (int*)12'", substrs=["p = 0x000000000000000c"])
- self.expect_var_path("p", value="0x000000000000000c")
- self.expect("frame variable 'p = (int *)0'", substrs=["p = 0x0000000000000000"])
+ self.expect(
+ "frame variable 'p = (int*)12'", substrs=["p = 0x000000000000000c"]
+ )
+ self.expect_var_path("p", value="0x000000000000000c")
+ self.expect(
+ "frame variable 'p = (int *)0'", substrs=["p = 0x0000000000000000"]
+ )
self.expect(
"frame variable 'p = farr'",
@@ -91,20 +98,20 @@ def test_assignment(self):
)
# Assigning to a bool
- self.expect_var_path("b", value = "false")
+ self.expect_var_path("b", value="false")
self.expect("frame variable 'b = true'", substrs=["b = true"])
- self.expect_var_path("b", value = "true")
- self.expect_var_path("(int)b", value = "")
+ self.expect_var_path("b", value="true")
+ self.expect_var_path("(int)b", value="")
self.expect("frame variable 'b = (bool)0'", substrs=["b = false"])
- self.expect_var_path("b", value = "false")
+ self.expect_var_path("b", value="false")
# Assigning to an array
self.expect("frame variable 'farr'", substrs=["([0] = 1, [1] = 2)"])
self.expect("frame variable 'farr[1] = f'", substrs=["farr[1] = f = 3.5"])
- self.expect_var_path("farr[1]", value = "3.5")
- self.expect("frame variable 'farr'", substrs = ["([0] = 1, [1] = 3.5)"])
- self.expect("frame variable 'arr'", substrs = ["([0] = 1, [1] = 2)"])
+ self.expect_var_path("farr[1]", value="3.5")
+ self.expect("frame variable 'farr'", substrs=["([0] = 1, [1] = 3.5)"])
+ self.expect("frame variable 'arr'", substrs=["([0] = 1, [1] = 2)"])
self.expect("frame variable 'arr[0] = 37'", substrs=["arr[0] = 37"])
self.expect("frame variable 'arr[1] = j'", substrs=["arr[1] = j = -4"])
- self.expect("frame variable 'arr'", substrs = ["([0] = 37, [1] = -4)"])
+ self.expect("frame variable 'arr'", substrs=["([0] = 37, [1] = -4)"])
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp
index 59ad9748f2bd3..ade851618f2b2 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp
@@ -4,7 +4,7 @@ int main(int argc, char **argv) {
int j = -4;
float f = 1.5f;
float pi = 3.14159f;
- int* p = (int *)10;
+ int *p = (int *)10;
bool b = false;
float farr[2] = {1.0f, 2.0f};
int arr[2] = {1, 2};
>From 82251f980f49332363c6177bf90b80ec70fb9eab Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 2 Apr 2026 12:14:24 -0700
Subject: [PATCH 3/6] Fix clang-format issues.
---
.../var-dil/expr/Assignment/TestFrameVarDILAssign.py | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
index 6ab1a7cc2ec7b..4362a9c334271 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
@@ -57,7 +57,6 @@ def test_assignment(self):
self.expect("frame variable 'f = 3.5f'", substrs=["f = 3.5"])
self.expect_var_path("f", value="3.5")
-
# Assigning to an enum
self.expect(
"frame variable 'i = eOne'",
@@ -75,13 +74,9 @@ def test_assignment(self):
)
if Is32Bit:
- self.expect(
- "frame variable 'p = (int*)12'", substrs=["p = 0x0000000c"]
- )
+ self.expect("frame variable 'p = (int*)12'", substrs=["p = 0x0000000c"])
self.expect_var_path("p", value="0x0000000c")
- self.expect(
- "frame variable 'p = 0'", substrs=["p = 0x00000000"]
- )
+ self.expect("frame variable 'p = 0'", substrs=["p = 0x00000000"])
else:
self.expect(
"frame variable 'p = (int*)12'", substrs=["p = 0x000000000000000c"]
@@ -94,7 +89,7 @@ def test_assignment(self):
self.expect(
"frame variable 'p = farr'",
error=True,
- substrs=["illegal argument: new value should be of the same size"]
+ substrs=["illegal argument: new value should be of the same size"],
)
# Assigning to a bool
@@ -105,7 +100,6 @@ def test_assignment(self):
self.expect("frame variable 'b = (bool)0'", substrs=["b = false"])
self.expect_var_path("b", value="false")
-
# Assigning to an array
self.expect("frame variable 'farr'", substrs=["([0] = 1, [1] = 2)"])
self.expect("frame variable 'farr[1] = f'", substrs=["farr[1] = f = 3.5"])
>From 6703a158cc0a259c7eebe66a5bcaa4225c02f39d Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 9 Apr 2026 08:28:36 -0700
Subject: [PATCH 4/6] Add arithmetic conversions to assignments & loosen size
restrictions on assignments slightly.
---
lldb/include/lldb/ValueObject/DILEval.h | 6 +-
lldb/source/ValueObject/DILEval.cpp | 61 +++++++++++++++----
lldb/source/ValueObject/ValueObject.cpp | 12 ++--
.../Assignment/TestFrameVarDILAddAssign.py | 1 +
.../expr/Assignment/TestFrameVarDILAssign.py | 33 +++++-----
.../frame/var-dil/expr/Assignment/main.cpp | 1 +
6 files changed, 79 insertions(+), 35 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index c43ac87b2b0e9..835bc6de525dc 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -83,11 +83,13 @@ class Interpreter : Visitor {
CompilerType &rhs_type);
/// Perform an arithmetic conversion on two values from an arithmetic
- /// operation.
+ /// operation. If this is part of an assignment operation (is_assign is
+ /// true), do not change the lhs, which is likely to be a program variable.
/// \returns The result type of an arithmetic operation.
llvm::Expected<CompilerType> ArithmeticConversion(lldb::ValueObjectSP &lhs,
lldb::ValueObjectSP &rhs,
- uint32_t location);
+ uint32_t location,
+ bool is_assign = false);
/// Add or subtract the offset to the pointer according to the pointee type
/// byte size.
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 479be4e09feff..e4d6cc8b7db0f 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -217,12 +217,15 @@ Interpreter::PromoteSignedInteger(CompilerType &lhs_type,
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;
+ lldb::ValueObjectSP &rhs, uint32_t location,
+ bool is_assign) {
+ // Apply unary conversion for both operands, unless doing assignment.
+ if (!is_assign) {
+ 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();
@@ -735,6 +738,18 @@ llvm::Expected<lldb::ValueObjectSP>
Interpreter::EvaluateAssignment(lldb::ValueObjectSP lhs,
lldb::ValueObjectSP rhs, uint32_t location) {
+ // Determine the appropriate type for the rhs.
+ auto type_or_err = ArithmeticConversion(lhs, rhs, location,
+ /*is_assign=*/ true);
+ if (!type_or_err)
+ return type_or_err.takeError();
+ CompilerType rhs_type = *type_or_err;
+ // If rhs_type contains a valid type, cast rhs to that type. Otherwise
+ // the conversion function did not find a better type, so leave rhs
+ // as is.
+ if (rhs_type.GetTypeName(false) != "<invalid>")
+ rhs = rhs->Cast(rhs_type);
+
Status status;
lhs->SetValueFromInteger(rhs, status, m_allow_var_updates);
if (status.Success())
@@ -747,16 +762,27 @@ Interpreter::EvaluateAssignment(lldb::ValueObjectSP lhs,
llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddAssign(
lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) {
- lldb::ValueObjectSP ret;
auto ret_or_err = EvaluateBinaryAddition(lhs, rhs, location);
if (!ret_or_err)
return ret_or_err;
- ret = *ret_or_err;
+ lldb::ValueObjectSP sum = *ret_or_err;
+
+ // Determine the appropriate type for sum.
+ auto type_or_err = ArithmeticConversion(lhs, sum, location,
+ /*is_assign=*/ true);
+ if (!type_or_err)
+ return type_or_err.takeError();
+ CompilerType sum_type = *type_or_err;
+ // If sum_type contains a valid type, cast sum to that type. Otherwise
+ // the conversion function did not find a better type, so leave sum
+ // as is.
+ if (sum_type.GetTypeName(false) != "<invalid>")
+ sum = sum->Cast(sum_type);
Status status;
- lhs->SetValueFromInteger(ret, status, m_allow_var_updates);
+ lhs->SetValueFromInteger(sum, status, m_allow_var_updates);
if (status.Success())
return lhs;
@@ -767,16 +793,27 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddAssign(
llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubAssign(
lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) {
- lldb::ValueObjectSP ret;
auto ret_or_err = EvaluateBinarySubtraction(lhs, rhs, location);
if (!ret_or_err)
return ret_or_err;
- ret = *ret_or_err;
+ lldb::ValueObjectSP diff = *ret_or_err;
+
+ // Determine the appropriate type for diff.
+ auto type_or_err = ArithmeticConversion(lhs, diff, location,
+ /*is_assign=*/ true);
+ if (!type_or_err)
+ return type_or_err.takeError();
+ CompilerType diff_type = *type_or_err;
+ // If diff_type contains a valid type, cast diff to that type. Otherwise
+ // the conversion function did not find a better type, so leave diff
+ // as is.
+ if (diff_type.GetTypeName(false) != "<invalid>")
+ diff = diff->Cast(diff_type);
Status status;
- lhs->SetValueFromInteger(ret, status, m_allow_var_updates);
+ lhs->SetValueFromInteger(diff, status, m_allow_var_updates);
if (status.Success())
return lhs;
diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp
index b821d26070850..7704db3936d07 100644
--- a/lldb/source/ValueObject/ValueObject.cpp
+++ b/lldb/source/ValueObject/ValueObject.cpp
@@ -1238,10 +1238,14 @@ void ValueObject::SetValueFromInteger(const llvm::APInt &value, Status &error,
if (auto temp = llvm::expectedToOptional(
GetCompilerType().GetByteSize(target.get())))
byte_size = temp.value();
- if (value.getBitWidth() != byte_size * CHAR_BIT) {
- error = Status::FromErrorString(
- "illegal argument: new value should be of the same size");
- return;
+ if (value.getBitWidth() > byte_size * CHAR_BIT) {
+ // The type is too big, but maybe the value itself is small enough?
+ uint64_t u_max = (1 << (byte_size * CHAR_BIT)) - 1;
+ if (*(value.getRawData()) > u_max) {
+ error =
+ Status::FromErrorString("illegal argument: new value is too big");
+ return;
+ }
}
}
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py
index 8559f35b7011e..3e3bc8b22d921 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py
@@ -20,6 +20,7 @@ def test_assignment(self):
self.runCmd("settings set target.experimental.use-DIL true")
self.expect("frame variable 'i += 1'", substrs=["2"])
+ self.expect("frame variable 's += i'", substrs=["9"])
self.expect("frame variable 'i += 2'", substrs=["4"])
self.expect("frame variable 'i += -4'", substrs=["0"])
self.expect("frame variable 'i += eOne'", substrs=["0"])
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
index 4362a9c334271..bd47cbcbf2769 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
@@ -45,6 +45,15 @@ def test_assignment(self):
self.expect("frame variable 'i = (int)eTwo'", substrs=["i = 1"])
self.expect_var_path("i", value="1")
+ # Assigning "int" with a small value to "short" should work.
+ self.expect("frame variable 's = i'", substrs=["s = 1"])
+ # Assigning "int" with a big value to "short" should fail.
+ self.expect(
+ "frame variable 's = 78246'",
+ error=True,
+ substrs=["new value is too big"],
+ )
+
# Assigning to a float var
self.expect_var_path("f", value="1.5")
self.expect("frame variable 'f = 17.823f'", substrs=["f = 17.823"])
@@ -58,26 +67,19 @@ def test_assignment(self):
self.expect_var_path("f", value="3.5")
# Assigning to an enum
- self.expect(
- "frame variable 'i = eOne'",
- error=True,
- substrs=["illegal argument: new value should be of the same size"],
- )
-
+ self.expect("frame variable 'i = eOne'", substrs=["0"])
self.expect("frame variable 'eOne = 1'", substrs=["eOne = TWO"])
# Assigning to a pointer
- self.expect(
- "frame variable 'p = 1'",
- error=True,
- substrs=["illegal argument: new value should be of the same size"],
- )
-
if Is32Bit:
+ self.expect("frame variable 'p = 1'", substrs=["p = 0x00000001"])
self.expect("frame variable 'p = (int*)12'", substrs=["p = 0x0000000c"])
self.expect_var_path("p", value="0x0000000c")
self.expect("frame variable 'p = 0'", substrs=["p = 0x00000000"])
else:
+ self.expect(
+ "frame variable 'p = 1'", substrs=["p = 0x0000000000000001"]
+ )
self.expect(
"frame variable 'p = (int*)12'", substrs=["p = 0x000000000000000c"]
)
@@ -86,11 +88,8 @@ def test_assignment(self):
"frame variable 'p = (int *)0'", substrs=["p = 0x0000000000000000"]
)
- self.expect(
- "frame variable 'p = farr'",
- error=True,
- substrs=["illegal argument: new value should be of the same size"],
- )
+ # Just verify the result value prefix is an address.
+ self.expect("frame variable 'p = farr'", substrs=["(int *) p = 0x0000"])
# Assigning to a bool
self.expect_var_path("b", value="false")
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp
index ade851618f2b2..424eecd2fa87a 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/main.cpp
@@ -6,6 +6,7 @@ int main(int argc, char **argv) {
float pi = 3.14159f;
int *p = (int *)10;
bool b = false;
+ short s = 7;
float farr[2] = {1.0f, 2.0f};
int arr[2] = {1, 2};
>From fb3f8265ba64cdb0ae2dfb4a48f7a754bac5750a Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 9 Apr 2026 08:34:38 -0700
Subject: [PATCH 5/6] Fix clang-format issues.
---
lldb/source/ValueObject/DILEval.cpp | 6 +++---
.../frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py | 6 ++----
2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index e4d6cc8b7db0f..43d6beb70b529 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -740,7 +740,7 @@ Interpreter::EvaluateAssignment(lldb::ValueObjectSP lhs,
// Determine the appropriate type for the rhs.
auto type_or_err = ArithmeticConversion(lhs, rhs, location,
- /*is_assign=*/ true);
+ /*is_assign=*/true);
if (!type_or_err)
return type_or_err.takeError();
CompilerType rhs_type = *type_or_err;
@@ -771,7 +771,7 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddAssign(
// Determine the appropriate type for sum.
auto type_or_err = ArithmeticConversion(lhs, sum, location,
- /*is_assign=*/ true);
+ /*is_assign=*/true);
if (!type_or_err)
return type_or_err.takeError();
CompilerType sum_type = *type_or_err;
@@ -802,7 +802,7 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubAssign(
// Determine the appropriate type for diff.
auto type_or_err = ArithmeticConversion(lhs, diff, location,
- /*is_assign=*/ true);
+ /*is_assign=*/true);
if (!type_or_err)
return type_or_err.takeError();
CompilerType diff_type = *type_or_err;
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
index bd47cbcbf2769..22b173b8a136b 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
@@ -77,9 +77,7 @@ def test_assignment(self):
self.expect_var_path("p", value="0x0000000c")
self.expect("frame variable 'p = 0'", substrs=["p = 0x00000000"])
else:
- self.expect(
- "frame variable 'p = 1'", substrs=["p = 0x0000000000000001"]
- )
+ self.expect("frame variable 'p = 1'", substrs=["p = 0x0000000000000001"])
self.expect(
"frame variable 'p = (int*)12'", substrs=["p = 0x000000000000000c"]
)
@@ -88,7 +86,7 @@ def test_assignment(self):
"frame variable 'p = (int *)0'", substrs=["p = 0x0000000000000000"]
)
- # Just verify the result value prefix is an address.
+ # Just verify the result value prefix is an address.
self.expect("frame variable 'p = farr'", substrs=["(int *) p = 0x0000"])
# Assigning to a bool
>From 5b3bc2f64754c7a98227aed08dcb1af349a44e04 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 9 Apr 2026 10:04:25 -0700
Subject: [PATCH 6/6] Add pointer-specific type checking.
---
lldb/source/ValueObject/DILEval.cpp | 36 +++++++++++++++++++
.../Assignment/TestFrameVarDILAddAssign.py | 19 ++++++++++
.../expr/Assignment/TestFrameVarDILAssign.py | 25 +++++++++++--
.../Assignment/TestFrameVarDILSubAssign.py | 17 +++++++++
4 files changed, 94 insertions(+), 3 deletions(-)
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 43d6beb70b529..035ebad50c4e2 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -750,6 +750,18 @@ Interpreter::EvaluateAssignment(lldb::ValueObjectSP lhs,
if (rhs_type.GetTypeName(false) != "<invalid>")
rhs = rhs->Cast(rhs_type);
+ // Pointer checks. Should only allow assigning pointer types to pointer
+ // variables.
+ bool lhs_is_pointer = lhs->GetCompilerType().IsPointerType();
+ bool rhs_is_pointer = rhs->GetCompilerType().IsPointerType();
+ if ((lhs_is_pointer && !rhs_is_pointer)
+ || (!lhs_is_pointer && rhs_is_pointer)) {
+ std::string errMsg =
+ "Invalid assignment: Can only assign pointers to pointers";
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+ location);
+ }
+
Status status;
lhs->SetValueFromInteger(rhs, status, m_allow_var_updates);
if (status.Success())
@@ -781,6 +793,18 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddAssign(
if (sum_type.GetTypeName(false) != "<invalid>")
sum = sum->Cast(sum_type);
+ // Pointer checks. Should only allow assigning pointer types to pointer
+ // variables.
+ bool lhs_is_pointer = lhs->GetCompilerType().IsPointerType();
+ bool rhs_is_pointer = sum->GetCompilerType().IsPointerType();
+ if ((lhs_is_pointer && !rhs_is_pointer)
+ || (!lhs_is_pointer && rhs_is_pointer)) {
+ std::string errMsg =
+ "Invalid assignment: Can only assign pointers to pointers";
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+ location);
+ }
+
Status status;
lhs->SetValueFromInteger(sum, status, m_allow_var_updates);
if (status.Success())
@@ -812,6 +836,18 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubAssign(
if (diff_type.GetTypeName(false) != "<invalid>")
diff = diff->Cast(diff_type);
+ // Pointer checks. Should only allow assigning pointer types to pointer
+ // variables.
+ bool lhs_is_pointer = lhs->GetCompilerType().IsPointerType();
+ bool rhs_is_pointer = diff->GetCompilerType().IsPointerType();
+ if ((lhs_is_pointer && !rhs_is_pointer)
+ || (!lhs_is_pointer && rhs_is_pointer)) {
+ std::string errMsg =
+ "Invalid assignment: Can only assign pointers to pointers";
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+ location);
+ }
+
Status status;
lhs->SetValueFromInteger(diff, status, m_allow_var_updates);
if (status.Success())
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py
index 3e3bc8b22d921..e687929ddfec5 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAddAssign.py
@@ -30,3 +30,22 @@ def test_assignment(self):
self.expect("frame variable 'f += -2.0f'", substrs=["0.5"])
self.expect("frame variable 'f += 2.5f'", substrs=["3"])
self.expect("frame variable 'f += eTwo'", substrs=["4"])
+
+ Is32Bit = False
+ if self.target().GetAddressByteSize() == 4:
+ Is32Bit = True
+
+ self.expect(
+ "frame variable 'i += p'", # Try assigning pointer to int.
+ error = True,
+ substrs = ["Invalid assignment: Can only assign pointers to pointers"],
+ )
+
+ if Is32Bit:
+ self.expect("frame variable 'p'", substrs=["p = 0x0000000a"])
+ self.expect("frame variable 'p += 2'", substrs=["p = 0x00000012"])
+ self.expect("frame variable 'p += i'", substrs=["p = 0x00000016"])
+ else:
+ self.expect("frame variable 'p'", substrs=["p = 0x000000000000000a"])
+ self.expect("frame variable 'p += 2'", substrs=["p = 0x0000000000000012"])
+ self.expect("frame variable 'p += i'", substrs=["p = 0x0000000000000016"])
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
index 22b173b8a136b..f8e36689e8210 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILAssign.py
@@ -71,13 +71,32 @@ def test_assignment(self):
self.expect("frame variable 'eOne = 1'", substrs=["eOne = TWO"])
# Assigning to a pointer
+ self.expect(
+ "frame variable 'p = 1'",
+ error=True,
+ substrs=["Invalid assignment: Can only assign pointers to pointers"],
+ )
+
+ self.expect(
+ "frame variable 'p = i + s'",
+ error=True,
+ substrs=["Invalid assignment: Can only assign pointers to pointers"],
+ )
+
+ self.expect(
+ "frame variable 'i = p'",
+ error=True,
+ substrs=["Invalid assignment: Can only assign pointers to pointers"],
+ )
+
if Is32Bit:
- self.expect("frame variable 'p = 1'", substrs=["p = 0x00000001"])
self.expect("frame variable 'p = (int*)12'", substrs=["p = 0x0000000c"])
self.expect_var_path("p", value="0x0000000c")
- self.expect("frame variable 'p = 0'", substrs=["p = 0x00000000"])
+ self.expect("frame variable 'p = p - s'", substrs=["p = 0x0000000b"])
+ self.expect(
+ "frame variable 'p = (int *)0'", substrs=["p = 0x00000000"]
+ )
else:
- self.expect("frame variable 'p = 1'", substrs=["p = 0x0000000000000001"])
self.expect(
"frame variable 'p = (int*)12'", substrs=["p = 0x000000000000000c"]
)
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILSubAssign.py b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILSubAssign.py
index d21dbb54d1680..139378e2d3f93 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILSubAssign.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Assignment/TestFrameVarDILSubAssign.py
@@ -29,3 +29,20 @@ def test_assignment(self):
self.expect("frame variable 'f -= -2.0f'", substrs=["2.5"])
self.expect("frame variable 'f -= -2.5f'", substrs=["5"])
self.expect("frame variable 'f -= eTwo'", substrs=["4"])
+
+ Is32Bit = False
+ if self.target().GetAddressByteSize() == 4:
+ Is32Bit = True
+
+ self.expect(
+ "frame variable 'i -= p'", # Try assigning pointer to int.
+ error = True,
+ substrs = ["invalid operands to binary expression"],
+ )
+
+ if Is32Bit:
+ self.expect("frame variable 'p'", substrs=["p = 0x0000000a"])
+ self.expect("frame variable 'p -= 2'", substrs=["p = 0x00000002"])
+ else:
+ self.expect("frame variable 'p'", substrs=["p = 0x000000000000000a"])
+ self.expect("frame variable 'p -= 2'", substrs=["p = 0x0000000000000002"])
More information about the lldb-commits
mailing list