[Lldb-commits] [lldb] [LLDB] Add unary plus and minus to DIL (PR #155617)
Ilia Kuklin via lldb-commits
lldb-commits at lists.llvm.org
Wed Sep 10 11:04:53 PDT 2025
https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/155617
>From 4d14bbb31d0411c45b95778d1659ccc416165be1 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Mon, 4 Aug 2025 19:11:55 +0500
Subject: [PATCH 1/9] [LLDB] Add unary plus and minus to DIL
---
lldb/include/lldb/ValueObject/DILAST.h | 2 +
lldb/source/ValueObject/DILEval.cpp | 155 ++++++++++++++++--
lldb/source/ValueObject/DILLexer.cpp | 1 -
lldb/source/ValueObject/DILParser.cpp | 10 +-
.../frame/var-dil/expr/Arithmetic/Makefile | 3 +
.../Arithmetic/TestFrameVarDILArithmetic.py | 40 +++++
.../frame/var-dil/expr/Arithmetic/main.cpp | 9 +
7 files changed, 203 insertions(+), 17 deletions(-)
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Arithmetic/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 1d10755c46e39..900eb8a792a1e 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -32,6 +32,8 @@ enum class NodeKind {
enum class UnaryOpKind {
AddrOf, // "&"
Deref, // "*"
+ Minus, // "-"
+ Plus, // "+"
};
/// Forward declaration, for use in DIL AST nodes. Definition is at the very
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index c6cf41ee9e9ee..595c25085bfed 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -21,6 +21,73 @@
namespace lldb_private::dil {
+static CompilerType GetBasicTypeFromCU(std::shared_ptr<StackFrame> ctx,
+ lldb::BasicType basic_type) {
+ SymbolContext symbol_context =
+ ctx->GetSymbolContext(lldb::eSymbolContextCompUnit);
+ auto language = symbol_context.comp_unit->GetLanguage();
+
+ symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule);
+ auto type_system =
+ symbol_context.module_sp->GetTypeSystemForLanguage(language);
+
+ if (type_system)
+ if (auto compiler_type = type_system.get()->GetBasicTypeFromAST(basic_type))
+ return compiler_type;
+
+ return CompilerType();
+}
+
+static CompilerType GetIntCompilerTypeForSize(uint32_t size, bool is_signed,
+ lldb::TypeSystemSP type_system,
+ std::shared_ptr<StackFrame> ctx) {
+ lldb::BasicType promote_types[] = {
+ // lldb::eBasicTypeChar, lldb::eBasicTypeUnsignedChar,
+ // lldb::eBasicTypeShort, lldb::eBasicTypeUnsignedShort,
+ lldb::eBasicTypeInt, lldb::eBasicTypeUnsignedInt,
+ lldb::eBasicTypeLong, lldb::eBasicTypeUnsignedLong,
+ lldb::eBasicTypeLongLong, lldb::eBasicTypeUnsignedLongLong,
+ };
+ for (auto &basic_type : promote_types) {
+ uint64_t byte_size = 0;
+ CompilerType type = GetBasicType(type_system, basic_type);
+ if (auto temp = type.GetByteSize(ctx.get()))
+ byte_size = *temp;
+ if (size < byte_size ||
+ (size == byte_size &&
+ is_signed == (bool)(type.GetTypeInfo() & lldb::eTypeIsSigned))) {
+ return type;
+ }
+ }
+
+ llvm_unreachable("size could not fit into long long");
+ return CompilerType();
+}
+
+static llvm::Expected<Scalar>
+GetScalarFromValueObject(lldb::ValueObjectSP valobj,
+ std::shared_ptr<StackFrame> ctx) {
+ auto type = valobj->GetCompilerType();
+ Scalar scalar;
+ bool resolved = valobj->ResolveValue(scalar);
+ if (resolved) {
+ if (scalar.GetType() == scalar.e_int) {
+ auto apsint = scalar.GetAPSInt();
+ auto type_bitsize = type.GetBitSize(ctx.get());
+ if (type_bitsize) {
+ llvm::APSInt adjusted;
+ if (type.IsSigned())
+ adjusted = apsint.sextOrTrunc(*type_bitsize);
+ else
+ adjusted = apsint.zextOrTrunc(*type_bitsize);
+ return Scalar(adjusted);
+ }
+ } else
+ return scalar;
+ }
+ return Scalar();
+}
+
static lldb::VariableSP DILFindVariable(ConstString name,
VariableList &variable_list) {
lldb::VariableSP exact_match;
@@ -175,21 +242,21 @@ Interpreter::Visit(const IdentifierNode *node) {
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const UnaryOpNode *node) {
Status error;
- auto rhs_or_err = Evaluate(node->GetOperand());
- if (!rhs_or_err)
- return rhs_or_err;
+ auto op_or_err = Evaluate(node->GetOperand());
+ if (!op_or_err)
+ return op_or_err;
- lldb::ValueObjectSP rhs = *rhs_or_err;
+ lldb::ValueObjectSP operand = *op_or_err;
switch (node->GetKind()) {
case UnaryOpKind::Deref: {
- lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_use_dynamic);
- if (dynamic_rhs)
- rhs = dynamic_rhs;
+ lldb::ValueObjectSP dynamic_op = operand->GetDynamicValue(m_use_dynamic);
+ if (dynamic_op)
+ operand = dynamic_op;
- lldb::ValueObjectSP child_sp = rhs->Dereference(error);
+ lldb::ValueObjectSP child_sp = operand->Dereference(error);
if (!child_sp && m_use_synthetic) {
- if (lldb::ValueObjectSP synth_obj_sp = rhs->GetSyntheticValue()) {
+ if (lldb::ValueObjectSP synth_obj_sp = operand->GetSyntheticValue()) {
error.Clear();
child_sp = synth_obj_sp->Dereference(error);
}
@@ -202,18 +269,78 @@ Interpreter::Visit(const UnaryOpNode *node) {
}
case UnaryOpKind::AddrOf: {
Status error;
- lldb::ValueObjectSP value = rhs->AddressOf(error);
+ lldb::ValueObjectSP value = operand->AddressOf(error);
if (error.Fail())
return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
node->GetLocation());
return value;
}
+ case UnaryOpKind::Minus: {
+ auto operand_type = operand->GetCompilerType();
+ if (!operand_type.IsScalarType()) {
+ std::string errMsg =
+ llvm::formatv("invalid argument type '{0}' to unary expression",
+ operand_type.GetTypeName());
+ return llvm::make_error<DILDiagnosticError>(m_expr, errMsg,
+ node->GetLocation());
+ }
+ auto scalar = GetScalarFromValueObject(operand, m_exe_ctx_scope);
+ if (!scalar)
+ break;
+
+ bool negated = scalar->UnaryNegate();
+ if (scalar->GetType() == Scalar::e_int) {
+ auto promo_type = GetIntCompilerTypeForSize(
+ scalar->GetByteSize(), scalar->IsSigned(),
+ operand_type.GetTypeSystem().GetSharedPointer(), m_exe_ctx_scope);
+ auto type_bitsize = promo_type.GetBitSize(m_exe_ctx_scope.get());
+
+ if (type_bitsize && negated) {
+ scalar->IntegralPromote(*type_bitsize, promo_type.IsSigned());
+ return ValueObject::CreateValueObjectFromScalar(m_target, *scalar,
+ promo_type, "result");
+ }
+ } else
+ return ValueObject::CreateValueObjectFromScalar(m_target, *scalar,
+ operand_type, "result");
+ break;
}
-
- // Unsupported/invalid operation.
- return llvm::make_error<DILDiagnosticError>(
- m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
+ case UnaryOpKind::Plus: {
+ auto operand_type = operand->GetCompilerType();
+ // Unary plus is allowed for pointers.
+ if (operand_type.IsPointerType())
+ return operand;
+ if (!operand_type.IsScalarType()) {
+ std::string errMsg =
+ llvm::formatv("invalid argument type '{0}' to unary expression",
+ operand_type.GetTypeName());
+ return llvm::make_error<DILDiagnosticError>(m_expr, errMsg,
+ node->GetLocation());
+ }
+ auto scalar = GetScalarFromValueObject(operand, m_exe_ctx_scope);
+ if (!scalar)
+ break;
+
+ if (scalar->GetType() == Scalar::e_int) {
+ auto promo_type = GetIntCompilerTypeForSize(
+ scalar->GetByteSize(), scalar->IsSigned(),
+ operand_type.GetTypeSystem().GetSharedPointer(), m_exe_ctx_scope);
+ auto type_bitsize = promo_type.GetBitSize(m_exe_ctx_scope.get());
+
+ if (type_bitsize) {
+ scalar->IntegralPromote(*type_bitsize, promo_type.IsSigned());
+ return ValueObject::CreateValueObjectFromScalar(m_target, *scalar,
+ promo_type, "result");
+ }
+ } else
+ return ValueObject::CreateValueObjectFromScalar(m_target, *scalar,
+ operand_type, "result");
+ break;
+ }
+ }
+ return llvm::make_error<DILDiagnosticError>(m_expr, "invalid unary operation",
+ node->GetLocation());
}
llvm::Expected<lldb::ValueObjectSP>
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index 0b2288a9d9230..77879f50cc9ad 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -42,7 +42,6 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
return "minus";
case Kind::period:
return "period";
- return "l_square";
case Kind::plus:
return "plus";
case Kind::r_paren:
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 8c4f7fdb25bea..b88345d18a6d4 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -95,7 +95,8 @@ ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
// "*"
//
ASTNodeUP DILParser::ParseUnaryExpression() {
- if (CurToken().IsOneOf({Token::amp, Token::star})) {
+ if (CurToken().IsOneOf(
+ {Token::amp, Token::star, Token::minus, Token::plus})) {
Token token = CurToken();
uint32_t loc = token.GetLocation();
m_dil_lexer.Advance();
@@ -107,7 +108,12 @@ ASTNodeUP DILParser::ParseUnaryExpression() {
case Token::amp:
return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::AddrOf,
std::move(rhs));
-
+ case Token::minus:
+ return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Minus,
+ std::move(rhs));
+ case Token::plus:
+ return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Plus,
+ std::move(rhs));
default:
llvm_unreachable("invalid token kind");
}
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/Makefile b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
new file mode 100644
index 0000000000000..d10622738cc6f
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
@@ -0,0 +1,40 @@
+"""
+Test DIL arithmetic.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILArithmetic(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_arithmetic(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")
+
+ # Check unary
+ self.expect_var_path("+0", value="0")
+ self.expect_var_path("-0", value="0")
+ self.expect_var_path("+1", value="1")
+ self.expect_var_path("-1", value="-1")
+ self.expect_var_path("s", value="10")
+ self.expect_var_path("+s", value="10")
+ self.expect_var_path("-s", value="-10")
+ self.expect_var_path("us", value="1")
+ self.expect_var_path("-us", value="-1")
+ self.expect_var_path("+0.0", value="0")
+ self.expect_var_path("-0.0", value="-0")
+ self.expect_var_path("-9223372036854775808", value="9223372036854775808")
+ self.expect_var_path("+p", type="int *")
+ self.expect(
+ "frame var -- '-p'",
+ error=True,
+ substrs=["invalid argument type 'int *' to unary expression"],
+ )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
new file mode 100644
index 0000000000000..2567453cc293d
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
@@ -0,0 +1,9 @@
+int main(int argc, char **argv) {
+ short s = 10;
+ unsigned short us = 1;
+
+ int x = 2;
+ int &r = x;
+ int *p = &x;
+ return 0; // Set a breakpoint here
+}
>From 424159def9255980463e654a032a628ed30bc0cb Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 7 Aug 2025 19:42:22 +0500
Subject: [PATCH 2/9] Type promotion
---
lldb/source/ValueObject/DILEval.cpp | 261 ++++++++++--------
.../Arithmetic/TestFrameVarDILArithmetic.py | 4 +
.../frame/var-dil/expr/Arithmetic/main.cpp | 10 +
3 files changed, 167 insertions(+), 108 deletions(-)
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 595c25085bfed..05ce5b4b8f7af 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -21,71 +21,153 @@
namespace lldb_private::dil {
-static CompilerType GetBasicTypeFromCU(std::shared_ptr<StackFrame> ctx,
- lldb::BasicType basic_type) {
+static llvm::Expected<lldb::TypeSystemSP>
+GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx) {
SymbolContext symbol_context =
ctx->GetSymbolContext(lldb::eSymbolContextCompUnit);
- auto language = symbol_context.comp_unit->GetLanguage();
+ lldb::LanguageType language = symbol_context.comp_unit->GetLanguage();
symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule);
- auto type_system =
- symbol_context.module_sp->GetTypeSystemForLanguage(language);
+ return symbol_context.module_sp->GetTypeSystemForLanguage(language);
+}
+static CompilerType GetBasicType(lldb::TypeSystemSP type_system,
+ lldb::BasicType basic_type) {
if (type_system)
- if (auto compiler_type = type_system.get()->GetBasicTypeFromAST(basic_type))
- return compiler_type;
+ return type_system.get()->GetBasicTypeFromAST(basic_type);
return CompilerType();
}
-static CompilerType GetIntCompilerTypeForSize(uint32_t size, bool is_signed,
- lldb::TypeSystemSP type_system,
- std::shared_ptr<StackFrame> ctx) {
- lldb::BasicType promote_types[] = {
- // lldb::eBasicTypeChar, lldb::eBasicTypeUnsignedChar,
- // lldb::eBasicTypeShort, lldb::eBasicTypeUnsignedShort,
- lldb::eBasicTypeInt, lldb::eBasicTypeUnsignedInt,
- lldb::eBasicTypeLong, lldb::eBasicTypeUnsignedLong,
- lldb::eBasicTypeLongLong, lldb::eBasicTypeUnsignedLongLong,
- };
- for (auto &basic_type : promote_types) {
- uint64_t byte_size = 0;
- CompilerType type = GetBasicType(type_system, basic_type);
- if (auto temp = type.GetByteSize(ctx.get()))
- byte_size = *temp;
- if (size < byte_size ||
- (size == byte_size &&
- is_signed == (bool)(type.GetTypeInfo() & lldb::eTypeIsSigned))) {
- return type;
+static llvm::Expected<CompilerType>
+DoIntegralPromotion(CompilerType from, lldb::TypeSystemSP type_system,
+ std::shared_ptr<StackFrame> ctx) {
+ if (!from.IsInteger() && !from.IsUnscopedEnumerationType())
+ return from;
+
+ if (!from.IsPromotableIntegerType())
+ return from;
+
+ if (from.IsUnscopedEnumerationType())
+ // TODO: fix this by creating CompilerType::GetEnumerationPromotionType()
+ return DoIntegralPromotion(from.GetEnumerationIntegerType(), type_system,
+ ctx);
+ lldb::BasicType builtin_type =
+ from.GetCanonicalType().GetBasicTypeEnumeration();
+
+ uint64_t from_size = 0;
+ if (builtin_type == lldb::eBasicTypeWChar ||
+ builtin_type == lldb::eBasicTypeSignedWChar ||
+ builtin_type == lldb::eBasicTypeUnsignedWChar ||
+ builtin_type == lldb::eBasicTypeChar16 ||
+ builtin_type == lldb::eBasicTypeChar32) {
+ // Find the type that can hold the entire range of values for our type.
+ bool is_signed = from.IsSigned();
+ llvm::Expected<uint64_t> from_size = from.GetByteSize(ctx.get());
+ if (!from_size)
+ return from_size.takeError();
+
+ CompilerType promote_types[] = {
+ GetBasicType(type_system, lldb::eBasicTypeInt),
+ GetBasicType(type_system, lldb::eBasicTypeUnsignedInt),
+ GetBasicType(type_system, lldb::eBasicTypeLong),
+ GetBasicType(type_system, lldb::eBasicTypeUnsignedLong),
+ GetBasicType(type_system, lldb::eBasicTypeLongLong),
+ GetBasicType(type_system, lldb::eBasicTypeUnsignedLongLong),
+ };
+ for (CompilerType &type : promote_types) {
+ llvm::Expected<uint64_t> byte_size = type.GetByteSize(ctx.get());
+ if (!byte_size)
+ return byte_size.takeError();
+ if (*from_size < *byte_size ||
+ (*from_size == *byte_size && is_signed == type.IsSigned())) {
+ return type;
+ }
}
+ llvm_unreachable("char type should fit into long long");
}
- llvm_unreachable("size could not fit into long long");
- return CompilerType();
+ // Here we can promote only to "int" or "unsigned int".
+ CompilerType int_type = GetBasicType(type_system, lldb::eBasicTypeInt);
+ llvm::Expected<uint64_t> int_byte_size = int_type.GetByteSize(ctx.get());
+ if (!int_byte_size)
+ return int_byte_size.takeError();
+
+ // Signed integer types can be safely promoted to "int".
+ if (from.IsSigned()) {
+ return int_type;
+ }
+ // Unsigned integer types are promoted to "unsigned int" if "int" cannot hold
+ // their entire value range.
+ return (from_size == *int_byte_size)
+ ? GetBasicType(type_system, lldb::eBasicTypeUnsignedInt)
+ : int_type;
}
-static llvm::Expected<Scalar>
-GetScalarFromValueObject(lldb::ValueObjectSP valobj,
+static lldb::ValueObjectSP
+ArrayToPointerConversion(lldb::ValueObjectSP valobj,
std::shared_ptr<StackFrame> ctx) {
- auto type = valobj->GetCompilerType();
- Scalar scalar;
- bool resolved = valobj->ResolveValue(scalar);
- if (resolved) {
- if (scalar.GetType() == scalar.e_int) {
- auto apsint = scalar.GetAPSInt();
- auto type_bitsize = type.GetBitSize(ctx.get());
- if (type_bitsize) {
- llvm::APSInt adjusted;
- if (type.IsSigned())
- adjusted = apsint.sextOrTrunc(*type_bitsize);
- else
- adjusted = apsint.zextOrTrunc(*type_bitsize);
- return Scalar(adjusted);
- }
- } else
- return scalar;
+ assert(valobj->IsArrayType() &&
+ "an argument to array-to-pointer conversion must be an array");
+
+ uint64_t addr = valobj->GetLoadAddress();
+ ExecutionContext exe_ctx;
+ ctx->CalculateExecutionContext(exe_ctx);
+ return ValueObject::CreateValueObjectFromAddress(
+ "result", addr, exe_ctx,
+ valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(),
+ /* do_deref */ false);
+}
+
+static llvm::Expected<lldb::ValueObjectSP>
+UnaryConversion(lldb::ValueObjectSP valobj, std::shared_ptr<StackFrame> ctx) {
+ // Perform usual conversions for unary operators. At the moment this includes
+ // array-to-pointer and the integral promotion for eligible types.
+ llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(ctx);
+ if (!type_system)
+ return type_system.takeError();
+ CompilerType in_type = valobj->GetCompilerType();
+ CompilerType result_type;
+ if (valobj->IsBitfield()) {
+ // Promote bitfields. If `int` can represent the bitfield value, it is
+ // converted to `int`. Otherwise, if `unsigned int` can represent it, it
+ // is converted to `unsigned int`. Otherwise, it is treated as its
+ // underlying type.
+ uint32_t bitfield_size = valobj->GetBitfieldBitSize();
+ // Some bitfields have undefined size (e.g. result of ternary operation).
+ // The AST's `bitfield_size` of those is 0, and no promotion takes place.
+ if (bitfield_size > 0 && in_type.IsInteger()) {
+ CompilerType int_type = GetBasicType(*type_system, lldb::eBasicTypeInt);
+ CompilerType uint_type =
+ GetBasicType(*type_system, lldb::eBasicTypeUnsignedInt);
+ llvm::Expected<uint64_t> int_bit_size = int_type.GetBitSize(ctx.get());
+ if (!int_bit_size)
+ return int_bit_size.takeError();
+ llvm::Expected<uint64_t> uint_bit_size = uint_type.GetBitSize(ctx.get());
+ if (!uint_bit_size)
+ return int_bit_size.takeError();
+ if (bitfield_size < *int_bit_size ||
+ (in_type.IsSigned() && bitfield_size == *int_bit_size))
+ valobj = valobj->CastToBasicType(int_type);
+ else if (bitfield_size <= *uint_bit_size)
+ valobj = valobj->CastToBasicType(uint_type);
+ }
}
- return Scalar();
+
+ if (in_type.IsArrayType())
+ valobj = ArrayToPointerConversion(valobj, ctx);
+
+ if (valobj->GetCompilerType().IsInteger() ||
+ valobj->GetCompilerType().IsUnscopedEnumerationType()) {
+ llvm::Expected<CompilerType> promoted_type =
+ DoIntegralPromotion(valobj->GetCompilerType(), *type_system, ctx);
+ if (!promoted_type)
+ return promoted_type.takeError();
+ if (!promoted_type->CompareTypes(valobj->GetCompilerType()))
+ return valobj->CastToBasicType(*promoted_type);
+ }
+
+ return valobj;
}
static lldb::VariableSP DILFindVariable(ConstString name,
@@ -277,7 +359,12 @@ Interpreter::Visit(const UnaryOpNode *node) {
return value;
}
case UnaryOpKind::Minus: {
- auto operand_type = operand->GetCompilerType();
+ llvm::Expected<lldb::ValueObjectSP> conv_op =
+ UnaryConversion(operand, m_exe_ctx_scope);
+ if (!conv_op)
+ return conv_op;
+ operand = *conv_op;
+ CompilerType operand_type = operand->GetCompilerType();
if (!operand_type.IsScalarType()) {
std::string errMsg =
llvm::formatv("invalid argument type '{0}' to unary expression",
@@ -285,58 +372,34 @@ Interpreter::Visit(const UnaryOpNode *node) {
return llvm::make_error<DILDiagnosticError>(m_expr, errMsg,
node->GetLocation());
}
- auto scalar = GetScalarFromValueObject(operand, m_exe_ctx_scope);
- if (!scalar)
+ Scalar scalar;
+ bool resolved = operand->ResolveValue(scalar);
+ if (!resolved)
break;
- bool negated = scalar->UnaryNegate();
- if (scalar->GetType() == Scalar::e_int) {
- auto promo_type = GetIntCompilerTypeForSize(
- scalar->GetByteSize(), scalar->IsSigned(),
- operand_type.GetTypeSystem().GetSharedPointer(), m_exe_ctx_scope);
- auto type_bitsize = promo_type.GetBitSize(m_exe_ctx_scope.get());
-
- if (type_bitsize && negated) {
- scalar->IntegralPromote(*type_bitsize, promo_type.IsSigned());
- return ValueObject::CreateValueObjectFromScalar(m_target, *scalar,
- promo_type, "result");
- }
- } else
- return ValueObject::CreateValueObjectFromScalar(m_target, *scalar,
- operand_type, "result");
+ bool negated = scalar.UnaryNegate();
+ if (negated)
+ return ValueObject::CreateValueObjectFromScalar(
+ m_target, scalar, operand->GetCompilerType(), "result");
break;
}
case UnaryOpKind::Plus: {
- auto operand_type = operand->GetCompilerType();
- // Unary plus is allowed for pointers.
- if (operand_type.IsPointerType())
- return operand;
- if (!operand_type.IsScalarType()) {
+ llvm::Expected<lldb::ValueObjectSP> conv_op =
+ UnaryConversion(operand, m_exe_ctx_scope);
+ if (!conv_op)
+ return conv_op;
+ operand = *conv_op;
+ CompilerType operand_type = operand->GetCompilerType();
+ if (!operand_type.IsScalarType() &&
+ // Unary plus is allowed for pointers.
+ !operand_type.IsPointerType()) {
std::string errMsg =
llvm::formatv("invalid argument type '{0}' to unary expression",
operand_type.GetTypeName());
return llvm::make_error<DILDiagnosticError>(m_expr, errMsg,
node->GetLocation());
}
- auto scalar = GetScalarFromValueObject(operand, m_exe_ctx_scope);
- if (!scalar)
- break;
-
- if (scalar->GetType() == Scalar::e_int) {
- auto promo_type = GetIntCompilerTypeForSize(
- scalar->GetByteSize(), scalar->IsSigned(),
- operand_type.GetTypeSystem().GetSharedPointer(), m_exe_ctx_scope);
- auto type_bitsize = promo_type.GetBitSize(m_exe_ctx_scope.get());
-
- if (type_bitsize) {
- scalar->IntegralPromote(*type_bitsize, promo_type.IsSigned());
- return ValueObject::CreateValueObjectFromScalar(m_target, *scalar,
- promo_type, "result");
- }
- } else
- return ValueObject::CreateValueObjectFromScalar(m_target, *scalar,
- operand_type, "result");
- break;
+ return operand;
}
}
return llvm::make_error<DILDiagnosticError>(m_expr, "invalid unary operation",
@@ -626,24 +689,6 @@ Interpreter::Visit(const BitFieldExtractionNode *node) {
return child_valobj_sp;
}
-static 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);
-}
-
-static CompilerType GetBasicType(lldb::TypeSystemSP type_system,
- lldb::BasicType basic_type) {
- if (type_system)
- return type_system.get()->GetBasicTypeFromAST(basic_type);
-
- return CompilerType();
-}
-
llvm::Expected<CompilerType>
Interpreter::PickIntegerType(lldb::TypeSystemSP type_system,
std::shared_ptr<ExecutionContextScope> ctx,
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
index d10622738cc6f..fc53b656d7591 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
@@ -32,6 +32,10 @@ def test_arithmetic(self):
self.expect_var_path("+0.0", value="0")
self.expect_var_path("-0.0", value="-0")
self.expect_var_path("-9223372036854775808", value="9223372036854775808")
+ self.expect_var_path("+array", type="int *")
+ self.expect_var_path("+enum_one", value="1")
+ self.expect_var_path("-enum_one", value="4294967295") # TODO: fix
+ self.expect_var_path("+bf.a", value="7")
self.expect_var_path("+p", type="int *")
self.expect(
"frame var -- '-p'",
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
index 2567453cc293d..125a64d7b7109 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
@@ -1,3 +1,5 @@
+#include <cstdint>
+
int main(int argc, char **argv) {
short s = 10;
unsigned short us = 1;
@@ -5,5 +7,13 @@ int main(int argc, char **argv) {
int x = 2;
int &r = x;
int *p = &x;
+ int array[] = {1};
+ enum Enum { kZero, kOne } enum_one = kOne;
+
+ struct BitFieldStruct {
+ uint16_t a : 10;
+ };
+ BitFieldStruct bf = {7};
+
return 0; // Set a breakpoint here
}
>From 05eec8b50f3f6aa93ffaa66ff9810bdcf0eb5546 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 19 Aug 2025 20:22:51 +0500
Subject: [PATCH 3/9] Move DoIntegralPromotion and IsPromotableIntegerType to
TypeSystem
---
lldb/include/lldb/Symbol/TypeSystem.h | 7 ++
.../TypeSystem/Clang/TypeSystemClang.cpp | 96 +++++++++++++++++++
.../TypeSystem/Clang/TypeSystemClang.h | 8 ++
lldb/source/Symbol/CompilerType.cpp | 28 +-----
lldb/source/Symbol/TypeSystem.cpp | 10 ++
lldb/source/ValueObject/DILEval.cpp | 82 +++-------------
.../Arithmetic/TestFrameVarDILArithmetic.py | 2 +-
7 files changed, 137 insertions(+), 96 deletions(-)
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 16a2e0b5a52fb..d74e6a0e05625 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -412,6 +412,13 @@ class TypeSystem : public PluginInterface,
GetIntegralTemplateArgument(lldb::opaque_compiler_type_t type, size_t idx,
bool expand_pack);
+ // DIL
+
+ virtual bool IsPromotableIntegerType(lldb::opaque_compiler_type_t type);
+
+ virtual llvm::Expected<CompilerType>
+ DoIntegralPromotion(CompilerType from, ExecutionContextScope *exe_scope);
+
// Dumping types
#ifndef NDEBUG
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 39aacdb58e694..4d4ca522e052a 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -7320,6 +7320,102 @@ CompilerType TypeSystemClang::GetTypeForFormatters(void *type) {
return CompilerType();
}
+bool TypeSystemClang::IsPromotableIntegerType(
+ lldb::opaque_compiler_type_t type) {
+ // Unscoped enums are always considered as promotable, even if their
+ // underlying type does not need to be promoted (e.g. "int").
+ bool is_signed = false;
+ bool isUnscopedEnumerationType =
+ IsEnumerationType(type, is_signed) && !IsScopedEnumerationType(type);
+ if (isUnscopedEnumerationType)
+ return true;
+
+ switch (GetBasicTypeEnumeration(type)) {
+ case lldb::eBasicTypeBool:
+ case lldb::eBasicTypeChar:
+ case lldb::eBasicTypeSignedChar:
+ case lldb::eBasicTypeUnsignedChar:
+ case lldb::eBasicTypeShort:
+ case lldb::eBasicTypeUnsignedShort:
+ case lldb::eBasicTypeWChar:
+ case lldb::eBasicTypeSignedWChar:
+ case lldb::eBasicTypeUnsignedWChar:
+ case lldb::eBasicTypeChar16:
+ case lldb::eBasicTypeChar32:
+ return true;
+
+ default:
+ return false;
+ }
+
+ llvm_unreachable("All cases handled above.");
+}
+
+llvm::Expected<CompilerType>
+TypeSystemClang::DoIntegralPromotion(CompilerType from,
+ ExecutionContextScope *exe_scope) {
+ if (!from.IsInteger() && !from.IsUnscopedEnumerationType())
+ return from;
+
+ if (!from.IsPromotableIntegerType())
+ return from;
+
+ if (from.IsUnscopedEnumerationType()) {
+ EnumDecl *enum_decl = GetAsEnumDecl(from);
+ CompilerType promotion_type = GetType(enum_decl->getPromotionType());
+ return DoIntegralPromotion(promotion_type, exe_scope);
+ }
+
+ lldb::BasicType builtin_type =
+ from.GetCanonicalType().GetBasicTypeEnumeration();
+ uint64_t from_size = 0;
+ if (builtin_type == lldb::eBasicTypeWChar ||
+ builtin_type == lldb::eBasicTypeSignedWChar ||
+ builtin_type == lldb::eBasicTypeUnsignedWChar ||
+ builtin_type == lldb::eBasicTypeChar16 ||
+ builtin_type == lldb::eBasicTypeChar32) {
+ // Find the type that can hold the entire range of values for our type.
+ bool is_signed = from.IsSigned();
+ llvm::Expected<uint64_t> from_size = from.GetByteSize(exe_scope);
+ if (!from_size)
+ return from_size.takeError();
+ CompilerType promote_types[] = {
+ GetBasicTypeFromAST(lldb::eBasicTypeInt),
+ GetBasicTypeFromAST(lldb::eBasicTypeUnsignedInt),
+ GetBasicTypeFromAST(lldb::eBasicTypeLong),
+ GetBasicTypeFromAST(lldb::eBasicTypeUnsignedLong),
+ GetBasicTypeFromAST(lldb::eBasicTypeLongLong),
+ GetBasicTypeFromAST(lldb::eBasicTypeUnsignedLongLong),
+ };
+ for (CompilerType &type : promote_types) {
+ llvm::Expected<uint64_t> byte_size = type.GetByteSize(exe_scope);
+ if (!byte_size)
+ return byte_size.takeError();
+ if (*from_size < *byte_size ||
+ (*from_size == *byte_size && is_signed == type.IsSigned())) {
+ return type;
+ }
+ }
+ llvm_unreachable("char type should fit into long long");
+ }
+
+ // Here we can promote only to "int" or "unsigned int".
+ CompilerType int_type = GetBasicTypeFromAST(lldb::eBasicTypeInt);
+ llvm::Expected<uint64_t> int_byte_size = int_type.GetByteSize(exe_scope);
+ if (!int_byte_size)
+ return int_byte_size.takeError();
+
+ // Signed integer types can be safely promoted to "int".
+ if (from.IsSigned()) {
+ return int_type;
+ }
+ // Unsigned integer types are promoted to "unsigned int" if "int" cannot hold
+ // their entire value range.
+ return (from_size == *int_byte_size)
+ ? GetBasicTypeFromAST(lldb::eBasicTypeUnsignedInt)
+ : int_type;
+}
+
clang::EnumDecl *TypeSystemClang::GetAsEnumDecl(const CompilerType &type) {
const clang::EnumType *enutype =
llvm::dyn_cast<clang::EnumType>(ClangUtil::GetCanonicalQualType(type));
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 709f89590ba3b..0ca1a1e2578b3 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -939,6 +939,14 @@ class TypeSystemClang : public TypeSystem {
CompilerType GetTypeForFormatters(void *type) override;
+ // DIL
+
+ bool IsPromotableIntegerType(lldb::opaque_compiler_type_t type) override;
+
+ llvm::Expected<CompilerType>
+ DoIntegralPromotion(CompilerType from,
+ ExecutionContextScope *exe_scope) override;
+
#define LLDB_INVALID_DECL_LEVEL UINT32_MAX
// LLDB_INVALID_DECL_LEVEL is returned by CountDeclLevels if child_decl_ctx
// could not be found in decl_ctx.
diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp
index 62c0ddf51c012..a22e817f818ce 100644
--- a/lldb/source/Symbol/CompilerType.cpp
+++ b/lldb/source/Symbol/CompilerType.cpp
@@ -373,30 +373,10 @@ bool CompilerType::IsScalarOrUnscopedEnumerationType() const {
}
bool CompilerType::IsPromotableIntegerType() const {
- // Unscoped enums are always considered as promotable, even if their
- // underlying type does not need to be promoted (e.g. "int").
- if (IsUnscopedEnumerationType())
- return true;
-
- switch (GetBasicTypeEnumeration()) {
- case lldb::eBasicTypeBool:
- case lldb::eBasicTypeChar:
- case lldb::eBasicTypeSignedChar:
- case lldb::eBasicTypeUnsignedChar:
- case lldb::eBasicTypeShort:
- case lldb::eBasicTypeUnsignedShort:
- case lldb::eBasicTypeWChar:
- case lldb::eBasicTypeSignedWChar:
- case lldb::eBasicTypeUnsignedWChar:
- case lldb::eBasicTypeChar16:
- case lldb::eBasicTypeChar32:
- return true;
-
- default:
- return false;
- }
-
- llvm_unreachable("All cases handled above.");
+ if (IsValid())
+ if (auto type_system_sp = GetTypeSystem())
+ return type_system_sp->IsPromotableIntegerType(m_type);
+ return false;
}
bool CompilerType::IsPointerToVoid() const {
diff --git a/lldb/source/Symbol/TypeSystem.cpp b/lldb/source/Symbol/TypeSystem.cpp
index f7d634ffa2dec..7df049084e6fb 100644
--- a/lldb/source/Symbol/TypeSystem.cpp
+++ b/lldb/source/Symbol/TypeSystem.cpp
@@ -123,6 +123,16 @@ CompilerType TypeSystem::GetTypeForFormatters(void *type) {
return CompilerType(weak_from_this(), type);
}
+bool TypeSystem::IsPromotableIntegerType(lldb::opaque_compiler_type_t type) {
+ return false;
+}
+
+llvm::Expected<CompilerType>
+TypeSystem::DoIntegralPromotion(CompilerType from,
+ ExecutionContextScope *exe_scope) {
+ return CompilerType();
+}
+
bool TypeSystem::IsTemplateType(lldb::opaque_compiler_type_t type) {
return false;
}
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 05ce5b4b8f7af..ff757f34772ef 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -22,12 +22,15 @@
namespace lldb_private::dil {
static llvm::Expected<lldb::TypeSystemSP>
-GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx) {
+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 =
- ctx->GetSymbolContext(lldb::eSymbolContextCompUnit);
+ stack_frame->GetSymbolContext(lldb::eSymbolContextCompUnit);
lldb::LanguageType language = symbol_context.comp_unit->GetLanguage();
- symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule);
+ symbol_context = stack_frame->GetSymbolContext(lldb::eSymbolContextModule);
return symbol_context.module_sp->GetTypeSystemForLanguage(language);
}
@@ -39,74 +42,9 @@ static CompilerType GetBasicType(lldb::TypeSystemSP type_system,
return CompilerType();
}
-static llvm::Expected<CompilerType>
-DoIntegralPromotion(CompilerType from, lldb::TypeSystemSP type_system,
- std::shared_ptr<StackFrame> ctx) {
- if (!from.IsInteger() && !from.IsUnscopedEnumerationType())
- return from;
-
- if (!from.IsPromotableIntegerType())
- return from;
-
- if (from.IsUnscopedEnumerationType())
- // TODO: fix this by creating CompilerType::GetEnumerationPromotionType()
- return DoIntegralPromotion(from.GetEnumerationIntegerType(), type_system,
- ctx);
- lldb::BasicType builtin_type =
- from.GetCanonicalType().GetBasicTypeEnumeration();
-
- uint64_t from_size = 0;
- if (builtin_type == lldb::eBasicTypeWChar ||
- builtin_type == lldb::eBasicTypeSignedWChar ||
- builtin_type == lldb::eBasicTypeUnsignedWChar ||
- builtin_type == lldb::eBasicTypeChar16 ||
- builtin_type == lldb::eBasicTypeChar32) {
- // Find the type that can hold the entire range of values for our type.
- bool is_signed = from.IsSigned();
- llvm::Expected<uint64_t> from_size = from.GetByteSize(ctx.get());
- if (!from_size)
- return from_size.takeError();
-
- CompilerType promote_types[] = {
- GetBasicType(type_system, lldb::eBasicTypeInt),
- GetBasicType(type_system, lldb::eBasicTypeUnsignedInt),
- GetBasicType(type_system, lldb::eBasicTypeLong),
- GetBasicType(type_system, lldb::eBasicTypeUnsignedLong),
- GetBasicType(type_system, lldb::eBasicTypeLongLong),
- GetBasicType(type_system, lldb::eBasicTypeUnsignedLongLong),
- };
- for (CompilerType &type : promote_types) {
- llvm::Expected<uint64_t> byte_size = type.GetByteSize(ctx.get());
- if (!byte_size)
- return byte_size.takeError();
- if (*from_size < *byte_size ||
- (*from_size == *byte_size && is_signed == type.IsSigned())) {
- return type;
- }
- }
- llvm_unreachable("char type should fit into long long");
- }
-
- // Here we can promote only to "int" or "unsigned int".
- CompilerType int_type = GetBasicType(type_system, lldb::eBasicTypeInt);
- llvm::Expected<uint64_t> int_byte_size = int_type.GetByteSize(ctx.get());
- if (!int_byte_size)
- return int_byte_size.takeError();
-
- // Signed integer types can be safely promoted to "int".
- if (from.IsSigned()) {
- return int_type;
- }
- // Unsigned integer types are promoted to "unsigned int" if "int" cannot hold
- // their entire value range.
- return (from_size == *int_byte_size)
- ? GetBasicType(type_system, lldb::eBasicTypeUnsignedInt)
- : int_type;
-}
-
static lldb::ValueObjectSP
ArrayToPointerConversion(lldb::ValueObjectSP valobj,
- std::shared_ptr<StackFrame> ctx) {
+ std::shared_ptr<ExecutionContextScope> ctx) {
assert(valobj->IsArrayType() &&
"an argument to array-to-pointer conversion must be an array");
@@ -120,7 +58,8 @@ ArrayToPointerConversion(lldb::ValueObjectSP valobj,
}
static llvm::Expected<lldb::ValueObjectSP>
-UnaryConversion(lldb::ValueObjectSP valobj, std::shared_ptr<StackFrame> ctx) {
+UnaryConversion(lldb::ValueObjectSP valobj,
+ std::shared_ptr<ExecutionContextScope> ctx) {
// Perform usual conversions for unary operators. At the moment this includes
// array-to-pointer and the integral promotion for eligible types.
llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(ctx);
@@ -160,7 +99,8 @@ UnaryConversion(lldb::ValueObjectSP valobj, std::shared_ptr<StackFrame> ctx) {
if (valobj->GetCompilerType().IsInteger() ||
valobj->GetCompilerType().IsUnscopedEnumerationType()) {
llvm::Expected<CompilerType> promoted_type =
- DoIntegralPromotion(valobj->GetCompilerType(), *type_system, ctx);
+ type_system.get()->DoIntegralPromotion(valobj->GetCompilerType(),
+ ctx.get());
if (!promoted_type)
return promoted_type.takeError();
if (!promoted_type->CompareTypes(valobj->GetCompilerType()))
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
index fc53b656d7591..c6a97e6f52089 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
@@ -34,7 +34,7 @@ def test_arithmetic(self):
self.expect_var_path("-9223372036854775808", value="9223372036854775808")
self.expect_var_path("+array", type="int *")
self.expect_var_path("+enum_one", value="1")
- self.expect_var_path("-enum_one", value="4294967295") # TODO: fix
+ self.expect_var_path("-enum_one", value="-1")
self.expect_var_path("+bf.a", value="7")
self.expect_var_path("+p", type="int *")
self.expect(
>From 86c86fc7accdde1b8914c1351ebb9a0044157469 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 19 Aug 2025 22:47:24 +0500
Subject: [PATCH 4/9] Adjust bitfield promotion
---
lldb/include/lldb/ValueObject/DILEval.h | 2 +
lldb/source/ValueObject/DILEval.cpp | 37 +++++++++++--------
.../Arithmetic/TestFrameVarDILArithmetic.py | 6 ++-
.../frame/var-dil/expr/Arithmetic/main.cpp | 7 +++-
4 files changed, 34 insertions(+), 18 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 5a48c2c989f4d..11f44336cbf55 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -59,6 +59,8 @@ class Interpreter : Visitor {
llvm::Expected<lldb::ValueObjectSP>
Visit(const FloatLiteralNode *node) override;
+ llvm::Expected<lldb::ValueObjectSP>
+ UnaryConversion(lldb::ValueObjectSP valobj);
llvm::Expected<CompilerType>
PickIntegerType(lldb::TypeSystemSP type_system,
std::shared_ptr<ExecutionContextScope> ctx,
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index ff757f34772ef..c31b90ce83663 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -57,12 +57,12 @@ ArrayToPointerConversion(lldb::ValueObjectSP valobj,
/* do_deref */ false);
}
-static llvm::Expected<lldb::ValueObjectSP>
-UnaryConversion(lldb::ValueObjectSP valobj,
- std::shared_ptr<ExecutionContextScope> ctx) {
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::UnaryConversion(lldb::ValueObjectSP valobj) {
// Perform usual conversions for unary operators. At the moment this includes
// array-to-pointer and the integral promotion for eligible types.
- llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(ctx);
+ llvm::Expected<lldb::TypeSystemSP> type_system =
+ GetTypeSystemFromCU(m_exe_ctx_scope);
if (!type_system)
return type_system.takeError();
CompilerType in_type = valobj->GetCompilerType();
@@ -79,28 +79,37 @@ UnaryConversion(lldb::ValueObjectSP valobj,
CompilerType int_type = GetBasicType(*type_system, lldb::eBasicTypeInt);
CompilerType uint_type =
GetBasicType(*type_system, lldb::eBasicTypeUnsignedInt);
- llvm::Expected<uint64_t> int_bit_size = int_type.GetBitSize(ctx.get());
+ llvm::Expected<uint64_t> int_bit_size =
+ int_type.GetBitSize(m_exe_ctx_scope.get());
if (!int_bit_size)
return int_bit_size.takeError();
- llvm::Expected<uint64_t> uint_bit_size = uint_type.GetBitSize(ctx.get());
+ llvm::Expected<uint64_t> uint_bit_size =
+ uint_type.GetBitSize(m_exe_ctx_scope.get());
if (!uint_bit_size)
return int_bit_size.takeError();
if (bitfield_size < *int_bit_size ||
(in_type.IsSigned() && bitfield_size == *int_bit_size))
- valobj = valobj->CastToBasicType(int_type);
- else if (bitfield_size <= *uint_bit_size)
- valobj = valobj->CastToBasicType(uint_type);
+ return valobj->CastToBasicType(int_type);
+ if (bitfield_size <= *uint_bit_size)
+ return valobj->CastToBasicType(uint_type);
+ // Re-create as a const value with the same underlying type
+ Scalar scalar;
+ bool resolved = valobj->ResolveValue(scalar);
+ if (!resolved)
+ return llvm::createStringError("invalid scalar value");
+ return ValueObject::CreateValueObjectFromScalar(m_target, scalar, in_type,
+ "result");
}
}
if (in_type.IsArrayType())
- valobj = ArrayToPointerConversion(valobj, ctx);
+ valobj = ArrayToPointerConversion(valobj, m_exe_ctx_scope);
if (valobj->GetCompilerType().IsInteger() ||
valobj->GetCompilerType().IsUnscopedEnumerationType()) {
llvm::Expected<CompilerType> promoted_type =
type_system.get()->DoIntegralPromotion(valobj->GetCompilerType(),
- ctx.get());
+ m_exe_ctx_scope.get());
if (!promoted_type)
return promoted_type.takeError();
if (!promoted_type->CompareTypes(valobj->GetCompilerType()))
@@ -299,8 +308,7 @@ Interpreter::Visit(const UnaryOpNode *node) {
return value;
}
case UnaryOpKind::Minus: {
- llvm::Expected<lldb::ValueObjectSP> conv_op =
- UnaryConversion(operand, m_exe_ctx_scope);
+ llvm::Expected<lldb::ValueObjectSP> conv_op = UnaryConversion(operand);
if (!conv_op)
return conv_op;
operand = *conv_op;
@@ -324,8 +332,7 @@ Interpreter::Visit(const UnaryOpNode *node) {
break;
}
case UnaryOpKind::Plus: {
- llvm::Expected<lldb::ValueObjectSP> conv_op =
- UnaryConversion(operand, m_exe_ctx_scope);
+ llvm::Expected<lldb::ValueObjectSP> conv_op = UnaryConversion(operand);
if (!conv_op)
return conv_op;
operand = *conv_op;
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
index c6a97e6f52089..d2b0ecd75966e 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
@@ -35,7 +35,11 @@ def test_arithmetic(self):
self.expect_var_path("+array", type="int *")
self.expect_var_path("+enum_one", value="1")
self.expect_var_path("-enum_one", value="-1")
- self.expect_var_path("+bf.a", value="7")
+ self.expect_var_path("-bitfield.a", value="-1", type="int")
+ self.expect_var_path("+bitfield.a", value="1", type="int")
+ self.expect_var_path("+bitfield.b", value="2", type="int")
+ self.expect_var_path("+bitfield.c", value="3", type="unsigned int")
+ self.expect_var_path("+bitfield.d", value="4", type="uint64_t")
self.expect_var_path("+p", type="int *")
self.expect(
"frame var -- '-p'",
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
index 125a64d7b7109..fdeb6cf49ccfd 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
@@ -11,9 +11,12 @@ int main(int argc, char **argv) {
enum Enum { kZero, kOne } enum_one = kOne;
struct BitFieldStruct {
- uint16_t a : 10;
+ char a : 4;
+ int b : 32;
+ unsigned int c : 32;
+ uint64_t d : 48;
};
- BitFieldStruct bf = {7};
+ BitFieldStruct bitfield = {1, 2, 3, 4};
return 0; // Set a breakpoint here
}
>From 80b5f94f4b70dd9dfee584bfa0c42fada5c6ef39 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Fri, 22 Aug 2025 17:18:33 +0500
Subject: [PATCH 5/9] Expand the test
---
.../Arithmetic/TestFrameVarDILArithmetic.py | 19 +++++++++++--------
.../frame/var-dil/expr/Arithmetic/main.cpp | 3 +++
2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
index d2b0ecd75966e..76edc5837cf2b 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
@@ -19,27 +19,30 @@ def test_arithmetic(self):
self.runCmd("settings set target.experimental.use-DIL true")
- # Check unary
+ # Check unary results and integral promotion
self.expect_var_path("+0", value="0")
self.expect_var_path("-0", value="0")
self.expect_var_path("+1", value="1")
self.expect_var_path("-1", value="-1")
- self.expect_var_path("s", value="10")
- self.expect_var_path("+s", value="10")
- self.expect_var_path("-s", value="-10")
- self.expect_var_path("us", value="1")
- self.expect_var_path("-us", value="-1")
+ self.expect_var_path("-9223372036854775808", value="9223372036854775808")
+ self.expect_var_path("s", value="10", type="short")
+ self.expect_var_path("+s", value="10", type="int")
+ self.expect_var_path("-s", value="-10", type="int")
+ self.expect_var_path("+us", value="1", type="int")
+ self.expect_var_path("-us", value="-1", type="int")
self.expect_var_path("+0.0", value="0")
self.expect_var_path("-0.0", value="-0")
- self.expect_var_path("-9223372036854775808", value="9223372036854775808")
- self.expect_var_path("+array", type="int *")
self.expect_var_path("+enum_one", value="1")
self.expect_var_path("-enum_one", value="-1")
+ self.expect_var_path("+wchar", value="1")
+ self.expect_var_path("+char16", value="2")
+ self.expect_var_path("+char32", value="3")
self.expect_var_path("-bitfield.a", value="-1", type="int")
self.expect_var_path("+bitfield.a", value="1", type="int")
self.expect_var_path("+bitfield.b", value="2", type="int")
self.expect_var_path("+bitfield.c", value="3", type="unsigned int")
self.expect_var_path("+bitfield.d", value="4", type="uint64_t")
+ self.expect_var_path("+array", type="int *")
self.expect_var_path("+p", type="int *")
self.expect(
"frame var -- '-p'",
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
index fdeb6cf49ccfd..5090ce8e34e3d 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
@@ -9,6 +9,9 @@ int main(int argc, char **argv) {
int *p = &x;
int array[] = {1};
enum Enum { kZero, kOne } enum_one = kOne;
+ wchar_t wchar = 1;
+ char16_t char16 = 2;
+ char32_t char32 = 3;
struct BitFieldStruct {
char a : 4;
>From 72b4fbc46c9e91fea5fb80bc97b045f8af9294ef Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 28 Aug 2025 22:05:57 +0500
Subject: [PATCH 6/9] Expand grammar
---
lldb/docs/dil-expr-lang.ebnf | 2 +-
lldb/source/ValueObject/DILParser.cpp | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 67328939ba420..20804935bf356 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -8,7 +8,7 @@ expression = unary_expression ;
unary_expression = postfix_expression
| unary_operator expression ;
-unary_operator = "*" | "&" ;
+unary_operator = "*" | "&" | "+" | "-";
postfix_expression = primary_expression
| postfix_expression "[" integer_literal "]"
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index b88345d18a6d4..b1ba25213c803 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -93,6 +93,8 @@ ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
// unary_operator:
// "&"
// "*"
+// "+"
+// "-"
//
ASTNodeUP DILParser::ParseUnaryExpression() {
if (CurToken().IsOneOf(
>From cf649bfad46db0fce4dd83af46d84b2447c4b125 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 9 Sep 2025 21:45:22 +0500
Subject: [PATCH 7/9] Add dereferencing
---
lldb/source/ValueObject/DILEval.cpp | 10 ++++++++++
.../expr/Arithmetic/TestFrameVarDILArithmetic.py | 7 +++++--
.../commands/frame/var-dil/expr/Arithmetic/main.cpp | 5 +++--
3 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index c31b90ce83663..81b62ea50f6ee 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -308,6 +308,11 @@ Interpreter::Visit(const UnaryOpNode *node) {
return value;
}
case UnaryOpKind::Minus: {
+ if (operand->GetCompilerType().IsReferenceType()) {
+ operand = operand->Dereference(error);
+ if (error.Fail())
+ return error.ToError();
+ }
llvm::Expected<lldb::ValueObjectSP> conv_op = UnaryConversion(operand);
if (!conv_op)
return conv_op;
@@ -332,6 +337,11 @@ Interpreter::Visit(const UnaryOpNode *node) {
break;
}
case UnaryOpKind::Plus: {
+ if (operand->GetCompilerType().IsReferenceType()) {
+ operand = operand->Dereference(error);
+ if (error.Fail())
+ return error.ToError();
+ }
llvm::Expected<lldb::ValueObjectSP> conv_op = UnaryConversion(operand);
if (!conv_op)
return conv_op;
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
index 76edc5837cf2b..ab6dba405585e 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
@@ -30,6 +30,8 @@ def test_arithmetic(self):
self.expect_var_path("-s", value="-10", type="int")
self.expect_var_path("+us", value="1", type="int")
self.expect_var_path("-us", value="-1", type="int")
+ self.expect_var_path("+ref", value="2", type="int")
+ self.expect_var_path("-ref", value="-2", type="int")
self.expect_var_path("+0.0", value="0")
self.expect_var_path("-0.0", value="-0")
self.expect_var_path("+enum_one", value="1")
@@ -43,9 +45,10 @@ def test_arithmetic(self):
self.expect_var_path("+bitfield.c", value="3", type="unsigned int")
self.expect_var_path("+bitfield.d", value="4", type="uint64_t")
self.expect_var_path("+array", type="int *")
- self.expect_var_path("+p", type="int *")
+ self.expect_var_path("+array_ref", type="int *")
+ self.expect_var_path("+ptr", type="int *")
self.expect(
- "frame var -- '-p'",
+ "frame var -- '-ptr'",
error=True,
substrs=["invalid argument type 'int *' to unary expression"],
)
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
index 5090ce8e34e3d..a077bcd3b326c 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
@@ -5,9 +5,10 @@ int main(int argc, char **argv) {
unsigned short us = 1;
int x = 2;
- int &r = x;
- int *p = &x;
+ int &ref = x;
+ int *ptr = &x;
int array[] = {1};
+ int(&array_ref)[1] = array;
enum Enum { kZero, kOne } enum_one = kOne;
wchar_t wchar = 1;
char16_t char16 = 2;
>From 3f3ca9457f92466520d5d6538f673036b4b62f85 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 9 Sep 2025 21:54:51 +0500
Subject: [PATCH 8/9] Split tests for pointer arithmetic
---
.../Arithmetic/TestFrameVarDILArithmetic.py | 8 -----
.../frame/var-dil/expr/Arithmetic/main.cpp | 3 --
.../var-dil/expr/PointerArithmetic/Makefile | 3 ++
.../TestFrameVarDILPointerArithmetic.py | 29 +++++++++++++++++++
.../var-dil/expr/PointerArithmetic/main.cpp | 8 +++++
5 files changed, 40 insertions(+), 11 deletions(-)
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
index ab6dba405585e..53a85fed303f4 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
@@ -44,11 +44,3 @@ def test_arithmetic(self):
self.expect_var_path("+bitfield.b", value="2", type="int")
self.expect_var_path("+bitfield.c", value="3", type="unsigned int")
self.expect_var_path("+bitfield.d", value="4", type="uint64_t")
- self.expect_var_path("+array", type="int *")
- self.expect_var_path("+array_ref", type="int *")
- self.expect_var_path("+ptr", type="int *")
- self.expect(
- "frame var -- '-ptr'",
- error=True,
- substrs=["invalid argument type 'int *' to unary expression"],
- )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
index a077bcd3b326c..2c70e93433f5f 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp
@@ -6,9 +6,6 @@ int main(int argc, char **argv) {
int x = 2;
int &ref = x;
- int *ptr = &x;
- int array[] = {1};
- int(&array_ref)[1] = array;
enum Enum { kZero, kOne } enum_one = kOne;
wchar_t wchar = 1;
char16_t char16 = 2;
diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/Makefile b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
new file mode 100644
index 0000000000000..88429b370710e
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
@@ -0,0 +1,29 @@
+"""
+Test DIL pointer arithmetic.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILPointerArithmetic(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_pointer_arithmetic(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_var_path("+array", type="int *")
+ self.expect_var_path("+array_ref", type="int *")
+ self.expect_var_path("+p_int0", type="int *")
+ self.expect(
+ "frame var -- '-p_int0'",
+ error=True,
+ substrs=["invalid argument type 'int *' to unary expression"],
+ )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
new file mode 100644
index 0000000000000..ec1fad6c11062
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
@@ -0,0 +1,8 @@
+int main(int argc, char **argv) {
+ int array[10];
+ array[0] = 0;
+ int(&array_ref)[10] = array;
+ int *p_int0 = &array[0];
+
+ return 0; // Set a breakpoint here
+}
>From 56adb0a05d701b55c983dd317d7d8587d4ad6e54 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 10 Sep 2025 23:01:58 +0500
Subject: [PATCH 9/9] Fix formatting
---
.../API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
index ec1fad6c11062..7518e40368ff4 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
@@ -1,7 +1,7 @@
int main(int argc, char **argv) {
int array[10];
array[0] = 0;
- int(&array_ref)[10] = array;
+ int (&array_ref)[10] = array;
int *p_int0 = &array[0];
return 0; // Set a breakpoint here
More information about the lldb-commits
mailing list