[Lldb-commits] [lldb] Make result variables obey their dynamic values in subsequent expressions (PR #168611)

via lldb-commits lldb-commits at lists.llvm.org
Tue Nov 18 13:56:47 PST 2025


https://github.com/jimingham updated https://github.com/llvm/llvm-project/pull/168611

>From e32025162f6865c1cb886ddb592a63050fafa174 Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Tue, 15 Nov 2022 13:03:55 -0800
Subject: [PATCH 1/2] Make result variables obey their dynamic values in
 subsequent expressions.

At this stage, result variables work, but persistent expression variables
do not.  I have a test that which should work but does not.
---
 .../lldb/Expression/ExpressionVariable.h      |  61 +++++++---
 .../Python/lldbsuite/test/lldbtest.py         |   4 +-
 lldb/source/Expression/ExpressionVariable.cpp |  74 +++++++++++-
 lldb/source/Expression/LLVMUserExpression.cpp |  17 ++-
 lldb/source/Expression/Materializer.cpp       |  45 +++----
 .../Clang/ClangExpressionVariable.cpp         |   2 +-
 lldb/source/Target/ABI.cpp                    |   2 +-
 .../functionalities/expr-result-var/Makefile  |   3 +
 .../expr-result-var/TestCPPExprResult.py      | 113 ++++++++++++++++++
 .../expr-result-var/two-bases.cpp             |  64 ++++++++++
 10 files changed, 339 insertions(+), 46 deletions(-)
 create mode 100644 lldb/test/API/functionalities/expr-result-var/Makefile
 create mode 100644 lldb/test/API/functionalities/expr-result-var/TestCPPExprResult.py
 create mode 100644 lldb/test/API/functionalities/expr-result-var/two-bases.cpp

diff --git a/lldb/include/lldb/Expression/ExpressionVariable.h b/lldb/include/lldb/Expression/ExpressionVariable.h
index 68fa1c878a0e3..8b7eee63f0f92 100644
--- a/lldb/include/lldb/Expression/ExpressionVariable.h
+++ b/lldb/include/lldb/Expression/ExpressionVariable.h
@@ -33,11 +33,21 @@ class ExpressionVariable
 
   virtual ~ExpressionVariable() = default;
 
-  llvm::Expected<uint64_t> GetByteSize() { return m_frozen_sp->GetByteSize(); }
+  llvm::Expected<uint64_t> GetByteSize() { 
+    return GetValueObject()->GetByteSize(); 
+  
+  }
 
   ConstString GetName() { return m_frozen_sp->GetName(); }
 
-  lldb::ValueObjectSP GetValueObject() { return m_frozen_sp; }
+  lldb::ValueObjectSP GetValueObject() {
+    lldb::ValueObjectSP dyn_sp = 
+        m_frozen_sp->GetDynamicValue(lldb::eDynamicDontRunTarget);
+    if (dyn_sp && dyn_sp->UpdateValueIfNeeded())
+      return dyn_sp;
+    else
+      return m_frozen_sp; 
+  }
 
   uint8_t *GetValueBytes();
 
@@ -52,7 +62,7 @@ class ExpressionVariable
         Value::ContextType::RegisterInfo, const_cast<RegisterInfo *>(reg_info));
   }
 
