[Lldb-commits] [lldb] [lldb] Add pointer arithmetics for addition and subtraction to DIL (PR #184652)

Ilia Kuklin via lldb-commits lldb-commits at lists.llvm.org
Fri Mar 13 07:10:32 PDT 2026


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

>From 18a38c88cb35c01e215def2659ad09b0945a2517 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 4 Mar 2026 19:46:38 +0500
Subject: [PATCH 1/4] [lldb] Add pointer arithmetics for addition and
 subtraction to DIL

---
 lldb/include/lldb/ValueObject/DILEval.h       |   4 +
 lldb/source/ValueObject/DILEval.cpp           | 110 +++++++++++++++--
 .../Arithmetic/TestFrameVarDILArithmetic.py   |  12 --
 .../TestFrameVarDILExprPointerArithmetic.py   | 113 ++++++++++++++++++
 .../var-dil/expr/PointerArithmetic/main.cpp   |  19 +++
 5 files changed, 239 insertions(+), 19 deletions(-)

diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index a6223c4d998ab..f4506bf626ab4 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -89,6 +89,10 @@ class Interpreter : Visitor {
   llvm::Expected<CompilerType> ArithmeticConversion(lldb::ValueObjectSP &lhs,
                                                     lldb::ValueObjectSP &rhs,
                                                     uint32_t location);
+  /// Add the offset to the pointer according to the pointee type byte size.
+  /// \returns A new `ValueObject` with a new pointer value.
+  llvm::Expected<lldb::ValueObjectSP>
+  PointerAdd(lldb::ValueObjectSP ptr, int64_t offset, uint32_t location);
   llvm::Expected<lldb::ValueObjectSP> EvaluateScalarOp(BinaryOpKind kind,
                                                        lldb::ValueObjectSP lhs,
                                                        lldb::ValueObjectSP rhs,
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index ec9ea8b9618fc..b0c30e3b152de 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -538,6 +538,29 @@ Interpreter::Visit(const UnaryOpNode &node) {
                                               node.GetLocation());
 }
 
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::PointerAdd(lldb::ValueObjectSP ptr, int64_t offset,
+                        uint32_t location) {
+  if (ptr->GetCompilerType().IsPointerToVoid())
+    return llvm::make_error<DILDiagnosticError>(
+        m_expr, "arithmetic on a pointer to void", location);
+  if (ptr->GetValueAsUnsigned(0) == 0 && offset != 0)
+    return llvm::make_error<DILDiagnosticError>(
+        m_expr, "arithmetic on a nullptr is undefined", location);
+
+  llvm::Expected<uint64_t> byte_size =
+      ptr->GetCompilerType().GetPointeeType().GetByteSize(
+          m_exe_ctx_scope.get());
+  if (!byte_size)
+    return byte_size.takeError();
+  uintptr_t addr = ptr->GetValueAsUnsigned(0) + offset * (*byte_size);
+
+  ExecutionContext exe_ctx(m_target.get(), false);
+  Scalar scalar(addr);
+  return ValueObject::CreateValueObjectFromScalar(
+      m_target, scalar, ptr->GetCompilerType(), "result");
+}
+
 llvm::Expected<lldb::ValueObjectSP>
 Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs,
                               lldb::ValueObjectSP rhs, CompilerType result_type,
@@ -569,7 +592,8 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition(
     lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) {
   // Operation '+' works for:
   //   {scalar,unscoped_enum} <-> {scalar,unscoped_enum}
-  // TODO: Pointer arithmetics
+  //   {integer,unscoped_enum} <-> pointer
+  //   pointer <-> {integer,unscoped_enum}
   auto orig_lhs_type = lhs->GetCompilerType();
   auto orig_rhs_type = rhs->GetCompilerType();
   auto type_or_err = ArithmeticConversion(lhs, rhs, location);
@@ -580,18 +604,34 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition(
   if (result_type.IsScalarType())
     return EvaluateScalarOp(BinaryOpKind::Add, lhs, rhs, result_type, location);
 
-  std::string errMsg =
-      llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')",
-                    orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName());
-  return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
-                                              location);
+  // Check for pointer arithmetics.
+  // One of the operands must be a pointer and the other one an integer.
+  lldb::ValueObjectSP ptr, offset;
+  if (lhs->GetCompilerType().IsPointerType()) {
+    ptr = lhs;
+    offset = rhs;
+  } else if (rhs->GetCompilerType().IsPointerType()) {
+    ptr = rhs;
+    offset = lhs;
+  }
+
+  if (!ptr || !offset->GetCompilerType().IsInteger()) {
+    std::string errMsg =
+        llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')",
+                      orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName());
+    return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+                                                location);
+  }
+
+  return PointerAdd(ptr, offset->GetValueAsUnsigned(0), location);
 }
 
 llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
     lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) {
   // Operation '-' works for:
   //   {scalar,unscoped_enum} <-> {scalar,unscoped_enum}
-  // TODO: Pointer arithmetics
+  //   pointer <-> {integer,unscoped_enum}
+  //   pointer <-> pointer (if pointee types are compatible)
   auto orig_lhs_type = lhs->GetCompilerType();
   auto orig_rhs_type = rhs->GetCompilerType();
   auto type_or_err = ArithmeticConversion(lhs, rhs, location);
@@ -602,6 +642,62 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
   if (result_type.IsScalarType())
     return EvaluateScalarOp(BinaryOpKind::Sub, lhs, rhs, result_type, location);
 
+  auto lhs_type = lhs->GetCompilerType();
+  auto rhs_type = rhs->GetCompilerType();
+
+  // "pointer - integer" operation.
+  if (lhs_type.IsPointerType() && rhs_type.IsInteger())
+    return PointerAdd(lhs, -rhs->GetValueAsUnsigned(0), location);
+
+  // "pointer - pointer" operation.
+  if (lhs_type.IsPointerType() && rhs_type.IsPointerType()) {
+    if (lhs_type.IsPointerToVoid() && rhs_type.IsPointerToVoid()) {
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, "arithmetic on pointers to void", location);
+    }
+    // Compare canonical unqualified pointer types.
+    CompilerType lhs_unqualified_type =
+        lhs_type.GetCanonicalType().GetFullyUnqualifiedType();
+    CompilerType rhs_unqualified_type =
+        rhs_type.GetCanonicalType().GetFullyUnqualifiedType();
+    bool comparable = lhs_unqualified_type.CompareTypes(rhs_unqualified_type);
+    if (!comparable) {
+      std::string errMsg = llvm::formatv(
+          "'{0}' and '{1}' are not pointers to compatible types",
+          orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName());
+      return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location);
+    }
+
+    llvm::Expected<uint64_t> lhs_byte_size =
+        lhs_type.GetPointeeType().GetByteSize(m_exe_ctx_scope.get());
+    if (!lhs_byte_size)
+      return lhs_byte_size.takeError();
+    // Since pointers have compatible types, both have the same pointee size.
+    int64_t item_size = *lhs_byte_size;
+    int64_t diff = static_cast<int64_t>(lhs->GetValueAsUnsigned(0) -
+                                        rhs->GetValueAsUnsigned(0));
+    if (diff % item_size != 0) {
+      // If address difference isn't divisible by pointee size then performing
+      // the operation is undefined behaviour.
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, "undefined pointer arithmetic", location);
+    }
+    diff /= item_size;
+
+    llvm::Expected<lldb::TypeSystemSP> type_system =
+        GetTypeSystemFromCU(m_exe_ctx_scope);
+    if (!type_system)
+      return type_system.takeError();
+    CompilerType ptrdiff_t = type_system.get()->GetPointerDiffType(true);
+    if (!ptrdiff_t)
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, "unable to determine pointer diff type", location);
+
+    Scalar scalar(diff);
+    return ValueObject::CreateValueObjectFromScalar(m_target, scalar, ptrdiff_t,
+                                                    "result");
+  }
+
   std::string errMsg =
       llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')",
                     orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName());
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
index 03075314ab9b6..fb3b877209d16 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
@@ -102,15 +102,3 @@ def test_arithmetic(self):
         self.expect_var_path("my_ref - 1", value="1")
         self.expect_var_path("ref + my_ref", value="4")
         self.expect_var_path("ref - my_ref", value="0")
