[Lldb-commits] [lldb] [lldb] Add evaluation modes to DIL (PR #178747)

Ilia Kuklin via lldb-commits lldb-commits at lists.llvm.org
Thu Feb 12 09:51:25 PST 2026


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

>From e606ad113488a752ca36e270ebb02424a69e8e33 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Fri, 30 Jan 2026 01:02:50 +0500
Subject: [PATCH 1/3] [lldb] Add evaluation modes to DIL

DIL will only attempt evaluating expressions that contain tokens
allowed by a selected mode:
 - Simple: identifiers, operators: '.'
 - Legacy: identifiers, integers, operators: '.', '->', '*', '&', '[]'
 - Full: everything supported by DIL
---
 lldb/include/lldb/API/SBFrame.h               | 10 +++++---
 lldb/include/lldb/Target/BorrowedStackFrame.h |  3 ++-
 lldb/include/lldb/Target/StackFrame.h         | 10 ++++++--
 lldb/include/lldb/ValueObject/DILLexer.h      |  4 +++-
 lldb/include/lldb/lldb-enumerations.h         | 13 ++++++++++
 lldb/source/API/SBFrame.cpp                   | 10 ++++----
 .../Commands/CommandObjectDWIMPrint.cpp       |  4 ++--
 .../Process/scripted/ScriptedFrame.cpp        |  3 ++-
 .../Plugins/Process/scripted/ScriptedFrame.h  |  3 ++-
 lldb/source/Target/BorrowedStackFrame.cpp     |  4 ++--
 lldb/source/Target/StackFrame.cpp             |  9 +++----
 lldb/source/ValueObject/DILLexer.cpp          | 24 +++++++++++++++++--
 .../AddressOf/TestFrameVarDILAddressOf.py     |  9 ++++++-
 .../TestFrameVarDILArraySubscript.py          |  9 ++++++-
 .../LocalVars/TestFrameVarDILLocalVars.py     |  9 ++++++-
 .../MemberOf/TestFrameVarDILMemberOf.py       | 22 +++++++++++++----
 .../TestFrameVarDILPointerDereference.py      |  9 ++++++-
 .../var-dil/expr/Casts/TestFrameVarDILCast.py |  7 ++++++
 .../lldb-dap/evaluate/TestDAP_evaluate.py     |  1 +
 .../Handler/EvaluateRequestHandler.cpp        |  4 ++--
 lldb/tools/lldb-dap/SourceBreakpoint.cpp      |  4 ++--
 21 files changed, 136 insertions(+), 35 deletions(-)

diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h
index 5283cdfe53faa..eaf9a4bfece96 100644
--- a/lldb/include/lldb/API/SBFrame.h
+++ b/lldb/include/lldb/API/SBFrame.h
@@ -182,12 +182,16 @@ class LLDB_API SBFrame {
   // expression result and is not a constant object like
   // SBFrame::EvaluateExpression(...) returns, but a child object of the
   // variable value.
-  lldb::SBValue GetValueForVariablePath(const char *var_expr_cstr,
-                                        DynamicValueType use_dynamic);
+  lldb::SBValue
+  GetValueForVariablePath(const char *var_expr_cstr,
+                          DynamicValueType use_dynamic,
+                          lldb::DILMode mode = lldb::eDILModeFull);
 
   /// The version that doesn't supply a 'use_dynamic' value will use the
   /// target's default.
-  lldb::SBValue GetValueForVariablePath(const char *var_path);
+  lldb::SBValue
+  GetValueForVariablePath(const char *var_path,
+                          lldb::DILMode mode = lldb::eDILModeFull);
 
   /// Find variables, register sets, registers, or persistent variables using
   /// the frame as the scope.
diff --git a/lldb/include/lldb/Target/BorrowedStackFrame.h b/lldb/include/lldb/Target/BorrowedStackFrame.h
index 72e7777961da7..2a34f2816aed2 100644
--- a/lldb/include/lldb/Target/BorrowedStackFrame.h
+++ b/lldb/include/lldb/Target/BorrowedStackFrame.h
@@ -86,7 +86,8 @@ class BorrowedStackFrame : public StackFrame {
 
   lldb::ValueObjectSP GetValueForVariableExpressionPath(
       llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
-      uint32_t options, lldb::VariableSP &var_sp, Status &error) override;
+      uint32_t options, lldb::VariableSP &var_sp, Status &error,
+      lldb::DILMode mode = lldb::eDILModeFull) override;
 
   bool HasDebugInformation() override;
 
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 5cba9afe2a7e8..c114cd2a13fdf 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -316,11 +316,16 @@ class StackFrame : public ExecutionContextScope,
   /// \param[in] error
   ///     Record any errors encountered while evaluating var_expr.
   ///
+  /// \param[in] mode
+  ///     Data Inspection Language (DIL) evaluation mode.
+  ///     \see lldb::DILMode
+  ///
   /// \return
   ///     A shared pointer to the ValueObject described by var_expr.
   virtual lldb::ValueObjectSP GetValueForVariableExpressionPath(
       llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
-      uint32_t options, lldb::VariableSP &var_sp, Status &error);
+      uint32_t options, lldb::VariableSP &var_sp, Status &error,
+      lldb::DILMode mode = lldb::eDILModeFull);
 
   /// Determine whether this StackFrame has debug information available or not.
   ///
@@ -615,7 +620,8 @@ class StackFrame : public ExecutionContextScope,
 
   lldb::ValueObjectSP DILGetValueForVariableExpressionPath(
       llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
-      uint32_t options, lldb::VariableSP &var_sp, Status &error);
+      uint32_t options, lldb::VariableSP &var_sp, Status &error,
+      lldb::DILMode mode = lldb::eDILModeFull);
 
   StackFrame(const StackFrame &) = delete;
   const StackFrame &operator=(const StackFrame &) = delete;
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index 47b117de7b80d..a927aa236377e 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -9,6 +9,7 @@
 #ifndef LLDB_VALUEOBJECT_DILLEXER_H
 #define LLDB_VALUEOBJECT_DILLEXER_H
 
