[Lldb-commits] [lldb] [LLDB] Add type casting to DIL, part 3 of 3 (PR #175061)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Jan 8 11:47:29 PST 2026
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 <cmtice at google.com>
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}};
More information about the lldb-commits
mailing list