[Lldb-commits] [lldb] [LLDB] Add unary operators Dereference and AddressOf to DIL (PR #134428)
Ilia Kuklin via lldb-commits
lldb-commits at lists.llvm.org
Thu Apr 10 12:59:29 PDT 2025
https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/134428
>From 58c4f082452bc0600dcf72ba89cbfeafc109cc54 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Fri, 4 Apr 2025 22:13:02 +0500
Subject: [PATCH 1/4] Add unary operators Dereference and AddressOf
---
lldb/docs/dil-expr-lang.ebnf | 4 +-
lldb/include/lldb/ValueObject/DILAST.h | 32 +++++
lldb/include/lldb/ValueObject/DILEval.h | 29 +++++
lldb/include/lldb/ValueObject/DILLexer.h | 2 +
lldb/include/lldb/ValueObject/DILParser.h | 3 +-
lldb/source/ValueObject/DILAST.cpp | 4 +
lldb/source/ValueObject/DILEval.cpp | 139 +++++++++++++++++++++-
lldb/source/ValueObject/DILLexer.cpp | 9 +-
lldb/source/ValueObject/DILParser.cpp | 32 ++++-
9 files changed, 245 insertions(+), 9 deletions(-)
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 0bbbecbdc78c1..13b9cc22e6000 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -3,7 +3,9 @@
(* This is currently a subset of the final DIL Language, matching the current
DIL implementation. *)
-expression = primary_expression ;
+expression = unary_expression ;
+
+unary_expression = unary_operator primary_expression ;
primary_expression = id_expression
| "(" expression ")";
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 05d87e9cc4b6b..323ebe8dd49ec 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -20,6 +20,13 @@ namespace lldb_private::dil {
enum class NodeKind {
eErrorNode,
eIdentifierNode,
+ eUnaryOpNode,
+};
+
+/// The Unary operators recognized by DIL.
+enum class UnaryOpKind {
+ AddrOf, // "&"
+ Deref, // "*"
};
/// Forward declaration, for use in DIL AST nodes. Definition is at the very
@@ -44,6 +51,8 @@ class ASTNode {
virtual llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const = 0;
+ virtual bool is_rvalue() const { return false; }
+
uint32_t GetLocation() const { return m_location; }
NodeKind GetKind() const { return m_kind; }
@@ -81,6 +90,27 @@ class IdentifierNode : public ASTNode {
std::string m_name;
};
+class UnaryOpNode : public ASTNode {
+public:
+ UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP rhs)
+ : ASTNode(location, NodeKind::eUnaryOpNode), m_kind(kind),
+ m_rhs(std::move(rhs)) {}
+
+ llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+ bool is_rvalue() const override { return m_kind != UnaryOpKind::Deref; }
+
+ UnaryOpKind kind() const { return m_kind; }
+ ASTNode *rhs() const { return m_rhs.get(); }
+
+ static bool classof(const ASTNode *node) {
+ return node->GetKind() == NodeKind::eUnaryOpNode;
+ }
+
+private:
+ UnaryOpKind m_kind;
+ ASTNodeUP m_rhs;
+};
+
/// This class contains one Visit method for each specialized type of
/// DIL AST node. The Visit methods are used to dispatch a DIL AST node to
/// the correct function in the DIL expression evaluator for evaluating that
@@ -90,6 +120,8 @@ class Visitor {
virtual ~Visitor() = default;
virtual llvm::Expected<lldb::ValueObjectSP>
Visit(const IdentifierNode *node) = 0;
+ virtual llvm::Expected<lldb::ValueObjectSP>
+ Visit(const UnaryOpNode *node) = 0;
};
} // namespace lldb_private::dil
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 335035d3f9248..0080f4dba9291 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -38,6 +38,18 @@ lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref,
lldb::DynamicValueType use_dynamic,
CompilerType *scope_ptr = nullptr);
+class FlowAnalysis {
+public:
+ FlowAnalysis(bool address_of_is_pending)
+ : m_address_of_is_pending(address_of_is_pending) {}
+
+ bool AddressOfIsPending() const { return m_address_of_is_pending; }
+ void DiscardAddressOf() { m_address_of_is_pending = false; }
+
+private:
+ bool m_address_of_is_pending;
+};
+
class Interpreter : Visitor {
public:
Interpreter(lldb::TargetSP target, llvm::StringRef expr,
@@ -47,12 +59,29 @@ class Interpreter : Visitor {
llvm::Expected<lldb::ValueObjectSP> Evaluate(const ASTNode *node);
private:
+ llvm::Expected<lldb::ValueObjectSP>
+ EvaluateNode(const ASTNode *node, FlowAnalysis *flow = nullptr);
+
llvm::Expected<lldb::ValueObjectSP>
Visit(const IdentifierNode *node) override;
+ llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override;
+
+ lldb::ValueObjectSP EvaluateDereference(lldb::ValueObjectSP rhs);
+
+ FlowAnalysis *flow_analysis() { return m_flow_analysis_chain.back(); }
// Used by the interpreter to create objects, perform casts, etc.
lldb::TargetSP m_target;
llvm::StringRef m_expr;
+ // Flow analysis chain represents the expression evaluation flow for the
+ // current code branch. Each node in the chain corresponds to an AST node,
+ // describing the semantics of the evaluation for it. Currently, flow analysis
+ // propagates the information about the pending address-of operator, so that
+ // combination of address-of and a subsequent dereference can be eliminated.
+ // End of the chain (i.e. `back()`) contains the flow analysis instance for
+ // the current node. It may be `nullptr` if no relevant information is
+ // available, the caller/user is supposed to check.
+ std::vector<FlowAnalysis *> m_flow_analysis_chain;
lldb::ValueObjectSP m_scope;
lldb::DynamicValueType m_default_dynamic;
std::shared_ptr<StackFrame> m_exe_ctx_scope;
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index d15fc382d1623..3508b8b7a85c6 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -24,11 +24,13 @@ namespace lldb_private::dil {
class Token {
public:
enum Kind {
+ amp,
coloncolon,
eof,
identifier,
l_paren,
r_paren,
+ star,
};
Token(Kind kind, std::string spelling, uint32_t start)
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 9b7a6cd487939..b755f7eeeac5a 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -43,7 +43,7 @@ class DILDiagnosticError
m_detail(std::move(detail)) {}
DILDiagnosticError(llvm::StringRef expr, const std::string &message,
- uint32_t loc, uint16_t err_len);
+ uint32_t loc, uint16_t err_len = 1);
std::unique_ptr<CloneableError> Clone() const override {
return std::make_unique<DILDiagnosticError>(m_detail);
@@ -83,6 +83,7 @@ class DILParser {
ASTNodeUP Run();
ASTNodeUP ParseExpression();
+ ASTNodeUP ParseUnaryExpression();
ASTNodeUP ParsePrimaryExpression();
std::string ParseNestedNameSpecifier();
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index e75958d784627..ea847587501ee 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -19,4 +19,8 @@ llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
return v->Visit(this);
}
+llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const {
+ return v->Visit(this);
+}
+
} // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 4889834c7a3c1..743b024b44b2e 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -18,6 +18,22 @@
namespace lldb_private::dil {
+static lldb::ValueObjectSP
+ArrayToPointerConversion(lldb::ValueObjectSP valobj,
+ std::shared_ptr<ExecutionContextScope> ctx) {
+ assert(valobj->IsArrayType() &&
+ "an argument to array-to-pointer conversion must be an array");
+
+ uint64_t addr = valobj->GetLoadAddress();
+ llvm::StringRef name = "result";
+ ExecutionContext exe_ctx;
+ ctx->CalculateExecutionContext(exe_ctx);
+ return ValueObject::CreateValueObjectFromAddress(
+ name, addr, exe_ctx,
+ valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(),
+ /* do_deref */ false);
+}
+
static lldb::ValueObjectSP LookupStaticIdentifier(
VariableList &variable_list, std::shared_ptr<StackFrame> exe_scope,
llvm::StringRef name_ref, llvm::StringRef unqualified_name) {
@@ -206,10 +222,25 @@ Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
: m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic),
m_exe_ctx_scope(frame_sp) {}
-llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) {
+llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *tree) {
+ // Evaluate an AST.
+ auto value_or_error = EvaluateNode(tree);
+
+ // Return the computed result-or-error.
+ return value_or_error;
+}
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::EvaluateNode(const ASTNode *node, FlowAnalysis *flow) {
+ // Set up the evaluation context for the current node.
+ m_flow_analysis_chain.push_back(flow);
// Traverse an AST pointed by the `node`.
- return node->Accept(this);
+ auto value_or_error = node->Accept(this);
+ // Cleanup the context.
+ m_flow_analysis_chain.pop_back();
+ // Return the computed value-or-error. The caller is responsible for
+ // checking if an error occured during the evaluation.
+ return value_or_error;
}
llvm::Expected<lldb::ValueObjectSP>
@@ -232,4 +263,106 @@ Interpreter::Visit(const IdentifierNode *node) {
return identifier;
}
-} // namespace lldb_private::dil
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::Visit(const UnaryOpNode *node) {
+ FlowAnalysis rhs_flow(
+ /* address_of_is_pending */ node->kind() == UnaryOpKind::AddrOf);
+
+ Status error;
+ auto rhs_or_err = EvaluateNode(node->rhs(), &rhs_flow);
+ if (!rhs_or_err) {
+ return rhs_or_err;
+ }
+ lldb::ValueObjectSP rhs = *rhs_or_err;
+
+ if (rhs->GetCompilerType().IsReferenceType()) {
+ rhs = rhs->Dereference(error);
+ if (error.Fail()) {
+ return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
+ node->GetLocation());
+ }
+ }
+ CompilerType rhs_type = rhs->GetCompilerType();
+ switch (node->kind()) {
+ case UnaryOpKind::Deref: {
+ if (rhs_type.IsArrayType())
+ rhs = ArrayToPointerConversion(rhs, m_exe_ctx_scope);
+
+ lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
+ if (dynamic_rhs)
+ rhs = dynamic_rhs;
+
+ if (rhs->GetCompilerType().IsPointerType())
+ return EvaluateDereference(rhs);
+ lldb::ValueObjectSP child_sp = rhs->Dereference(error);
+ if (error.Success())
+ rhs = child_sp;
+
+ return rhs;
+ }
+ case UnaryOpKind::AddrOf: {
+ if (node->rhs()->is_rvalue()) {
+ std::string errMsg =
+ llvm::formatv("cannot take the address of an rvalue of type {0}",
+ rhs_type.TypeDescription());
+ return llvm::make_error<DILDiagnosticError>(m_expr, errMsg,
+ node->GetLocation());
+ }
+ if (rhs->IsBitfield()) {
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, "address of bit-field requested", node->GetLocation());
+ }
+ // If the address-of operation wasn't cancelled during the evaluation of
+ // RHS (e.g. because of the address-of-a-dereference elision), apply it
+ // here.
+ if (rhs_flow.AddressOfIsPending()) {
+ Status error;
+ lldb::ValueObjectSP value = rhs->AddressOf(error);
+ if (error.Fail()) {
+ return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
+ node->GetLocation());
+ }
+ return value;
+ }
+ return rhs;
+ }
+ }
+
+ // Unsupported/invalid operation.
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, "invalid ast: unexpected binary operator", node->GetLocation(),
+ 1);
+}
+
+lldb::ValueObjectSP Interpreter::EvaluateDereference(lldb::ValueObjectSP rhs) {
+ // If rhs is a reference, dereference it first.
+ Status error;
+ if (rhs->GetCompilerType().IsReferenceType())
+ rhs = rhs->Dereference(error);
+
+ assert(rhs->GetCompilerType().IsPointerType() &&
+ "invalid ast: must be a pointer type");
+
+ if (rhs->GetDerefValobj())
+ return rhs->GetDerefValobj()->GetSP();
+
+ CompilerType pointer_type = rhs->GetCompilerType();
+ lldb::addr_t base_addr = rhs->GetValueAsUnsigned(0);
+
+ llvm::StringRef name = "result";
+ ExecutionContext exe_ctx(m_target.get(), false);
+ lldb::ValueObjectSP value = ValueObject::CreateValueObjectFromAddress(
+ name, base_addr, exe_ctx, pointer_type,
+ /* do_deref */ false);
+
+ // If we're in the address-of context, skip the dereference and cancel the
+ // pending address-of operation as well.
+ if (flow_analysis() && flow_analysis()->AddressOfIsPending()) {
+ flow_analysis()->DiscardAddressOf();
+ return value;
+ }
+
+ return value->Dereference(error);
+}
+
+} // namespace lldb_private::dil
\ No newline at end of file
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index 1f013288c839b..b9c2e7971e3b4 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -19,6 +19,8 @@ namespace lldb_private::dil {
llvm::StringRef Token::GetTokenName(Kind kind) {
switch (kind) {
+ case Kind::amp:
+ return "amp";
case Kind::coloncolon:
return "coloncolon";
case Kind::eof:
@@ -29,6 +31,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
return "l_paren";
case Kind::r_paren:
return "r_paren";
+ case Token::star:
+ return "star";
}
llvm_unreachable("Unknown token name");
}
@@ -82,9 +86,8 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
return Token(Token::identifier, maybe_word->str(), position);
constexpr std::pair<Token::Kind, const char *> operators[] = {
- {Token::l_paren, "("},
- {Token::r_paren, ")"},
- {Token::coloncolon, "::"},
+ {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("},
+ {Token::r_paren, ")"}, {Token::star, "*"},
};
for (auto [kind, str] : operators) {
if (remainder.consume_front(str))
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index a8baba2c06e7a..c233c535c0355 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -82,7 +82,37 @@ ASTNodeUP DILParser::Run() {
// expression:
// primary_expression
//
-ASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
+ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
+
+// Parse an unary_expression.
+//
+// unary_expression:
+// unary_operator primary_expression
+//
+// unary_operator:
+// "&"
+// "*"
+//
+ASTNodeUP DILParser::ParseUnaryExpression() {
+ if (CurToken().IsOneOf({Token::amp, Token::star})) {
+ Token token = CurToken();
+ uint32_t loc = token.GetLocation();
+ m_dil_lexer.Advance();
+ auto rhs = ParsePrimaryExpression();
+ switch (token.GetKind()) {
+ case Token::star:
+ return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Deref,
+ std::move(rhs));
+ case Token::amp:
+ return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::AddrOf,
+ std::move(rhs));
+
+ default:
+ llvm_unreachable("invalid token kind");
+ }
+ }
+ return ParsePrimaryExpression();
+}
// Parse a primary_expression.
//
>From 36d4f95784756fb38305697e88ceabd22cf5370f Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 8 Apr 2025 00:25:57 +0500
Subject: [PATCH 2/4] Fix parsing, remove dereferencing, add unit tests
---
lldb/docs/dil-expr-lang.ebnf | 5 +-
lldb/include/lldb/API/SBFrame.h | 3 +
lldb/include/lldb/Target/StackFrame.h | 3 +
lldb/source/API/SBFrame.cpp | 35 +
lldb/source/Target/StackFrame.cpp | 17 +-
lldb/source/ValueObject/DILEval.cpp | 15 +-
lldb/source/ValueObject/DILParser.cpp | 5 +-
lldb/unittests/CMakeLists.txt | 1 +
lldb/unittests/DIL/CMakeLists.txt | 15 +
.../{ValueObject => DIL}/DILLexerTests.cpp | 0
lldb/unittests/DIL/DILTests.cpp | 303 ++++
lldb/unittests/DIL/Inputs/CMakeLists.txt | 36 +
lldb/unittests/DIL/Inputs/test_binary.cpp | 1255 +++++++++++++++++
lldb/unittests/DIL/Inputs/test_extern.cpp | 9 +
lldb/unittests/DIL/Runner.cpp | 146 ++
lldb/unittests/DIL/Runner.h | 22 +
lldb/unittests/ValueObject/CMakeLists.txt | 1 -
17 files changed, 1856 insertions(+), 15 deletions(-)
create mode 100644 lldb/unittests/DIL/CMakeLists.txt
rename lldb/unittests/{ValueObject => DIL}/DILLexerTests.cpp (100%)
create mode 100644 lldb/unittests/DIL/DILTests.cpp
create mode 100644 lldb/unittests/DIL/Inputs/CMakeLists.txt
create mode 100644 lldb/unittests/DIL/Inputs/test_binary.cpp
create mode 100644 lldb/unittests/DIL/Inputs/test_extern.cpp
create mode 100644 lldb/unittests/DIL/Runner.cpp
create mode 100644 lldb/unittests/DIL/Runner.h
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 13b9cc22e6000..c8bf4231b3e4a 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -5,7 +5,10 @@
expression = unary_expression ;
-unary_expression = unary_operator primary_expression ;
+unary_expression = unary_operator expression
+ | primary_expression ;
+
+unary_operator = "*" | "&" ;
primary_expression = id_expression
| "(" expression ")";
diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h
index 3635ee5a537ad..e0778623d503d 100644
--- a/lldb/include/lldb/API/SBFrame.h
+++ b/lldb/include/lldb/API/SBFrame.h
@@ -182,6 +182,9 @@ class LLDB_API SBFrame {
// variable value.
lldb::SBValue GetValueForVariablePath(const char *var_expr_cstr,
DynamicValueType use_dynamic);
+ lldb::SBValue TestGetValueForVariablePath(const char *var_expr_cstr,
+ DynamicValueType use_dynamic,
+ bool use_DIL = false);
/// The version that doesn't supply a 'use_dynamic' value will use the
/// target's default.
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 3f51c9a7f22f0..3673040335140 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -308,6 +308,9 @@ class StackFrame : public ExecutionContextScope,
lldb::ValueObjectSP GetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
uint32_t options, lldb::VariableSP &var_sp, Status &error);
+ lldb::ValueObjectSP GetValueForVariableExpressionPath(
+ llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
+ uint32_t options, lldb::VariableSP &var_sp, Status &error, bool use_DIL);
/// Determine whether this StackFrame has debug information available or not.
///
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index 5b69cf1ee2641..c77ab4f335c68 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -492,6 +492,41 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
return sb_value;
}
+lldb::SBValue SBFrame::TestGetValueForVariablePath(const char *var_path,
+ DynamicValueType use_dynamic,
+ bool use_DIL) {
+ LLDB_INSTRUMENT_VA(this, var_path, use_dynamic);
+
+ SBValue sb_value;
+ if (var_path == nullptr || var_path[0] == '\0') {
+ return sb_value;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ VariableSP var_sp;
+ Status error;
+ ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath(
+ var_path, eNoDynamicValues,
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
+ var_sp, error, use_DIL));
+ sb_value.SetSP(value_sp, use_dynamic);
+ }
+ }
+ }
+ return sb_value;
+}
+
SBValue SBFrame::FindVariable(const char *name) {
LLDB_INSTRUMENT_VA(this, name);
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 0306f68169a98..05603cde27a65 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -523,6 +523,17 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
var_sp, error);
}
+ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
+ llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options,
+ VariableSP &var_sp, Status &error, bool use_DIL) {
+ if (use_DIL)
+ return DILGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
+ var_sp, error);
+
+ return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
+ var_sp, error);
+}
+
ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
uint32_t options, lldb::VariableSP &var_sp, Status &error) {
@@ -538,7 +549,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
auto lex_or_err = dil::DILLexer::Create(var_expr);
if (!lex_or_err) {
error = Status::FromError(lex_or_err.takeError());
- return ValueObjectSP();
+ return ValueObjectConstResult::Create(nullptr, std::move(error));
}
// Parse the expression.
@@ -547,7 +558,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
!no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
if (!tree_or_error) {
error = Status::FromError(tree_or_error.takeError());
- return ValueObjectSP();
+ return ValueObjectConstResult::Create(nullptr, std::move(error));
}
// Evaluate the parsed expression.
@@ -558,7 +569,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
auto valobj_or_error = interpreter.Evaluate((*tree_or_error).get());
if (!valobj_or_error) {
error = Status::FromError(valobj_or_error.takeError());
- return ValueObjectSP();
+ return ValueObjectConstResult::Create(nullptr, std::move(error));
}
return *valobj_or_error;
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 743b024b44b2e..8db20da4132a9 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -275,13 +275,6 @@ Interpreter::Visit(const UnaryOpNode *node) {
}
lldb::ValueObjectSP rhs = *rhs_or_err;
- if (rhs->GetCompilerType().IsReferenceType()) {
- rhs = rhs->Dereference(error);
- if (error.Fail()) {
- return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
- node->GetLocation());
- }
- }
CompilerType rhs_type = rhs->GetCompilerType();
switch (node->kind()) {
case UnaryOpKind::Deref: {
@@ -292,8 +285,14 @@ Interpreter::Visit(const UnaryOpNode *node) {
if (dynamic_rhs)
rhs = dynamic_rhs;
- if (rhs->GetCompilerType().IsPointerType())
+ if (rhs->GetCompilerType().IsPointerType()) {
+ if (rhs->GetCompilerType().IsPointerToVoid()) {
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, "indirection not permitted on operand of type 'void *'",
+ node->GetLocation(), 1);
+ }
return EvaluateDereference(rhs);
+ }
lldb::ValueObjectSP child_sp = rhs->Dereference(error);
if (error.Success())
rhs = child_sp;
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index c233c535c0355..553b4dc31d9c0 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -87,7 +87,8 @@ ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
// Parse an unary_expression.
//
// unary_expression:
-// unary_operator primary_expression
+// unary_operator expression
+// primary_expression
//
// unary_operator:
// "&"
@@ -98,7 +99,7 @@ ASTNodeUP DILParser::ParseUnaryExpression() {
Token token = CurToken();
uint32_t loc = token.GetLocation();
m_dil_lexer.Advance();
- auto rhs = ParsePrimaryExpression();
+ auto rhs = ParseExpression();
switch (token.GetKind()) {
case Token::star:
return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Deref,
diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt
index cc9d45ebf981d..dc8b4c84e22f7 100644
--- a/lldb/unittests/CMakeLists.txt
+++ b/lldb/unittests/CMakeLists.txt
@@ -54,6 +54,7 @@ endif()
add_subdirectory(Breakpoint)
add_subdirectory(Callback)
add_subdirectory(Core)
+add_subdirectory(DIL)
add_subdirectory(DataFormatter)
add_subdirectory(Disassembler)
add_subdirectory(Editline)
diff --git a/lldb/unittests/DIL/CMakeLists.txt b/lldb/unittests/DIL/CMakeLists.txt
new file mode 100644
index 0000000000000..07fb7172a2b4c
--- /dev/null
+++ b/lldb/unittests/DIL/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_lldb_unittest(DILTests
+ DILTests.cpp
+ DILLexerTests.cpp
+ Runner.cpp
+
+ LINK_LIBS
+ liblldb
+ lldbUtilityHelpers
+ lldbValueObject
+ LLVMTestingSupport
+ )
+add_subdirectory(Inputs)
+add_dependencies(DILTests test_binary)
+
+add_unittest_inputs(DILTests "test_binary.cpp")
diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/DIL/DILLexerTests.cpp
similarity index 100%
rename from lldb/unittests/ValueObject/DILLexerTests.cpp
rename to lldb/unittests/DIL/DILLexerTests.cpp
diff --git a/lldb/unittests/DIL/DILTests.cpp b/lldb/unittests/DIL/DILTests.cpp
new file mode 100644
index 0000000000000..c9c3620cf3162
--- /dev/null
+++ b/lldb/unittests/DIL/DILTests.cpp
@@ -0,0 +1,303 @@
+//===-- DILTests.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Runner.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBThread.h"
+#include "lldb/API/SBType.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using ::testing::MakeMatcher;
+using ::testing::Matcher;
+using ::testing::MatcherInterface;
+using ::testing::MatchResultListener;
+
+struct EvalResult {
+ lldb::SBError lldb_DIL_error;
+ mutable lldb::SBValue lldb_DIL_value;
+ mutable std::optional<lldb::SBValue> lldb_value;
+};
+
+class EvaluatorHelper {
+public:
+ EvaluatorHelper(lldb::SBFrame frame, bool compare_with_frame_var)
+ : frame_(frame), compare_with_frame_var_(compare_with_frame_var) {}
+
+public:
+ EvalResult Eval(const std::string &expr) {
+ EvalResult ret;
+ ret.lldb_DIL_value = frame_.TestGetValueForVariablePath(
+ expr.c_str(), lldb::eNoDynamicValues, true);
+ if (!ret.lldb_DIL_value.GetError().Success())
+ ret.lldb_DIL_error = ret.lldb_DIL_value.GetError();
+ if (compare_with_frame_var_) {
+ ret.lldb_value = frame_.TestGetValueForVariablePath(
+ expr.c_str(), lldb::eNoDynamicValues, false);
+ }
+ return ret;
+ }
+
+private:
+ lldb::SBFrame frame_;
+ bool compare_with_frame_var_;
+};
+
+void PrintError(::testing::MatchResultListener *listener,
+ const std::string &error) {
+ *listener << "error:";
+ // Print multiline errors on a separate line.
+ if (error.find('\n') != std::string::npos) {
+ *listener << "\n";
+ } else {
+ *listener << " ";
+ }
+ *listener << error;
+}
+
+class IsOkMatcher : public MatcherInterface<EvalResult> {
+public:
+ explicit IsOkMatcher(bool compare_types) : compare_types_(compare_types) {}
+
+ bool MatchAndExplain(EvalResult result,
+ MatchResultListener *listener) const override {
+ if (result.lldb_DIL_error.GetError()) {
+ PrintError(listener, result.lldb_DIL_error.GetCString());
+ return false;
+ }
+
+ std::string actual = result.lldb_DIL_value.GetValue();
+ // Compare only if we tried to evaluate with LLDB.
+ if (result.lldb_value.has_value()) {
+ if (result.lldb_value.value().GetError().GetError()) {
+ *listener << "values produced by DIL and 'frame var' don't match\n"
+ << "DIL : " << actual << "\n"
+ << "frame var: "
+ << result.lldb_value.value().GetError().GetCString();
+ return false;
+
+ } else if (actual != result.lldb_value.value().GetValue()) {
+ *listener << "values produced by DIL and 'frame var' don't match\n"
+ << "DIL : " << actual << "\n"
+ << "frame var: " << result.lldb_value.value().GetValue();
+ return false;
+ }
+
+ if (compare_types_) {
+ const char *lldb_DIL_type =
+ result.lldb_DIL_value.GetType().GetUnqualifiedType().GetName();
+ const char *lldb_type =
+ result.lldb_value.value().GetType().GetUnqualifiedType().GetName();
+ if (strcmp(lldb_DIL_type, lldb_type) != 0) {
+ *listener << "types produced by DIL and 'frame var' don't match\n"
+ << "DIL : " << lldb_DIL_type << "\n"
+ << "frame var: " << lldb_type;
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ void DescribeTo(std::ostream *os) const override {
+ *os << "evaluates without an error and equals to LLDB";
+ }
+
+private:
+ bool compare_types_;
+};
+
+Matcher<EvalResult> IsOk(bool compare_types = true) {
+ return MakeMatcher(new IsOkMatcher(compare_types));
+}
+
+class IsEqualMatcher : public MatcherInterface<EvalResult> {
+public:
+ IsEqualMatcher(std::string value, bool compare_types)
+ : value_(std::move(value)), compare_types_(compare_types) {}
+
+public:
+ bool MatchAndExplain(EvalResult result,
+ MatchResultListener *listener) const override {
+ if (result.lldb_DIL_error.GetError()) {
+ PrintError(listener, result.lldb_DIL_error.GetCString());
+ return false;
+ }
+
+ std::string actual = result.lldb_DIL_value.GetValue();
+ if (actual != value_) {
+ *listener << "evaluated to '" << actual << "'";
+ return false;
+ }
+
+ // Compare only if we tried to evaluate with LLDB.
+ if (result.lldb_value.has_value()) {
+ if (result.lldb_value.value().GetError().GetError()) {
+ *listener << "values produced by DIL and 'frame var' don't match\n"
+ << "DIL : " << actual << "\n"
+ << "frame var: "
+ << result.lldb_value.value().GetError().GetCString();
+ return false;
+
+ } else if (actual != result.lldb_value.value().GetValue()) {
+ *listener << "values produced by DIL and 'frame var' don't match\n"
+ << "DIL : " << actual << "\n"
+ << "frame var: " << result.lldb_value.value().GetValue();
+ return false;
+ }
+
+ if (compare_types_) {
+ const char *lldb_DIL_type =
+ result.lldb_DIL_value.GetType().GetUnqualifiedType().GetName();
+ const char *lldb_type =
+ result.lldb_value.value().GetType().GetUnqualifiedType().GetName();
+ if (strcmp(lldb_DIL_type, lldb_type) != 0) {
+ *listener << "types produced by DIL and 'frame var' don't match\n"
+ << "DIL : " << lldb_DIL_type << "\n"
+ << "frame var: " << lldb_type;
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ void DescribeTo(std::ostream *os) const override {
+ *os << "evaluates to '" << value_ << "'";
+ }
+
+private:
+ std::string value_;
+ bool compare_types_;
+};
+
+Matcher<EvalResult> IsEqual(std::string value, bool compare_types = true) {
+ return MakeMatcher(new IsEqualMatcher(std::move(value), compare_types));
+}
+
+class IsErrorMatcher : public MatcherInterface<EvalResult> {
+public:
+ explicit IsErrorMatcher(std::string value) : value_(std::move(value)) {}
+
+public:
+ bool MatchAndExplain(EvalResult result,
+ MatchResultListener *listener) const override {
+ if (!result.lldb_DIL_error.GetError()) {
+ *listener << "evaluated to '" << result.lldb_DIL_value.GetValue() << "'";
+ return false;
+ }
+ std::string message = result.lldb_DIL_error.GetCString();
+ if (message.find(value_) == std::string::npos) {
+ PrintError(listener, message);
+ return false;
+ }
+
+ return true;
+ }
+
+ void DescribeTo(std::ostream *os) const override {
+ *os << "evaluates with an error: '" << value_ << "'";
+ }
+
+private:
+ std::string value_;
+};
+
+Matcher<EvalResult> IsError(std::string value) {
+ return MakeMatcher(new IsErrorMatcher(std::move(value)));
+}
+
+class EvalTest : public ::testing::Test {
+protected:
+ static void SetUpTestSuite() { lldb::SBDebugger::Initialize(); }
+
+ static void TearDownTestSuite() { lldb::SBDebugger::Terminate(); }
+
+ void SetUp() override {
+ std::string test_name =
+ ::testing::UnitTest::GetInstance()->current_test_info()->name();
+ std::string break_line = "// BREAK(" + test_name + ")";
+
+ std::string binary_path =
+ lldb_private::GetInputFilePath("test_binary.bin");
+ std::string source_path = lldb_private::GetInputFilePath("test_binary.cpp");
+
+ debugger_ = lldb::SBDebugger::Create(false);
+ process_ =
+ LaunchTestProgram(debugger_, source_path, binary_path, break_line);
+ frame_ = process_.GetSelectedThread().GetSelectedFrame();
+ }
+
+ void TearDown() override {
+ process_.Destroy();
+ lldb::SBDebugger::Destroy(debugger_);
+ }
+
+ EvalResult Eval(const std::string &expr) {
+ return EvaluatorHelper(frame_, compare_with_frame_var_).Eval(expr);
+ }
+
+ bool Is32Bit() const {
+ if (process_.GetAddressByteSize() == 4) {
+ return true;
+ }
+ return false;
+ }
+
+protected:
+ lldb::SBDebugger debugger_;
+ lldb::SBProcess process_;
+ lldb::SBFrame frame_;
+
+ // Evaluate with both DIL and LLDB by default.
+ bool compare_with_frame_var_ = true;
+};
+
+TEST_F(EvalTest, TestSymbols) {
+ EXPECT_GT(frame_.GetModule().GetNumSymbols(), (size_t)0)
+ << "No symbols might indicate that the test binary was built incorrectly";
+}
+
+TEST_F(EvalTest, TestPointerDereference) {
+ EXPECT_THAT(Eval("*p_int0"), IsEqual("0"));
+ EXPECT_THAT(Eval("*cp_int5"), IsEqual("5"));
+ EXPECT_THAT(Eval("*rcp_int0"), IsOk());
+
+ EXPECT_THAT(Eval("&*p_void"),
+ IsError("indirection not permitted on operand of type"
+ " 'void *'"));
+
+ this->compare_with_frame_var_ = false;
+ EXPECT_THAT(Eval("*array"), IsEqual("0"));
+ EXPECT_THAT(Eval("&*p_null"),
+ IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000"));
+ EXPECT_THAT(Eval("**pp_int0"), IsEqual("0"));
+ EXPECT_THAT(Eval("&**pp_int0"), IsOk());
+}
+
+TEST_F(EvalTest, TestAddressOf) {
+ EXPECT_THAT(Eval("&x"), IsOk());
+ EXPECT_THAT(Eval("r"), IsOk());
+ EXPECT_THAT(Eval("&r"), IsOk());
+ EXPECT_THAT(Eval("pr"), IsOk());
+ EXPECT_THAT(Eval("&pr"), IsOk());
+ EXPECT_THAT(Eval("my_pr"), IsOk());
+ EXPECT_THAT(Eval("&my_pr"), IsOk());
+
+ EXPECT_THAT(Eval("&globalVar"), IsOk());
+ EXPECT_THAT(Eval("&s_str"), IsOk());
+ EXPECT_THAT(Eval("¶m"), IsOk());
+}
diff --git a/lldb/unittests/DIL/Inputs/CMakeLists.txt b/lldb/unittests/DIL/Inputs/CMakeLists.txt
new file mode 100644
index 0000000000000..73e179d08d059
--- /dev/null
+++ b/lldb/unittests/DIL/Inputs/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Build `test_binary.cc` and put the binary in the Inputs folder,
+# allowing `lldb_private::GetInputFilePath` to find it.
+# Projects that must be enabled: clang;lldb;lld
+# Runtimes that must be enabled: libcxx;libcxxabi;libunwind
+if ("libcxx" IN_LIST LLVM_ENABLE_RUNTIMES)
+ if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
+ set(LIBCXX_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE})
+ set(LIBCXX_GENERATED_INCLUDE_DIR "${LLVM_BINARY_DIR}/include/c++/v1")
+ set(LIBCXX_GENERATED_INCLUDE_TARGET_DIR "${LLVM_BINARY_DIR}/include/${LLVM_DEFAULT_TARGET_TRIPLE}/c++/v1")
+ else()
+ set(LIBCXX_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBCXX_LIBDIR_SUFFIX})
+ set(LIBCXX_GENERATED_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include/c++/v1")
+ endif()
+
+ if(DEFINED LIBCXX_GENERATED_INCLUDE_TARGET_DIR)
+ set(INCLUDE_TARGET_DIR_OPTION "-cxx-isystem" "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}")
+ endif()
+
+ get_target_property(EXE_PATH DILTests RUNTIME_OUTPUT_DIRECTORY)
+ add_custom_command(
+ OUTPUT test_binary.bin
+ COMMAND $<TARGET_FILE:clang> ${CMAKE_CURRENT_SOURCE_DIR}/test_binary.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_extern.cpp
+ -O0 -g -std=c++17 -fuse-ld=lld -B$<TARGET_FILE:lld>
+ -nostdlib++ -nostdinc++ -cxx-isystem ${LIBCXX_GENERATED_INCLUDE_DIR}
+ ${INCLUDE_TARGET_DIR_OPTION}
+ -L${LIBCXX_LIBRARY_DIR} -Wl,-rpath,${LIBCXX_LIBRARY_DIR} -lc++
+ -o ${EXE_PATH}/Inputs/test_binary.bin
+ DEPENDS test_binary.cpp test_extern.cpp clang lld
+ )
+ add_custom_target(test_binary
+ DEPENDS test_binary.bin
+ )
+else()
+ message(FATAL_ERROR "libcxx runtime must be enabled.")
+endif()
diff --git a/lldb/unittests/DIL/Inputs/test_binary.cpp b/lldb/unittests/DIL/Inputs/test_binary.cpp
new file mode 100644
index 0000000000000..115add3129bcd
--- /dev/null
+++ b/lldb/unittests/DIL/Inputs/test_binary.cpp
@@ -0,0 +1,1255 @@
+//===-- test_binary.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <string>
+
+static void TestArithmetic() {
+ char c = 10;
+ unsigned char uc = 1;
+ int a = 1;
+ int int_max = std::numeric_limits<int>::max();
+ int int_min = std::numeric_limits<int>::min();
+ unsigned int uint_max = std::numeric_limits<unsigned int>::max();
+ unsigned int uint_zero = 0;
+ long long ll_max = std::numeric_limits<long long>::max();
+ long long ll_min = std::numeric_limits<long long>::min();
+ unsigned long long ull_max = std::numeric_limits<unsigned long long>::max();
+ unsigned long long ull_zero = 0;
+
+ int x = 2;
+ int &r = x;
+ int *p = &x;
+
+ typedef int &myr;
+ myr my_r = x;
+
+ auto fnan = std::numeric_limits<float>::quiet_NaN();
+ auto fsnan = std::numeric_limits<float>::signaling_NaN();
+ // Smallest positive non-zero float denormal
+ auto fdenorm = 0x0.1p-145f;
+
+ // BREAK(TestArithmetic)
+ // BREAK(TestZeroDivision)
+}
+
+static void TestBitwiseOperators() {
+ bool var_true = true;
+ bool var_false = false;
+
+ unsigned long long ull_max = std::numeric_limits<unsigned long long>::max();
+ unsigned long long ull_zero = 0;
+
+ struct S {
+ } s;
+
+ const char *p = nullptr;
+
+ uint32_t mask_ff = 0xFF;
+
+ // BREAK(TestBitwiseOperators)
+}
+
+static void TestPointerArithmetic() {
+ int *p_null = nullptr;
+ const char *p_char1 = "hello";
+
+ typedef const char *my_char_ptr;
+ my_char_ptr my_p_char1 = p_char1;
+
+ int offset = 5;
+ int array[10];
+ array[0] = 0;
+ array[offset] = offset;
+
+ int(&array_ref)[10] = array;
+
+ int *p_int0 = &array[0];
+ int **pp_int0 = &p_int0;
+ const int *cp_int0 = &array[0];
+ const int *cp_int5 = &array[offset];
+ const int *&rcp_int0 = cp_int0;
+
+ typedef int *td_int_ptr_t;
+ td_int_ptr_t td_int_ptr0 = &array[0];
+
+ void *p_void = (void *)p_char1;
+ void **pp_void0 = &p_void;
+ void **pp_void1 = pp_void0 + 1;
+
+ std::nullptr_t std_nullptr_t = nullptr;
+
+ // BREAK(TestPointerArithmetic)
+ // BREAK(PointerPointerArithmeticFloat)
+ // BREAK(PointerPointerComparison)
+ // BREAK(PointerIntegerComparison)
+ // BREAK(TestPointerDereference)
+}
+
+static void TestLogicalOperators() {
+ bool trueVar = true;
+ bool falseVar = false;
+
+ const char *p_ptr = "🦊";
+ const char *p_nullptr = nullptr;
+
+ int array[2] = {1, 2};
+
+ struct S {
+ } s;
+
+ // BREAK(TestLogicalOperators)
+}
+
+static void TestLocalVariables() {
+ int a = 1;
+ int b = 2;
+
+ char c = -3;
+ unsigned short s = 4;
+
+ // BREAK(TestLocalVariables)
+}
+
+static void TestMemberOf() {
+ int x = 2;
+ struct Sx {
+ int x;
+ int &r;
+ char y;
+ } s{1, x, 2};
+
+ Sx &sr = s;
+ Sx *sp = &s;
+
+ Sx sarr[2] = {{5, x, 2}, {1, x, 3}};
+
+ using SxAlias = Sx;
+ SxAlias sa{3, x, 4};
+
+ // BREAK(TestMemberOf)
+}
+
+static void TestMemberOfInheritance() {
+ struct A {
+ int a_;
+ } a{1};
+
+ struct B {
+ int b_;
+ } b{2};
+
+ struct C : A, B {
+ int c_;
+ } c{{1}, {2}, 3};
+
+ struct D : C {
+ int d_;
+ A fa_;
+ } d{{{1}, {2}, 3}, 4, {5}};
+
+ // Virtual inheritance example.
+ struct Animal {
+ virtual ~Animal() = default;
+ int weight_;
+ };
+ struct Mammal : virtual Animal {};
+ struct WingedAnimal : virtual Animal {};
+ struct Bat : Mammal, WingedAnimal {
+ } bat;
+ bat.weight_ = 10;
+
+ // Empty bases example.
+ struct IPlugin {
+ virtual ~IPlugin() {}
+ };
+ struct Plugin : public IPlugin {
+ int x;
+ int y;
+ };
+ Plugin plugin;
+ plugin.x = 1;
+ plugin.y = 2;
+
+ struct ObjectBase {
+ int x;
+ };
+ struct Object : ObjectBase {};
+ struct Engine : Object {
+ int y;
+ int z;
+ };
+
+ Engine engine;
+ engine.x = 1;
+ engine.y = 2;
+ engine.z = 3;
+
+ // Empty multiple inheritance with empty base.
+ struct Base {
+ int x;
+ int y;
+ virtual void Do() = 0;
+ virtual ~Base() {}
+ };
+ struct Mixin {};
+ struct Parent : private Mixin, public Base {
+ int z;
+ virtual void Do() {};
+ };
+ Parent obj;
+ obj.x = 1;
+ obj.y = 2;
+ obj.z = 3;
+ Base *parent_base = &obj;
+ Parent *parent = &obj;
+
+ // BREAK(TestMemberOfInheritance)
+}
+
+static void TestMemberOfAnonymousMember() {
+ struct A {
+ struct {
+ int x = 1;
+ };
+ int y = 2;
+ } a;
+
+ struct B {
+ // Anonymous struct inherits another struct.
+ struct : public A {
+ int z = 3;
+ };
+ int w = 4;
+ A a;
+ } b;
+
+ // Anonymous classes and unions.
+ struct C {
+ union {
+ int x = 5;
+ };
+ class {
+ public:
+ int y = 6;
+ };
+ } c;
+
+ // Multiple levels of anonymous structs.
+ struct D {
+ struct {
+ struct {
+ int x = 7;
+ struct {
+ int y = 8;
+ };
+ };
+ int z = 9;
+ struct {
+ int w = 10;
+ };
+ };
+ } d;
+
+ struct E {
+ struct IsNotAnon {
+ int x = 11;
+ };
+ } e;
+
+ struct F {
+ struct {
+ int x = 12;
+ } named_field;
+ } f;
+
+ // Inherited unnamed struct without an enclosing parent class.
+ struct : public A {
+ struct {
+ int z = 13;
+ };
+ } unnamed_derived;
+
+ struct DerivedB : public B {
+ struct {
+ // `w` in anonymous struct overrides `w` from `B`.
+ int w = 14;
+ int k = 15;
+ };
+ } derb;
+
+ // BREAK(TestMemberOfAnonymousMember)
+}
+
+static void TestIndirection() {
+ int val = 1;
+ int *p = &val;
+
+ typedef int *myp;
+ myp my_p = &val;
+
+ typedef int *&mypr;
+ mypr my_pr = p;
+
+ // BREAK(TestIndirection)
+}
+
+// Referenced by TestInstanceVariables
+class C {
+public:
+ int field_ = 1337;
+};
+
+// Referenced by TestAddressOf
+int globalVar = 0xDEADBEEF;
+extern int externGlobalVar;
+
+int *globalPtr = &globalVar;
+int &globalRef = globalVar;
+
+namespace ns {
+int globalVar = 13;
+int *globalPtr = &globalVar;
+int &globalRef = globalVar;
+} // namespace ns
+
+void TestGlobalVariableLookup() {
+ // BREAK(TestGlobalVariableLookup)
+}
+
+class TestMethods {
+public:
+ void TestInstanceVariables() {
+ C c;
+ c.field_ = -1;
+
+ C &c_ref = c;
+ C *c_ptr = &c;
+
+ // BREAK(TestInstanceVariables)
+ }
+
+ void TestAddressOf(int param) {
+ int x = 42;
+ int &r = x;
+ int *p = &x;
+ int *&pr = p;
+
+ typedef int *&mypr;
+ mypr my_pr = p;
+
+ std::string s = "hello";
+ const char *s_str = s.c_str();
+
+ char c = 1;
+
+ // BREAK(TestAddressOf)
+ }
+
+private:
+ int field_ = 1;
+};
+
+static void TestSubscript() {
+ const char *char_ptr = "lorem";
+ const char char_arr[] = "ipsum";
+
+ int int_arr[] = {1, 2, 3};
+
+ C c_arr[2];
+ c_arr[0].field_ = 0;
+ c_arr[1].field_ = 1;
+
+ C(&c_arr_ref)[2] = c_arr;
+
+ int idx_1 = 1;
+ const int &idx_1_ref = idx_1;
+
+ typedef int td_int_t;
+ typedef td_int_t td_td_int_t;
+ typedef int *td_int_ptr_t;
+ typedef int &td_int_ref_t;
+
+ td_int_t td_int_idx_1 = 1;
+ td_td_int_t td_td_int_idx_2 = 2;
+
+ td_int_t td_int_arr[3] = {1, 2, 3};
+ td_int_ptr_t td_int_ptr = td_int_arr;
+
+ td_int_ref_t td_int_idx_1_ref = td_int_idx_1;
+ td_int_t(&td_int_arr_ref)[3] = td_int_arr;
+
+ unsigned char uchar_idx = std::numeric_limits<unsigned char>::max();
+ uint8_t uint8_arr[256];
+ uint8_arr[255] = 0xAB;
+ uint8_t *uint8_ptr = uint8_arr;
+
+ enum Enum { kZero, kOne } enum_one = kOne;
+ Enum &enum_ref = enum_one;
+
+ // BREAK(TestSubscript)
+}
+
+static void TestArrayDereference() {
+ int arr_1d[2] = {1, 2};
+ int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}};
+
+ // BREAK(TestArrayDereference)
+}
+
+// Referenced by TestCStyleCast
+namespace ns {
+
+typedef int myint;
+
+class Foo {};
+
+namespace inner {
+
+using mydouble = double;
+
+class Foo {};
+
+} // namespace inner
+
+} // namespace ns
+
+static void TestCStyleCast() {
+ int a = 1;
+ int *ap = &a;
+ void *vp = &a;
+ int arr[2] = {1, 2};
+
+ int na = -1;
+ float f = 1.1;
+
+ typedef int myint;
+
+ myint myint_ = 1;
+ ns::myint ns_myint_ = 2;
+ 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();
+
+ // BREAK(TestCStyleCastBuiltins)
+ // BREAK(TestCStyleCastBasicType)
+ // BREAK(TestCStyleCastPointer)
+ // BREAK(TestCStyleCastNullptrType)
+
+ 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}};
+
+ // BREAK(TestCStyleCastArray)
+ // BREAK(TestCStyleCastReference)
+}
+
+// Referenced by TestCxxCast
+struct CxxVirtualBase {
+ int a;
+ virtual ~CxxVirtualBase(){};
+};
+struct CxxVirtualParent : CxxVirtualBase {
+ int b;
+};
+
+static void TestCxxCast() {
+ struct CxxBase {
+ int a;
+ int b;
+ };
+ struct CxxParent : CxxBase {
+ long long c;
+ short d;
+ };
+
+ enum UEnum { kUZero, kUOne, kUTwo };
+ enum class SEnum { kSZero, kSOne };
+
+ UEnum u_enum = kUTwo;
+ SEnum s_enum = SEnum::kSOne;
+
+ typedef int td_int_t;
+ typedef int *td_int_ptr_t;
+ typedef int &td_int_ref_t;
+ typedef SEnum td_senum_t;
+ td_int_t td_int = 13;
+ td_int_ptr_t td_int_ptr = &td_int;
+ td_int_ref_t td_int_ref = td_int;
+ td_senum_t td_senum = s_enum;
+
+ CxxParent parent;
+ parent.a = 1;
+ parent.b = 2;
+ parent.c = 3;
+ parent.d = 4;
+
+ CxxBase *base = &parent;
+
+ int arr[] = {1, 2, 3, 4, 5};
+ int *ptr = arr;
+
+ // BREAK(TestCxxStaticCast)
+ // BREAK(TestCxxReinterpretCast)
+
+ CxxVirtualParent v_parent;
+ v_parent.a = 1;
+ v_parent.b = 2;
+ CxxVirtualBase *v_base = &v_parent;
+
+ // BREAK(TestCxxDynamicCast)
+}
+
+void TestCastInheritedTypes() {
+ struct CxxEmpty {};
+ struct CxxA {
+ short a;
+ };
+ struct CxxB {
+ long long b;
+ };
+ struct CxxC : CxxEmpty, CxxA, CxxB {
+ int c;
+ };
+ struct CxxD {
+ long long d;
+ };
+ struct CxxE : CxxD, CxxC {
+ int e;
+ };
+
+ CxxA a{1};
+ CxxB b{2};
+ CxxC c;
+ c.a = 3;
+ c.b = 4;
+ c.c = 5;
+ CxxD d{6};
+ CxxE e;
+ e.a = 7;
+ e.b = 8;
+ e.c = 9;
+ e.d = 10;
+ e.e = 11;
+
+ struct CxxVC : virtual CxxA, virtual CxxB {
+ int c;
+ };
+ struct CxxVE : CxxD, CxxVC {
+ int e;
+ };
+
+ CxxVC vc;
+ vc.a = 12;
+ vc.b = 13;
+ vc.c = 14;
+ CxxVE ve;
+ ve.a = 15;
+ ve.b = 16;
+ ve.c = 17;
+ ve.d = 18;
+ ve.e = 19;
+
+ CxxB *e_as_b = &e;
+ CxxB *ve_as_b = &ve;
+
+ // BREAK(TestCastBaseToDerived)
+ // BREAK(TestCastDerivedToBase)
+}
+
+// Referenced by TestQualifiedId.
+namespace ns {
+
+int i = 1;
+
+namespace ns {
+
+int i = 2;
+
+} // namespace ns
+
+} // namespace ns
+
+static void TestQualifiedId() {
+ // BREAK(TestQualifiedId)
+}
+
+namespace outer {
+
+namespace inner {
+
+class Vars {
+public:
+ inline static double inline_static = 1.5;
+ static constexpr int static_constexpr = 2;
+ static const unsigned int static_const;
+
+ struct Nested {
+ static const int static_const;
+ };
+};
+
+const unsigned int Vars::static_const = 3;
+const int Vars::Nested::static_const = 10;
+
+using MyVars = Vars;
+
+} // namespace inner
+
+class Vars {
+public:
+ inline static double inline_static = 4.5;
+ static constexpr int static_constexpr = 5;
+ static const unsigned int static_const;
+
+ struct Nested {
+ static const int static_const;
+ };
+};
+
+const unsigned int Vars::static_const = 6;
+const int Vars::Nested::static_const = 20;
+
+} // namespace outer
+
+class Vars {
+public:
+ inline static double inline_static = 7.5;
+ static constexpr int static_constexpr = 8;
+ static const unsigned int static_const;
+
+ struct Nested {
+ static const int static_const;
+ };
+};
+
+const unsigned int Vars::static_const = 9;
+const int Vars::Nested::static_const = 30;
+
+static void TestStaticConst() {
+ Vars vars;
+ outer::Vars outer_vars;
+ outer::inner::Vars outer_inner_vars;
+
+ using MyVars = Vars;
+ using MyOuterVars = outer::Vars;
+
+ MyVars my_vars;
+ MyOuterVars my_outer_vars;
+ outer::inner::MyVars my_outer_inner_vars;
+
+ // BREAK(TestStaticConstDeclaredInline)
+ // BREAK(TestStaticConstDeclaredInlineScoped)
+ // BREAK(TestStaticConstDeclaredOutsideTheClass)
+ // BREAK(TestStaticConstDeclaredOutsideTheClassScoped)
+}
+
+// Referenced by TestTemplateTypes.
+template <typename T> struct T_1 {
+ static const int cx;
+ typedef double myint;
+
+ T_1() {}
+ T_1(T x) : x(x) {}
+ T x;
+};
+
+template <typename T> const int T_1<T>::cx = 42;
+
+template <> const int T_1<int>::cx = 24;
+
+template <typename T1, typename T2> struct T_2 {
+ typedef float myint;
+
+ T_2() {}
+ T1 x;
+ T2 y;
+};
+
+namespace ns {
+
+template <typename T> struct T_1 {
+ static const int cx;
+ typedef int myint;
+
+ T_1() {}
+ T_1(T x) : x(x) {}
+ T x;
+};
+
+template <typename T> const int T_1<T>::cx = 46;
+
+template <> const int T_1<int>::cx = 64;
+
+} // namespace ns
+
+static void TestTemplateTypes() {
+ int i;
+ int *p = &i;
+
+ { T_1<int> _; }
+ { T_1<int *> _; }
+ { T_1<int **> _; }
+ { T_1<int &> _(i); }
+ { T_1<int *&> _(p); }
+ { T_1<double> _; }
+ { T_2<int, char> _; }
+ { T_2<char, int> _; }
+ { T_2<T_1<int>, T_1<char>> _; }
+ { T_2<T_1<T_1<int>>, T_1<char>> _; }
+
+ { ns::T_1<int> _; }
+ { ns::T_1<int *> _; }
+ { ns::T_1<int **> _; }
+ { ns::T_1<int &> _(i); }
+ { ns::T_1<int *&> _(p); }
+ { ns::T_1<ns::T_1<int>> _; }
+ { ns::T_1<ns::T_1<int *>> _; }
+ { ns::T_1<ns::T_1<int **>> _; }
+ { ns::T_1<ns::T_1<int &>> _(i); }
+ { ns::T_1<ns::T_1<int *&>> _(p); }
+
+ { T_1<int>::myint _ = 0; }
+ { T_1<int *>::myint _ = 0; }
+ { T_1<int **>::myint _ = 0; }
+ { T_1<int &>::myint _ = 0; }
+ { T_1<int *&>::myint _ = 0; }
+ { T_1<T_1<int>>::myint _ = 0; }
+ { T_1<T_1<T_1<int>>>::myint _ = 0; }
+ { T_1<T_1<int *>>::myint _ = 0; }
+ { T_1<T_1<int **>>::myint _ = 0; }
+ { T_1<T_1<int &>>::myint _ = 0; }
+ { T_1<T_1<int *&>>::myint _ = 0; }
+
+ { T_2<int, char>::myint _ = 0; }
+ { T_2<int *, char &>::myint _ = 0; }
+ { T_2<int &, char *>::myint _ = 0; }
+ { T_2<T_1<T_1<int>>, T_1<char>>::myint _ = 0; }
+
+ { ns::T_1<int>::myint _ = 0; }
+ { ns::T_1<int *>::myint _ = 0; }
+ { ns::T_1<int **>::myint _ = 0; }
+ { ns::T_1<int &>::myint _ = 0; }
+ { ns::T_1<int *&>::myint _ = 0; }
+ { ns::T_1<T_1<int>>::myint _ = 0; }
+ { ns::T_1<T_1<int *>>::myint _ = 0; }
+ { ns::T_1<T_1<int **>>::myint _ = 0; }
+ { ns::T_1<T_1<int &>>::myint _ = 0; }
+ { ns::T_1<T_1<int *&>>::myint _ = 0; }
+ { ns::T_1<ns::T_1<int>>::myint _ = 0; }
+ { ns::T_1<ns::T_1<int *>>::myint _ = 0; }
+ { ns::T_1<ns::T_1<int **>>::myint _ = 0; }
+ { ns::T_1<ns::T_1<int &>>::myint _ = 0; }
+ { ns::T_1<ns::T_1<int *&>>::myint _ = 0; }
+
+ (void)T_1<double>::cx;
+ (void)ns::T_1<double>::cx;
+ (void)ns::T_1<ns::T_1<int>>::cx;
+
+ int T_1 = 2;
+
+ // BREAK(TestTemplateTypes)
+ // BREAK(TestTemplateCpp11)
+}
+
+template <typename T, typename TAllocator> struct TArray {
+ using ElementType = T;
+ T t_;
+ TAllocator a_;
+};
+
+template <int Size> struct Allocator {
+ int size = Size;
+};
+
+void TestTemplateWithNumericArguments() {
+ Allocator<4> a4;
+ Allocator<8> a8;
+ TArray<int, Allocator<4>> arr;
+ decltype(arr)::ElementType *el = 0;
+
+ // BREAK(TestTemplateWithNumericArguments)
+}
+
+namespace test_scope {
+
+class Value {
+public:
+ Value(int x, float y) : x_(x), y_(y) {}
+
+ // Static members
+ enum ValueEnum { A, B };
+ static double static_var;
+
+private:
+ int x_;
+ float y_;
+};
+
+double Value::static_var = 3.5;
+
+} // namespace test_scope
+
+void TestValueScope() {
+ test_scope::Value var(1, 2.5f);
+ test_scope::Value &var_ref = var;
+ uint64_t z_ = 3;
+
+ // "raw" representation of the Value.
+ int bytes[] = {1, 0x40200000};
+
+ auto val_enum = test_scope::Value::A;
+ (void)val_enum;
+ (void)test_scope::Value::static_var;
+
+ // BREAK(TestValueScope)
+ // BREAK(TestReferenceScope)
+}
+
+void TestBitField() {
+ enum BitFieldEnum : uint32_t { kZero, kOne };
+
+ struct BitFieldStruct {
+ uint16_t a : 10;
+ uint32_t b : 4;
+ bool c : 1;
+ bool d : 1;
+ int32_t e : 32;
+ uint32_t f : 32;
+ uint32_t g : 31;
+ uint64_t h : 31;
+ uint64_t i : 33;
+ BitFieldEnum j : 10;
+ };
+
+ BitFieldStruct bf;
+ bf.a = 0b1111111111;
+ bf.b = 0b1001;
+ bf.c = 0b0;
+ bf.d = 0b1;
+ bf.e = 0b1;
+ bf.f = 0b1;
+ bf.g = 0b1;
+ bf.h = 0b1;
+ bf.i = 0b1;
+ bf.j = BitFieldEnum::kOne;
+
+ struct AlignedBitFieldStruct {
+ uint16_t a : 10;
+ uint8_t b : 4;
+ unsigned char : 0;
+ uint16_t c : 2;
+ };
+
+ uint32_t data = ~0;
+ AlignedBitFieldStruct abf = (AlignedBitFieldStruct &)data;
+
+ // BREAK(TestBitField)
+ // BREAK(TestBitFieldScoped)
+ // BREAK(TestBitFieldPromotion)
+ // BREAK(TestBitFieldWithSideEffects)
+}
+
+void TestContextVariables() {
+ struct Scope {
+ int a = 10;
+ const char *ptr = "hello";
+ };
+
+ Scope s;
+
+ // BREAK(TestContextVariables)
+ // BREAK(TestContextVariablesSubset)
+}
+
+// Referenced by TestScopedEnum.
+enum class ScopedEnum { kFoo, kBar };
+enum class ScopedEnumUInt8 : uint8_t { kFoo, kBar };
+
+void TestScopedEnum() {
+ auto enum_foo = ScopedEnum::kFoo;
+ auto enum_bar = ScopedEnum::kBar;
+ auto enum_neg = (ScopedEnum)-1;
+
+ auto enum_u8_foo = ScopedEnumUInt8::kFoo;
+ auto enum_u8_bar = ScopedEnumUInt8::kBar;
+
+ // BREAK(TestScopedEnum)
+ // BREAK(TestScopedEnumArithmetic)
+ // BREAK(TestScopedEnumWithUnderlyingType)
+}
+
+enum UnscopedEnum { kZero, kOne, kTwo };
+enum UnscopedEnumUInt8 : uint8_t { kZeroU8, kOneU8, kTwoU8 };
+enum UnscopedEnumInt8 : int8_t { kZero8, kOne8, kTwo8 };
+enum UnscopedEnumEmpty : uint8_t {};
+
+// UnscopedEnum global_enum = UnscopedEnum::kOne;
+
+void TestUnscopedEnum() {
+ auto enum_zero = UnscopedEnum::kZero;
+ auto enum_one = UnscopedEnum::kOne;
+ auto enum_two = UnscopedEnum::kTwo;
+
+ auto &enum_one_ref = enum_one;
+ auto &enum_two_ref = enum_two;
+
+ auto enum_zero_u8 = UnscopedEnumUInt8::kZeroU8;
+ auto enum_one_u8 = UnscopedEnumUInt8::kOneU8;
+ auto enum_two_u8 = UnscopedEnumUInt8::kTwoU8;
+
+ UnscopedEnumEmpty enum_empty{};
+
+ auto enum_one_8 = UnscopedEnumInt8::kOne8;
+ auto enum_neg_8 = (UnscopedEnumInt8)-1;
+
+ // BREAK(TestUnscopedEnum)
+ // BREAK(TestUnscopedEnumNegation)
+ // BREAK(TestUnscopedEnumWithUnderlyingType)
+ // BREAK(TestUnscopedEnumEmpty)
+}
+
+void TestTernaryOperator() {
+ int i = 1;
+ int *pi = &i;
+ char c = 2;
+ int arr2[2] = {1, 2};
+ int arr3[3] = {1, 2, 3};
+ double dbl_arr[2] = {1.0, 2.0};
+ struct T {
+ } t;
+ enum EnumA { kOneA = 1, kTwoA } a_enum = kTwoA;
+ enum EnumB { kOneB = 1 } b_enum = kOneB;
+ // BREAK(TestTernaryOperator)
+}
+
+void TestSizeOf() {
+ int i = 1;
+ int *p = &i;
+ int arr[] = {1, 2, 3};
+
+ struct SizeOfFoo {
+ int x, y;
+ } foo;
+
+ // BREAK(TestSizeOf)
+}
+
+void TestBuiltinFunction_Log2() {
+ struct Foo {
+ } foo;
+
+ enum CEnum { kFoo = 129 } c_enum = kFoo;
+ enum class CxxEnum { kFoo = 129 } cxx_enum = CxxEnum::kFoo;
+
+ CEnum &c_enum_ref = c_enum;
+ CxxEnum &cxx_enum_ref = cxx_enum;
+
+ // BREAK(TestBuiltinFunction_Log2)
+}
+
+void TestBuiltinFunction_findnonnull() {
+ uint8_t array_of_uint8[] = {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};
+ uint8_t *pointer_to_uint8 = array_of_uint8;
+
+ int *array_of_pointers[] = {(int *)1, (int *)1, (int *)0, (int *)0, (int *)1};
+ int **pointer_to_pointers = array_of_pointers;
+
+ // BREAK(TestBuiltinFunction_findnonnull)
+}
+
+void TestPrefixIncDec() {
+ auto enum_foo = ScopedEnum::kFoo;
+ int i = 1;
+
+ // BREAK(TestPrefixIncDec)
+ // BREAK(TestPostfixIncDec)
+}
+
+void TestDereferencedType() {
+ struct TTuple {
+ int x = 1;
+ };
+ using TPair = TTuple;
+
+ TPair p{};
+ const TPair &p_ref = p;
+ const TPair *p_ptr = &p;
+
+ // BREAK(TestDereferencedType)
+}
+
+void TestMemberFunctionCall() {
+ struct C {
+ int m() { return 1; }
+ };
+
+ C c;
+ c.m();
+
+ // BREAK(TestMemberFunctionCall)
+}
+
+void TestCompositeAssignment() {
+ int i = 10;
+ float f = 1.5f;
+ float *p = &f;
+
+ enum Enum { ONE, TWO };
+ Enum eOne = ONE;
+ Enum eTwo = TWO;
+
+ // BREAK(TestAssignment)
+ // BREAK(TestCompositeAssignmentInvalid)
+ // BREAK(TestCompositeAssignmentAdd)
+ // BREAK(TestCompositeAssignmentSub)
+ // BREAK(TestCompositeAssignmentMul)
+ // BREAK(TestCompositeAssignmentDiv)
+ // BREAK(TestCompositeAssignmentRem)
+ // BREAK(TestCompositeAssignmentBitwise)
+}
+
+void TestSideEffects() {
+ int x = 1;
+ int xa[] = {1, 2};
+ int *p = &x;
+
+ // BREAK(TestSideEffects)
+}
+
+void TestUniquePtr() {
+ struct NodeU {
+ std::unique_ptr<NodeU> next;
+ int value;
+ };
+ auto ptr_node = std::unique_ptr<NodeU>(new NodeU{nullptr, 2});
+ ptr_node = std::unique_ptr<NodeU>(new NodeU{std::move(ptr_node), 1});
+
+ std::unique_ptr<char> ptr_null;
+ auto ptr_int = std::make_unique<int>(1);
+ auto ptr_float = std::make_unique<float>(1.1f);
+
+ auto deleter = [](void const *data) {
+ delete static_cast<int const *>(data);
+ };
+ std::unique_ptr<void, decltype(deleter)> ptr_void(new int(42), deleter);
+
+ // BREAK(TestUniquePtr)
+ // BREAK(TestUniquePtrDeref)
+ // BREAK(TestUniquePtrCompare)
+}
+
+void TestSharedPtr() {
+ struct NodeS {
+ std::shared_ptr<NodeS> next;
+ int value;
+ };
+ auto ptr_node = std::shared_ptr<NodeS>(new NodeS{nullptr, 2});
+ ptr_node = std::shared_ptr<NodeS>(new NodeS{std::move(ptr_node), 1});
+
+ std::shared_ptr<char> ptr_null;
+ auto ptr_int = std::make_shared<int>(1);
+ auto ptr_float = std::make_shared<float>(1.1f);
+
+ std::weak_ptr<int> ptr_int_weak = ptr_int;
+
+ std::shared_ptr<void> ptr_void = ptr_int;
+
+ // BREAK(TestSharedPtr)
+ // BREAK(TestSharedPtrDeref)
+ // BREAK(TestSharedPtrCompare)
+}
+
+void TestTypeComparison() {
+ int i = 1;
+ int const *const icpc = &i;
+ int *ip = &i;
+ int const *const *const icpcpc = &icpc;
+ int **ipp = &ip;
+
+ using MyInt = int;
+ using MyPtr = MyInt *;
+ MyInt mi = 2;
+ MyPtr *mipp = ipp;
+
+ using MyConstInt = const int;
+ using MyConstPtr = MyConstInt *const;
+ MyConstPtr *const micpcpc = icpcpc;
+
+ char c = 2;
+ signed char sc = 65;
+ const char cc = 66;
+ using mychar = char;
+ mychar mc = 67;
+
+ // BREAK(TestTypeComparison)
+}
+
+static void TestTypeDeclaration() {
+ wchar_t wchar = 0;
+ char16_t char16 = 0;
+ char32_t char32 = 0;
+
+ using mylong = long;
+ mylong my_long = 1;
+
+ // BREAK(TestBasicTypeDeclaration)
+ // BREAK(TestUserTypeDeclaration)
+}
+
+static void TestTypeVsIdentifier() {
+ struct StructOrVar {
+ int x = 1;
+ } s;
+ short StructOrVar = 2;
+
+ class ClassOrVar {
+ public:
+ int x = 3;
+ };
+ ClassOrVar ClassOrVar;
+
+ union UnionOrVar {
+ int x;
+ } u;
+ int UnionOrVar[2] = {1, 2};
+
+ enum EnumOrVar { kFoo, kBar };
+ EnumOrVar EnumOrVar = kFoo;
+
+ enum class CxxEnumOrVar { kCxxFoo, kCxxBar };
+ CxxEnumOrVar CxxEnumOrVar = CxxEnumOrVar::kCxxFoo;
+
+ int OnlyVar = 4;
+
+ // BREAK(TestTypeVsIdentifier)
+}
+
+static void TestSeparateParsing() {
+ struct StructA {
+ int a_;
+ } a{1};
+
+ struct StructB {
+ int b_;
+ } b{2};
+
+ struct StructC : public StructA, public StructB {
+ int c_;
+ } c{{3}, {4}, 5};
+
+ struct StructD : public StructC {
+ int d_;
+ } d{{{6}, {7}, 8}, 9};
+
+ // BREAK(TestSeparateParsing)
+ // BREAK(TestSeparateParsingWithContextVars)
+}
+
+// Used by TestRegistersNoDollar
+int rcx = 42;
+
+struct RegisterCtx {
+ int rbx = 42;
+
+ void TestRegisters() {
+ int rax = 42;
+
+ // BREAK(TestRegisters)
+ // BREAK(TestRegistersNoDollar)
+ }
+};
+
+static void TestCharParsing() {
+ // BREAK(TestCharParsing)
+}
+
+static void TestStringParsing() {
+ // BREAK(TestStringParsing)
+}
+
+namespace test_binary {
+
+void main() {
+ // BREAK(TestSymbols)
+
+ TestMethods tm;
+
+ TestArithmetic();
+ TestBitwiseOperators();
+ TestPointerArithmetic();
+ TestLogicalOperators();
+ TestLocalVariables();
+ TestMemberOf();
+ TestMemberOfInheritance();
+ TestMemberOfAnonymousMember();
+ TestGlobalVariableLookup();
+ tm.TestInstanceVariables();
+ TestIndirection();
+ tm.TestAddressOf(42);
+ TestSubscript();
+ TestCStyleCast();
+ TestCxxCast();
+ TestCastInheritedTypes();
+ TestQualifiedId();
+ TestStaticConst();
+ TestTypeDeclaration();
+ TestTemplateTypes();
+ TestTemplateWithNumericArguments();
+ TestValueScope();
+ TestBitField();
+ TestContextVariables();
+ TestPrefixIncDec();
+ TestScopedEnum();
+ TestUnscopedEnum();
+ TestTernaryOperator();
+ TestSizeOf();
+ TestBuiltinFunction_Log2();
+ TestBuiltinFunction_findnonnull();
+ TestArrayDereference();
+ TestDereferencedType();
+ TestMemberFunctionCall();
+ TestCompositeAssignment();
+ TestSideEffects();
+ TestUniquePtr();
+ TestSharedPtr();
+ TestTypeComparison();
+ TestTypeVsIdentifier();
+ TestSeparateParsing();
+
+ RegisterCtx rc;
+ rc.TestRegisters();
+
+ TestCharParsing();
+ TestStringParsing();
+
+ // BREAK HERE
+}
+
+} // namespace test_binary
+
+int main() { test_binary::main(); }
diff --git a/lldb/unittests/DIL/Inputs/test_extern.cpp b/lldb/unittests/DIL/Inputs/test_extern.cpp
new file mode 100644
index 0000000000000..d97c06f51f44e
--- /dev/null
+++ b/lldb/unittests/DIL/Inputs/test_extern.cpp
@@ -0,0 +1,9 @@
+//===-- test_extern.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+int externGlobalVar = 0x00C0FFEE;
diff --git a/lldb/unittests/DIL/Runner.cpp b/lldb/unittests/DIL/Runner.cpp
new file mode 100644
index 0000000000000..5524caab43ad2
--- /dev/null
+++ b/lldb/unittests/DIL/Runner.cpp
@@ -0,0 +1,146 @@
+//===-- Runner.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Runner.h"
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "lldb/API/SBBreakpoint.h"
+#include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBListener.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBThread.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-types.h"
+
+#ifdef CONFIG_VALGRIND
+// Running a process under Valgrind can be extremely slow.
+const uint32_t kWaitForEventTimeout = 30;
+#else
+// Running a process can be slow when built with sanitizers.
+const uint32_t kWaitForEventTimeout = 5;
+#endif
+
+int FindBreakpointLine(const std::string &file_path,
+ const std::string &break_line) {
+ // Read the source file to find the breakpoint location.
+ std::ifstream infile(file_path);
+ std::string line;
+ int line_num = 1;
+ while (std::getline(infile, line)) {
+ if (line.find(break_line) != std::string::npos) {
+ return line_num;
+ }
+ ++line_num;
+ }
+
+ std::cerr << "Can't find the breakpoint location." << std::endl;
+ exit(1);
+}
+
+std::string filename_of_source_path(const std::string &source_path) {
+ auto idx = source_path.find_last_of("/\\");
+ if (idx == std::string::npos) {
+ idx = 0;
+ } else {
+ idx++;
+ }
+
+ return source_path.substr(idx);
+}
+
+lldb::SBProcess LaunchTestProgram(lldb::SBDebugger debugger,
+ const std::string &source_path,
+ const std::string &binary_path,
+ const std::string &break_line) {
+ auto target = debugger.CreateTarget(binary_path.c_str());
+
+ auto source_file = filename_of_source_path(source_path);
+
+ const char *argv[] = {binary_path.c_str(), nullptr};
+
+ auto bp = target.BreakpointCreateByLocation(
+ source_file.c_str(), FindBreakpointLine(source_path.c_str(), break_line));
+ // Test programs don't perform any I/O, so current directory doesn't
+ // matter.
+ if (bp.GetNumLocations() == 0)
+ std::cerr
+ << "WARNING: Unable to resolve breakpoint to any actual locations."
+ << std::endl;
+ auto process = target.LaunchSimple(argv, nullptr, ".");
+ if (!process.IsValid()) {
+ std::cerr << "ERROR: Unable to launch process. Check that the path to the "
+ "binary is valid."
+ << std::endl;
+ return process;
+ }
+ lldb::SBEvent event;
+ auto listener = debugger.GetListener();
+
+ while (true) {
+ if (!listener.WaitForEvent(kWaitForEventTimeout, event)) {
+ std::cerr
+ << "Timeout while waiting for the event, kill the process and exit."
+ << std::endl;
+ process.Destroy();
+ exit(1);
+ }
+
+ if (!lldb::SBProcess::EventIsProcessEvent(event)) {
+ std::cerr << "Got some random event: "
+ << lldb::SBEvent::GetCStringFromEvent(event) << std::endl;
+ continue;
+ }
+
+ auto state = lldb::SBProcess::GetStateFromEvent(event);
+ if (state == lldb::eStateInvalid) {
+ std::cerr << "process event: "
+ << lldb::SBEvent::GetCStringFromEvent(event) << std::endl;
+ continue;
+ }
+
+ if (state == lldb::eStateExited) {
+ std::cerr << "Process exited: " << process.GetExitStatus() << std::endl;
+ process.Destroy();
+ exit(1);
+ }
+
+ if (state != lldb::eStateStopped) {
+ continue;
+ }
+
+ auto thread = process.GetSelectedThread();
+ auto stopReason = thread.GetStopReason();
+
+ if (stopReason != lldb::eStopReasonBreakpoint) {
+ continue;
+ }
+
+ auto bpId =
+ static_cast<lldb::break_id_t>(thread.GetStopReasonDataAtIndex(0));
+ if (bpId != bp.GetID()) {
+ std::cerr << "Stopped at unknown breakpoint: " << bpId << std::endl
+ << "Now killing process and exiting" << std::endl;
+ process.Destroy();
+ exit(1);
+ }
+
+ return process;
+ }
+}
diff --git a/lldb/unittests/DIL/Runner.h b/lldb/unittests/DIL/Runner.h
new file mode 100644
index 0000000000000..f2723518e8ac1
--- /dev/null
+++ b/lldb/unittests/DIL/Runner.h
@@ -0,0 +1,22 @@
+//===-- Runner.h --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_DIL_RUNNER_H_
+#define LLDB_DIL_RUNNER_H_
+
+#include <string>
+
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBProcess.h"
+
+lldb::SBProcess LaunchTestProgram(lldb::SBDebugger debugger,
+ const std::string &source_path,
+ const std::string &binary_path,
+ const std::string &break_line);
+
+#endif // LLDB_DIL_RUNNER_H_
\ No newline at end of file
diff --git a/lldb/unittests/ValueObject/CMakeLists.txt b/lldb/unittests/ValueObject/CMakeLists.txt
index 6ef0091647a59..20c10532ec14c 100644
--- a/lldb/unittests/ValueObject/CMakeLists.txt
+++ b/lldb/unittests/ValueObject/CMakeLists.txt
@@ -1,6 +1,5 @@
add_lldb_unittest(LLDBValueObjectTests
DumpValueObjectOptionsTests.cpp
- DILLexerTests.cpp
DynamicValueObjectLocalBuffer.cpp
LINK_LIBS
>From d7fdf3f675199947ab57a3e6c84172d83268804b Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 9 Apr 2025 00:05:19 +0500
Subject: [PATCH 3/4] Remove unit tests, add Python tests
---
lldb/include/lldb/API/SBFrame.h | 3 -
lldb/include/lldb/Target/StackFrame.h | 3 -
lldb/source/API/SBFrame.cpp | 35 -
lldb/source/Target/StackFrame.cpp | 11 -
.../frame/var-dil/basics/AddressOf/Makefile | 3 +
.../AddressOf/TestFrameVarDILAddressOf.py | 38 +
.../frame/var-dil/basics/AddressOf/main.cpp | 16 +
.../var-dil/basics/PointerArithmetic/Makefile | 3 +
.../TestFrameVarDILPointerArithmetic.py | 43 +
.../var-dil/basics/PointerArithmetic/main.cpp | 29 +
lldb/unittests/CMakeLists.txt | 1 -
lldb/unittests/DIL/CMakeLists.txt | 15 -
lldb/unittests/DIL/DILTests.cpp | 303 ----
lldb/unittests/DIL/Inputs/CMakeLists.txt | 36 -
lldb/unittests/DIL/Inputs/test_binary.cpp | 1255 -----------------
lldb/unittests/DIL/Inputs/test_extern.cpp | 9 -
lldb/unittests/DIL/Runner.cpp | 146 --
lldb/unittests/DIL/Runner.h | 22 -
lldb/unittests/ValueObject/CMakeLists.txt | 1 +
.../{DIL => ValueObject}/DILLexerTests.cpp | 0
20 files changed, 133 insertions(+), 1839 deletions(-)
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp
delete mode 100644 lldb/unittests/DIL/CMakeLists.txt
delete mode 100644 lldb/unittests/DIL/DILTests.cpp
delete mode 100644 lldb/unittests/DIL/Inputs/CMakeLists.txt
delete mode 100644 lldb/unittests/DIL/Inputs/test_binary.cpp
delete mode 100644 lldb/unittests/DIL/Inputs/test_extern.cpp
delete mode 100644 lldb/unittests/DIL/Runner.cpp
delete mode 100644 lldb/unittests/DIL/Runner.h
rename lldb/unittests/{DIL => ValueObject}/DILLexerTests.cpp (100%)
diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h
index e0778623d503d..3635ee5a537ad 100644
--- a/lldb/include/lldb/API/SBFrame.h
+++ b/lldb/include/lldb/API/SBFrame.h
@@ -182,9 +182,6 @@ class LLDB_API SBFrame {
// variable value.
lldb::SBValue GetValueForVariablePath(const char *var_expr_cstr,
DynamicValueType use_dynamic);
- lldb::SBValue TestGetValueForVariablePath(const char *var_expr_cstr,
- DynamicValueType use_dynamic,
- bool use_DIL = false);
/// The version that doesn't supply a 'use_dynamic' value will use the
/// target's default.
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 3673040335140..3f51c9a7f22f0 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -308,9 +308,6 @@ class StackFrame : public ExecutionContextScope,
lldb::ValueObjectSP GetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
uint32_t options, lldb::VariableSP &var_sp, Status &error);
- lldb::ValueObjectSP GetValueForVariableExpressionPath(
- llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
- uint32_t options, lldb::VariableSP &var_sp, Status &error, bool use_DIL);
/// Determine whether this StackFrame has debug information available or not.
///
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index c77ab4f335c68..5b69cf1ee2641 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -492,41 +492,6 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
return sb_value;
}
-lldb::SBValue SBFrame::TestGetValueForVariablePath(const char *var_path,
- DynamicValueType use_dynamic,
- bool use_DIL) {
- LLDB_INSTRUMENT_VA(this, var_path, use_dynamic);
-
- SBValue sb_value;
- if (var_path == nullptr || var_path[0] == '\0') {
- return sb_value;
- }
-
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- VariableSP var_sp;
- Status error;
- ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath(
- var_path, eNoDynamicValues,
- StackFrame::eExpressionPathOptionCheckPtrVsMember |
- StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
- var_sp, error, use_DIL));
- sb_value.SetSP(value_sp, use_dynamic);
- }
- }
- }
- return sb_value;
-}
-
SBValue SBFrame::FindVariable(const char *name) {
LLDB_INSTRUMENT_VA(this, name);
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 05603cde27a65..97da16c3647a5 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -523,17 +523,6 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
var_sp, error);
}
-ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
- llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options,
- VariableSP &var_sp, Status &error, bool use_DIL) {
- if (use_DIL)
- return DILGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
- var_sp, error);
-
- return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
- var_sp, error);
-}
-
ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
uint32_t options, lldb::VariableSP &var_sp, Status &error) {
diff --git a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
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
new file mode 100644
index 0000000000000..8eab75949047d
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
@@ -0,0 +1,38 @@
+"""
+Test DIL address calculation.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILGlobalVariableLookup(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None):
+ value_dil = super().expect_var_path(expr, value=value, type=type)
+ if compare_to_framevar:
+ self.runCmd("settings set target.experimental.use-DIL false")
+ value_frv = super().expect_var_path(expr, value=value, type=type)
+ self.runCmd("settings set target.experimental.use-DIL true")
+ self.assertEqual(value_dil.GetValue(), value_frv.GetValue())
+
+ def test_frame_var(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.runCmd("settings set target.experimental.use-DIL true")
+ self.expect_var_path("&x", True, type="int *")
+ self.expect_var_path("r", True, type="int &")
+ self.expect_var_path("&r", True, type="int &*")
+ self.expect_var_path("pr", True, type="int *&")
+ self.expect_var_path("&pr", True, type="int *&*")
+ self.expect_var_path("my_pr", True)
+ self.expect_var_path("&my_pr", True, type="mypr *")
+ 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 *")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp
new file mode 100644
index 0000000000000..29341e1d8e478
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp
@@ -0,0 +1,16 @@
+int globalVar = 0xDEADBEEF;
+
+int main(int argc, char **argv) {
+ int x = 42;
+ int &r = x;
+ int *p = &x;
+ int *&pr = p;
+
+ typedef int *&mypr;
+ mypr my_pr = p;
+
+ const char *s_str = "hello";
+
+ char c = 1;
+ return 0; // Set a breakpoint here
+}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
new file mode 100644
index 0000000000000..1e90433e331f5
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
@@ -0,0 +1,43 @@
+"""
+Test DIL pointer arithmetic.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILGlobalVariableLookup(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None):
+ value_dil = super().expect_var_path(expr, value=value, type=type)
+ if compare_to_framevar:
+ self.runCmd("settings set target.experimental.use-DIL false")
+ value_frv = super().expect_var_path(expr, value=value, type=type)
+ self.runCmd("settings set target.experimental.use-DIL true")
+ self.assertEqual(value_dil.GetValue(), value_frv.GetValue())
+
+ def test_dereference(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+ is_32bit = self.process().GetAddressByteSize() == 4
+
+ self.runCmd("settings set target.experimental.use-DIL true")
+ self.expect_var_path("*p_int0", True, value="0")
+ self.expect_var_path("*cp_int5", True, value="5")
+ self.expect_var_path("*rcp_int0", True, type="const int *")
+ self.expect_var_path("*array", value="0")
+ self.expect_var_path(
+ "&*p_null", value="0x00000000" if is_32bit else "0x0000000000000000"
+ )
+ self.expect_var_path("**pp_int0", value="0")
+ self.expect_var_path("&**pp_int0", type="int *")
+ self.expect(
+ "frame var '&*p_void'",
+ error=True,
+ substrs=["indirection not permitted on operand of type 'void *'"],
+ )
diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp
new file mode 100644
index 0000000000000..1f2ac522ef0ef
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp
@@ -0,0 +1,29 @@
+int main(int argc, char **argv) {
+ int *p_null = nullptr;
+ const char *p_char1 = "hello";
+
+ typedef const char *my_char_ptr;
+ my_char_ptr my_p_char1 = p_char1;
+
+ int offset = 5;
+ int array[10];
+ array[0] = 0;
+ array[offset] = offset;
+
+ int(&array_ref)[10] = array;
+
+ int *p_int0 = &array[0];
+ int **pp_int0 = &p_int0;
+ const int *cp_int0 = &array[0];
+ const int *cp_int5 = &array[offset];
+ const int *&rcp_int0 = cp_int0;
+
+ typedef int *td_int_ptr_t;
+ td_int_ptr_t td_int_ptr0 = &array[0];
+
+ void *p_void = (void *)p_char1;
+ void **pp_void0 = &p_void;
+ void **pp_void1 = pp_void0 + 1;
+
+ return 0; // Set a breakpoint here
+}
diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt
index dc8b4c84e22f7..cc9d45ebf981d 100644
--- a/lldb/unittests/CMakeLists.txt
+++ b/lldb/unittests/CMakeLists.txt
@@ -54,7 +54,6 @@ endif()
add_subdirectory(Breakpoint)
add_subdirectory(Callback)
add_subdirectory(Core)
-add_subdirectory(DIL)
add_subdirectory(DataFormatter)
add_subdirectory(Disassembler)
add_subdirectory(Editline)
diff --git a/lldb/unittests/DIL/CMakeLists.txt b/lldb/unittests/DIL/CMakeLists.txt
deleted file mode 100644
index 07fb7172a2b4c..0000000000000
--- a/lldb/unittests/DIL/CMakeLists.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-add_lldb_unittest(DILTests
- DILTests.cpp
- DILLexerTests.cpp
- Runner.cpp
-
- LINK_LIBS
- liblldb
- lldbUtilityHelpers
- lldbValueObject
- LLVMTestingSupport
- )
-add_subdirectory(Inputs)
-add_dependencies(DILTests test_binary)
-
-add_unittest_inputs(DILTests "test_binary.cpp")
diff --git a/lldb/unittests/DIL/DILTests.cpp b/lldb/unittests/DIL/DILTests.cpp
deleted file mode 100644
index c9c3620cf3162..0000000000000
--- a/lldb/unittests/DIL/DILTests.cpp
+++ /dev/null
@@ -1,303 +0,0 @@
-//===-- DILTests.cpp --------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "Runner.h"
-#include "TestingSupport/TestUtilities.h"
-#include "lldb/API/SBDebugger.h"
-#include "lldb/API/SBError.h"
-#include "lldb/API/SBFrame.h"
-#include "lldb/API/SBProcess.h"
-#include "lldb/API/SBTarget.h"
-#include "lldb/API/SBThread.h"
-#include "lldb/API/SBType.h"
-#include "lldb/lldb-enumerations.h"
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-using ::testing::MakeMatcher;
-using ::testing::Matcher;
-using ::testing::MatcherInterface;
-using ::testing::MatchResultListener;
-
-struct EvalResult {
- lldb::SBError lldb_DIL_error;
- mutable lldb::SBValue lldb_DIL_value;
- mutable std::optional<lldb::SBValue> lldb_value;
-};
-
-class EvaluatorHelper {
-public:
- EvaluatorHelper(lldb::SBFrame frame, bool compare_with_frame_var)
- : frame_(frame), compare_with_frame_var_(compare_with_frame_var) {}
-
-public:
- EvalResult Eval(const std::string &expr) {
- EvalResult ret;
- ret.lldb_DIL_value = frame_.TestGetValueForVariablePath(
- expr.c_str(), lldb::eNoDynamicValues, true);
- if (!ret.lldb_DIL_value.GetError().Success())
- ret.lldb_DIL_error = ret.lldb_DIL_value.GetError();
- if (compare_with_frame_var_) {
- ret.lldb_value = frame_.TestGetValueForVariablePath(
- expr.c_str(), lldb::eNoDynamicValues, false);
- }
- return ret;
- }
-
-private:
- lldb::SBFrame frame_;
- bool compare_with_frame_var_;
-};
-
-void PrintError(::testing::MatchResultListener *listener,
- const std::string &error) {
- *listener << "error:";
- // Print multiline errors on a separate line.
- if (error.find('\n') != std::string::npos) {
- *listener << "\n";
- } else {
- *listener << " ";
- }
- *listener << error;
-}
-
-class IsOkMatcher : public MatcherInterface<EvalResult> {
-public:
- explicit IsOkMatcher(bool compare_types) : compare_types_(compare_types) {}
-
- bool MatchAndExplain(EvalResult result,
- MatchResultListener *listener) const override {
- if (result.lldb_DIL_error.GetError()) {
- PrintError(listener, result.lldb_DIL_error.GetCString());
- return false;
- }
-
- std::string actual = result.lldb_DIL_value.GetValue();
- // Compare only if we tried to evaluate with LLDB.
- if (result.lldb_value.has_value()) {
- if (result.lldb_value.value().GetError().GetError()) {
- *listener << "values produced by DIL and 'frame var' don't match\n"
- << "DIL : " << actual << "\n"
- << "frame var: "
- << result.lldb_value.value().GetError().GetCString();
- return false;
-
- } else if (actual != result.lldb_value.value().GetValue()) {
- *listener << "values produced by DIL and 'frame var' don't match\n"
- << "DIL : " << actual << "\n"
- << "frame var: " << result.lldb_value.value().GetValue();
- return false;
- }
-
- if (compare_types_) {
- const char *lldb_DIL_type =
- result.lldb_DIL_value.GetType().GetUnqualifiedType().GetName();
- const char *lldb_type =
- result.lldb_value.value().GetType().GetUnqualifiedType().GetName();
- if (strcmp(lldb_DIL_type, lldb_type) != 0) {
- *listener << "types produced by DIL and 'frame var' don't match\n"
- << "DIL : " << lldb_DIL_type << "\n"
- << "frame var: " << lldb_type;
- return false;
- }
- }
- }
-
- return true;
- }
-
- void DescribeTo(std::ostream *os) const override {
- *os << "evaluates without an error and equals to LLDB";
- }
-
-private:
- bool compare_types_;
-};
-
-Matcher<EvalResult> IsOk(bool compare_types = true) {
- return MakeMatcher(new IsOkMatcher(compare_types));
-}
-
-class IsEqualMatcher : public MatcherInterface<EvalResult> {
-public:
- IsEqualMatcher(std::string value, bool compare_types)
- : value_(std::move(value)), compare_types_(compare_types) {}
-
-public:
- bool MatchAndExplain(EvalResult result,
- MatchResultListener *listener) const override {
- if (result.lldb_DIL_error.GetError()) {
- PrintError(listener, result.lldb_DIL_error.GetCString());
- return false;
- }
-
- std::string actual = result.lldb_DIL_value.GetValue();
- if (actual != value_) {
- *listener << "evaluated to '" << actual << "'";
- return false;
- }
-
- // Compare only if we tried to evaluate with LLDB.
- if (result.lldb_value.has_value()) {
- if (result.lldb_value.value().GetError().GetError()) {
- *listener << "values produced by DIL and 'frame var' don't match\n"
- << "DIL : " << actual << "\n"
- << "frame var: "
- << result.lldb_value.value().GetError().GetCString();
- return false;
-
- } else if (actual != result.lldb_value.value().GetValue()) {
- *listener << "values produced by DIL and 'frame var' don't match\n"
- << "DIL : " << actual << "\n"
- << "frame var: " << result.lldb_value.value().GetValue();
- return false;
- }
-
- if (compare_types_) {
- const char *lldb_DIL_type =
- result.lldb_DIL_value.GetType().GetUnqualifiedType().GetName();
- const char *lldb_type =
- result.lldb_value.value().GetType().GetUnqualifiedType().GetName();
- if (strcmp(lldb_DIL_type, lldb_type) != 0) {
- *listener << "types produced by DIL and 'frame var' don't match\n"
- << "DIL : " << lldb_DIL_type << "\n"
- << "frame var: " << lldb_type;
- return false;
- }
- }
- }
- return true;
- }
-
- void DescribeTo(std::ostream *os) const override {
- *os << "evaluates to '" << value_ << "'";
- }
-
-private:
- std::string value_;
- bool compare_types_;
-};
-
-Matcher<EvalResult> IsEqual(std::string value, bool compare_types = true) {
- return MakeMatcher(new IsEqualMatcher(std::move(value), compare_types));
-}
-
-class IsErrorMatcher : public MatcherInterface<EvalResult> {
-public:
- explicit IsErrorMatcher(std::string value) : value_(std::move(value)) {}
-
-public:
- bool MatchAndExplain(EvalResult result,
- MatchResultListener *listener) const override {
- if (!result.lldb_DIL_error.GetError()) {
- *listener << "evaluated to '" << result.lldb_DIL_value.GetValue() << "'";
- return false;
- }
- std::string message = result.lldb_DIL_error.GetCString();
- if (message.find(value_) == std::string::npos) {
- PrintError(listener, message);
- return false;
- }
-
- return true;
- }
-
- void DescribeTo(std::ostream *os) const override {
- *os << "evaluates with an error: '" << value_ << "'";
- }
-
-private:
- std::string value_;
-};
-
-Matcher<EvalResult> IsError(std::string value) {
- return MakeMatcher(new IsErrorMatcher(std::move(value)));
-}
-
-class EvalTest : public ::testing::Test {
-protected:
- static void SetUpTestSuite() { lldb::SBDebugger::Initialize(); }
-
- static void TearDownTestSuite() { lldb::SBDebugger::Terminate(); }
-
- void SetUp() override {
- std::string test_name =
- ::testing::UnitTest::GetInstance()->current_test_info()->name();
- std::string break_line = "// BREAK(" + test_name + ")";
-
- std::string binary_path =
- lldb_private::GetInputFilePath("test_binary.bin");
- std::string source_path = lldb_private::GetInputFilePath("test_binary.cpp");
-
- debugger_ = lldb::SBDebugger::Create(false);
- process_ =
- LaunchTestProgram(debugger_, source_path, binary_path, break_line);
- frame_ = process_.GetSelectedThread().GetSelectedFrame();
- }
-
- void TearDown() override {
- process_.Destroy();
- lldb::SBDebugger::Destroy(debugger_);
- }
-
- EvalResult Eval(const std::string &expr) {
- return EvaluatorHelper(frame_, compare_with_frame_var_).Eval(expr);
- }
-
- bool Is32Bit() const {
- if (process_.GetAddressByteSize() == 4) {
- return true;
- }
- return false;
- }
-
-protected:
- lldb::SBDebugger debugger_;
- lldb::SBProcess process_;
- lldb::SBFrame frame_;
-
- // Evaluate with both DIL and LLDB by default.
- bool compare_with_frame_var_ = true;
-};
-
-TEST_F(EvalTest, TestSymbols) {
- EXPECT_GT(frame_.GetModule().GetNumSymbols(), (size_t)0)
- << "No symbols might indicate that the test binary was built incorrectly";
-}
-
-TEST_F(EvalTest, TestPointerDereference) {
- EXPECT_THAT(Eval("*p_int0"), IsEqual("0"));
- EXPECT_THAT(Eval("*cp_int5"), IsEqual("5"));
- EXPECT_THAT(Eval("*rcp_int0"), IsOk());
-
- EXPECT_THAT(Eval("&*p_void"),
- IsError("indirection not permitted on operand of type"
- " 'void *'"));
-
- this->compare_with_frame_var_ = false;
- EXPECT_THAT(Eval("*array"), IsEqual("0"));
- EXPECT_THAT(Eval("&*p_null"),
- IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000"));
- EXPECT_THAT(Eval("**pp_int0"), IsEqual("0"));
- EXPECT_THAT(Eval("&**pp_int0"), IsOk());
-}
-
-TEST_F(EvalTest, TestAddressOf) {
- EXPECT_THAT(Eval("&x"), IsOk());
- EXPECT_THAT(Eval("r"), IsOk());
- EXPECT_THAT(Eval("&r"), IsOk());
- EXPECT_THAT(Eval("pr"), IsOk());
- EXPECT_THAT(Eval("&pr"), IsOk());
- EXPECT_THAT(Eval("my_pr"), IsOk());
- EXPECT_THAT(Eval("&my_pr"), IsOk());
-
- EXPECT_THAT(Eval("&globalVar"), IsOk());
- EXPECT_THAT(Eval("&s_str"), IsOk());
- EXPECT_THAT(Eval("¶m"), IsOk());
-}
diff --git a/lldb/unittests/DIL/Inputs/CMakeLists.txt b/lldb/unittests/DIL/Inputs/CMakeLists.txt
deleted file mode 100644
index 73e179d08d059..0000000000000
--- a/lldb/unittests/DIL/Inputs/CMakeLists.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-# Build `test_binary.cc` and put the binary in the Inputs folder,
-# allowing `lldb_private::GetInputFilePath` to find it.
-# Projects that must be enabled: clang;lldb;lld
-# Runtimes that must be enabled: libcxx;libcxxabi;libunwind
-if ("libcxx" IN_LIST LLVM_ENABLE_RUNTIMES)
- if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
- set(LIBCXX_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE})
- set(LIBCXX_GENERATED_INCLUDE_DIR "${LLVM_BINARY_DIR}/include/c++/v1")
- set(LIBCXX_GENERATED_INCLUDE_TARGET_DIR "${LLVM_BINARY_DIR}/include/${LLVM_DEFAULT_TARGET_TRIPLE}/c++/v1")
- else()
- set(LIBCXX_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBCXX_LIBDIR_SUFFIX})
- set(LIBCXX_GENERATED_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include/c++/v1")
- endif()
-
- if(DEFINED LIBCXX_GENERATED_INCLUDE_TARGET_DIR)
- set(INCLUDE_TARGET_DIR_OPTION "-cxx-isystem" "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}")
- endif()
-
- get_target_property(EXE_PATH DILTests RUNTIME_OUTPUT_DIRECTORY)
- add_custom_command(
- OUTPUT test_binary.bin
- COMMAND $<TARGET_FILE:clang> ${CMAKE_CURRENT_SOURCE_DIR}/test_binary.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/test_extern.cpp
- -O0 -g -std=c++17 -fuse-ld=lld -B$<TARGET_FILE:lld>
- -nostdlib++ -nostdinc++ -cxx-isystem ${LIBCXX_GENERATED_INCLUDE_DIR}
- ${INCLUDE_TARGET_DIR_OPTION}
- -L${LIBCXX_LIBRARY_DIR} -Wl,-rpath,${LIBCXX_LIBRARY_DIR} -lc++
- -o ${EXE_PATH}/Inputs/test_binary.bin
- DEPENDS test_binary.cpp test_extern.cpp clang lld
- )
- add_custom_target(test_binary
- DEPENDS test_binary.bin
- )
-else()
- message(FATAL_ERROR "libcxx runtime must be enabled.")
-endif()
diff --git a/lldb/unittests/DIL/Inputs/test_binary.cpp b/lldb/unittests/DIL/Inputs/test_binary.cpp
deleted file mode 100644
index 115add3129bcd..0000000000000
--- a/lldb/unittests/DIL/Inputs/test_binary.cpp
+++ /dev/null
@@ -1,1255 +0,0 @@
-//===-- test_binary.cpp --------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include <cstdint>
-#include <limits>
-#include <memory>
-#include <string>
-
-static void TestArithmetic() {
- char c = 10;
- unsigned char uc = 1;
- int a = 1;
- int int_max = std::numeric_limits<int>::max();
- int int_min = std::numeric_limits<int>::min();
- unsigned int uint_max = std::numeric_limits<unsigned int>::max();
- unsigned int uint_zero = 0;
- long long ll_max = std::numeric_limits<long long>::max();
- long long ll_min = std::numeric_limits<long long>::min();
- unsigned long long ull_max = std::numeric_limits<unsigned long long>::max();
- unsigned long long ull_zero = 0;
-
- int x = 2;
- int &r = x;
- int *p = &x;
-
- typedef int &myr;
- myr my_r = x;
-
- auto fnan = std::numeric_limits<float>::quiet_NaN();
- auto fsnan = std::numeric_limits<float>::signaling_NaN();
- // Smallest positive non-zero float denormal
- auto fdenorm = 0x0.1p-145f;
-
- // BREAK(TestArithmetic)
- // BREAK(TestZeroDivision)
-}
-
-static void TestBitwiseOperators() {
- bool var_true = true;
- bool var_false = false;
-
- unsigned long long ull_max = std::numeric_limits<unsigned long long>::max();
- unsigned long long ull_zero = 0;
-
- struct S {
- } s;
-
- const char *p = nullptr;
-
- uint32_t mask_ff = 0xFF;
-
- // BREAK(TestBitwiseOperators)
-}
-
-static void TestPointerArithmetic() {
- int *p_null = nullptr;
- const char *p_char1 = "hello";
-
- typedef const char *my_char_ptr;
- my_char_ptr my_p_char1 = p_char1;
-
- int offset = 5;
- int array[10];
- array[0] = 0;
- array[offset] = offset;
-
- int(&array_ref)[10] = array;
-
- int *p_int0 = &array[0];
- int **pp_int0 = &p_int0;
- const int *cp_int0 = &array[0];
- const int *cp_int5 = &array[offset];
- const int *&rcp_int0 = cp_int0;
-
- typedef int *td_int_ptr_t;
- td_int_ptr_t td_int_ptr0 = &array[0];
-
- void *p_void = (void *)p_char1;
- void **pp_void0 = &p_void;
- void **pp_void1 = pp_void0 + 1;
-
- std::nullptr_t std_nullptr_t = nullptr;
-
- // BREAK(TestPointerArithmetic)
- // BREAK(PointerPointerArithmeticFloat)
- // BREAK(PointerPointerComparison)
- // BREAK(PointerIntegerComparison)
- // BREAK(TestPointerDereference)
-}
-
-static void TestLogicalOperators() {
- bool trueVar = true;
- bool falseVar = false;
-
- const char *p_ptr = "🦊";
- const char *p_nullptr = nullptr;
-
- int array[2] = {1, 2};
-
- struct S {
- } s;
-
- // BREAK(TestLogicalOperators)
-}
-
-static void TestLocalVariables() {
- int a = 1;
- int b = 2;
-
- char c = -3;
- unsigned short s = 4;
-
- // BREAK(TestLocalVariables)
-}
-
-static void TestMemberOf() {
- int x = 2;
- struct Sx {
- int x;
- int &r;
- char y;
- } s{1, x, 2};
-
- Sx &sr = s;
- Sx *sp = &s;
-
- Sx sarr[2] = {{5, x, 2}, {1, x, 3}};
-
- using SxAlias = Sx;
- SxAlias sa{3, x, 4};
-
- // BREAK(TestMemberOf)
-}
-
-static void TestMemberOfInheritance() {
- struct A {
- int a_;
- } a{1};
-
- struct B {
- int b_;
- } b{2};
-
- struct C : A, B {
- int c_;
- } c{{1}, {2}, 3};
-
- struct D : C {
- int d_;
- A fa_;
- } d{{{1}, {2}, 3}, 4, {5}};
-
- // Virtual inheritance example.
- struct Animal {
- virtual ~Animal() = default;
- int weight_;
- };
- struct Mammal : virtual Animal {};
- struct WingedAnimal : virtual Animal {};
- struct Bat : Mammal, WingedAnimal {
- } bat;
- bat.weight_ = 10;
-
- // Empty bases example.
- struct IPlugin {
- virtual ~IPlugin() {}
- };
- struct Plugin : public IPlugin {
- int x;
- int y;
- };
- Plugin plugin;
- plugin.x = 1;
- plugin.y = 2;
-
- struct ObjectBase {
- int x;
- };
- struct Object : ObjectBase {};
- struct Engine : Object {
- int y;
- int z;
- };
-
- Engine engine;
- engine.x = 1;
- engine.y = 2;
- engine.z = 3;
-
- // Empty multiple inheritance with empty base.
- struct Base {
- int x;
- int y;
- virtual void Do() = 0;
- virtual ~Base() {}
- };
- struct Mixin {};
- struct Parent : private Mixin, public Base {
- int z;
- virtual void Do() {};
- };
- Parent obj;
- obj.x = 1;
- obj.y = 2;
- obj.z = 3;
- Base *parent_base = &obj;
- Parent *parent = &obj;
-
- // BREAK(TestMemberOfInheritance)
-}
-
-static void TestMemberOfAnonymousMember() {
- struct A {
- struct {
- int x = 1;
- };
- int y = 2;
- } a;
-
- struct B {
- // Anonymous struct inherits another struct.
- struct : public A {
- int z = 3;
- };
- int w = 4;
- A a;
- } b;
-
- // Anonymous classes and unions.
- struct C {
- union {
- int x = 5;
- };
- class {
- public:
- int y = 6;
- };
- } c;
-
- // Multiple levels of anonymous structs.
- struct D {
- struct {
- struct {
- int x = 7;
- struct {
- int y = 8;
- };
- };
- int z = 9;
- struct {
- int w = 10;
- };
- };
- } d;
-
- struct E {
- struct IsNotAnon {
- int x = 11;
- };
- } e;
-
- struct F {
- struct {
- int x = 12;
- } named_field;
- } f;
-
- // Inherited unnamed struct without an enclosing parent class.
- struct : public A {
- struct {
- int z = 13;
- };
- } unnamed_derived;
-
- struct DerivedB : public B {
- struct {
- // `w` in anonymous struct overrides `w` from `B`.
- int w = 14;
- int k = 15;
- };
- } derb;
-
- // BREAK(TestMemberOfAnonymousMember)
-}
-
-static void TestIndirection() {
- int val = 1;
- int *p = &val;
-
- typedef int *myp;
- myp my_p = &val;
-
- typedef int *&mypr;
- mypr my_pr = p;
-
- // BREAK(TestIndirection)
-}
-
-// Referenced by TestInstanceVariables
-class C {
-public:
- int field_ = 1337;
-};
-
-// Referenced by TestAddressOf
-int globalVar = 0xDEADBEEF;
-extern int externGlobalVar;
-
-int *globalPtr = &globalVar;
-int &globalRef = globalVar;
-
-namespace ns {
-int globalVar = 13;
-int *globalPtr = &globalVar;
-int &globalRef = globalVar;
-} // namespace ns
-
-void TestGlobalVariableLookup() {
- // BREAK(TestGlobalVariableLookup)
-}
-
-class TestMethods {
-public:
- void TestInstanceVariables() {
- C c;
- c.field_ = -1;
-
- C &c_ref = c;
- C *c_ptr = &c;
-
- // BREAK(TestInstanceVariables)
- }
-
- void TestAddressOf(int param) {
- int x = 42;
- int &r = x;
- int *p = &x;
- int *&pr = p;
-
- typedef int *&mypr;
- mypr my_pr = p;
-
- std::string s = "hello";
- const char *s_str = s.c_str();
-
- char c = 1;
-
- // BREAK(TestAddressOf)
- }
-
-private:
- int field_ = 1;
-};
-
-static void TestSubscript() {
- const char *char_ptr = "lorem";
- const char char_arr[] = "ipsum";
-
- int int_arr[] = {1, 2, 3};
-
- C c_arr[2];
- c_arr[0].field_ = 0;
- c_arr[1].field_ = 1;
-
- C(&c_arr_ref)[2] = c_arr;
-
- int idx_1 = 1;
- const int &idx_1_ref = idx_1;
-
- typedef int td_int_t;
- typedef td_int_t td_td_int_t;
- typedef int *td_int_ptr_t;
- typedef int &td_int_ref_t;
-
- td_int_t td_int_idx_1 = 1;
- td_td_int_t td_td_int_idx_2 = 2;
-
- td_int_t td_int_arr[3] = {1, 2, 3};
- td_int_ptr_t td_int_ptr = td_int_arr;
-
- td_int_ref_t td_int_idx_1_ref = td_int_idx_1;
- td_int_t(&td_int_arr_ref)[3] = td_int_arr;
-
- unsigned char uchar_idx = std::numeric_limits<unsigned char>::max();
- uint8_t uint8_arr[256];
- uint8_arr[255] = 0xAB;
- uint8_t *uint8_ptr = uint8_arr;
-
- enum Enum { kZero, kOne } enum_one = kOne;
- Enum &enum_ref = enum_one;
-
- // BREAK(TestSubscript)
-}
-
-static void TestArrayDereference() {
- int arr_1d[2] = {1, 2};
- int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}};
-
- // BREAK(TestArrayDereference)
-}
-
-// Referenced by TestCStyleCast
-namespace ns {
-
-typedef int myint;
-
-class Foo {};
-
-namespace inner {
-
-using mydouble = double;
-
-class Foo {};
-
-} // namespace inner
-
-} // namespace ns
-
-static void TestCStyleCast() {
- int a = 1;
- int *ap = &a;
- void *vp = &a;
- int arr[2] = {1, 2};
-
- int na = -1;
- float f = 1.1;
-
- typedef int myint;
-
- myint myint_ = 1;
- ns::myint ns_myint_ = 2;
- 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();
-
- // BREAK(TestCStyleCastBuiltins)
- // BREAK(TestCStyleCastBasicType)
- // BREAK(TestCStyleCastPointer)
- // BREAK(TestCStyleCastNullptrType)
-
- 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}};
-
- // BREAK(TestCStyleCastArray)
- // BREAK(TestCStyleCastReference)
-}
-
-// Referenced by TestCxxCast
-struct CxxVirtualBase {
- int a;
- virtual ~CxxVirtualBase(){};
-};
-struct CxxVirtualParent : CxxVirtualBase {
- int b;
-};
-
-static void TestCxxCast() {
- struct CxxBase {
- int a;
- int b;
- };
- struct CxxParent : CxxBase {
- long long c;
- short d;
- };
-
- enum UEnum { kUZero, kUOne, kUTwo };
- enum class SEnum { kSZero, kSOne };
-
- UEnum u_enum = kUTwo;
- SEnum s_enum = SEnum::kSOne;
-
- typedef int td_int_t;
- typedef int *td_int_ptr_t;
- typedef int &td_int_ref_t;
- typedef SEnum td_senum_t;
- td_int_t td_int = 13;
- td_int_ptr_t td_int_ptr = &td_int;
- td_int_ref_t td_int_ref = td_int;
- td_senum_t td_senum = s_enum;
-
- CxxParent parent;
- parent.a = 1;
- parent.b = 2;
- parent.c = 3;
- parent.d = 4;
-
- CxxBase *base = &parent;
-
- int arr[] = {1, 2, 3, 4, 5};
- int *ptr = arr;
-
- // BREAK(TestCxxStaticCast)
- // BREAK(TestCxxReinterpretCast)
-
- CxxVirtualParent v_parent;
- v_parent.a = 1;
- v_parent.b = 2;
- CxxVirtualBase *v_base = &v_parent;
-
- // BREAK(TestCxxDynamicCast)
-}
-
-void TestCastInheritedTypes() {
- struct CxxEmpty {};
- struct CxxA {
- short a;
- };
- struct CxxB {
- long long b;
- };
- struct CxxC : CxxEmpty, CxxA, CxxB {
- int c;
- };
- struct CxxD {
- long long d;
- };
- struct CxxE : CxxD, CxxC {
- int e;
- };
-
- CxxA a{1};
- CxxB b{2};
- CxxC c;
- c.a = 3;
- c.b = 4;
- c.c = 5;
- CxxD d{6};
- CxxE e;
- e.a = 7;
- e.b = 8;
- e.c = 9;
- e.d = 10;
- e.e = 11;
-
- struct CxxVC : virtual CxxA, virtual CxxB {
- int c;
- };
- struct CxxVE : CxxD, CxxVC {
- int e;
- };
-
- CxxVC vc;
- vc.a = 12;
- vc.b = 13;
- vc.c = 14;
- CxxVE ve;
- ve.a = 15;
- ve.b = 16;
- ve.c = 17;
- ve.d = 18;
- ve.e = 19;
-
- CxxB *e_as_b = &e;
- CxxB *ve_as_b = &ve;
-
- // BREAK(TestCastBaseToDerived)
- // BREAK(TestCastDerivedToBase)
-}
-
-// Referenced by TestQualifiedId.
-namespace ns {
-
-int i = 1;
-
-namespace ns {
-
-int i = 2;
-
-} // namespace ns
-
-} // namespace ns
-
-static void TestQualifiedId() {
- // BREAK(TestQualifiedId)
-}
-
-namespace outer {
-
-namespace inner {
-
-class Vars {
-public:
- inline static double inline_static = 1.5;
- static constexpr int static_constexpr = 2;
- static const unsigned int static_const;
-
- struct Nested {
- static const int static_const;
- };
-};
-
-const unsigned int Vars::static_const = 3;
-const int Vars::Nested::static_const = 10;
-
-using MyVars = Vars;
-
-} // namespace inner
-
-class Vars {
-public:
- inline static double inline_static = 4.5;
- static constexpr int static_constexpr = 5;
- static const unsigned int static_const;
-
- struct Nested {
- static const int static_const;
- };
-};
-
-const unsigned int Vars::static_const = 6;
-const int Vars::Nested::static_const = 20;
-
-} // namespace outer
-
-class Vars {
-public:
- inline static double inline_static = 7.5;
- static constexpr int static_constexpr = 8;
- static const unsigned int static_const;
-
- struct Nested {
- static const int static_const;
- };
-};
-
-const unsigned int Vars::static_const = 9;
-const int Vars::Nested::static_const = 30;
-
-static void TestStaticConst() {
- Vars vars;
- outer::Vars outer_vars;
- outer::inner::Vars outer_inner_vars;
-
- using MyVars = Vars;
- using MyOuterVars = outer::Vars;
-
- MyVars my_vars;
- MyOuterVars my_outer_vars;
- outer::inner::MyVars my_outer_inner_vars;
-
- // BREAK(TestStaticConstDeclaredInline)
- // BREAK(TestStaticConstDeclaredInlineScoped)
- // BREAK(TestStaticConstDeclaredOutsideTheClass)
- // BREAK(TestStaticConstDeclaredOutsideTheClassScoped)
-}
-
-// Referenced by TestTemplateTypes.
-template <typename T> struct T_1 {
- static const int cx;
- typedef double myint;
-
- T_1() {}
- T_1(T x) : x(x) {}
- T x;
-};
-
-template <typename T> const int T_1<T>::cx = 42;
-
-template <> const int T_1<int>::cx = 24;
-
-template <typename T1, typename T2> struct T_2 {
- typedef float myint;
-
- T_2() {}
- T1 x;
- T2 y;
-};
-
-namespace ns {
-
-template <typename T> struct T_1 {
- static const int cx;
- typedef int myint;
-
- T_1() {}
- T_1(T x) : x(x) {}
- T x;
-};
-
-template <typename T> const int T_1<T>::cx = 46;
-
-template <> const int T_1<int>::cx = 64;
-
-} // namespace ns
-
-static void TestTemplateTypes() {
- int i;
- int *p = &i;
-
- { T_1<int> _; }
- { T_1<int *> _; }
- { T_1<int **> _; }
- { T_1<int &> _(i); }
- { T_1<int *&> _(p); }
- { T_1<double> _; }
- { T_2<int, char> _; }
- { T_2<char, int> _; }
- { T_2<T_1<int>, T_1<char>> _; }
- { T_2<T_1<T_1<int>>, T_1<char>> _; }
-
- { ns::T_1<int> _; }
- { ns::T_1<int *> _; }
- { ns::T_1<int **> _; }
- { ns::T_1<int &> _(i); }
- { ns::T_1<int *&> _(p); }
- { ns::T_1<ns::T_1<int>> _; }
- { ns::T_1<ns::T_1<int *>> _; }
- { ns::T_1<ns::T_1<int **>> _; }
- { ns::T_1<ns::T_1<int &>> _(i); }
- { ns::T_1<ns::T_1<int *&>> _(p); }
-
- { T_1<int>::myint _ = 0; }
- { T_1<int *>::myint _ = 0; }
- { T_1<int **>::myint _ = 0; }
- { T_1<int &>::myint _ = 0; }
- { T_1<int *&>::myint _ = 0; }
- { T_1<T_1<int>>::myint _ = 0; }
- { T_1<T_1<T_1<int>>>::myint _ = 0; }
- { T_1<T_1<int *>>::myint _ = 0; }
- { T_1<T_1<int **>>::myint _ = 0; }
- { T_1<T_1<int &>>::myint _ = 0; }
- { T_1<T_1<int *&>>::myint _ = 0; }
-
- { T_2<int, char>::myint _ = 0; }
- { T_2<int *, char &>::myint _ = 0; }
- { T_2<int &, char *>::myint _ = 0; }
- { T_2<T_1<T_1<int>>, T_1<char>>::myint _ = 0; }
-
- { ns::T_1<int>::myint _ = 0; }
- { ns::T_1<int *>::myint _ = 0; }
- { ns::T_1<int **>::myint _ = 0; }
- { ns::T_1<int &>::myint _ = 0; }
- { ns::T_1<int *&>::myint _ = 0; }
- { ns::T_1<T_1<int>>::myint _ = 0; }
- { ns::T_1<T_1<int *>>::myint _ = 0; }
- { ns::T_1<T_1<int **>>::myint _ = 0; }
- { ns::T_1<T_1<int &>>::myint _ = 0; }
- { ns::T_1<T_1<int *&>>::myint _ = 0; }
- { ns::T_1<ns::T_1<int>>::myint _ = 0; }
- { ns::T_1<ns::T_1<int *>>::myint _ = 0; }
- { ns::T_1<ns::T_1<int **>>::myint _ = 0; }
- { ns::T_1<ns::T_1<int &>>::myint _ = 0; }
- { ns::T_1<ns::T_1<int *&>>::myint _ = 0; }
-
- (void)T_1<double>::cx;
- (void)ns::T_1<double>::cx;
- (void)ns::T_1<ns::T_1<int>>::cx;
-
- int T_1 = 2;
-
- // BREAK(TestTemplateTypes)
- // BREAK(TestTemplateCpp11)
-}
-
-template <typename T, typename TAllocator> struct TArray {
- using ElementType = T;
- T t_;
- TAllocator a_;
-};
-
-template <int Size> struct Allocator {
- int size = Size;
-};
-
-void TestTemplateWithNumericArguments() {
- Allocator<4> a4;
- Allocator<8> a8;
- TArray<int, Allocator<4>> arr;
- decltype(arr)::ElementType *el = 0;
-
- // BREAK(TestTemplateWithNumericArguments)
-}
-
-namespace test_scope {
-
-class Value {
-public:
- Value(int x, float y) : x_(x), y_(y) {}
-
- // Static members
- enum ValueEnum { A, B };
- static double static_var;
-
-private:
- int x_;
- float y_;
-};
-
-double Value::static_var = 3.5;
-
-} // namespace test_scope
-
-void TestValueScope() {
- test_scope::Value var(1, 2.5f);
- test_scope::Value &var_ref = var;
- uint64_t z_ = 3;
-
- // "raw" representation of the Value.
- int bytes[] = {1, 0x40200000};
-
- auto val_enum = test_scope::Value::A;
- (void)val_enum;
- (void)test_scope::Value::static_var;
-
- // BREAK(TestValueScope)
- // BREAK(TestReferenceScope)
-}
-
-void TestBitField() {
- enum BitFieldEnum : uint32_t { kZero, kOne };
-
- struct BitFieldStruct {
- uint16_t a : 10;
- uint32_t b : 4;
- bool c : 1;
- bool d : 1;
- int32_t e : 32;
- uint32_t f : 32;
- uint32_t g : 31;
- uint64_t h : 31;
- uint64_t i : 33;
- BitFieldEnum j : 10;
- };
-
- BitFieldStruct bf;
- bf.a = 0b1111111111;
- bf.b = 0b1001;
- bf.c = 0b0;
- bf.d = 0b1;
- bf.e = 0b1;
- bf.f = 0b1;
- bf.g = 0b1;
- bf.h = 0b1;
- bf.i = 0b1;
- bf.j = BitFieldEnum::kOne;
-
- struct AlignedBitFieldStruct {
- uint16_t a : 10;
- uint8_t b : 4;
- unsigned char : 0;
- uint16_t c : 2;
- };
-
- uint32_t data = ~0;
- AlignedBitFieldStruct abf = (AlignedBitFieldStruct &)data;
-
- // BREAK(TestBitField)
- // BREAK(TestBitFieldScoped)
- // BREAK(TestBitFieldPromotion)
- // BREAK(TestBitFieldWithSideEffects)
-}
-
-void TestContextVariables() {
- struct Scope {
- int a = 10;
- const char *ptr = "hello";
- };
-
- Scope s;
-
- // BREAK(TestContextVariables)
- // BREAK(TestContextVariablesSubset)
-}
-
-// Referenced by TestScopedEnum.
-enum class ScopedEnum { kFoo, kBar };
-enum class ScopedEnumUInt8 : uint8_t { kFoo, kBar };
-
-void TestScopedEnum() {
- auto enum_foo = ScopedEnum::kFoo;
- auto enum_bar = ScopedEnum::kBar;
- auto enum_neg = (ScopedEnum)-1;
-
- auto enum_u8_foo = ScopedEnumUInt8::kFoo;
- auto enum_u8_bar = ScopedEnumUInt8::kBar;
-
- // BREAK(TestScopedEnum)
- // BREAK(TestScopedEnumArithmetic)
- // BREAK(TestScopedEnumWithUnderlyingType)
-}
-
-enum UnscopedEnum { kZero, kOne, kTwo };
-enum UnscopedEnumUInt8 : uint8_t { kZeroU8, kOneU8, kTwoU8 };
-enum UnscopedEnumInt8 : int8_t { kZero8, kOne8, kTwo8 };
-enum UnscopedEnumEmpty : uint8_t {};
-
-// UnscopedEnum global_enum = UnscopedEnum::kOne;
-
-void TestUnscopedEnum() {
- auto enum_zero = UnscopedEnum::kZero;
- auto enum_one = UnscopedEnum::kOne;
- auto enum_two = UnscopedEnum::kTwo;
-
- auto &enum_one_ref = enum_one;
- auto &enum_two_ref = enum_two;
-
- auto enum_zero_u8 = UnscopedEnumUInt8::kZeroU8;
- auto enum_one_u8 = UnscopedEnumUInt8::kOneU8;
- auto enum_two_u8 = UnscopedEnumUInt8::kTwoU8;
-
- UnscopedEnumEmpty enum_empty{};
-
- auto enum_one_8 = UnscopedEnumInt8::kOne8;
- auto enum_neg_8 = (UnscopedEnumInt8)-1;
-
- // BREAK(TestUnscopedEnum)
- // BREAK(TestUnscopedEnumNegation)
- // BREAK(TestUnscopedEnumWithUnderlyingType)
- // BREAK(TestUnscopedEnumEmpty)
-}
-
-void TestTernaryOperator() {
- int i = 1;
- int *pi = &i;
- char c = 2;
- int arr2[2] = {1, 2};
- int arr3[3] = {1, 2, 3};
- double dbl_arr[2] = {1.0, 2.0};
- struct T {
- } t;
- enum EnumA { kOneA = 1, kTwoA } a_enum = kTwoA;
- enum EnumB { kOneB = 1 } b_enum = kOneB;
- // BREAK(TestTernaryOperator)
-}
-
-void TestSizeOf() {
- int i = 1;
- int *p = &i;
- int arr[] = {1, 2, 3};
-
- struct SizeOfFoo {
- int x, y;
- } foo;
-
- // BREAK(TestSizeOf)
-}
-
-void TestBuiltinFunction_Log2() {
- struct Foo {
- } foo;
-
- enum CEnum { kFoo = 129 } c_enum = kFoo;
- enum class CxxEnum { kFoo = 129 } cxx_enum = CxxEnum::kFoo;
-
- CEnum &c_enum_ref = c_enum;
- CxxEnum &cxx_enum_ref = cxx_enum;
-
- // BREAK(TestBuiltinFunction_Log2)
-}
-
-void TestBuiltinFunction_findnonnull() {
- uint8_t array_of_uint8[] = {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};
- uint8_t *pointer_to_uint8 = array_of_uint8;
-
- int *array_of_pointers[] = {(int *)1, (int *)1, (int *)0, (int *)0, (int *)1};
- int **pointer_to_pointers = array_of_pointers;
-
- // BREAK(TestBuiltinFunction_findnonnull)
-}
-
-void TestPrefixIncDec() {
- auto enum_foo = ScopedEnum::kFoo;
- int i = 1;
-
- // BREAK(TestPrefixIncDec)
- // BREAK(TestPostfixIncDec)
-}
-
-void TestDereferencedType() {
- struct TTuple {
- int x = 1;
- };
- using TPair = TTuple;
-
- TPair p{};
- const TPair &p_ref = p;
- const TPair *p_ptr = &p;
-
- // BREAK(TestDereferencedType)
-}
-
-void TestMemberFunctionCall() {
- struct C {
- int m() { return 1; }
- };
-
- C c;
- c.m();
-
- // BREAK(TestMemberFunctionCall)
-}
-
-void TestCompositeAssignment() {
- int i = 10;
- float f = 1.5f;
- float *p = &f;
-
- enum Enum { ONE, TWO };
- Enum eOne = ONE;
- Enum eTwo = TWO;
-
- // BREAK(TestAssignment)
- // BREAK(TestCompositeAssignmentInvalid)
- // BREAK(TestCompositeAssignmentAdd)
- // BREAK(TestCompositeAssignmentSub)
- // BREAK(TestCompositeAssignmentMul)
- // BREAK(TestCompositeAssignmentDiv)
- // BREAK(TestCompositeAssignmentRem)
- // BREAK(TestCompositeAssignmentBitwise)
-}
-
-void TestSideEffects() {
- int x = 1;
- int xa[] = {1, 2};
- int *p = &x;
-
- // BREAK(TestSideEffects)
-}
-
-void TestUniquePtr() {
- struct NodeU {
- std::unique_ptr<NodeU> next;
- int value;
- };
- auto ptr_node = std::unique_ptr<NodeU>(new NodeU{nullptr, 2});
- ptr_node = std::unique_ptr<NodeU>(new NodeU{std::move(ptr_node), 1});
-
- std::unique_ptr<char> ptr_null;
- auto ptr_int = std::make_unique<int>(1);
- auto ptr_float = std::make_unique<float>(1.1f);
-
- auto deleter = [](void const *data) {
- delete static_cast<int const *>(data);
- };
- std::unique_ptr<void, decltype(deleter)> ptr_void(new int(42), deleter);
-
- // BREAK(TestUniquePtr)
- // BREAK(TestUniquePtrDeref)
- // BREAK(TestUniquePtrCompare)
-}
-
-void TestSharedPtr() {
- struct NodeS {
- std::shared_ptr<NodeS> next;
- int value;
- };
- auto ptr_node = std::shared_ptr<NodeS>(new NodeS{nullptr, 2});
- ptr_node = std::shared_ptr<NodeS>(new NodeS{std::move(ptr_node), 1});
-
- std::shared_ptr<char> ptr_null;
- auto ptr_int = std::make_shared<int>(1);
- auto ptr_float = std::make_shared<float>(1.1f);
-
- std::weak_ptr<int> ptr_int_weak = ptr_int;
-
- std::shared_ptr<void> ptr_void = ptr_int;
-
- // BREAK(TestSharedPtr)
- // BREAK(TestSharedPtrDeref)
- // BREAK(TestSharedPtrCompare)
-}
-
-void TestTypeComparison() {
- int i = 1;
- int const *const icpc = &i;
- int *ip = &i;
- int const *const *const icpcpc = &icpc;
- int **ipp = &ip;
-
- using MyInt = int;
- using MyPtr = MyInt *;
- MyInt mi = 2;
- MyPtr *mipp = ipp;
-
- using MyConstInt = const int;
- using MyConstPtr = MyConstInt *const;
- MyConstPtr *const micpcpc = icpcpc;
-
- char c = 2;
- signed char sc = 65;
- const char cc = 66;
- using mychar = char;
- mychar mc = 67;
-
- // BREAK(TestTypeComparison)
-}
-
-static void TestTypeDeclaration() {
- wchar_t wchar = 0;
- char16_t char16 = 0;
- char32_t char32 = 0;
-
- using mylong = long;
- mylong my_long = 1;
-
- // BREAK(TestBasicTypeDeclaration)
- // BREAK(TestUserTypeDeclaration)
-}
-
-static void TestTypeVsIdentifier() {
- struct StructOrVar {
- int x = 1;
- } s;
- short StructOrVar = 2;
-
- class ClassOrVar {
- public:
- int x = 3;
- };
- ClassOrVar ClassOrVar;
-
- union UnionOrVar {
- int x;
- } u;
- int UnionOrVar[2] = {1, 2};
-
- enum EnumOrVar { kFoo, kBar };
- EnumOrVar EnumOrVar = kFoo;
-
- enum class CxxEnumOrVar { kCxxFoo, kCxxBar };
- CxxEnumOrVar CxxEnumOrVar = CxxEnumOrVar::kCxxFoo;
-
- int OnlyVar = 4;
-
- // BREAK(TestTypeVsIdentifier)
-}
-
-static void TestSeparateParsing() {
- struct StructA {
- int a_;
- } a{1};
-
- struct StructB {
- int b_;
- } b{2};
-
- struct StructC : public StructA, public StructB {
- int c_;
- } c{{3}, {4}, 5};
-
- struct StructD : public StructC {
- int d_;
- } d{{{6}, {7}, 8}, 9};
-
- // BREAK(TestSeparateParsing)
- // BREAK(TestSeparateParsingWithContextVars)
-}
-
-// Used by TestRegistersNoDollar
-int rcx = 42;
-
-struct RegisterCtx {
- int rbx = 42;
-
- void TestRegisters() {
- int rax = 42;
-
- // BREAK(TestRegisters)
- // BREAK(TestRegistersNoDollar)
- }
-};
-
-static void TestCharParsing() {
- // BREAK(TestCharParsing)
-}
-
-static void TestStringParsing() {
- // BREAK(TestStringParsing)
-}
-
-namespace test_binary {
-
-void main() {
- // BREAK(TestSymbols)
-
- TestMethods tm;
-
- TestArithmetic();
- TestBitwiseOperators();
- TestPointerArithmetic();
- TestLogicalOperators();
- TestLocalVariables();
- TestMemberOf();
- TestMemberOfInheritance();
- TestMemberOfAnonymousMember();
- TestGlobalVariableLookup();
- tm.TestInstanceVariables();
- TestIndirection();
- tm.TestAddressOf(42);
- TestSubscript();
- TestCStyleCast();
- TestCxxCast();
- TestCastInheritedTypes();
- TestQualifiedId();
- TestStaticConst();
- TestTypeDeclaration();
- TestTemplateTypes();
- TestTemplateWithNumericArguments();
- TestValueScope();
- TestBitField();
- TestContextVariables();
- TestPrefixIncDec();
- TestScopedEnum();
- TestUnscopedEnum();
- TestTernaryOperator();
- TestSizeOf();
- TestBuiltinFunction_Log2();
- TestBuiltinFunction_findnonnull();
- TestArrayDereference();
- TestDereferencedType();
- TestMemberFunctionCall();
- TestCompositeAssignment();
- TestSideEffects();
- TestUniquePtr();
- TestSharedPtr();
- TestTypeComparison();
- TestTypeVsIdentifier();
- TestSeparateParsing();
-
- RegisterCtx rc;
- rc.TestRegisters();
-
- TestCharParsing();
- TestStringParsing();
-
- // BREAK HERE
-}
-
-} // namespace test_binary
-
-int main() { test_binary::main(); }
diff --git a/lldb/unittests/DIL/Inputs/test_extern.cpp b/lldb/unittests/DIL/Inputs/test_extern.cpp
deleted file mode 100644
index d97c06f51f44e..0000000000000
--- a/lldb/unittests/DIL/Inputs/test_extern.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-//===-- test_extern.cpp --------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-int externGlobalVar = 0x00C0FFEE;
diff --git a/lldb/unittests/DIL/Runner.cpp b/lldb/unittests/DIL/Runner.cpp
deleted file mode 100644
index 5524caab43ad2..0000000000000
--- a/lldb/unittests/DIL/Runner.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-//===-- Runner.cpp --------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "Runner.h"
-
-#include <fstream>
-#include <iostream>
-#include <string>
-
-#include "lldb/API/SBBreakpoint.h"
-#include "lldb/API/SBBreakpointLocation.h"
-#include "lldb/API/SBCommandInterpreter.h"
-#include "lldb/API/SBCommandReturnObject.h"
-#include "lldb/API/SBDebugger.h"
-#include "lldb/API/SBDefines.h"
-#include "lldb/API/SBEvent.h"
-#include "lldb/API/SBFileSpec.h"
-#include "lldb/API/SBFrame.h"
-#include "lldb/API/SBListener.h"
-#include "lldb/API/SBProcess.h"
-#include "lldb/API/SBTarget.h"
-#include "lldb/API/SBThread.h"
-#include "lldb/API/SBValue.h"
-#include "lldb/lldb-enumerations.h"
-#include "lldb/lldb-types.h"
-
-#ifdef CONFIG_VALGRIND
-// Running a process under Valgrind can be extremely slow.
-const uint32_t kWaitForEventTimeout = 30;
-#else
-// Running a process can be slow when built with sanitizers.
-const uint32_t kWaitForEventTimeout = 5;
-#endif
-
-int FindBreakpointLine(const std::string &file_path,
- const std::string &break_line) {
- // Read the source file to find the breakpoint location.
- std::ifstream infile(file_path);
- std::string line;
- int line_num = 1;
- while (std::getline(infile, line)) {
- if (line.find(break_line) != std::string::npos) {
- return line_num;
- }
- ++line_num;
- }
-
- std::cerr << "Can't find the breakpoint location." << std::endl;
- exit(1);
-}
-
-std::string filename_of_source_path(const std::string &source_path) {
- auto idx = source_path.find_last_of("/\\");
- if (idx == std::string::npos) {
- idx = 0;
- } else {
- idx++;
- }
-
- return source_path.substr(idx);
-}
-
-lldb::SBProcess LaunchTestProgram(lldb::SBDebugger debugger,
- const std::string &source_path,
- const std::string &binary_path,
- const std::string &break_line) {
- auto target = debugger.CreateTarget(binary_path.c_str());
-
- auto source_file = filename_of_source_path(source_path);
-
- const char *argv[] = {binary_path.c_str(), nullptr};
-
- auto bp = target.BreakpointCreateByLocation(
- source_file.c_str(), FindBreakpointLine(source_path.c_str(), break_line));
- // Test programs don't perform any I/O, so current directory doesn't
- // matter.
- if (bp.GetNumLocations() == 0)
- std::cerr
- << "WARNING: Unable to resolve breakpoint to any actual locations."
- << std::endl;
- auto process = target.LaunchSimple(argv, nullptr, ".");
- if (!process.IsValid()) {
- std::cerr << "ERROR: Unable to launch process. Check that the path to the "
- "binary is valid."
- << std::endl;
- return process;
- }
- lldb::SBEvent event;
- auto listener = debugger.GetListener();
-
- while (true) {
- if (!listener.WaitForEvent(kWaitForEventTimeout, event)) {
- std::cerr
- << "Timeout while waiting for the event, kill the process and exit."
- << std::endl;
- process.Destroy();
- exit(1);
- }
-
- if (!lldb::SBProcess::EventIsProcessEvent(event)) {
- std::cerr << "Got some random event: "
- << lldb::SBEvent::GetCStringFromEvent(event) << std::endl;
- continue;
- }
-
- auto state = lldb::SBProcess::GetStateFromEvent(event);
- if (state == lldb::eStateInvalid) {
- std::cerr << "process event: "
- << lldb::SBEvent::GetCStringFromEvent(event) << std::endl;
- continue;
- }
-
- if (state == lldb::eStateExited) {
- std::cerr << "Process exited: " << process.GetExitStatus() << std::endl;
- process.Destroy();
- exit(1);
- }
-
- if (state != lldb::eStateStopped) {
- continue;
- }
-
- auto thread = process.GetSelectedThread();
- auto stopReason = thread.GetStopReason();
-
- if (stopReason != lldb::eStopReasonBreakpoint) {
- continue;
- }
-
- auto bpId =
- static_cast<lldb::break_id_t>(thread.GetStopReasonDataAtIndex(0));
- if (bpId != bp.GetID()) {
- std::cerr << "Stopped at unknown breakpoint: " << bpId << std::endl
- << "Now killing process and exiting" << std::endl;
- process.Destroy();
- exit(1);
- }
-
- return process;
- }
-}
diff --git a/lldb/unittests/DIL/Runner.h b/lldb/unittests/DIL/Runner.h
deleted file mode 100644
index f2723518e8ac1..0000000000000
--- a/lldb/unittests/DIL/Runner.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//===-- Runner.h --------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_DIL_RUNNER_H_
-#define LLDB_DIL_RUNNER_H_
-
-#include <string>
-
-#include "lldb/API/SBDebugger.h"
-#include "lldb/API/SBProcess.h"
-
-lldb::SBProcess LaunchTestProgram(lldb::SBDebugger debugger,
- const std::string &source_path,
- const std::string &binary_path,
- const std::string &break_line);
-
-#endif // LLDB_DIL_RUNNER_H_
\ No newline at end of file
diff --git a/lldb/unittests/ValueObject/CMakeLists.txt b/lldb/unittests/ValueObject/CMakeLists.txt
index 20c10532ec14c..6ef0091647a59 100644
--- a/lldb/unittests/ValueObject/CMakeLists.txt
+++ b/lldb/unittests/ValueObject/CMakeLists.txt
@@ -1,5 +1,6 @@
add_lldb_unittest(LLDBValueObjectTests
DumpValueObjectOptionsTests.cpp
+ DILLexerTests.cpp
DynamicValueObjectLocalBuffer.cpp
LINK_LIBS
diff --git a/lldb/unittests/DIL/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp
similarity index 100%
rename from lldb/unittests/DIL/DILLexerTests.cpp
rename to lldb/unittests/ValueObject/DILLexerTests.cpp
>From 336d59faeda26e9038c42267ee1e1330009cacbd Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 10 Apr 2025 22:08:35 +0500
Subject: [PATCH 4/4] Remove array conversion, cut down evaluation of & and *.
---
lldb/include/lldb/ValueObject/DILAST.h | 3 -
lldb/include/lldb/ValueObject/DILEval.h | 28 -----
lldb/source/ValueObject/DILEval.cpp | 118 +++---------------
.../TestFrameVarDILPointerArithmetic.py | 17 ++-
.../var-dil/basics/PointerArithmetic/main.cpp | 2 +
5 files changed, 32 insertions(+), 136 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 323ebe8dd49ec..d00f726115757 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -51,8 +51,6 @@ class ASTNode {
virtual llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const = 0;
- virtual bool is_rvalue() const { return false; }
-
uint32_t GetLocation() const { return m_location; }
NodeKind GetKind() const { return m_kind; }
@@ -97,7 +95,6 @@ class UnaryOpNode : public ASTNode {
m_rhs(std::move(rhs)) {}
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
- bool is_rvalue() const override { return m_kind != UnaryOpKind::Deref; }
UnaryOpKind kind() const { return m_kind; }
ASTNode *rhs() const { return m_rhs.get(); }
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 0080f4dba9291..b1dd3fdb49739 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -38,18 +38,6 @@ lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref,
lldb::DynamicValueType use_dynamic,
CompilerType *scope_ptr = nullptr);
-class FlowAnalysis {
-public:
- FlowAnalysis(bool address_of_is_pending)
- : m_address_of_is_pending(address_of_is_pending) {}
-
- bool AddressOfIsPending() const { return m_address_of_is_pending; }
- void DiscardAddressOf() { m_address_of_is_pending = false; }
-
-private:
- bool m_address_of_is_pending;
-};
-
class Interpreter : Visitor {
public:
Interpreter(lldb::TargetSP target, llvm::StringRef expr,
@@ -59,29 +47,13 @@ class Interpreter : Visitor {
llvm::Expected<lldb::ValueObjectSP> Evaluate(const ASTNode *node);
private:
- llvm::Expected<lldb::ValueObjectSP>
- EvaluateNode(const ASTNode *node, FlowAnalysis *flow = nullptr);
-
llvm::Expected<lldb::ValueObjectSP>
Visit(const IdentifierNode *node) override;
llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override;
- lldb::ValueObjectSP EvaluateDereference(lldb::ValueObjectSP rhs);
-
- FlowAnalysis *flow_analysis() { return m_flow_analysis_chain.back(); }
-
// Used by the interpreter to create objects, perform casts, etc.
lldb::TargetSP m_target;
llvm::StringRef m_expr;
- // Flow analysis chain represents the expression evaluation flow for the
- // current code branch. Each node in the chain corresponds to an AST node,
- // describing the semantics of the evaluation for it. Currently, flow analysis
- // propagates the information about the pending address-of operator, so that
- // combination of address-of and a subsequent dereference can be eliminated.
- // End of the chain (i.e. `back()`) contains the flow analysis instance for
- // the current node. It may be `nullptr` if no relevant information is
- // available, the caller/user is supposed to check.
- std::vector<FlowAnalysis *> m_flow_analysis_chain;
lldb::ValueObjectSP m_scope;
lldb::DynamicValueType m_default_dynamic;
std::shared_ptr<StackFrame> m_exe_ctx_scope;
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 8db20da4132a9..b8095f1169651 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -18,22 +18,6 @@
namespace lldb_private::dil {
-static lldb::ValueObjectSP
-ArrayToPointerConversion(lldb::ValueObjectSP valobj,
- std::shared_ptr<ExecutionContextScope> ctx) {
- assert(valobj->IsArrayType() &&
- "an argument to array-to-pointer conversion must be an array");
-
- uint64_t addr = valobj->GetLoadAddress();
- llvm::StringRef name = "result";
- ExecutionContext exe_ctx;
- ctx->CalculateExecutionContext(exe_ctx);
- return ValueObject::CreateValueObjectFromAddress(
- name, addr, exe_ctx,
- valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(),
- /* do_deref */ false);
-}
-
static lldb::ValueObjectSP LookupStaticIdentifier(
VariableList &variable_list, std::shared_ptr<StackFrame> exe_scope,
llvm::StringRef name_ref, llvm::StringRef unqualified_name) {
@@ -222,22 +206,9 @@ Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
: m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic),
m_exe_ctx_scope(frame_sp) {}
-llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *tree) {
+llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) {
// Evaluate an AST.
- auto value_or_error = EvaluateNode(tree);
-
- // Return the computed result-or-error.
- return value_or_error;
-}
-
-llvm::Expected<lldb::ValueObjectSP>
-Interpreter::EvaluateNode(const ASTNode *node, FlowAnalysis *flow) {
- // Set up the evaluation context for the current node.
- m_flow_analysis_chain.push_back(flow);
- // Traverse an AST pointed by the `node`.
auto value_or_error = node->Accept(this);
- // Cleanup the context.
- m_flow_analysis_chain.pop_back();
// Return the computed value-or-error. The caller is responsible for
// checking if an error occured during the evaluation.
return value_or_error;
@@ -265,33 +236,29 @@ Interpreter::Visit(const IdentifierNode *node) {
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const UnaryOpNode *node) {
- FlowAnalysis rhs_flow(
- /* address_of_is_pending */ node->kind() == UnaryOpKind::AddrOf);
-
Status error;
- auto rhs_or_err = EvaluateNode(node->rhs(), &rhs_flow);
+ auto rhs_or_err = Evaluate(node->rhs());
if (!rhs_or_err) {
return rhs_or_err;
}
lldb::ValueObjectSP rhs = *rhs_or_err;
- CompilerType rhs_type = rhs->GetCompilerType();
switch (node->kind()) {
case UnaryOpKind::Deref: {
- if (rhs_type.IsArrayType())
- rhs = ArrayToPointerConversion(rhs, m_exe_ctx_scope);
-
lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
if (dynamic_rhs)
rhs = dynamic_rhs;
- if (rhs->GetCompilerType().IsPointerType()) {
- if (rhs->GetCompilerType().IsPointerToVoid()) {
- return llvm::make_error<DILDiagnosticError>(
- m_expr, "indirection not permitted on operand of type 'void *'",
- node->GetLocation(), 1);
- }
- return EvaluateDereference(rhs);
+ CompilerType rhs_type = rhs->GetCompilerType();
+ if (!rhs_type.IsReferenceType() && !rhs_type.IsPointerType())
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, "not a pointer or reference type",
+ node->rhs()->GetLocation());
+
+ if (rhs_type.IsPointerToVoid()) {
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, "indirection not permitted on operand of type 'void *'",
+ node->GetLocation());
}
lldb::ValueObjectSP child_sp = rhs->Dereference(error);
if (error.Success())
@@ -300,68 +267,19 @@ Interpreter::Visit(const UnaryOpNode *node) {
return rhs;
}
case UnaryOpKind::AddrOf: {
- if (node->rhs()->is_rvalue()) {
- std::string errMsg =
- llvm::formatv("cannot take the address of an rvalue of type {0}",
- rhs_type.TypeDescription());
- return llvm::make_error<DILDiagnosticError>(m_expr, errMsg,
+ Status error;
+ lldb::ValueObjectSP value = rhs->AddressOf(error);
+ if (error.Fail()) {
+ return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
node->GetLocation());
}
- if (rhs->IsBitfield()) {
- return llvm::make_error<DILDiagnosticError>(
- m_expr, "address of bit-field requested", node->GetLocation());
- }
- // If the address-of operation wasn't cancelled during the evaluation of
- // RHS (e.g. because of the address-of-a-dereference elision), apply it
- // here.
- if (rhs_flow.AddressOfIsPending()) {
- Status error;
- lldb::ValueObjectSP value = rhs->AddressOf(error);
- if (error.Fail()) {
- return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
- node->GetLocation());
- }
- return value;
- }
- return rhs;
+ return value;
}
}
// Unsupported/invalid operation.
return llvm::make_error<DILDiagnosticError>(
- m_expr, "invalid ast: unexpected binary operator", node->GetLocation(),
- 1);
-}
-
-lldb::ValueObjectSP Interpreter::EvaluateDereference(lldb::ValueObjectSP rhs) {
- // If rhs is a reference, dereference it first.
- Status error;
- if (rhs->GetCompilerType().IsReferenceType())
- rhs = rhs->Dereference(error);
-
- assert(rhs->GetCompilerType().IsPointerType() &&
- "invalid ast: must be a pointer type");
-
- if (rhs->GetDerefValobj())
- return rhs->GetDerefValobj()->GetSP();
-
- CompilerType pointer_type = rhs->GetCompilerType();
- lldb::addr_t base_addr = rhs->GetValueAsUnsigned(0);
-
- llvm::StringRef name = "result";
- ExecutionContext exe_ctx(m_target.get(), false);
- lldb::ValueObjectSP value = ValueObject::CreateValueObjectFromAddress(
- name, base_addr, exe_ctx, pointer_type,
- /* do_deref */ false);
-
- // If we're in the address-of context, skip the dereference and cancel the
- // pending address-of operation as well.
- if (flow_analysis() && flow_analysis()->AddressOfIsPending()) {
- flow_analysis()->DiscardAddressOf();
- return value;
- }
-
- return value->Dereference(error);
+ m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
}
} // namespace lldb_private::dil
\ No newline at end of file
diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
index 1e90433e331f5..79008dab2131d 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
@@ -24,18 +24,25 @@ def test_dereference(self):
lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)
- is_32bit = self.process().GetAddressByteSize() == 4
self.runCmd("settings set target.experimental.use-DIL true")
self.expect_var_path("*p_int0", True, value="0")
self.expect_var_path("*cp_int5", True, value="5")
self.expect_var_path("*rcp_int0", True, type="const int *")
- self.expect_var_path("*array", value="0")
- self.expect_var_path(
- "&*p_null", value="0x00000000" if is_32bit else "0x0000000000000000"
- )
+ self.expect_var_path("*offset_p", True, value="5")
+ self.expect_var_path("*offset_pref", True, type="int *")
self.expect_var_path("**pp_int0", value="0")
self.expect_var_path("&**pp_int0", type="int *")
+ self.expect(
+ "frame var '*array'",
+ error=True,
+ substrs=["not a pointer or reference type"],
+ )
+ self.expect(
+ "frame var '&*p_null'",
+ error=True,
+ substrs=["doesn't have a valid address"],
+ )
self.expect(
"frame var '&*p_void'",
error=True,
diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp
index 1f2ac522ef0ef..b43b030fba049 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp
@@ -6,6 +6,8 @@ int main(int argc, char **argv) {
my_char_ptr my_p_char1 = p_char1;
int offset = 5;
+ int *offset_p = &offset;
+ int *&offset_pref = offset_p;
int array[10];
array[0] = 0;
array[offset] = offset;
More information about the lldb-commits
mailing list