-
-        # TODO: Pointer arithmetics
-        self.expect(
-            "frame var -- 'p + 1'",
-            error=True,
-            substrs=["invalid operands to binary expression ('int *' and 'int')"],
-        )
-        self.expect(
-            "frame var -- 'p - 1'",
-            error=True,
-            substrs=["invalid operands to binary expression ('int *' and 'int')"],
-        )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py
index 448cd5b1ec7e0..b027fab1e176f 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py
@@ -22,8 +22,121 @@ def test_pointer_arithmetic(self):
         self.expect_var_path("+array", type="int *")
         self.expect_var_path("+array_ref", type="int *")
         self.expect_var_path("+p_int0", type="int *")
+
+        # Binary operations
+        self.expect_var_path("p_char1", type="const char *")
+        self.expect_var_path("p_char1 + 1", type="const char *")
+        self.expect_var_path("p_char1 + offset", type="const char *")
+
+        self.expect_var_path("my_p_char1", type="my_char_ptr")
+        self.expect_var_path("my_p_char1 + 1", type="my_char_ptr")
+        self.expect_var_path("my_p_char1 + offset", type="my_char_ptr")
+
+        self.expect_var_path("*(p_char1 + 0)", value="'h'")
+        self.expect_var_path("*(4 + p_char1)", value="'o'")
+        self.expect_var_path("*(p_char1 + offset - 1)", value="'o'")
+
+        self.expect_var_path("*p_int0", value="0")
+        self.expect_var_path("*cp_int5", value="5")
+        self.expect_var_path("*(&*(cp_int5 + 1) - 1)", value="5")
+
+        self.expect_var_path("p_int0 - p_int0", value="0", type="__ptrdiff_t")
+        self.expect_var_path("cp_int5 - p_int0", value="5", type="__ptrdiff_t")
+        self.expect_var_path("cp_int5 - td_int_ptr0", value="5", type="__ptrdiff_t")
+        self.expect_var_path("td_int_ptr0 - cp_int5", value="-5", type="__ptrdiff_t")
+
+        # Check arrays
+        self.expect_var_path("array + 1", type="int *")
+        self.expect_var_path("1 + array", type="int *")
+        self.expect_var_path("array_ref + 1", type="int *")
+        self.expect_var_path("1 + array_ref", type="int *")
+        self.expect_var_path("array - 1", type="int *")
+        self.expect_var_path("array_ref - 1", type="int *")
+        self.expect_var_path("array - array", value="0", type="__ptrdiff_t")
+        self.expect_var_path("array - array_ref", value="0", type="__ptrdiff_t")
+        self.expect_var_path("array_ref - array_ref", value="0", type="__ptrdiff_t")
+
+        # Errors
         self.expect(
             "frame var -- '-p_int0'",
             error=True,
             substrs=["invalid argument type 'int *' to unary expression"],
         )
