[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