-  CompilerType GetCompilerType() { return m_frozen_sp->GetCompilerType(); }
+  CompilerType GetCompilerType() { return GetValueObject()->GetCompilerType(); }
 
   void SetCompilerType(const CompilerType &compiler_type) {
     m_frozen_sp->GetValue().SetCompilerType(compiler_type);
@@ -60,22 +70,32 @@ class ExpressionVariable
 
   void SetName(ConstString name) { m_frozen_sp->SetName(name); }
 
-  // this function is used to copy the address-of m_live_sp into m_frozen_sp
-  // this is necessary because the results of certain cast and pointer-
+  // This function is used to copy the address-of m_live_sp into m_frozen_sp.
+  // This is necessary because the results of certain cast and pointer-
   // arithmetic operations (such as those described in bugzilla issues 11588
   // and 11618) generate frozen objects that do not have a valid address-of,
   // which can be troublesome when using synthetic children providers.
   // Transferring the address-of the live object solves these issues and
-  // provides the expected user-level behavior
-  void TransferAddress(bool force = false) {
-    if (m_live_sp.get() == nullptr)
-      return;
-
-    if (m_frozen_sp.get() == nullptr)
-      return;
-
-    if (force || (m_frozen_sp->GetLiveAddress() == LLDB_INVALID_ADDRESS))
-      m_frozen_sp->SetLiveAddress(m_live_sp->GetLiveAddress());
+  // provides the expected user-level behavior.
+  // The other job we do in TransferAddress is adjust the value in the live
+  // address slot in the target for the "offset to top" in multiply inherited
+  // class hierarchies.
+  void TransferAddress(bool force = false);
+
+  // When we build an expression variable we know whether we're going to use the
+  // static or dynamic result.  If we present the dynamic value once, we should
+  // use the dynamic value in future references to the variable, so we record
+  // that fact here.
+  void PreserveDynamicOption(lldb::DynamicValueType dyn_type) {
+    m_dyn_option = dyn_type;
+  }
+  // We don't try to get the dynamic value of the live object when we fetch
+  // it here.  The live object describes the container of the value in the
+  // target, but it's type is of the object for convenience.  So it can't 
+  // produce the dynamic value.  Instead, we use TransferAddress to adjust the
+  // value held by the LiveObject.
+  lldb::ValueObjectSP GetLiveObject() {
+    return m_live_sp;
   }
 
   enum Flags {
@@ -110,6 +130,14 @@ class ExpressionVariable
   /// These members should be private.
   /// @{
   /// A value object whose value's data lives in host (lldb's) memory.
+  /// The m_frozen_sp holds the data & type of the expression variable or result
+  /// in the host.  The m_frozen_sp also can present a dynamic value if one is
+  /// available.
+  /// The m_frozen_sp manages the copy of this value in m_frozen_sp that we 
+  /// insert in the target so that it can be referred to in future expressions.
+  /// We don't actually use the contents of the live_sp to create the value in
+  /// the target, that comes from the frozen sp.  The live_sp is mostly to track
+  /// the target-side of the value.
   lldb::ValueObjectSP m_frozen_sp;
   /// The ValueObject counterpart to m_frozen_sp that tracks the value in
   /// inferior memory. This object may not always exist; its presence depends on
@@ -119,6 +147,9 @@ class ExpressionVariable
   /// track.
   lldb::ValueObjectSP m_live_sp;
   /// @}
+
+  //LLVMCastKind m_kind;
+  lldb::DynamicValueType m_dyn_option = lldb::eNoDynamicValues;
 };
 
 /// \class ExpressionVariableList ExpressionVariable.h
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 8c1eea97620e2..927fa7679bbb5 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -2540,6 +2540,7 @@ def expect_expr(
         result_value=None,
         result_type=None,
         result_children=None,
+        options=None
     ):
         """
         Evaluates the given expression and verifies the result.
@@ -2556,7 +2557,8 @@ def expect_expr(
         )
 
         frame = self.frame()
-        options = lldb.SBExpressionOptions()
+        if not options:
+          options = lldb.SBExpressionOptions()
 
         # Disable fix-its that tests don't pass by accident.
         options.SetAutoApplyFixIts(False)
diff --git a/lldb/source/Expression/ExpressionVariable.cpp b/lldb/source/Expression/ExpressionVariable.cpp
index 9e8ea60f8e052..9d361bc853620 100644
--- a/lldb/source/Expression/ExpressionVariable.cpp
+++ b/lldb/source/Expression/ExpressionVariable.cpp
@@ -20,15 +20,16 @@ char ExpressionVariable::ID;
 ExpressionVariable::ExpressionVariable() : m_flags(0) {}
 
 uint8_t *ExpressionVariable::GetValueBytes() {
+  lldb::ValueObjectSP valobj_sp = GetValueObject();
   std::optional<uint64_t> byte_size =
-      llvm::expectedToOptional(m_frozen_sp->GetByteSize());
+      llvm::expectedToOptional(valobj_sp->GetByteSize());
   if (byte_size && *byte_size) {
-    if (m_frozen_sp->GetDataExtractor().GetByteSize() < *byte_size) {
-      m_frozen_sp->GetValue().ResizeData(*byte_size);
-      m_frozen_sp->GetValue().GetData(m_frozen_sp->GetDataExtractor());
+    if (valobj_sp->GetDataExtractor().GetByteSize() < *byte_size) {
+      valobj_sp->GetValue().ResizeData(*byte_size);
+      valobj_sp->GetValue().GetData(valobj_sp->GetDataExtractor());
     }
     return const_cast<uint8_t *>(
-        m_frozen_sp->GetDataExtractor().GetDataStart());
+        valobj_sp->GetDataExtractor().GetDataStart());
   }
   return nullptr;
 }
@@ -37,6 +38,69 @@ char PersistentExpressionState::ID;
 
 PersistentExpressionState::PersistentExpressionState() = default;
 
+void ExpressionVariable::TransferAddress(bool force) {
+  if (m_live_sp.get() == nullptr)
+    return;
+
+  if (m_frozen_sp.get() == nullptr)
+    return;
+
+  if (force || (m_frozen_sp->GetLiveAddress() == LLDB_INVALID_ADDRESS)) {
+    lldb::addr_t live_addr = m_live_sp->GetLiveAddress();
+    m_frozen_sp->SetLiveAddress(live_addr);
+    // One more detail, if there's an offset_to_top in the frozen_sp, then we
+    // need to appy that offset by hand.  The live_sp can't compute this 
+    // itself as its type is the type of the contained object which confuses
+    // the dynamic type calculation.  So we have to update the contents of the
+    // m_live_sp with the dynamic value.
+    // Note: We could get this right when we originally write the address, but
+    // that happens in different ways for the various flavors of 
+    // Entity*::Materialize, but everything comes through here, and it's just
+    // one extra memory write.
+    
+    // You can only have an "offset_to_top" with pointers or references:
+    if (!m_frozen_sp->GetCompilerType().IsPointerOrReferenceType())
+      return;
+
+    lldb::ProcessSP process_sp = m_frozen_sp->GetProcessSP();
+    // If there's no dynamic value, then there can't be an offset_to_top:
+    if (!process_sp 
+        || !process_sp->IsPossibleDynamicValue(*(m_frozen_sp.get())))
+      return;
+
+    lldb::ValueObjectSP dyn_sp = m_frozen_sp->GetDynamicValue(m_dyn_option);
+    if (!dyn_sp)
+      return;
+    ValueObject::AddrAndType static_addr = m_frozen_sp->GetPointerValue();
+    if (static_addr.type != eAddressTypeLoad)
+      return;
+
+    ValueObject::AddrAndType dynamic_addr= dyn_sp->GetPointerValue();
+    if (dynamic_addr.type != eAddressTypeLoad || static_addr.address == dynamic_addr.address)
+        return;
+      
+    Status error;
+    Log *log = GetLog(LLDBLog::Expressions);
+    lldb::addr_t cur_value 
+        = process_sp->ReadPointerFromMemory(live_addr, error);
+    if (error.Fail())
+      return;
+    
+    if (cur_value != static_addr.address) {
+      LLDB_LOG(log, "Stored value: {0} read from {1} doesn't "
+               "match static addr: {2}",
+               cur_value, live_addr, static_addr.address);
+      return;
+    }
+    
+    if (!process_sp->WritePointerToMemory(live_addr, dynamic_addr.address, error)) {
+      LLDB_LOG(log, "Got error: {0} writing dynamic value: {1} to {2}",
+               error, dynamic_addr.address, live_addr);
+      return;
+    }
+  }
+}
+
 PersistentExpressionState::~PersistentExpressionState() = default;
 
 lldb::addr_t PersistentExpressionState::LookupSymbol(ConstString name) {
diff --git a/lldb/source/Expression/LLVMUserExpression.cpp b/lldb/source/Expression/LLVMUserExpression.cpp
index 2d59194027b57..dac572e815fac 100644
--- a/lldb/source/Expression/LLVMUserExpression.cpp
+++ b/lldb/source/Expression/LLVMUserExpression.cpp
@@ -65,7 +65,7 @@ LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager,
                               ExecutionContext &exe_ctx,
                               const EvaluateExpressionOptions &options,
                               lldb::UserExpressionSP &shared_ptr_to_me,
-                              lldb::ExpressionVariableSP &result) {
+                              lldb::ExpressionVariableSP &result_sp) {
   // The expression log is quite verbose, and if you're just tracking the
   // execution of the expression, it's quite convenient to have these logs come
   // out with the STEP log as well.
@@ -250,8 +250,13 @@ LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager,
     }
   }
 
-  if (FinalizeJITExecution(diagnostic_manager, exe_ctx, result,
+  if (FinalizeJITExecution(diagnostic_manager, exe_ctx, result_sp,
                            function_stack_bottom, function_stack_top)) {
+//    if (result_sp) {
+//      // This is a bit of a hack, replace with a real API.  Trying to force 
+//      // fetching all the dynamic info at this point.
+//      result_sp->GetValueObject();
+//    }
     return lldb::eExpressionCompleted;
   }
 
@@ -289,8 +294,14 @@ bool LLVMUserExpression::FinalizeJITExecution(
   result =
       GetResultAfterDematerialization(exe_ctx.GetBestExecutionContextScope());
 
-  if (result)
+  if (result) {
+    EvaluateExpressionOptions *options = GetOptions();
+    // TransferAddress also does the offset_to_top calculation, so record the
+    // dynamic option before we do that.
+    if (options)
+      result->PreserveDynamicOption(options->GetUseDynamic());
     result->TransferAddress();
+  }
 
   m_dematerializer_sp.reset();
 
diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp
index 771a9ab84a20c..6561aff39217a 100644
--- a/lldb/source/Expression/Materializer.cpp
+++ b/lldb/source/Expression/Materializer.cpp
@@ -76,9 +76,9 @@ class EntityPersistentVariable : public Materializer::Entity {
 
     const bool zero_memory = false;
     IRMemoryMap::AllocationPolicy used_policy;
-    auto address_or_error = map.Malloc(
-        llvm::expectedToOptional(m_persistent_variable_sp->GetByteSize())
-            .value_or(0),
+    uint64_t malloc_size = llvm::expectedToOptional(
+        m_persistent_variable_sp->GetByteSize()).value_or(0);
+    auto address_or_error = map.Malloc(malloc_size,
         8, lldb::ePermissionsReadable | lldb::ePermissionsWritable,
         IRMemoryMap::eAllocationPolicyMirror, zero_memory, &used_policy);
     if (!address_or_error) {
@@ -90,7 +90,8 @@ class EntityPersistentVariable : public Materializer::Entity {
     }
     lldb::addr_t mem = *address_or_error;
 
-    LLDB_LOGF(log, "Allocated %s (0x%" PRIx64 ") successfully",
+    LLDB_LOGF(log, "Allocated 0x%" PRIx64 "bytes for %s (0x%" PRIx64 
+              ") successfully", malloc_size, 
               m_persistent_variable_sp->GetName().GetCString(), mem);
 
     // Put the location of the spare memory into the live data of the
@@ -143,12 +144,14 @@ class EntityPersistentVariable : public Materializer::Entity {
   void DestroyAllocation(IRMemoryMap &map, Status &err) {
     Status deallocate_error;
 
-    map.Free((lldb::addr_t)m_persistent_variable_sp->m_live_sp->GetValue()
+    lldb::ValueObjectSP live_valobj_sp 
+        = m_persistent_variable_sp->GetLiveObject();
+    map.Free((lldb::addr_t)live_valobj_sp->GetValue()
                  .GetScalar()
                  .ULongLong(),
              deallocate_error);
 
-    m_persistent_variable_sp->m_live_sp.reset();
+    live_valobj_sp.reset();
 
     if (!deallocate_error.Success()) {
       err = Status::FromErrorStringWithFormat(
@@ -183,16 +186,17 @@ class EntityPersistentVariable : public Materializer::Entity {
         return;
     }
 
+    lldb::ValueObjectSP live_valobj_sp 
+        = m_persistent_variable_sp->GetLiveObject();
     if ((m_persistent_variable_sp->m_flags &
-             ExpressionVariable::EVIsProgramReference &&
-         m_persistent_variable_sp->m_live_sp) ||
+             ExpressionVariable::EVIsProgramReference && live_valobj_sp) ||
         m_persistent_variable_sp->m_flags &
             ExpressionVariable::EVIsLLDBAllocated) {
       Status write_error;
 
       map.WriteScalarToMemory(
           load_addr,
-          m_persistent_variable_sp->m_live_sp->GetValue().GetScalar(),
+          live_valobj_sp->GetValue().GetScalar(),
           map.GetAddressByteSize(), write_error);
 
       if (!write_error.Success()) {
@@ -229,18 +233,20 @@ class EntityPersistentVariable : public Materializer::Entity {
       m_delegate->DidDematerialize(m_persistent_variable_sp);
     }
 
+    lldb::ValueObjectSP live_valobj_sp 
+          = m_persistent_variable_sp->GetLiveObject();
     if ((m_persistent_variable_sp->m_flags &
          ExpressionVariable::EVIsLLDBAllocated) ||
         (m_persistent_variable_sp->m_flags &
          ExpressionVariable::EVIsProgramReference)) {
       if (m_persistent_variable_sp->m_flags &
               ExpressionVariable::EVIsProgramReference &&
-          !m_persistent_variable_sp->m_live_sp) {
+          !live_valobj_sp) {
         // If the reference comes from the program, then the
         // ClangExpressionVariable's live variable data hasn't been set up yet.
         // Do this now.
 
-        lldb::addr_t location;
+       lldb::addr_t location;
         Status read_error;
 
         map.ReadPointerFromMemory(&location, load_addr, read_error);
@@ -255,7 +261,7 @@ class EntityPersistentVariable : public Materializer::Entity {
 
         m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create(
             map.GetBestExecutionContextScope(),
-            m_persistent_variable_sp.get()->GetCompilerType(),
+            m_persistent_variable_sp->GetCompilerType(),
             m_persistent_variable_sp->GetName(), location, eAddressTypeLoad,
             llvm::expectedToOptional(m_persistent_variable_sp->GetByteSize())
                 .value_or(0));
@@ -277,19 +283,19 @@ class EntityPersistentVariable : public Materializer::Entity {
         }
       }
 
-      lldb::addr_t mem = m_persistent_variable_sp->m_live_sp->GetValue()
-                             .GetScalar()
-                             .ULongLong();
-
-      if (!m_persistent_variable_sp->m_live_sp) {
+      if (!live_valobj_sp) {
         err = Status::FromErrorStringWithFormat(
             "couldn't find the memory area used to store %s",
             m_persistent_variable_sp->GetName().GetCString());
         return;
       }
 
-      if (m_persistent_variable_sp->m_live_sp->GetValue()
-              .GetValueAddressType() != eAddressTypeLoad) {
+      lldb::addr_t mem = live_valobj_sp->GetValue()
+                             .GetScalar()
+                             .ULongLong();
+
+      if (live_valobj_sp->GetValue().GetValueAddressType() 
+          != eAddressTypeLoad) {
         err = Status::FromErrorStringWithFormat(
             "the address of the memory area for %s is in an incorrect format",
             m_persistent_variable_sp->GetName().GetCString());
@@ -326,7 +332,6 @@ class EntityPersistentVariable : public Materializer::Entity {
               read_error.AsCString());
           return;
         }
-
         m_persistent_variable_sp->m_flags &=
             ~ExpressionVariable::EVNeedsFreezeDry;
       }
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp
index e2fb4a054daf3..d7b3fe0167299 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp
@@ -59,6 +59,6 @@ ClangExpressionVariable::ClangExpressionVariable(
 }
 
 TypeFromUser ClangExpressionVariable::GetTypeFromUser() {
-  TypeFromUser tfu(m_frozen_sp->GetCompilerType());
+  TypeFromUser tfu(GetValueObject()->GetCompilerType());
   return tfu;
 }
diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp
index 3c51074340149..33bfa6b66fd06 100644
--- a/lldb/source/Target/ABI.cpp
+++ b/lldb/source/Target/ABI.cpp
@@ -136,7 +136,7 @@ ValueObjectSP ABI::GetReturnValueObject(Thread &thread, CompilerType &ast_type,
           ExpressionVariable::EVNeedsAllocation;
       break;
     case Value::ValueType::LoadAddress:
-      expr_variable_sp->m_live_sp = live_valobj_sp;
+      expr_variable_sp->GetLiveObject() = live_valobj_sp;
       expr_variable_sp->m_flags |=
           ExpressionVariable::EVIsProgramReference;
       break;
diff --git a/lldb/test/API/functionalities/expr-result-var/Makefile b/lldb/test/API/functionalities/expr-result-var/Makefile
new file mode 100644
index 0000000000000..6c259307ef229
--- /dev/null
+++ b/lldb/test/API/functionalities/expr-result-var/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := two-bases.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/expr-result-var/TestCPPExprResult.py b/lldb/test/API/functionalities/expr-result-var/TestCPPExprResult.py
new file mode 100644
index 0000000000000..020fdd341faaa
--- /dev/null
+++ b/lldb/test/API/functionalities/expr-result-var/TestCPPExprResult.py
@@ -0,0 +1,113 @@
+"""
+Test the reuse of  C++ result variables, particularly making sure
+that the dynamic typing is preserved.
+"""
+
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCPPResultVariables(TestBase):
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setUp(self):
+        TestBase.setUp(self)
+        self.main_source_file = lldb.SBFileSpec("two-bases.cpp")
+        
+    def check_dereference(self, result_varname, frame, expr_options):
+        deref_expr = "*{0}".format(result_varname)
+        base_children = ValueCheck(name="Base", value="", children=[ValueCheck(name="base_int", value="100")])
+        base_1_arr_children= [ValueCheck(name="[0]", value="100"), ValueCheck(name="[1]", value="101"),
+                              ValueCheck(name="[2]", value="102"), ValueCheck(name="[3]", value="103"),
+                              ValueCheck(name="[4]", value="104"), ValueCheck(name="[5]", value="105"),
+                              ValueCheck(name="[6]", value="106"), ValueCheck(name="[7]", value="107"),
+                              ValueCheck(name="[8]", value="108"), ValueCheck(name="[9]", value="109")]
+        base_2_arr_children= [ValueCheck(name="[0]", value="200"), ValueCheck(name="[1]", value="201"),
+                              ValueCheck(name="[2]", value="202"), ValueCheck(name="[3]", value="203"),
+                              ValueCheck(name="[4]", value="204"), ValueCheck(name="[5]", value="205"),
+                              ValueCheck(name="[6]", value="206"), ValueCheck(name="[7]", value="207"),
+                              ValueCheck(name="[8]", value="208"), ValueCheck(name="[9]", value="209")]
+        deref_children=[ValueCheck(name="Base_1", value="",
+                                   children=[base_children, ValueCheck(name="base_1_arr", value="", children=base_1_arr_children)]),
+                        ValueCheck(name="Base_2", value="",
+                                   children=[base_children, ValueCheck(name="base_2_arr", value="", children = base_2_arr_children)]),
+                        ValueCheck(name="derived_int", value="1000")]
+        result_var_deref = self.expect_expr(deref_expr, result_type="Derived",
+                                            result_children = deref_children, options=expr_options) 
+
+        direct_access_expr = "{0}->derived_int".format(result_varname)
+        self.expect_expr(direct_access_expr, result_type="int", result_value="1000")
+
+        # Also check this by directly accessing the result variable:
+        result_value = frame.FindValue(result_varname, lldb.eValueTypeConstResult, True)
+        self.assertTrue(result_value.error.success, "Found my result variable")
+        value_check = ValueCheck(children = deref_children)
+        value_check.check_value(self, result_value, f"{result_varname} children are correct")
+        
+        # Make sure we can also call a function through the derived type:
+        method_result = self.expect_expr(
+            f"{result_varname}->method_of_derived()",
+            result_type="int",
+            options=expr_options)
+        self.assertEqual(method_result.signed, 500, "Got the right result value")
+
+    def test_virtual_dynamic_results(self):
+        self.do_test_dynamic_results(True)
+
+    def test_non_virtual_dynamic_results(self):
+        self.do_test_dynamic_results(False)
+        
+    def do_test_dynamic_results(self, virtual):
+        """Test that when we uses a result variable in a subsequent expression it
+           uses the dynamic value - if that was requested when the result variable was made."""
+        if virtual:
+            self.build(dictionary={"CFLAGS_EXTRAS": "-DVIRTUAL=''"})
+        else:
+            self.build(dictionary={"CFLAGS_EXTRAS": "-DVIRTUAL='virtual'"})
+
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
+                                    "Set a breakpoint here", self.main_source_file)
+
+        frame = thread.GetFrameAtIndex(0)
+        expr_options = lldb.SBExpressionOptions()
+        expr_options.SetFetchDynamicValue(lldb.eDynamicDontRunTarget)
+        base_1_ptr = self.expect_expr("base_1_ptr", result_type="Derived *", options=expr_options)
+        result_varname = base_1_ptr.GetName()
+        self.check_dereference(result_varname, frame, expr_options)
+
+        # Now do the same thing, but use a persistent result variable:
+        empty_var = frame.EvaluateExpression("void *$base_1_ptr = base_1_ptr", expr_options)
+        self.assertIn(
+            empty_var.error.description, "unknown error",
+            "Expressions that don't have results return this error"
+        )
+        persist_base_1_ptr = frame.FindValue("$base_1_ptr", lldb.eValueTypeConstResult, True)
+        self.assertTrue(persist_base_1_ptr.error.success, "Got the persistent variable")
+        self.check_dereference("$base_1_ptr", frame, expr_options)
+        
+        # Now check the second of the multiply inherited bases, this one will have an offset_to_top
+        # that we need to calculate:
+        base_2_ptr = self.expect_expr("base_2_ptr", result_type="Derived *", options=expr_options)
+        self.check_dereference(base_2_ptr.GetName(), frame, expr_options)
+
+        # Again, do the same thing for a persistent expression variable:
+        empty_var = frame.EvaluateExpression("void *$base_2_ptr = base_2_ptr", expr_options)
+        self.check_dereference("$base_2_ptr", frame, expr_options)
+
+        # Now try starting from a virtual base class of both our bases:
+        base_through_1 = self.expect_expr("base_through_1", result_type = "Derived *", options=expr_options)
+        self.check_dereference(base_through_1.GetName(), frame, expr_options)
+
+        # Now try starting from a virtual base class of both our bases:
+        base_through_2 = self.expect_expr("base_through_2", result_type = "Derived *", options=expr_options)
+        self.check_dereference(base_through_2.GetName(), frame, expr_options)
+        
+        # Now check that we get the right results when we run an
+        # expression to get the base class object:
+        base_through_expr = self.expect_expr("MakeADerivedReportABase()", result_type = "Derived *", options=expr_options)
+        self.check_dereference(base_through_expr.GetName(), frame, expr_options) 
diff --git a/lldb/test/API/functionalities/expr-result-var/two-bases.cpp b/lldb/test/API/functionalities/expr-result-var/two-bases.cpp
new file mode 100644
index 0000000000000..fa6fab0b47279
--- /dev/null
+++ b/lldb/test/API/functionalities/expr-result-var/two-bases.cpp
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <stdint.h>
+
+struct Base {
+  virtual ~Base() = default;
+  int base_int = 100;
+  Base *return_me() { return this; }
+};
+
+struct Base_1 : public VIRTUAL Base {
+  virtual ~Base_1() = default;
+  int base_1_arr[10] = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };
+  Base *return_base_1() { return return_me(); }
+};
+
+struct Base_2 :public VIRTUAL Base {
+  virtual ~Base_2() = default;
+  int base_2_arr[10] = { 200, 201, 202, 203, 204, 205, 206, 207, 208, 209 };
+  Base *return_base_2() { return return_me(); }
+};
+
+struct Derived : public Base_1, Base_2
+{
+  virtual ~Derived() = default;
+  int derived_int = 1000;
+  int method_of_derived() {
+    return 500;
+  }
+};
+
+Base *MakeADerivedReportABase() {
+  return (Base *) ((Base_1 *) new Derived());
+}
+
+int
+main()
+{
+  Derived my_derived;
+  int call_it = my_derived.method_of_derived();
+
+  Base_1 *base_1_ptr = (Base_1 *) &my_derived;
+
+  Base_2 *base_2_ptr = (Base_2 *) &my_derived;
+
+  Base *base_through_1 = my_derived.return_base_1();
+  Base *base_through_2 = my_derived.return_base_2();;
+
+  // Call this to make sure the compiler makes it. 
+  Base *fake_base = MakeADerivedReportABase();
+
+  uint64_t base_through_1_addr = (uint64_t) base_through_1;
+  uint64_t base_through_2_addr = (uint64_t) base_through_2;
+  int64_t base_offset = base_through_2_addr - base_through_1_addr;
+  printf("Base offset (should be 0): 0x%llx.\n", base_offset);
+  uint64_t base_1_addr = (uint64_t) base_1_ptr;
+  uint64_t base_2_addr = (uint64_t) base_2_ptr;
+  int64_t offset = base_2_addr - base_1_addr;
+
+  // Set a breakpoint here
+  return my_derived.derived_int + base_1_ptr->base_1_arr[0]
+    + base_2_ptr->base_2_arr[0] + my_derived.return_base_1()->base_int;
+  
+}
+

>From de74f3ffca33d0b4879006800b8f792350f5131d Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Tue, 18 Nov 2025 13:56:28 -0800
Subject: [PATCH 2/2] Formatting

---
 .../lldb/Expression/ExpressionVariable.h      |  19 +--
 .../Python/lldbsuite/test/lldbtest.py         |   4 +-
 lldb/source/Expression/ExpressionVariable.cpp |  40 ++---
 lldb/source/Expression/LLVMUserExpression.cpp |  11 +-
 lldb/source/Expression/Materializer.cpp       |  50 +++---
 .../expr-result-var/TestCPPExprResult.py      | 142 ++++++++++++------
 .../expr-result-var/two-bases.cpp             |  46 +++---
 7 files changed, 179 insertions(+), 133 deletions(-)

diff --git a/lldb/include/lldb/Expression/ExpressionVariable.h b/lldb/include/lldb/Expression/ExpressionVariable.h
index 8b7eee63f0f92..705263a3715e3 100644
--- a/lldb/include/lldb/Expression/ExpressionVariable.h
+++ b/lldb/include/lldb/Expression/ExpressionVariable.h
@@ -33,20 +33,19 @@ class ExpressionVariable
 
   virtual ~ExpressionVariable() = default;
 
-  llvm::Expected<uint64_t> GetByteSize() { 
-    return GetValueObject()->GetByteSize(); 
-  
+  llvm::Expected<uint64_t> GetByteSize() {
+    return GetValueObject()->GetByteSize();
   }
 
   ConstString GetName() { return m_frozen_sp->GetName(); }
 
   lldb::ValueObjectSP GetValueObject() {
-    lldb::ValueObjectSP dyn_sp = 
+    lldb::ValueObjectSP dyn_sp =
         m_frozen_sp->GetDynamicValue(lldb::eDynamicDontRunTarget);
     if (dyn_sp && dyn_sp->UpdateValueIfNeeded())
       return dyn_sp;
     else
-      return m_frozen_sp; 
+      return m_frozen_sp;
   }
 
   uint8_t *GetValueBytes();
@@ -91,12 +90,10 @@ class ExpressionVariable
   }
   // We don't try to get the dynamic value of the live object when we fetch
   // it here.  The live object describes the container of the value in the
-  // target, but it's type is of the object for convenience.  So it can't 
+  // target, but it's type is of the object for convenience.  So it can't
   // produce the dynamic value.  Instead, we use TransferAddress to adjust the
   // value held by the LiveObject.
-  lldb::ValueObjectSP GetLiveObject() {
-    return m_live_sp;
-  }
+  lldb::ValueObjectSP GetLiveObject() { return m_live_sp; }
 
   enum Flags {
     EVNone = 0,
@@ -133,7 +130,7 @@ class ExpressionVariable
   /// The m_frozen_sp holds the data & type of the expression variable or result
   /// in the host.  The m_frozen_sp also can present a dynamic value if one is
   /// available.
-  /// The m_frozen_sp manages the copy of this value in m_frozen_sp that we 
+  /// The m_frozen_sp manages the copy of this value in m_frozen_sp that we
   /// insert in the target so that it can be referred to in future expressions.
   /// We don't actually use the contents of the live_sp to create the value in
   /// the target, that comes from the frozen sp.  The live_sp is mostly to track
@@ -148,7 +145,7 @@ class ExpressionVariable
   lldb::ValueObjectSP m_live_sp;
   /// @}
 
-  //LLVMCastKind m_kind;
+  // LLVMCastKind m_kind;
   lldb::DynamicValueType m_dyn_option = lldb::eNoDynamicValues;
 };
 
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 927fa7679bbb5..f326909308589 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -2540,7 +2540,7 @@ def expect_expr(
         result_value=None,
         result_type=None,
         result_children=None,
-        options=None
+        options=None,
     ):
         """
         Evaluates the given expression and verifies the result.
@@ -2558,7 +2558,7 @@ def expect_expr(
 
         frame = self.frame()
         if not options:
-          options = lldb.SBExpressionOptions()
+            options = lldb.SBExpressionOptions()
 
         # Disable fix-its that tests don't pass by accident.
         options.SetAutoApplyFixIts(False)
diff --git a/lldb/source/Expression/ExpressionVariable.cpp b/lldb/source/Expression/ExpressionVariable.cpp
index 9d361bc853620..7c9c99f0a5cd6 100644
--- a/lldb/source/Expression/ExpressionVariable.cpp
+++ b/lldb/source/Expression/ExpressionVariable.cpp
@@ -28,8 +28,7 @@ uint8_t *ExpressionVariable::GetValueBytes() {
       valobj_sp->GetValue().ResizeData(*byte_size);
       valobj_sp->GetValue().GetData(valobj_sp->GetDataExtractor());
     }
-    return const_cast<uint8_t *>(
-        valobj_sp->GetDataExtractor().GetDataStart());
+    return const_cast<uint8_t *>(valobj_sp->GetDataExtractor().GetDataStart());
   }
   return nullptr;
 }
@@ -49,23 +48,23 @@ void ExpressionVariable::TransferAddress(bool force) {
     lldb::addr_t live_addr = m_live_sp->GetLiveAddress();
     m_frozen_sp->SetLiveAddress(live_addr);
     // One more detail, if there's an offset_to_top in the frozen_sp, then we
-    // need to appy that offset by hand.  The live_sp can't compute this 
+    // need to appy that offset by hand.  The live_sp can't compute this
     // itself as its type is the type of the contained object which confuses
     // the dynamic type calculation.  So we have to update the contents of the
     // m_live_sp with the dynamic value.
     // Note: We could get this right when we originally write the address, but
-    // that happens in different ways for the various flavors of 
+    // that happens in different ways for the various flavors of
     // Entity*::Materialize, but everything comes through here, and it's just
     // one extra memory write.
-    
+
     // You can only have an "offset_to_top" with pointers or references:
     if (!m_frozen_sp->GetCompilerType().IsPointerOrReferenceType())
       return;
 
     lldb::ProcessSP process_sp = m_frozen_sp->GetProcessSP();
     // If there's no dynamic value, then there can't be an offset_to_top:
-    if (!process_sp 
-        || !process_sp->IsPossibleDynamicValue(*(m_frozen_sp.get())))
+    if (!process_sp ||
+        !process_sp->IsPossibleDynamicValue(*(m_frozen_sp.get())))
       return;
 
     lldb::ValueObjectSP dyn_sp = m_frozen_sp->GetDynamicValue(m_dyn_option);
@@ -75,27 +74,30 @@ void ExpressionVariable::TransferAddress(bool force) {
     if (static_addr.type != eAddressTypeLoad)
       return;
 
-    ValueObject::AddrAndType dynamic_addr= dyn_sp->GetPointerValue();
-    if (dynamic_addr.type != eAddressTypeLoad || static_addr.address == dynamic_addr.address)
-        return;
-      
+    ValueObject::AddrAndType dynamic_addr = dyn_sp->GetPointerValue();
+    if (dynamic_addr.type != eAddressTypeLoad ||
+        static_addr.address == dynamic_addr.address)
+      return;
+
     Status error;
     Log *log = GetLog(LLDBLog::Expressions);
-    lldb::addr_t cur_value 
-        = process_sp->ReadPointerFromMemory(live_addr, error);
+    lldb::addr_t cur_value =
+        process_sp->ReadPointerFromMemory(live_addr, error);
     if (error.Fail())
       return;
-    
+
     if (cur_value != static_addr.address) {
-      LLDB_LOG(log, "Stored value: {0} read from {1} doesn't "
+      LLDB_LOG(log,
+               "Stored value: {0} read from {1} doesn't "
                "match static addr: {2}",
                cur_value, live_addr, static_addr.address);
       return;
     }
-    
-    if (!process_sp->WritePointerToMemory(live_addr, dynamic_addr.address, error)) {
-      LLDB_LOG(log, "Got error: {0} writing dynamic value: {1} to {2}",
-               error, dynamic_addr.address, live_addr);
+
+    if (!process_sp->WritePointerToMemory(live_addr, dynamic_addr.address,
+                                          error)) {
+      LLDB_LOG(log, "Got error: {0} writing dynamic value: {1} to {2}", error,
+               dynamic_addr.address, live_addr);
       return;
     }
   }
diff --git a/lldb/source/Expression/LLVMUserExpression.cpp b/lldb/source/Expression/LLVMUserExpression.cpp
index dac572e815fac..e78543c4abdb2 100644
--- a/lldb/source/Expression/LLVMUserExpression.cpp
+++ b/lldb/source/Expression/LLVMUserExpression.cpp
@@ -252,11 +252,12 @@ LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager,
 
   if (FinalizeJITExecution(diagnostic_manager, exe_ctx, result_sp,
                            function_stack_bottom, function_stack_top)) {
-//    if (result_sp) {
-//      // This is a bit of a hack, replace with a real API.  Trying to force 
-//      // fetching all the dynamic info at this point.
-//      result_sp->GetValueObject();
-//    }
+    //    if (result_sp) {
+    //      // This is a bit of a hack, replace with a real API.  Trying to
+    //      force
+    //      // fetching all the dynamic info at this point.
+    //      result_sp->GetValueObject();
+    //    }
     return lldb::eExpressionCompleted;
   }
 
diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp
index 6561aff39217a..cbc197e3d1392 100644
--- a/lldb/source/Expression/Materializer.cpp
+++ b/lldb/source/Expression/Materializer.cpp
@@ -76,10 +76,11 @@ class EntityPersistentVariable : public Materializer::Entity {
 
     const bool zero_memory = false;
     IRMemoryMap::AllocationPolicy used_policy;
-    uint64_t malloc_size = llvm::expectedToOptional(
-        m_persistent_variable_sp->GetByteSize()).value_or(0);
-    auto address_or_error = map.Malloc(malloc_size,
-        8, lldb::ePermissionsReadable | lldb::ePermissionsWritable,
+    uint64_t malloc_size =
+        llvm::expectedToOptional(m_persistent_variable_sp->GetByteSize())
+            .value_or(0);
+    auto address_or_error = map.Malloc(
+        malloc_size, 8, lldb::ePermissionsReadable | lldb::ePermissionsWritable,
         IRMemoryMap::eAllocationPolicyMirror, zero_memory, &used_policy);
     if (!address_or_error) {
       err = Status::FromErrorStringWithFormat(
@@ -90,9 +91,9 @@ class EntityPersistentVariable : public Materializer::Entity {
     }
     lldb::addr_t mem = *address_or_error;
 
-    LLDB_LOGF(log, "Allocated 0x%" PRIx64 "bytes for %s (0x%" PRIx64 
-              ") successfully", malloc_size, 
-              m_persistent_variable_sp->GetName().GetCString(), mem);
+    LLDB_LOGF(
+        log, "Allocated 0x%" PRIx64 "bytes for %s (0x%" PRIx64 ") successfully",
+        malloc_size, m_persistent_variable_sp->GetName().GetCString(), mem);
 
     // Put the location of the spare memory into the live data of the
     // ValueObject.
@@ -144,11 +145,9 @@ class EntityPersistentVariable : public Materializer::Entity {
   void DestroyAllocation(IRMemoryMap &map, Status &err) {
     Status deallocate_error;
 
-    lldb::ValueObjectSP live_valobj_sp 
-        = m_persistent_variable_sp->GetLiveObject();
-    map.Free((lldb::addr_t)live_valobj_sp->GetValue()
-                 .GetScalar()
-                 .ULongLong(),
+    lldb::ValueObjectSP live_valobj_sp =
+        m_persistent_variable_sp->GetLiveObject();
+    map.Free((lldb::addr_t)live_valobj_sp->GetValue().GetScalar().ULongLong(),
              deallocate_error);
 
     live_valobj_sp.reset();
@@ -186,18 +185,17 @@ class EntityPersistentVariable : public Materializer::Entity {
         return;
     }
 
-    lldb::ValueObjectSP live_valobj_sp 
-        = m_persistent_variable_sp->GetLiveObject();
+    lldb::ValueObjectSP live_valobj_sp =
+        m_persistent_variable_sp->GetLiveObject();
     if ((m_persistent_variable_sp->m_flags &
-             ExpressionVariable::EVIsProgramReference && live_valobj_sp) ||
+             ExpressionVariable::EVIsProgramReference &&
+         live_valobj_sp) ||
         m_persistent_variable_sp->m_flags &
             ExpressionVariable::EVIsLLDBAllocated) {
       Status write_error;
 
-      map.WriteScalarToMemory(
-          load_addr,
-          live_valobj_sp->GetValue().GetScalar(),
-          map.GetAddressByteSize(), write_error);
+      map.WriteScalarToMemory(load_addr, live_valobj_sp->GetValue().GetScalar(),
+                              map.GetAddressByteSize(), write_error);
 
       if (!write_error.Success()) {
         err = Status::FromErrorStringWithFormat(
@@ -233,8 +231,8 @@ class EntityPersistentVariable : public Materializer::Entity {
       m_delegate->DidDematerialize(m_persistent_variable_sp);
     }
 
-    lldb::ValueObjectSP live_valobj_sp 
-          = m_persistent_variable_sp->GetLiveObject();
+    lldb::ValueObjectSP live_valobj_sp =
+        m_persistent_variable_sp->GetLiveObject();
     if ((m_persistent_variable_sp->m_flags &
          ExpressionVariable::EVIsLLDBAllocated) ||
         (m_persistent_variable_sp->m_flags &
@@ -246,7 +244,7 @@ class EntityPersistentVariable : public Materializer::Entity {
         // ClangExpressionVariable's live variable data hasn't been set up yet.
         // Do this now.
 
-       lldb::addr_t location;
+        lldb::addr_t location;
         Status read_error;
 
         map.ReadPointerFromMemory(&location, load_addr, read_error);
@@ -290,12 +288,10 @@ class EntityPersistentVariable : public Materializer::Entity {
         return;
       }
 
-      lldb::addr_t mem = live_valobj_sp->GetValue()
-                             .GetScalar()
-                             .ULongLong();
+      lldb::addr_t mem = live_valobj_sp->GetValue().GetScalar().ULongLong();
 
-      if (live_valobj_sp->GetValue().GetValueAddressType() 
-          != eAddressTypeLoad) {
+      if (live_valobj_sp->GetValue().GetValueAddressType() !=
+          eAddressTypeLoad) {
         err = Status::FromErrorStringWithFormat(
             "the address of the memory area for %s is in an incorrect format",
             m_persistent_variable_sp->GetName().GetCString());
diff --git a/lldb/test/API/functionalities/expr-result-var/TestCPPExprResult.py b/lldb/test/API/functionalities/expr-result-var/TestCPPExprResult.py
index 020fdd341faaa..d560ce9059d15 100644
--- a/lldb/test/API/functionalities/expr-result-var/TestCPPExprResult.py
+++ b/lldb/test/API/functionalities/expr-result-var/TestCPPExprResult.py
@@ -4,7 +4,6 @@
 """
 
 
-
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -12,33 +11,70 @@
 
 
 class TestCPPResultVariables(TestBase):
-
     NO_DEBUG_INFO_TESTCASE = True
 
     def setUp(self):
         TestBase.setUp(self)
         self.main_source_file = lldb.SBFileSpec("two-bases.cpp")
-        
+
     def check_dereference(self, result_varname, frame, expr_options):
         deref_expr = "*{0}".format(result_varname)
-        base_children = ValueCheck(name="Base", value="", children=[ValueCheck(name="base_int", value="100")])
-        base_1_arr_children= [ValueCheck(name="[0]", value="100"), ValueCheck(name="[1]", value="101"),
-                              ValueCheck(name="[2]", value="102"), ValueCheck(name="[3]", value="103"),
-                              ValueCheck(name="[4]", value="104"), ValueCheck(name="[5]", value="105"),
-                              ValueCheck(name="[6]", value="106"), ValueCheck(name="[7]", value="107"),
-                              ValueCheck(name="[8]", value="108"), ValueCheck(name="[9]", value="109")]
-        base_2_arr_children= [ValueCheck(name="[0]", value="200"), ValueCheck(name="[1]", value="201"),
-                              ValueCheck(name="[2]", value="202"), ValueCheck(name="[3]", value="203"),
-                              ValueCheck(name="[4]", value="204"), ValueCheck(name="[5]", value="205"),
-                              ValueCheck(name="[6]", value="206"), ValueCheck(name="[7]", value="207"),
-                              ValueCheck(name="[8]", value="208"), ValueCheck(name="[9]", value="209")]
-        deref_children=[ValueCheck(name="Base_1", value="",
-                                   children=[base_children, ValueCheck(name="base_1_arr", value="", children=base_1_arr_children)]),
-                        ValueCheck(name="Base_2", value="",
-                                   children=[base_children, ValueCheck(name="base_2_arr", value="", children = base_2_arr_children)]),
-                        ValueCheck(name="derived_int", value="1000")]
-        result_var_deref = self.expect_expr(deref_expr, result_type="Derived",
-                                            result_children = deref_children, options=expr_options) 
+        base_children = ValueCheck(
+            name="Base", value="", children=[ValueCheck(name="base_int", value="100")]
+        )
+        base_1_arr_children = [
+            ValueCheck(name="[0]", value="100"),
+            ValueCheck(name="[1]", value="101"),
+            ValueCheck(name="[2]", value="102"),
+            ValueCheck(name="[3]", value="103"),
+            ValueCheck(name="[4]", value="104"),
+            ValueCheck(name="[5]", value="105"),
+            ValueCheck(name="[6]", value="106"),
+            ValueCheck(name="[7]", value="107"),
+            ValueCheck(name="[8]", value="108"),
+            ValueCheck(name="[9]", value="109"),
+        ]
+        base_2_arr_children = [
+            ValueCheck(name="[0]", value="200"),
+            ValueCheck(name="[1]", value="201"),
+            ValueCheck(name="[2]", value="202"),
+            ValueCheck(name="[3]", value="203"),
+            ValueCheck(name="[4]", value="204"),
+            ValueCheck(name="[5]", value="205"),
+            ValueCheck(name="[6]", value="206"),
+            ValueCheck(name="[7]", value="207"),
+            ValueCheck(name="[8]", value="208"),
+            ValueCheck(name="[9]", value="209"),
+        ]
+        deref_children = [
+            ValueCheck(
+                name="Base_1",
+                value="",
+                children=[
+                    base_children,
+                    ValueCheck(
+                        name="base_1_arr", value="", children=base_1_arr_children
+                    ),
+                ],
+            ),
+            ValueCheck(
+                name="Base_2",
+                value="",
+                children=[
+                    base_children,
+                    ValueCheck(
+                        name="base_2_arr", value="", children=base_2_arr_children
+                    ),
+                ],
+            ),
+            ValueCheck(name="derived_int", value="1000"),
+        ]
+        result_var_deref = self.expect_expr(
+            deref_expr,
+            result_type="Derived",
+            result_children=deref_children,
+            options=expr_options,
+        )
 
         direct_access_expr = "{0}->derived_int".format(result_varname)
         self.expect_expr(direct_access_expr, result_type="int", result_value="1000")
@@ -46,14 +82,17 @@ def check_dereference(self, result_varname, frame, expr_options):
         # Also check this by directly accessing the result variable:
         result_value = frame.FindValue(result_varname, lldb.eValueTypeConstResult, True)
         self.assertTrue(result_value.error.success, "Found my result variable")
-        value_check = ValueCheck(children = deref_children)
-        value_check.check_value(self, result_value, f"{result_varname} children are correct")
-        
+        value_check = ValueCheck(children=deref_children)
+        value_check.check_value(
+            self, result_value, f"{result_varname} children are correct"
+        )
+
         # Make sure we can also call a function through the derived type:
         method_result = self.expect_expr(
             f"{result_varname}->method_of_derived()",
             result_type="int",
-            options=expr_options)
+            options=expr_options,
+        )
         self.assertEqual(method_result.signed, 500, "Got the right result value")
 
     def test_virtual_dynamic_results(self):
@@ -61,53 +100,72 @@ def test_virtual_dynamic_results(self):
 
     def test_non_virtual_dynamic_results(self):
         self.do_test_dynamic_results(False)
-        
+
     def do_test_dynamic_results(self, virtual):
         """Test that when we uses a result variable in a subsequent expression it
-           uses the dynamic value - if that was requested when the result variable was made."""
+        uses the dynamic value - if that was requested when the result variable was made.
+        """
         if virtual:
             self.build(dictionary={"CFLAGS_EXTRAS": "-DVIRTUAL=''"})
         else:
             self.build(dictionary={"CFLAGS_EXTRAS": "-DVIRTUAL='virtual'"})
 
-        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
-                                    "Set a breakpoint here", self.main_source_file)
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, "Set a breakpoint here", self.main_source_file
+        )
 
         frame = thread.GetFrameAtIndex(0)
         expr_options = lldb.SBExpressionOptions()
         expr_options.SetFetchDynamicValue(lldb.eDynamicDontRunTarget)
-        base_1_ptr = self.expect_expr("base_1_ptr", result_type="Derived *", options=expr_options)
+        base_1_ptr = self.expect_expr(
+            "base_1_ptr", result_type="Derived *", options=expr_options
+        )
         result_varname = base_1_ptr.GetName()
         self.check_dereference(result_varname, frame, expr_options)
 
         # Now do the same thing, but use a persistent result variable:
-        empty_var = frame.EvaluateExpression("void *$base_1_ptr = base_1_ptr", expr_options)
+        empty_var = frame.EvaluateExpression(
+            "void *$base_1_ptr = base_1_ptr", expr_options
+        )
         self.assertIn(
-            empty_var.error.description, "unknown error",
-            "Expressions that don't have results return this error"
+            empty_var.error.description,
+            "unknown error",
+            "Expressions that don't have results return this error",
+        )
+        persist_base_1_ptr = frame.FindValue(
+            "$base_1_ptr", lldb.eValueTypeConstResult, True
         )
-        persist_base_1_ptr = frame.FindValue("$base_1_ptr", lldb.eValueTypeConstResult, True)
         self.assertTrue(persist_base_1_ptr.error.success, "Got the persistent variable")
         self.check_dereference("$base_1_ptr", frame, expr_options)
-        
+
         # Now check the second of the multiply inherited bases, this one will have an offset_to_top
         # that we need to calculate:
-        base_2_ptr = self.expect_expr("base_2_ptr", result_type="Derived *", options=expr_options)
+        base_2_ptr = self.expect_expr(
+            "base_2_ptr", result_type="Derived *", options=expr_options
+        )
         self.check_dereference(base_2_ptr.GetName(), frame, expr_options)
 
         # Again, do the same thing for a persistent expression variable:
-        empty_var = frame.EvaluateExpression("void *$base_2_ptr = base_2_ptr", expr_options)
+        empty_var = frame.EvaluateExpression(
+            "void *$base_2_ptr = base_2_ptr", expr_options
+        )
         self.check_dereference("$base_2_ptr", frame, expr_options)
 
         # Now try starting from a virtual base class of both our bases:
-        base_through_1 = self.expect_expr("base_through_1", result_type = "Derived *", options=expr_options)
+        base_through_1 = self.expect_expr(
+            "base_through_1", result_type="Derived *", options=expr_options
+        )
         self.check_dereference(base_through_1.GetName(), frame, expr_options)
 
         # Now try starting from a virtual base class of both our bases:
-        base_through_2 = self.expect_expr("base_through_2", result_type = "Derived *", options=expr_options)
+        base_through_2 = self.expect_expr(
+            "base_through_2", result_type="Derived *", options=expr_options
+        )
         self.check_dereference(base_through_2.GetName(), frame, expr_options)
-        
+
         # Now check that we get the right results when we run an
         # expression to get the base class object:
-        base_through_expr = self.expect_expr("MakeADerivedReportABase()", result_type = "Derived *", options=expr_options)
-        self.check_dereference(base_through_expr.GetName(), frame, expr_options) 
+        base_through_expr = self.expect_expr(
+            "MakeADerivedReportABase()", result_type="Derived *", options=expr_options
+        )
+        self.check_dereference(base_through_expr.GetName(), frame, expr_options)
diff --git a/lldb/test/API/functionalities/expr-result-var/two-bases.cpp b/lldb/test/API/functionalities/expr-result-var/two-bases.cpp
index fa6fab0b47279..21e277cd8595c 100644
--- a/lldb/test/API/functionalities/expr-result-var/two-bases.cpp
+++ b/lldb/test/API/functionalities/expr-result-var/two-bases.cpp
@@ -1,5 +1,5 @@
-#include <stdio.h>
 #include <stdint.h>
+#include <stdio.h>
 
 struct Base {
   virtual ~Base() = default;
@@ -9,56 +9,48 @@ struct Base {
 
 struct Base_1 : public VIRTUAL Base {
   virtual ~Base_1() = default;
-  int base_1_arr[10] = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };
+  int base_1_arr[10] = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109};
   Base *return_base_1() { return return_me(); }
 };
 
-struct Base_2 :public VIRTUAL Base {
+struct Base_2 : public VIRTUAL Base {
   virtual ~Base_2() = default;
-  int base_2_arr[10] = { 200, 201, 202, 203, 204, 205, 206, 207, 208, 209 };
+  int base_2_arr[10] = {200, 201, 202, 203, 204, 205, 206, 207, 208, 209};
   Base *return_base_2() { return return_me(); }
 };
 
-struct Derived : public Base_1, Base_2
-{
+struct Derived : public Base_1, Base_2 {
   virtual ~Derived() = default;
   int derived_int = 1000;
-  int method_of_derived() {
-    return 500;
-  }
+  int method_of_derived() { return 500; }
 };
 
-Base *MakeADerivedReportABase() {
-  return (Base *) ((Base_1 *) new Derived());
-}
+Base *MakeADerivedReportABase() { return (Base *)((Base_1 *)new Derived()); }
 
-int
-main()
-{
+int main() {
   Derived my_derived;
   int call_it = my_derived.method_of_derived();
 
-  Base_1 *base_1_ptr = (Base_1 *) &my_derived;
+  Base_1 *base_1_ptr = (Base_1 *)&my_derived;
 
-  Base_2 *base_2_ptr = (Base_2 *) &my_derived;
+  Base_2 *base_2_ptr = (Base_2 *)&my_derived;
 
   Base *base_through_1 = my_derived.return_base_1();
-  Base *base_through_2 = my_derived.return_base_2();;
+  Base *base_through_2 = my_derived.return_base_2();
+  ;
 
-  // Call this to make sure the compiler makes it. 
+  // Call this to make sure the compiler makes it.
   Base *fake_base = MakeADerivedReportABase();
 
-  uint64_t base_through_1_addr = (uint64_t) base_through_1;
-  uint64_t base_through_2_addr = (uint64_t) base_through_2;
+  uint64_t base_through_1_addr = (uint64_t)base_through_1;
+  uint64_t base_through_2_addr = (uint64_t)base_through_2;
   int64_t base_offset = base_through_2_addr - base_through_1_addr;
   printf("Base offset (should be 0): 0x%llx.\n", base_offset);
-  uint64_t base_1_addr = (uint64_t) base_1_ptr;
-  uint64_t base_2_addr = (uint64_t) base_2_ptr;
+  uint64_t base_1_addr = (uint64_t)base_1_ptr;
+  uint64_t base_2_addr = (uint64_t)base_2_ptr;
   int64_t offset = base_2_addr - base_1_addr;
 
   // Set a breakpoint here
-  return my_derived.derived_int + base_1_ptr->base_1_arr[0]
-    + base_2_ptr->base_2_arr[0] + my_derived.return_base_1()->base_int;
-  
+  return my_derived.derived_int + base_1_ptr->base_1_arr[0] +
+         base_2_ptr->base_2_arr[0] + my_derived.return_base_1()->base_int;
 }
-



More information about the lldb-commits mailing list