+#include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -74,7 +75,8 @@ class DILLexer {
 public:
   /// Lexes all the tokens in expr and calls the private constructor
   /// with the lexed tokens.
-  static llvm::Expected<DILLexer> Create(llvm::StringRef expr);
+  static llvm::Expected<DILLexer>
+  Create(llvm::StringRef expr, lldb::DILMode mode = lldb::eDILModeFull);
 
   /// Return the current token to be handled by the DIL parser.
   const Token &GetCurrentToken() { return m_lexed_tokens[m_tokens_idx]; }
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 4cbbabbf879ad..67600c8bb4248 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -1420,6 +1420,19 @@ enum NameMatchStyle {
   eNameMatchStyleRegex = eFunctionNameTypeSelector << 1
 };
 
+/// Data Inspection Language (DIL) evaluation modes.
+/// DIL will only attempt evaluating expressions that contain tokens
+/// allowed by a selected mode.
+enum DILMode {
+  /// Allowed: identifiers, operators: '.'.
+  eDILModeSimple,
+  /// Allowed: identifiers, integers, operators: '.', '->', '*', '&', '[]'.
+  eDILModeLegacy,
+  /// Allowed: everything supported by DIL.
+  /// \see lldb/docs/dil-expr-lang.ebnf
+  eDILModeFull
+};
+
 } // namespace lldb
 
 #endif // LLDB_LLDB_ENUMERATIONS_H
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index 31947a054aaaf..c0f932cc2c43b 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -363,7 +363,8 @@ void SBFrame::Clear() {
   m_opaque_sp->Clear();
 }
 
-lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path) {
+lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
+                                               lldb::DILMode mode) {
   LLDB_INSTRUMENT_VA(this, var_path);
 
   SBValue sb_value;
@@ -377,13 +378,14 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path) {
   if (StackFrame *frame = exe_ctx->GetFramePtr()) {
     lldb::DynamicValueType use_dynamic =
         frame->CalculateTarget()->GetPreferDynamicValue();
-    sb_value = GetValueForVariablePath(var_path, use_dynamic);
+    sb_value = GetValueForVariablePath(var_path, use_dynamic, mode);
   }
   return sb_value;
 }
 
 lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
-                                               DynamicValueType use_dynamic) {
+                                               DynamicValueType use_dynamic,
+                                               lldb::DILMode mode) {
   LLDB_INSTRUMENT_VA(this, var_path, use_dynamic);
 
   SBValue sb_value;
@@ -405,7 +407,7 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
         var_path, eNoDynamicValues,
         StackFrame::eExpressionPathOptionCheckPtrVsMember |
             StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
-        var_sp, error));
+        var_sp, error, mode));
     sb_value.SetSP(value_sp, use_dynamic);
   }
   return sb_value;
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 40f00c90bbbfb..27bd71c21ad3f 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -170,8 +170,8 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
     Status status;
     auto valobj_sp = frame->GetValueForVariableExpressionPath(
         expr, eval_options.GetUseDynamic(),
-        StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, var_sp,
-        status);
+        StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, var_sp, status,
+        lldb::eDILModeSimple);
     if (valobj_sp && status.Success() && valobj_sp->GetError().Success()) {
       if (!suppress_result) {
         if (auto persisted_valobj = valobj_sp->Persist())
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
index ab7f70efb4bd4..54b65dcaed873 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
@@ -316,7 +316,8 @@ lldb::ValueObjectSP ScriptedFrame::GetValueObjectForFrameVariable(
 
 lldb::ValueObjectSP ScriptedFrame::GetValueForVariableExpressionPath(
     llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
-    uint32_t options, lldb::VariableSP &var_sp, Status &error) {
+    uint32_t options, lldb::VariableSP &var_sp, Status &error,
+    lldb::DILMode mode) {
   // Unless the frame implementation knows how to create variables (which it
   // doesn't), we can't construct anything for the variable. This may seem
   // somewhat out of place, but it's basically because of how this API is used -
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
index fe154792c745b..e4c57831c2576 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
@@ -76,7 +76,8 @@ class ScriptedFrame : public lldb_private::StackFrame {
 
   lldb::ValueObjectSP GetValueForVariableExpressionPath(
       llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
-      uint32_t options, lldb::VariableSP &var_sp, Status &error) override;
+      uint32_t options, lldb::VariableSP &var_sp, Status &error,
+      lldb::DILMode mode = lldb::eDILModeFull) override;
 
   bool isA(const void *ClassID) const override {
     return ClassID == &ID || StackFrame::isA(ClassID);
diff --git a/lldb/source/Target/BorrowedStackFrame.cpp b/lldb/source/Target/BorrowedStackFrame.cpp
index 5afadf21fde03..5b81a1f05bbfb 100644
--- a/lldb/source/Target/BorrowedStackFrame.cpp
+++ b/lldb/source/Target/BorrowedStackFrame.cpp
@@ -99,9 +99,9 @@ BorrowedStackFrame::GetInScopeVariableList(bool get_file_globals,
 
 ValueObjectSP BorrowedStackFrame::GetValueForVariableExpressionPath(
     llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options,
-    VariableSP &var_sp, Status &error) {
+    VariableSP &var_sp, Status &error, lldb::DILMode mode) {
   return m_borrowed_frame_sp->GetValueForVariableExpressionPath(
-      var_expr, use_dynamic, options, var_sp, error);
+      var_expr, use_dynamic, options, var_sp, error, mode);
 }
 
 bool BorrowedStackFrame::HasDebugInformation() {
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 340607e14abed..9c80e8c0b8ccf 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -524,13 +524,13 @@ StackFrame::GetInScopeVariableList(bool get_file_globals,
 
 ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
     llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options,
-    VariableSP &var_sp, Status &error) {
+    VariableSP &var_sp, Status &error, lldb::DILMode mode) {
   ExecutionContext exe_ctx;
   CalculateExecutionContext(exe_ctx);
   bool use_DIL = exe_ctx.GetTargetRef().GetUseDIL(&exe_ctx);
   if (use_DIL)
     return DILGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
-                                                var_sp, error);
+                                                var_sp, error, mode);
 
   return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
                                                  var_sp, error);
@@ -538,7 +538,8 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
 
 ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
     llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
-    uint32_t options, lldb::VariableSP &var_sp, Status &error) {
+    uint32_t options, lldb::VariableSP &var_sp, Status &error,
+    lldb::DILMode mode) {
 
   const bool check_ptr_vs_member =
       (options & eExpressionPathOptionCheckPtrVsMember) != 0;
@@ -548,7 +549,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
       (options & eExpressionPathOptionsNoSyntheticChildren) != 0;
 
   // Lex the expression.
-  auto lex_or_err = dil::DILLexer::Create(var_expr);
+  auto lex_or_err = dil::DILLexer::Create(var_expr, mode);
   if (!lex_or_err) {
     error = Status::FromError(lex_or_err.takeError());
     return ValueObjectConstResult::Create(nullptr, std::move(error));
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index 72c97f7bf272b..dc5508cbf706b 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -111,12 +111,32 @@ static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder,
   return std::nullopt;
 }
 
-llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr) {
+llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr,
+                                          lldb::DILMode mode) {
   std::vector<Token> tokens;
   llvm::StringRef remainder = expr;
   do {
     if (llvm::Expected<Token> t = Lex(expr, remainder)) {
-      tokens.push_back(std::move(*t));
+      Token token = *t;
+      if (mode == lldb::eDILModeSimple &&
+          !token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
+        std::string errMsg =
+            llvm::formatv("token '{0}' is not allowed in DIL simple mode",
+                          Token::GetTokenName(token.GetKind()));
+        return llvm::make_error<DILDiagnosticError>(expr, errMsg,
+                                                    token.GetLocation());
+      } else if (mode == lldb::eDILModeLegacy &&
+                 !token.IsOneOf({Token::identifier, Token::integer_constant,
+                                 Token::period, Token::arrow, Token::star,
+                                 Token::amp, Token::l_square, Token::r_square,
+                                 Token::eof})) {
+        std::string errMsg =
+            llvm::formatv("token '{0}' is not allowed in DIL legacy mode",
+                          Token::GetTokenName(token.GetKind()));
+        return llvm::make_error<DILDiagnosticError>(expr, errMsg,
+                                                    token.GetLocation());
+      }
+      tokens.push_back(std::move(token));
     } else {
       return t.takeError();
     }
diff --git a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
index 8eab75949047d..bfe29370b4704 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
@@ -21,7 +21,7 @@ def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None
 
     def test_frame_var(self):
         self.build()
-        lldbutil.run_to_source_breakpoint(
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
             self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
         )
 
@@ -36,3 +36,10 @@ def test_frame_var(self):
         self.expect_var_path("&globalVar", True, type="int *")
         self.expect_var_path("&s_str", True, type="const char **")
         self.expect_var_path("&argc", True, type="int *")
+
+        # Check that '&' is not allowed in simple mode, but allowed in legacy mode
+        frame = thread.GetFrameAtIndex(0)
+        simple = frame.GetValueForVariablePath("&x", lldb.eDILModeSimple)
+        legacy = frame.GetValueForVariablePath("&x", lldb.eDILModeLegacy)
+        self.assertFailure(simple.GetError())
+        self.assertSuccess(legacy.GetError())
diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
index b22a445e603cd..6f3bbfb970aa4 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
@@ -13,7 +13,7 @@ class TestFrameVarDILArraySubscript(TestBase):
 
     def test_subscript(self):
         self.build()
-        lldbutil.run_to_source_breakpoint(
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
             self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
         )
 
@@ -87,6 +87,13 @@ def test_subscript(self):
             substrs=["subscript of pointer to incomplete type 'void'"],
         )
 
+        # Check that subscription is not allowed in simple mode, but allowed in legacy mode
+        frame = thread.GetFrameAtIndex(0)
+        simple = frame.GetValueForVariablePath("int_arr[0]", lldb.eDILModeSimple)
+        legacy = frame.GetValueForVariablePath("int_arr[0]", lldb.eDILModeLegacy)
+        self.assertFailure(simple.GetError())
+        self.assertSuccess(legacy.GetError())
+
     def test_subscript_synthetic(self):
         self.build()
         lldbutil.run_to_source_breakpoint(
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
index b082d977a3a99..79d8d7abd4671 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -16,7 +16,7 @@ class TestFrameVarDILLocalVars(TestBase):
 
     def test_frame_var(self):
         self.build()
-        lldbutil.run_to_source_breakpoint(
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
             self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
         )
 
@@ -25,3 +25,10 @@ def test_frame_var(self):
         self.expect_var_path("b", value="2")
         self.expect_var_path("c", value="'\\xfd'")
         self.expect_var_path("s", value="4")
+
+        # Check that identifiers are allowed in both simple and legacy modes
+        frame = thread.GetFrameAtIndex(0)
+        simple = frame.GetValueForVariablePath("a", lldb.eDILModeSimple)
+        legacy = frame.GetValueForVariablePath("a", lldb.eDILModeLegacy)
+        self.assertSuccess(simple.GetError())
+        self.assertSuccess(legacy.GetError())
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
index ca6754a556d89..d37ce0bbc4201 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
@@ -11,6 +11,7 @@
 import shutil
 import time
 
+
 class TestFrameVarDILMemberOf(TestBase):
     # If your test case doesn't stress debug info, then
     # set this to true.  That way it won't be run once for
@@ -19,11 +20,11 @@ class TestFrameVarDILMemberOf(TestBase):
 
     def test_frame_var(self):
         self.build()
-        lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
-                                          lldb.SBFileSpec("main.cpp"))
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+        )
 
-        self.expect("settings set target.experimental.use-DIL true",
-                    substrs=[""])
+        self.expect("settings set target.experimental.use-DIL true", substrs=[""])
         self.expect_var_path("s.x", value="1")
         self.expect_var_path("s.r", type="int &")
         self.expect_var_path("sr.x", value="1")
@@ -48,3 +49,16 @@ def test_frame_var(self):
         # Test for record typedefs.
         self.expect_var_path("sa.x", value="3")
         self.expect_var_path("sa.y", value="'\\x04'")
+
+        # Check that '.' is allowed in both simple and legacy modes
+        frame = thread.GetFrameAtIndex(0)
+        simple = frame.GetValueForVariablePath("s.x", lldb.eDILModeSimple)
+        legacy = frame.GetValueForVariablePath("s.x", lldb.eDILModeLegacy)
+        self.assertSuccess(simple.GetError())
+        self.assertSuccess(legacy.GetError())
+
+        # Check that '->' is not allowed in simple mode, but allowed in legacy mode
+        simple = frame.GetValueForVariablePath("sp->x", lldb.eDILModeSimple)
+        legacy = frame.GetValueForVariablePath("sp->x", lldb.eDILModeLegacy)
+        self.assertFailure(simple.GetError())
+        self.assertSuccess(legacy.GetError())
diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py
index ffb447441e982..e18e18e146a46 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py
@@ -17,7 +17,7 @@ class TestFrameVarDILPointerDereference(TestBase):
 
     def test_frame_var(self):
         self.build()
-        lldbutil.run_to_source_breakpoint(
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
             self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
         )
 
@@ -46,3 +46,10 @@ def test_frame_var(self):
             pp_int0_2stars_got.GetValueAsAddress(),
             pp_int0_2stars_exp.GetValueAsAddress(),
         )
+
+        # Check that * is not allowed in simple mode, but allowed in legacy mode
+        frame = thread.GetFrameAtIndex(0)
+        simple = frame.GetValueForVariablePath("*p_int0", lldb.eDILModeSimple)
+        legacy = frame.GetValueForVariablePath("*p_int0", lldb.eDILModeLegacy)
+        self.assertFailure(simple.GetError())
+        self.assertSuccess(legacy.GetError())
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
index 6e83622ef9fb9..50affd4ccb03e 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
@@ -285,3 +285,10 @@ def test_type_cast(self):
             error=True,
             substrs=["expected 'eof', got: <'InnerFoo' (identifier)>"],
         )
+
+        # Check that casts are not allowed in both simple and legacy modes
+        frame = thread.GetFrameAtIndex(0)
+        simple = frame.GetValueForVariablePath("(char)a", lldb.eDILModeSimple)
+        legacy = frame.GetValueForVariablePath("(char)a", lldb.eDILModeLegacy)
+        self.assertFailure(simple.GetError())
+        self.assertFailure(legacy.GetError())
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index 98ed0b9df228a..556168e5adfa8 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -253,6 +253,7 @@ def run_test_evaluate_expressions(
             self.assertEvaluateFailure("a_function(1)")
             self.assertEvaluateFailure("var2 + struct1.foo")
             self.assertEvaluateFailure("foo_func")
+            self.assertEvaluateFailure("(float) var2")
             self.assertEvaluate("foo_var", "44")
 
         # Expressions at breakpoint 2, which is an anonymous block
diff --git a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
index 82a011b8088df..44a5be5c17b20 100644
--- a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
@@ -54,8 +54,8 @@ static lldb::SBValue EvaluateVariableExpression(lldb::SBTarget &target,
     // Check if it is a variable or an expression path for a variable. i.e.
     // 'foo->bar' finds the 'bar' variable. It is more reliable than the
     // expression parser in many cases and it is faster.
-    value = frame.GetValueForVariablePath(expression_cstr,
-                                          lldb::eDynamicDontRunTarget);
+    value = frame.GetValueForVariablePath(
+        expression_cstr, lldb::eDynamicDontRunTarget, lldb::eDILModeLegacy);
     if (value || !run_as_expression)
       return value;
 
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
index 6f776f4d5f429..233dbff81f337 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
@@ -397,8 +397,8 @@ bool SourceBreakpoint::BreakpointHitCallback(
       // evaluation
       const std::string &expr_str = messagePart.text;
       const char *expr = expr_str.c_str();
-      lldb::SBValue value =
-          frame.GetValueForVariablePath(expr, lldb::eDynamicDontRunTarget);
+      lldb::SBValue value = frame.GetValueForVariablePath(
+          expr, lldb::eDynamicDontRunTarget, lldb::eDILModeLegacy);
       if (value.GetError().Fail())
         value = frame.EvaluateExpression(expr);
       output += VariableDescription(

>From 3eca690db908f51343209442bea640e3efd87d45 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 5 Feb 2026 20:42:37 +0500
Subject: [PATCH 2/3] Move token checking to a separate function

---
 lldb/source/ValueObject/DILLexer.cpp | 43 ++++++++++++++++------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index dc5508cbf706b..7936e71c9210a 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -111,6 +111,29 @@ static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder,
   return std::nullopt;
 }
 
+static llvm::Error IsNotAllowedByMode(llvm::StringRef expr, Token token,
+                                      lldb::DILMode mode) {
+  if (mode == lldb::eDILModeSimple &&
+      !token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
+    std::string errMsg =
+        llvm::formatv("token '{0}' is not allowed in DIL simple mode",
+                      Token::GetTokenName(token.GetKind()));
+    return llvm::make_error<DILDiagnosticError>(expr, errMsg,
+                                                token.GetLocation());
+  } else if (mode == lldb::eDILModeLegacy &&
+             !token.IsOneOf({Token::identifier, Token::integer_constant,
+                             Token::period, Token::arrow, Token::star,
+                             Token::amp, Token::l_square, Token::r_square,
+                             Token::eof})) {
+    std::string errMsg =
+        llvm::formatv("token '{0}' is not allowed in DIL legacy mode",
+                      Token::GetTokenName(token.GetKind()));
+    return llvm::make_error<DILDiagnosticError>(expr, errMsg,
+                                                token.GetLocation());
+  }
+  return llvm::Error::success();
+}
+
 llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr,
                                           lldb::DILMode mode) {
   std::vector<Token> tokens;
@@ -118,24 +141,8 @@ llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr,
   do {
     if (llvm::Expected<Token> t = Lex(expr, remainder)) {
       Token token = *t;
-      if (mode == lldb::eDILModeSimple &&
-          !token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
-        std::string errMsg =
-            llvm::formatv("token '{0}' is not allowed in DIL simple mode",
-                          Token::GetTokenName(token.GetKind()));
-        return llvm::make_error<DILDiagnosticError>(expr, errMsg,
-                                                    token.GetLocation());
-      } else if (mode == lldb::eDILModeLegacy &&
-                 !token.IsOneOf({Token::identifier, Token::integer_constant,
-                                 Token::period, Token::arrow, Token::star,
-                                 Token::amp, Token::l_square, Token::r_square,
-                                 Token::eof})) {
-        std::string errMsg =
-            llvm::formatv("token '{0}' is not allowed in DIL legacy mode",
-                          Token::GetTokenName(token.GetKind()));
-        return llvm::make_error<DILDiagnosticError>(expr, errMsg,
-                                                    token.GetLocation());
-      }
+      if (llvm::Error error = IsNotAllowedByMode(expr, token, mode))
+        return error;
       tokens.push_back(std::move(token));
     } else {
       return t.takeError();

>From 007c9cd9cddbdcba97df41bf164fb47bc848d540 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 10 Feb 2026 15:42:27 +0500
Subject: [PATCH 3/3] Check modes in a switch statement

---
 lldb/source/ValueObject/DILLexer.cpp | 36 +++++++++++++++-------------
 1 file changed, 19 insertions(+), 17 deletions(-)

diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index 7936e71c9210a..c1ad354502438 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -113,23 +113,25 @@ static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder,
 
 static llvm::Error IsNotAllowedByMode(llvm::StringRef expr, Token token,
                                       lldb::DILMode mode) {
-  if (mode == lldb::eDILModeSimple &&
-      !token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
-    std::string errMsg =
-        llvm::formatv("token '{0}' is not allowed in DIL simple mode",
-                      Token::GetTokenName(token.GetKind()));
-    return llvm::make_error<DILDiagnosticError>(expr, errMsg,
-                                                token.GetLocation());
-  } else if (mode == lldb::eDILModeLegacy &&
-             !token.IsOneOf({Token::identifier, Token::integer_constant,
-                             Token::period, Token::arrow, Token::star,
-                             Token::amp, Token::l_square, Token::r_square,
-                             Token::eof})) {
-    std::string errMsg =
-        llvm::formatv("token '{0}' is not allowed in DIL legacy mode",
-                      Token::GetTokenName(token.GetKind()));
-    return llvm::make_error<DILDiagnosticError>(expr, errMsg,
-                                                token.GetLocation());
+  switch (mode) {
+  case lldb::eDILModeSimple:
+    if (!token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
+      return llvm::make_error<DILDiagnosticError>(
+          expr, llvm::formatv("{0} is not allowed in DIL simple mode", token),
+          token.GetLocation());
+    }
+    break;
+  case lldb::eDILModeLegacy:
+    if (!token.IsOneOf({Token::identifier, Token::integer_constant,
+                        Token::period, Token::arrow, Token::star, Token::amp,
+                        Token::l_square, Token::r_square, Token::eof})) {
+      return llvm::make_error<DILDiagnosticError>(
+          expr, llvm::formatv("{0} is not allowed in DIL legacy mode", token),
+          token.GetLocation());
+    }
+    break;
+  case lldb::eDILModeFull:
+    break;
   }
   return llvm::Error::success();
 }



More information about the lldb-commits mailing list