[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("&param"), 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("&param"), 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