+        self.expect(
+            "frame var -- 'cp_int5 - p_char1'",
+            error=True,
+            substrs=[
+                "'const int *' and 'const char *' are not pointers to compatible types"
+            ],
+        )
+        self.expect(
+            "frame var -- 'p_int0 + cp_int5'",
+            error=True,
+            substrs=[
+                "invalid operands to binary expression ('int *' and 'const int *')"
+            ],
+        )
+        self.expect(
+            "frame var -- 'p_void + 1'",
+            error=True,
+            substrs=["arithmetic on a pointer to void"],
+        )
+        self.expect(
+            "frame var -- 'p_void - 1'",
+            error=True,
+            substrs=["arithmetic on a pointer to void"],
+        )
+        self.expect(
+            "frame var -- 'p_void - p_char1'",
+            error=True,
+            substrs=[
+                "'void *' and 'const char *' are not pointers to compatible types"
+            ],
+        )
+        self.expect(
+            "frame var -- 'p_void - p_void'",
+            error=True,
+            substrs=["arithmetic on pointers to void"],
+        )
+        self.expect(
+            "frame var -- 'pp_void0 - p_char1'",
+            error=True,
+            substrs=[
+                "'void **' and 'const char *' are not pointers to compatible types"
+            ],
+        )
+        self.expect(
+            "frame var -- 'p_int0 - 1.0'",
+            error=True,
+            substrs=["invalid operands to binary expression ('int *' and 'double')"],
+        )
+        self.expect(
+            "frame var -- '1.0f + p_int0'",
+            error=True,
+            substrs=["invalid operands to binary expression ('float' and 'int *')"],
+        )
+        self.expect(
+            "frame var -- '1 - array'",
+            error=True,
+            substrs=["invalid operands to binary expression ('int' and 'int[10]')"],
+        )
+        self.expect(
+            "frame var -- 'array + array'",
+            error=True,
+            substrs=["invalid operands to binary expression ('int[10]' and 'int[10]')"],
+        )
+        self.expect(
+            "frame var -- 'array + array'",
+            error=True,
+            substrs=["invalid operands to binary expression ('int[10]' and 'int[10]')"],
+        )
+        self.expect(
+            "frame var -- 'int_null + 1'",
+            error=True,
+            substrs=["arithmetic on a nullptr is undefined"],
+        )
+        self.expect(
+            "frame var -- 'int_null - 1'",
+            error=True,
+            substrs=["arithmetic on a nullptr is undefined"],
+        )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
index b4e0e88b1ffc9..be1f3424f62dc 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
@@ -1,11 +1,30 @@
 void stop() {}
 
 int main(int argc, char **argv) {
+  int offset = 5;
   int array[10];
   array[0] = 0;
+  array[offset] = offset;
   int (&array_ref)[10] = array;
   int *p_int0 = &array[0];
 
+  const char *p_char1 = "hello";
+  typedef const char *my_char_ptr;
+  my_char_ptr my_p_char1 = p_char1;
+
+  int **pp_int0 = &p_int0;
+  const int *cp_int0 = &array[0];
+  const int *cp_int5 = &array[offset];
+
+  typedef int *td_int_ptr_t;
+  td_int_ptr_t td_int_ptr0 = &array[0];
+
+  void *p_void = (void *)p_char1;
+  void **pp_void0 = &p_void;
+  void **pp_void1 = pp_void0 + 1;
+
+  int *int_null = nullptr;
+
   stop(); // Set a breakpoint here
   return 0;
 }

