https://github.com/cmtice created 
https://github.com/llvm/llvm-project/pull/175061

This PR updates type parsing in DIL to recognize user-defined types (classes, 
namespaces, etc.), and allows this to be used in type casting.

>From cf0161bf183944ab37c1e6952e5cd2e883b4d212 Mon Sep 17 00:00:00 2001
From: Caroline Tice <[email protected]>
Date: Thu, 8 Jan 2026 11:45:30 -0800
Subject: [PATCH] [LLDB] Add type casting to DIL, part 3 of 3

This PR updates type parsing in DIL to recognize user-defined types
(classes, namespaces, etc.), and allows this to be used in type casting.
---
 lldb/include/lldb/ValueObject/DILParser.h     |   7 +
 lldb/source/ValueObject/DILEval.cpp           |  14 +-
 lldb/source/ValueObject/DILParser.cpp         | 176 +++++++++++++++++-
 .../var-dil/expr/Casts/TestFrameVarDILCast.py |  26 +++
 .../frame/var-dil/expr/Casts/main.cpp         |  20 ++
 5 files changed, 227 insertions(+), 16 deletions(-)

diff --git a/lldb/include/lldb/ValueObject/DILParser.h 
b/lldb/include/lldb/ValueObject/DILParser.h
index bd2fc373cd9b5..3a6261aaac5fc 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -11,6 +11,7 @@
 
 #include "lldb/Host/common/DiagnosticsRendering.h"
 #include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/StackFrame.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/ValueObject/DILAST.h"
 #include "lldb/ValueObject/DILLexer.h"
@@ -31,6 +32,9 @@ enum class ErrorCode : unsigned char {
   kUnknown,
 };
 
