https://github.com/hawkinsw updated 
https://github.com/llvm/llvm-project/pull/215706

>From 8bfc4321853e0794450b63e751b776d25453bf92 Mon Sep 17 00:00:00 2001
From: Will Hawkins <[email protected]>
Date: Tue, 11 Aug 2026 21:46:30 -0400
Subject: [PATCH] [lldb] Support Persistent Variables as DIL Identifiers

When a persistent variable is used in a DIL expression, look it up as if
it were any other identifier in the global scope.

Signed-off-by: Will Hawkins <[email protected]>
---
 lldb/include/lldb/ValueObject/DILEval.h       |  7 ++
 .../Commands/CommandObjectDWIMPrint.cpp       | 10 --
 lldb/source/ValueObject/DILEval.cpp           | 35 +++++++
 .../PersistentResultVariableLookup/Makefile   |  3 +
 .../TestFrameVarDILGlobalVariableLookup.py    | 96 +++++++++++++++++++
 .../PersistentResultVariableLookup/main.cpp   | 23 +++++
 6 files changed, 164 insertions(+), 10 deletions(-)
 create mode 100644 
lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/Makefile
 create mode 100644 
lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/TestFrameVarDILGlobalVariableLookup.py
 create mode 100644 
lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/main.cpp

diff --git a/lldb/include/lldb/ValueObject/DILEval.h 
b/lldb/include/lldb/ValueObject/DILEval.h
index 489dc801b20db..3a873063adda1 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -18,6 +18,13 @@
 
 namespace lldb_private::dil {
 
+/// Given the name of a persistent identifier (i.e., one that starts with a $),
+/// find the ValueObject for that name (if it exists).
+lldb::ValueObjectSP LookupPersistentIdentifier(llvm::StringRef name_ref,
+                                               StackFrame &stack_frame,
+                                               lldb::TargetSP target_sp,
+                                               lldb::LanguageType language);
+
 /// Given the name of an identifier (variable name, member name, type name,
 /// etc.), find the ValueObject for that name (if it exists), excluding global
 /// variables, and create and return an IdentifierInfo object containing all
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp 
b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 1b0b4c7881cfc..5b3f5707b3bef 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -191,16 +191,6 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
     }
   }
 
-  // Second, try `expr` as a persistent variable.
-  if (expr.starts_with("$"))
-    if (auto *state = target.GetPersistentExpressionStateForLanguage(
-            language.AsLanguageType()))
-      if (auto var_sp = state->GetVariable(expr))
-        if (auto valobj_sp = var_sp->GetValueObject()) {
-          dump_val_object(*valobj_sp);
-          return;
-        }
-
   // Third, and lastly, try `expr` as a source expression to evaluate.
   {
     auto *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
diff --git a/lldb/source/ValueObject/DILEval.cpp 
b/lldb/source/ValueObject/DILEval.cpp
index 0ef4e244410a4..853d085f221cc 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -8,10 +8,12 @@
 
 #include "lldb/ValueObject/DILEval.h"
 #include "lldb/Core/Module.h"
+#include "lldb/Expression/ExpressionVariable.h"
 #include "lldb/Symbol/CompileUnit.h"
 #include "lldb/Symbol/TypeSystem.h"
 #include "lldb/Symbol/VariableList.h"
 #include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/ValueObject/DILAST.h"
 #include "lldb/ValueObject/DILParser.h"
@@ -44,6 +46,17 @@ static lldb::ValueObjectSP 
ArrayToPointerConversion(ValueObject &valobj,
       /* do_deref */ false);
 }
 
+static llvm::Expected<lldb::LanguageType>
+GetSourceLanguageFromCU(StackFrame &ctx) {
+  SymbolContext symbol_context =
+      ctx.GetSymbolContext(lldb::eSymbolContextCompUnit);
+  if (!symbol_context.comp_unit)
+    return llvm::createStringErrorV("no compile unit for frame: {}",
+                                    ctx.GetFunctionName());
+
+  return symbol_context.comp_unit->GetLanguage();
+}
+
 static llvm::Expected<lldb::TypeSystemSP> GetTypeSystemFromCU(StackFrame &ctx) 
{
   SymbolContext symbol_context =
       ctx.GetSymbolContext(lldb::eSymbolContextCompUnit);
@@ -323,6 +336,20 @@ lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef 
name_ref,
   return nullptr;
 }
 