>From 04ab6908a755f2956777c662341cffa3b62411bb Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Fri, 13 Mar 2026 16:14:30 +0500
Subject: [PATCH 2/4] Fixes after rebase

---
 lldb/source/ValueObject/DILEval.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index b0c30e3b152de..8df4cce3ea970 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -558,7 +558,7 @@ Interpreter::PointerAdd(lldb::ValueObjectSP ptr, int64_t offset,
   ExecutionContext exe_ctx(m_target.get(), false);
   Scalar scalar(addr);
   return ValueObject::CreateValueObjectFromScalar(
-      m_target, scalar, ptr->GetCompilerType(), "result");
+      m_exe_ctx_scope, scalar, ptr->GetCompilerType(), "result");
 }
 
 llvm::Expected<lldb::ValueObjectSP>
@@ -694,8 +694,8 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
           m_expr, "unable to determine pointer diff type", location);
 
     Scalar scalar(diff);
-    return ValueObject::CreateValueObjectFromScalar(m_target, scalar, ptrdiff_t,
-                                                    "result");
+    return ValueObject::CreateValueObjectFromScalar(m_exe_ctx_scope, scalar,
+                                                    ptrdiff_t, "result");
   }
 
   std::string errMsg =

>From e691eaaecec04d0df3a0169be778e746a5284872 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Fri, 13 Mar 2026 16:27:28 +0500
Subject: [PATCH 3/4] Fix adding/subtracting pointer with a negative value