+llvm::Expected<lldb::TypeSystemSP>
+GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx);
+
 // The following is modeled on class OptionParseError.
 class DILDiagnosticError
     : public llvm::ErrorInfo<DILDiagnosticError, DiagnosticError> {
@@ -103,6 +107,9 @@ class DILParser {
   ASTNodeUP ParseCastExpression();
   std::optional<CompilerType> ParseBuiltinType();
   std::optional<CompilerType> ParseTypeId();
+  void ParseTypeSpecifierSeq(std::string &type_name);
+  bool ParseTypeSpecifier(std::string &user_type_name);
+  std::string ParseTypeName();
   CompilerType ResolveTypeDeclarators(CompilerType type,
                                       const std::vector<Token> &ptr_operators);
 
diff --git a/lldb/source/ValueObject/DILEval.cpp 
b/lldb/source/ValueObject/DILEval.cpp
index 575dfae850c19..604c5da9d5aa7 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -13,6 +13,7 @@
 #include "lldb/Symbol/VariableList.h"
 #include "lldb/Target/RegisterContext.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"
@@ -43,19 +44,6 @@ GetDynamicOrSyntheticValue(lldb::ValueObjectSP value_sp,
   return value_sp;
 }
 
-static llvm::Expected<lldb::TypeSystemSP>
-GetTypeSystemFromCU(std::shared_ptr<ExecutionContextScope> ctx) {
-  auto stack_frame = ctx->CalculateStackFrame();
-  if (!stack_frame)
-    return llvm::createStringError("no stack frame in this context");
-  SymbolContext symbol_context =
-      stack_frame->GetSymbolContext(lldb::eSymbolContextCompUnit);
-  lldb::LanguageType language = symbol_context.comp_unit->GetLanguage();
-
-  symbol_context = stack_frame->GetSymbolContext(lldb::eSymbolContextModule);
-  return symbol_context.module_sp->GetTypeSystemForLanguage(language);
-}
-
 static CompilerType GetBasicType(lldb::TypeSystemSP type_system,
                                  lldb::BasicType basic_type) {
   if (type_system)
diff --git a/lldb/source/ValueObject/DILParser.cpp 
b/lldb/source/ValueObject/DILParser.cpp
index f3027a3d82fa2..de09dd5f612bc 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -44,6 +44,72 @@ DILDiagnosticError::DILDiagnosticError(llvm::StringRef expr,
   m_detail.rendered = std::move(rendered_msg);
 }
 
+llvm::Expected<lldb::TypeSystemSP>
+GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx) {
+  SymbolContext symbol_context =
+      ctx->GetSymbolContext(lldb::eSymbolContextCompUnit);
+  lldb::LanguageType language = symbol_context.comp_unit->GetLanguage();
+
+  symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule);
+  return symbol_context.module_sp->GetTypeSystemForLanguage(language);
+}
+
+CompilerType
+ResolveTypeByName(const std::string &name,
+                  std::shared_ptr<ExecutionContextScope> ctx_scope) {
+  // Internally types don't have global scope qualifier in their names and
+  // LLDB doesn't support queries with it too.
+  llvm::StringRef name_ref(name);
+
+  if (name_ref.starts_with("::"))
+    name_ref = name_ref.drop_front(2);
+
+  std::vector<CompilerType> result_type_list;
+  lldb::TargetSP target_sp = ctx_scope->CalculateTarget();
+  const char *type_name = name_ref.data();
+  if (type_name && type_name[0] && target_sp) {
+    ModuleList &images = target_sp->GetImages();
+    ConstString const_type_name(type_name);
+    TypeQuery query(type_name);
+    TypeResults results;
+    images.FindTypes(nullptr, query, results);
+    for (const lldb::TypeSP &type_sp : results.GetTypeMap().Types())
+      if (type_sp)
+        result_type_list.push_back(type_sp->GetFullCompilerType());
+
+    if (auto process_sp = target_sp->GetProcessSP()) {
+      for (auto *runtime : process_sp->GetLanguageRuntimes()) {
+        if (auto *vendor = runtime->GetDeclVendor()) {
+          auto types = vendor->FindTypes(const_type_name, UINT32_MAX);
+          for (auto type : types)
+            result_type_list.push_back(type);
+        }
+      }
+    }
+  }
+
+  // We've found multiple types, try finding the "correct" one.
+  CompilerType full_match;
+  std::vector<CompilerType> partial_matches;
+
+  for (uint32_t i = 0; i < result_type_list.size(); ++i) {
+    CompilerType type = result_type_list[i];
+    llvm::StringRef type_name_ref = type.GetTypeName().GetStringRef();
+
+    if (type_name_ref == name_ref && type.IsValid())
+      return type;
+
+    if (type_name_ref.ends_with(name_ref))
+      partial_matches.push_back(type);
+  }
+
+  // If we have partial matches, pick a "random" one.
+  if (partial_matches.size() > 0)
+    return partial_matches.back();
+
+  return {};
+}
+
 llvm::Expected<ASTNodeUP>
 DILParser::Parse(llvm::StringRef dil_input_expr, DILLexer lexer,
                  std::shared_ptr<StackFrame> frame_sp,
@@ -339,12 +405,32 @@ std::string DILParser::ParseNestedNameSpecifier() {
 //
 std::optional<CompilerType> DILParser::ParseTypeId() {
   CompilerType type;
-  // For now only allow builtin types -- will expand add to this later.
   auto maybe_builtin_type = ParseBuiltinType();
   if (maybe_builtin_type) {
     type = *maybe_builtin_type;
-  } else
-    return {};
+  } else {
+    // Check to see if we have a user-defined type here.
+    // First build  up the user-defined type name.
+    std::string type_name;
+    ParseTypeSpecifierSeq(type_name);
+
+    if (type_name.size() == 0)
+      return {};
+    type = ResolveTypeByName(type_name, m_ctx_scope);
+    if (!type.IsValid())
+      return {};
+
+    // Same-name identifiers should be preferred over typenames.
+    if (LookupIdentifier(type_name, m_ctx_scope, m_use_dynamic))
+      // TODO: Make type accessible with 'class', 'struct' and 'union' 
keywords.
+      return {};
+
+    // Same-name identifiers should be preferred over typenames.
+    if (LookupGlobalIdentifier(type_name, m_ctx_scope,
+                               m_ctx_scope->CalculateTarget(), m_use_dynamic))
+      // TODO: Make type accessible with 'class', 'struct' and 'union' keywords
+      return {};
+  }
 
   //
   //  abstract_declarator:
@@ -400,6 +486,90 @@ std::optional<CompilerType> DILParser::ParseBuiltinType() {
   return {};
 }
 
+// Parse a type_specifier_seq.
+//
+//  type_specifier_seq:
+//    type_specifier [type_specifier_seq]
+//
+void DILParser::ParseTypeSpecifierSeq(std::string &type_name) {
+  while (true) {
+    bool type_specifier = ParseTypeSpecifier(type_name);
+    if (!type_specifier) {
+      break;
+    }
+  }
+}
+
+// Parse a type_specifier.
+//
+//  type_specifier:
+//    ["::"] [nested_name_specifier] type_name
+//
+// Returns TRUE if a type_specifier was successfully parsed at this location.
+//
+bool DILParser::ParseTypeSpecifier(std::string &user_type_name) {
+  // The type_specifier must be a user-defined type. Try parsing a
+  // simple_type_specifier.
+  {
+    // Try parsing optional global scope operator.
+    bool global_scope = false;
+    if (CurToken().Is(Token::coloncolon)) {
+      global_scope = true;
+      m_dil_lexer.Advance();
+    }
+
+    // uint32_t loc = CurToken().GetLocation();
+
+    // Try parsing optional nested_name_specifier.
+    auto nested_name_specifier = ParseNestedNameSpecifier();
+
+    // Try parsing required type_name.
+    auto type_name = ParseTypeName();
+
+    // If there is a type_name, then this is indeed a simple_type_specifier.
+    // Global and qualified (namespace/class) scopes can be empty, since 
they're
+    // optional. In this case type_name is type we're looking for.
+    if (!type_name.empty()) {
+      // User-defined typenames can't be combined with builtin keywords.
+      user_type_name = llvm::formatv("{0}{1}{2}", global_scope ? "::" : "",
+                                     nested_name_specifier, type_name);
+      return true;
+    }
+  }
+
+  // No type_specifier was found here.
+  return false;
+}
+
+// Parse a type_name.
+//
+//  type_name:
+//    class_name
+//    enum_name
+//    typedef_name
+//
+//  class_name
+//    identifier
+//
+//  enum_name
+//    identifier
+//
+//  typedef_name
+//    identifier
+//
+std::string DILParser::ParseTypeName() {
+  // Typename always starts with an identifier.
+  if (CurToken().IsNot(Token::identifier)) {
+    return "";
+  }
+
+  // Otherwise look for a class_name, enum_name or a typedef_name.
+  std::string identifier = CurToken().GetSpelling();
+  m_dil_lexer.Advance();
+
+  return identifier;
+}
+
 // Parse an id_expression.
 //
 //  id_expression:
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 b1f91e55353f3..c999b420f066e 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
@@ -117,10 +117,34 @@ def test_type_cast(self):
             substrs=["cannot convert 'ns::Foo' to 'int'"],
         )
 
+        # Test with typedefs and namespaces.
+        self.expect_var_path("(myint)1", type="myint", value="1")
+        self.expect_var_path("(myint)1LL", type="myint", value="1")
+        self.expect_var_path("(ns::myint)1", type="ns::myint", value="1")
+        self.expect_var_path("(::ns::myint)1", type="ns::myint", value="1")
+        self.expect_var_path("(::ns::myint)myint_", type="ns::myint", 
value="1")
+
         self.expect_var_path("(int)myint_", type="int", value="1")
         self.expect_var_path("(int)ns_myint_", type="int", value="2")
         self.expect_var_path("(long long)myint_", type="long long", value="1")
         self.expect_var_path("(long long)ns_myint_", type="long long", 
value="2")
+        self.expect_var_path("(::ns::myint)myint_", type="ns::myint", 
value="1")
+
+        self.expect_var_path(
+            "(ns::inner::mydouble)1", type="ns::inner::mydouble", value="1"
+        )
+        self.expect_var_path(
+            "(::ns::inner::mydouble)1.2", type="ns::inner::mydouble", 
value="1.2"
+        )
+        self.expect_var_path(
+            "(ns::inner::mydouble)myint_", type="ns::inner::mydouble", 
value="1"
+        )
+        self.expect_var_path(
+            "(::ns::inner::mydouble)ns_inner_mydouble_",
+            type="ns::inner::mydouble",
+            value="1.2",
+        )
+        self.expect_var_path("(myint)ns_inner_mydouble_", type="myint", 
value="1")
 
         # Test with pointers and arrays.
         self.expect_var_path("(long long)ap", type="long long")
@@ -180,6 +204,8 @@ def test_type_cast(self):
             error=True,
             substrs=["cannot cast from type 'double' to pointer type 'char 
*'"],
         )
+        self.expect_var_path("(ns::Foo*)ns_inner_foo_ptr_", type="ns::Foo *")
+        self.expect_var_path("(ns::inner::Foo*)ns_foo_ptr_", 
type="ns::inner::Foo *")
 
         self.expect_var_path("*(int*)(void*)ap", type="int", value="1")
 
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp 
b/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
index 3977283f54cc6..7c83f18b70a34 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
@@ -8,6 +8,14 @@ typedef int myint;
 
 class Foo {};
 
+namespace inner {
+
+using mydouble = double;
+
+class Foo {};
+
+} // namespace inner
+
 } // namespace ns
 
 int main(int argc, char **argv) {
@@ -33,12 +41,24 @@ int main(int argc, char **argv) {
   ns::Foo ns_foo_;
   ns::Foo *ns_foo_ptr_ = &ns_foo_;
 
+  ns::inner::mydouble ns_inner_mydouble_ = 1.2;
+  ns::inner::Foo ns_inner_foo_;
+  ns::inner::Foo *ns_inner_foo_ptr_ = &ns_inner_foo_;
+
   float finf = std::numeric_limits<float>::infinity();
   float fnan = std::numeric_limits<float>::quiet_NaN();
   float fsnan = std::numeric_limits<float>::signaling_NaN();
   float fmax = std::numeric_limits<float>::max();
   float fdenorm = std::numeric_limits<float>::denorm_min();
 
+  struct InnerFoo {
+    int a;
+    int b;
+  };
+
+  InnerFoo ifoo;
+  (void)ifoo;
+
   int arr_1d[] = {1, 2, 3, 4};
   int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}};
 

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

Reply via email to