[Lldb-commits] [lldb] WIP: [lldb] Support Persistent Variables as DIL Identifiers (PR #215706)
Will Hawkins via lldb-commits
lldb-commits at lists.llvm.org
Wed Aug 19 02:08:35 PDT 2026
https://github.com/hawkinsw updated https://github.com/llvm/llvm-project/pull/215706
>From 22fc78067e2ae82e8991ff3b9e5a877bf8fb9f35 Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
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 <hawkinsw at obs.cr>
---
lldb/include/lldb/ValueObject/DILEval.h | 7 ++++
.../Commands/CommandObjectDWIMPrint.cpp | 3 ++
lldb/source/ValueObject/DILEval.cpp | 37 ++++++++++++++++++
.../PersistentResultVariableLookup/Makefile | 3 ++
.../TestFrameVarDILGlobalVariableLookup.py | 39 +++++++++++++++++++
.../PersistentResultVariableLookup/main.cpp | 4 ++
.../Shell/Commands/command-dwim-print.test | 7 ++++
7 files changed, 100 insertions(+)
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..6332fbf2b291c 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -191,6 +191,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
}
}
+ #if 0
// Second, try `expr` as a persistent variable.
if (expr.starts_with("$"))
if (auto *state = target.GetPersistentExpressionStateForLanguage(
@@ -201,6 +202,8 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
return;
}
+ #endif
+
// 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..da7c7eaac16ac 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -8,16 +8,20 @@
#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"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/ValueObject/ValueObjectRegister.h"
#include "lldb/ValueObject/ValueObjectVariable.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private-types.h"
#include "llvm/Support/ErrorExtras.h"
#include "llvm/Support/FormatAdapters.h"
#include <memory>
@@ -44,6 +48,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 +338,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 +488,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..a3d3825532ccc
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -0,0 +1,39 @@
+"""
+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()
+ lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.runCmd("settings set target.experimental.use-DIL true")
+ self.expect("dwim-print --persistent-result true -- foo + 5", startstr="(int) $0 = ")
+ self.expect_var_path("$0", type="int", value="6")
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..fd23884132e8d
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/PersistentResultVariableLookup/main.cpp
@@ -0,0 +1,4 @@
+int main(int argc, char **argv) {
+ int foo = 1;
+ return 0; // Set a breakpoint here
+}
diff --git a/lldb/test/Shell/Commands/command-dwim-print.test b/lldb/test/Shell/Commands/command-dwim-print.test
index 8c2697d8ebf8c..26662ea822648 100644
--- a/lldb/test/Shell/Commands/command-dwim-print.test
+++ b/lldb/test/Shell/Commands/command-dwim-print.test
@@ -14,3 +14,10 @@
# RUN: echo quit | %lldb -o "settings set show-inline-diagnostics false" \
# RUN: -o "dwim-print a" 2>&1 | FileCheck %s --check-prefix=CHECK4
# CHECK4: error: <user expression 0>:1:1: use of undeclared identifier
+# RUN: %clang_host -g %S/Inputs/main.c -o %t
+# RUN: echo quit | %lldb %t -o "b main" -o "r" -o \
+# RUN: "dwim-print --persistent-result true -- foo" -o \
+# RUN: "settings set dwim-print-verbosity full" -o \
+# RUN: "dwim-print -- $0" | FileCheck %s --check-prefix=CHECK5
+# (lldb) dwim-print -- $0
+# CHECK5:note: ran `frame variable $0`
\ No newline at end of file
More information about the lldb-commits
mailing list