---
 lldb/include/lldb/ValueObject/DILEval.h       |  9 ++--
 lldb/source/ValueObject/DILEval.cpp           | 21 +++++++---
 .../TestFrameVarDILExprPointerArithmetic.py   | 42 +++++++++++++------
 .../var-dil/expr/PointerArithmetic/main.cpp   |  7 ++--
 4 files changed, 56 insertions(+), 23 deletions(-)

diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index f4506bf626ab4..4140c011f1580 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -89,10 +89,13 @@ class Interpreter : Visitor {
   llvm::Expected<CompilerType> ArithmeticConversion(lldb::ValueObjectSP &lhs,
                                                     lldb::ValueObjectSP &rhs,
                                                     uint32_t location);
-  /// Add the offset to the pointer according to the pointee type byte size.
+  /// Add or subtract the offset to the pointer according to the pointee type
+  /// byte size.
   /// \returns A new `ValueObject` with a new pointer value.
-  llvm::Expected<lldb::ValueObjectSP>
-  PointerAdd(lldb::ValueObjectSP ptr, int64_t offset, uint32_t location);
+  llvm::Expected<lldb::ValueObjectSP> PointerOffset(lldb::ValueObjectSP ptr,
+                                                    lldb::ValueObjectSP offset,
+                                                    bool positive,
+                                                    uint32_t location);
   llvm::Expected<lldb::ValueObjectSP> EvaluateScalarOp(BinaryOpKind kind,
                                                        lldb::ValueObjectSP lhs,
                                                        lldb::ValueObjectSP rhs,
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 8df4cce3ea970..cc402f1863e2a 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -539,8 +539,8 @@ Interpreter::Visit(const UnaryOpNode &node) {
 }
 
 llvm::Expected<lldb::ValueObjectSP>
-Interpreter::PointerAdd(lldb::ValueObjectSP ptr, int64_t offset,
-                        uint32_t location) {
+Interpreter::PointerOffset(lldb::ValueObjectSP ptr, lldb::ValueObjectSP offset,
+                           bool positive, uint32_t location) {
   if (ptr->GetCompilerType().IsPointerToVoid())
     return llvm::make_error<DILDiagnosticError>(
         m_expr, "arithmetic on a pointer to void", location);
@@ -548,12 +548,23 @@ Interpreter::PointerAdd(lldb::ValueObjectSP ptr, int64_t offset,
     return llvm::make_error<DILDiagnosticError>(
         m_expr, "arithmetic on a nullptr is undefined", location);
 
+  bool success;
+  int64_t offset_int = offset->GetValueAsSigned(0, &success);
+  if (!success) {
+    std::string errMsg = llvm::formatv("could not get the offset: {0}",
+                                       offset->GetError().AsCString());
+    return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+                                                location);
+  }
+
   llvm::Expected<uint64_t> byte_size =
       ptr->GetCompilerType().GetPointeeType().GetByteSize(
           m_exe_ctx_scope.get());
   if (!byte_size)
     return byte_size.takeError();
-  uintptr_t addr = ptr->GetValueAsUnsigned(0) + offset * (*byte_size);
+  if (!positive)
+    offset_int = -offset_int;
+  uintptr_t addr = ptr->GetValueAsUnsigned(0) + offset_int * (*byte_size);
 
   ExecutionContext exe_ctx(m_target.get(), false);
   Scalar scalar(addr);
@@ -623,7 +634,7 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition(
                                                 location);
   }
 
-  return PointerAdd(ptr, offset->GetValueAsUnsigned(0), location);
+  return PointerOffset(ptr, offset, true, location);
 }
 
 llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
@@ -647,7 +658,7 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
 
   // "pointer - integer" operation.
   if (lhs_type.IsPointerType() && rhs_type.IsInteger())
-    return PointerAdd(lhs, -rhs->GetValueAsUnsigned(0), location);
+    return PointerOffset(lhs, rhs, false, location);
 
   // "pointer - pointer" operation.
   if (lhs_type.IsPointerType() && rhs_type.IsPointerType()) {
diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py
index b027fab1e176f..5da79a29bad01 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py
@@ -24,17 +24,25 @@ def test_pointer_arithmetic(self):
         self.expect_var_path("+p_int0", type="int *")
 
         # Binary operations
-        self.expect_var_path("p_char1", type="const char *")
-        self.expect_var_path("p_char1 + 1", type="const char *")
-        self.expect_var_path("p_char1 + offset", type="const char *")
+        self.expect_var_path("p_char", type="const char *")
+        self.expect_var_path("p_char + 1", type="const char *")
+        self.expect_var_path("p_char + offset", type="const char *")
+        self.expect_var_path("p_char5 + -1", type="const char *")
+        self.expect_var_path("p_char5 - 1", type="const char *")
+        self.expect_var_path("p_char5 - offset", type="const char *")
 
-        self.expect_var_path("my_p_char1", type="my_char_ptr")
-        self.expect_var_path("my_p_char1 + 1", type="my_char_ptr")
-        self.expect_var_path("my_p_char1 + offset", type="my_char_ptr")
+        self.expect_var_path("my_p_char", type="my_char_ptr")
+        self.expect_var_path("my_p_char + 1", type="my_char_ptr")
+        self.expect_var_path("my_p_char - 1", type="my_char_ptr")
 
-        self.expect_var_path("*(p_char1 + 0)", value="'h'")
-        self.expect_var_path("*(4 + p_char1)", value="'o'")
-        self.expect_var_path("*(p_char1 + offset - 1)", value="'o'")
+        self.expect_var_path("*(p_char + 0)", value="'h'")
+        self.expect_var_path("*(5 + p_char)", value="'!'")
+        self.expect_var_path("*(p_char5 + -5)", value="'h'")
+        self.expect_var_path("*(p_char5 - 5)", value="'h'")
+        self.expect_var_path("*(p_char - -5)", value="'!'")
+        self.expect_var_path("*(p_char5 - offset + 5)", value="'!'")
+        self.expect_var_path("*((p_char + offset) - 5)", value="'h'")
+        self.expect_var_path("*(p_char + (offset - 5))", value="'h'")
 
         self.expect_var_path("*p_int0", value="0")
         self.expect_var_path("*cp_int5", value="5")
@@ -63,7 +71,7 @@ def test_pointer_arithmetic(self):
             substrs=["invalid argument type 'int *' to unary expression"],
         )
         self.expect(
-            "frame var -- 'cp_int5 - p_char1'",
+            "frame var -- 'cp_int5 - p_char'",
             error=True,
             substrs=[
                 "'const int *' and 'const char *' are not pointers to compatible types"
@@ -87,7 +95,7 @@ def test_pointer_arithmetic(self):
             substrs=["arithmetic on a pointer to void"],
         )
         self.expect(
-            "frame var -- 'p_void - p_char1'",
+            "frame var -- 'p_void - p_char'",
             error=True,
             substrs=[
                 "'void *' and 'const char *' are not pointers to compatible types"
@@ -99,7 +107,7 @@ def test_pointer_arithmetic(self):
             substrs=["arithmetic on pointers to void"],
         )
         self.expect(
-            "frame var -- 'pp_void0 - p_char1'",
+            "frame var -- 'pp_void0 - p_char'",
             error=True,
             substrs=[
                 "'void **' and 'const char *' are not pointers to compatible types"
@@ -140,3 +148,13 @@ def test_pointer_arithmetic(self):
             error=True,
             substrs=["arithmetic on a nullptr is undefined"],
         )
+        self.expect(
+            "frame var -- 'p_char + *((int*) 0)'",
+            error=True,
+            substrs=["could not get the offset: parent is NULL"],
+        )
+        self.expect(
+            "frame var -- 'p_char - *((int*) 0)'",
+            error=True,
+            substrs=["could not get the offset: parent is NULL"],
+        )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
index be1f3424f62dc..02754e846f754 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
@@ -8,9 +8,10 @@ int main(int argc, char **argv) {
   int (&array_ref)[10] = array;
   int *p_int0 = &array[0];
 
-  const char *p_char1 = "hello";
+  const char *p_char = "hello!";
+  const char *p_char5 = p_char + 5;
   typedef const char *my_char_ptr;
-  my_char_ptr my_p_char1 = p_char1;
+  my_char_ptr my_p_char = p_char;
 
   int **pp_int0 = &p_int0;
   const int *cp_int0 = &array[0];
@@ -19,7 +20,7 @@ int main(int argc, char **argv) {
   typedef int *td_int_ptr_t;
   td_int_ptr_t td_int_ptr0 = &array[0];
 
-  void *p_void = (void *)p_char1;
+  void *p_void = (void *)p_char;
   void **pp_void0 = &p_void;
   void **pp_void1 = pp_void0 + 1;
 

>From 89114be1cfb255d12009ada2c6e007a03e493508 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Fri, 13 Mar 2026 18:07:50 +0500
Subject: [PATCH 4/4] Reduce comparing code, add assert, rename ptrdiff_t

---
 lldb/source/ValueObject/DILEval.cpp | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index cc402f1863e2a..447e4f637e84d 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -667,12 +667,9 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
           m_expr, "arithmetic on pointers to void", location);
     }
     // Compare canonical unqualified pointer types.
-    CompilerType lhs_unqualified_type =
-        lhs_type.GetCanonicalType().GetFullyUnqualifiedType();
-    CompilerType rhs_unqualified_type =
-        rhs_type.GetCanonicalType().GetFullyUnqualifiedType();
-    bool comparable = lhs_unqualified_type.CompareTypes(rhs_unqualified_type);
-    if (!comparable) {
+    CompilerType lhs_unqualified_type = lhs_type.GetCanonicalType();
+    CompilerType rhs_unqualified_type = rhs_type.GetCanonicalType();
+    if (!lhs_unqualified_type.CompareTypes(rhs_unqualified_type)) {
       std::string errMsg = llvm::formatv(
           "'{0}' and '{1}' are not pointers to compatible types",
           orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName());
@@ -687,6 +684,7 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
     int64_t item_size = *lhs_byte_size;
     int64_t diff = static_cast<int64_t>(lhs->GetValueAsUnsigned(0) -
                                         rhs->GetValueAsUnsigned(0));
+    assert(item_size > 0 && "Pointer size cannot be 0");
     if (diff % item_size != 0) {
       // If address difference isn't divisible by pointee size then performing
       // the operation is undefined behaviour.
@@ -699,14 +697,14 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
         GetTypeSystemFromCU(m_exe_ctx_scope);
     if (!type_system)
       return type_system.takeError();
-    CompilerType ptrdiff_t = type_system.get()->GetPointerDiffType(true);
-    if (!ptrdiff_t)
+    CompilerType ptrdiff_type = type_system.get()->GetPointerDiffType(true);
+    if (!ptrdiff_type)
       return llvm::make_error<DILDiagnosticError>(
           m_expr, "unable to determine pointer diff type", location);
 
     Scalar scalar(diff);
     return ValueObject::CreateValueObjectFromScalar(m_exe_ctx_scope, scalar,
-                                                    ptrdiff_t, "result");
+                                                    ptrdiff_type, "result");
   }
 
   std::string errMsg =



More information about the lldb-commits mailing list