+lldb::ValueObjectSP LookupPersistentIdentifier(llvm::StringRef name_ref,
+                                               StackFrame &stack_frame,
+                                               lldb::TargetSP target_sp,
+                                               lldb::LanguageType language) {
+  if (name_ref.starts_with("$")) {
+    if (auto *state =
+            target_sp->GetPersistentExpressionStateForLanguage(language))
+      if (auto var_sp = state->GetVariable(name_ref))
+        if (auto valobj_sp = var_sp->GetValueObject())
+          return valobj_sp;
+  }
+  return nullptr;
+}
+
 lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
                                      StackFrame &stack_frame,
                                      lldb::DynamicValueType use_dynamic) {
@@ -459,6 +486,14 @@ Interpreter::Visit(const IdentifierNode &node) {
   if (!identifier)
     identifier = LookupEnumValue(node.GetName(), m_stack_frame);
 
+  if (!identifier && node.GetName()[0] == '$') {
+    auto language = GetSourceLanguageFromCU(m_stack_frame);
+    if (!language)
+      return language.takeError();
+    identifier = LookupPersistentIdentifier(node.GetName(), m_stack_frame,
+                                            m_target, language.get());
+  }
+
   if (!identifier && node.GetName() == "nullptr") {
     // If we got a "nullptr" identifier, and there is no defined variable with
     // this name, resolve it as a null pointer.
diff --git 
a/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/Makefile
 
b/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git 
a/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/TestFrameVarDILGlobalVariableLookup.py
 
b/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/TestFrameVarDILGlobalVariableLookup.py
new file mode 100644
index 0000000000000..36a44dfc08806
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -0,0 +1,96 @@
+"""
+Make sure 'frame var' using DIL parser/evaluator works for persistent/result 
variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+
+class TestFrameVarDILGlobalVariableLookup(TestBase):
+    # If your test case doesn't stress debug info, then
+    # set this to true.  That way it won't be run once for
+    # each debug info format.
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @skipIf(macos_version=["<", "15.0"], archs=["arm64", "arm64e"])
+    @skipIf(
+        dwarf_version=["<", "5"],
+        oslist=[lldbplatformutil.getDarwinOSTriples()],
+    )
+    @expectedFailureAll(
+        compiler="clang",
+        compiler_version=["<", "19.0"],
+        oslist=[lldbplatformutil.getDarwinOSTriples()],
+    )
+    def test_frame_var(self):
+        self.build()
+        _, process, _, _ = lldbutil.run_to_source_breakpoint(
+            self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+        )
+
+        self.runCmd("settings set target.experimental.use-DIL true")
+
+        # Establish a persistent variable with integer type.
+        self.expect(
+            "dwim-print --persistent-result true -- foo + 5", startstr="(int) 
$0 = "
+        )
+        self.expect_var_path("$0", type="int", value="6")
+
+        # Establish a persistent variable using dwim-print with derived type 
(using C specification terminology).
+        self.expect(
+            "dwim-print --persistent-result true -- hsmt",
+            startstr="(HasMembersT) $1 = ",
+        )
+        self.expect_var_path(
+            "$1",
+            type="HasMembersT",
+            children=[
+                ValueCheck(name="intm", value="1", type="int"),
+                ValueCheck(name="doublem", value="2", type="double"),
+                ValueCheck(
+                    name="nestedm",
+                    type="NestedT",
+                    children=[ValueCheck(name="charm", type="char", 
value="'c'")],
+                ),
+            ],
+        )
+        # Check that accessing fields of persistent variables works.
+        self.expect_var_path("$1.intm", type="int", value="1")
+        self.expect_var_path("$1.nestedm.charm", type="char", value="'c'")
+        self.expect_var_path("$1.intm + $0", type="int", value="7")
+
+        # Check that types work correctly when adding an int and a double.
+        self.expect_var_path("$1.intm + $1.doublem", type="double", value="3")
+
+        # Establish persistent variable using expression.
+        self.expect(
+            "expression foo",
+            startstr="(int) $2 = 1",
+        )
+        self.expect_var_path("$2", type="int", value="1")
+
+        # Establish persistent variables with user-defined names.
+        self.runCmd(
+            "expression int *$foop = &foo",
+        )
+        self.runCmd(
+            "expression HasMembersT *$hsmtp = &hsmt",
+        )
+
+        self.expect_var_path("*$foop", type="int", value="1")
+        self.expect_var_path("(*$hsmtp).doublem", type="double", value="2")
+
+        # Step past statements that update variable values.
+        lldbutil.continue_to_source_breakpoint(
+            self, process, "Set a second breakpoint here", 
lldb.SBFileSpec("main.cpp")
+        )
+
+        # Make sure that the value accessed through the pointer in persistent 
variables are updated.
+        self.expect_var_path("*$foop", type="int", value="2")
+        self.expect_var_path("(*$hsmtp).doublem", type="double", value="3")
diff --git 
a/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/main.cpp
 
b/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/main.cpp
new file mode 100644
index 0000000000000..1bdcf11b92480
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/main.cpp
@@ -0,0 +1,23 @@
+typedef struct {
+  char charm;
+} NestedT;
+
+typedef struct {
+  int intm;
+  double doublem;
+  NestedT nestedm;
+
+} HasMembersT;
+
+int main(int argc, char **argv) {
+  HasMembersT hsmt;
+
+  hsmt.nestedm.charm = 'c';
+  hsmt.intm = 1;
+  hsmt.doublem = 2.0;
+
+  int foo = 1;
+  foo = 2; // Set a breakpoint here
+  hsmt.doublem = 3.0;
+  return 0; // Set a second breakpoint here
+}

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to