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

>From 29fac5661bf969ad8f74415a07671681d00d15e2 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       |  24 +++-
 lldb/source/ValueObject/DILEval.cpp           |  35 ++++++
 .../PersistentResultVariableLookup/Makefile   |   3 +
 ...ameVarDILPersistentResultVariableLookup.py | 119 ++++++++++++++++++
 .../PersistentResultVariableLookup/main.cpp   |  23 ++++
 6 files changed, 206 insertions(+), 5 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/TestFrameVarDILPersistentResultVariableLookup.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 1510a1f06c7fa..2b8f49c3c440b 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..2d6dd142aec73 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -154,14 +154,19 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
     result.SetStatus(eReturnStatusSuccessFinishResult);
   };
 
-  // First, try `expr` as a _limited_ frame variable expression path: only the
-  // dot operator (`.`) is permitted for this case.
+  // Case (1): Try `expr` as a _limited_ frame variable expression path: only
+  // the dot operator (`.`) is permitted for this case.
   //
   // This is limited to support only unambiguous expression paths. Of note,
   // expression paths are not attempted if the expression contain either the
   // arrow operator (`->`) or the subscript operator (`[]`). This is because
   // both operators can be overloaded in C++, and could result in ambiguity in
   // how the expression is handled. Additionally, `*` and `&` are not 
supported.
+  //
+  // This case handles variable expression paths based on persistent variables,
+  // but the limitation noted above means that variable expression paths based
+  // on persistent variables that are ambiguous will be handled by Case (2),
+  // below.
   const bool try_variable_path =
       expr.find_first_of("*&->[]") == StringRef::npos;
   if (frame && try_variable_path) {
@@ -191,17 +196,26 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
     }
   }
 
-  // Second, try `expr` as a persistent variable.
+  // Case (2): 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 var_sp = state->GetVariable(expr)) {
         if (auto valobj_sp = var_sp->GetValueObject()) {
+
+          if (verbosity == eDWIMPrintVerbosityFull) {
+            StringRef flags;
+            if (args.HasArgs())
+              flags = args.GetArgString();
+            result.AppendNoteWithFormatv("ran `expression {0}{1}`", flags,
+                                         expr);
+          }
           dump_val_object(*valobj_sp);
           return;
         }
+      }
 
-  // Third, and lastly, try `expr` as a source expression to evaluate.
+  // Case (3): (lastly) Try `expr` as a source expression to evaluate.
   {
     auto *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
     ValueObjectSP valobj_sp;
diff --git a/lldb/source/ValueObject/DILEval.cpp 
b/lldb/source/ValueObject/DILEval.cpp
index d448444b43eba..c548dd41fcefd 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/TestFrameVarDILPersistentResultVariableLookup.py
 
b/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/TestFrameVarDILPersistentResultVariableLookup.py
new file mode 100644
index 0000000000000..5d230850bd252
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/TestFrameVarDILPersistentResultVariableLookup.py
@@ -0,0 +1,119 @@
+"""
+Make sure accessing persistent/result variables works using DIL 
parser/evaluator.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILPersistentResultVariableLookup(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
+
+    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 persistent variables.
+
+        # Establish a persistent variable with integer type.
+        self.expect(
+            "dwim-print --persistent-result true -- foo + 5", startstr="(int) 
$0 = "
+        )
+        # 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 = ",
+        )
+        # Establish a persistent variable using dwim-print as pointer to 
variable
+        # with derived type (using C specification terminology).
+        self.expect(
+            "dwim-print --persistent-result true -- &hsmt",
+            startstr="(HasMembersT *) $2 = ",
+        )
+        # Establish result variable using expression.
+        self.expect(
+            "expression foo",
+            startstr="(int) $3 = 1",
+        )
+        # Establish result variables with user-defined names.
+        self.runCmd(
+            "expression int *$foop = &foo",
+        )
+        self.runCmd(
+            "expression HasMembersT *$hsmtp = &hsmt",
+        )
+
+        # Test that accessing those persistent/result variables yields the 
proper values.
+
+        # Check to make sure that `dwim-print`'s note indicates the proper 
path through the code was taken.
+        self.runCmd("settings set dwim-print-verbosity full")
+        # Case (1) checks (see 
lldb/source/Commands/CommandObjectDWIMPrint.cpp).
+        self.expect("dwim-print $0", startstr="note: ran `frame variable $0`")
+        self.expect(
+            "dwim-print $1.doublem", startstr="note: ran `frame variable 
$1.doublem`"
+        )
+        # Case (2) check (see lldb/source/Commands/CommandObjectDWIMPrint.cpp).
+        self.expect(
+            "dwim-print $2->doublem", startstr="note: ran `expression 
$2->doublem`"
+        )
+        self.runCmd("settings set dwim-print-verbosity none")
+
+        # Check simple persistent variable accesses.
+        self.expect_var_path("$0", type="int", value="6")
+        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'")],
+                ),
+            ],
+        )
+        self.expect_var_path(
+            "$2",
+            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'")],
+                ),
+            ],
+        )
+        self.expect_var_path("$3", type="int", value="1")
+
+        # 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")
+
+        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 to which persistent
+        # variables refer.
+        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