[llvm-branch-commits] [llvm] [Dexter] Allow matching lists of values for aggregate members (PR #204160)
Stephen Tozer via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Jun 16 09:17:32 PDT 2026
https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/204160
>From c0c900557b5ad49b744f78f83593780e093bc747 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Tue, 16 Jun 2026 11:42:24 +0100
Subject: [PATCH] [Dexter] Allow matching lists of values for aggregate members
This patch slightly extends the matching of aggregate members to allow for
lists of expected values for individual members, functioning the same as
lists of expected values for scalar values.
---
.../dexter/dex/evaluation/ExpectMatch.py | 11 +-
.../evaluation/eval_sublist_aggregates.cpp | 136 ++++++++++++++++++
.../eval_sublist_aggregates_addresses.cpp | 54 +++++++
3 files changed, 198 insertions(+), 3 deletions(-)
create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/evaluation/eval_sublist_aggregates.cpp
create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/evaluation/eval_sublist_aggregates_addresses.cpp
diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectMatch.py b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectMatch.py
index 5bda99a9281f0..1ef9a41c6c8e4 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectMatch.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectMatch.py
@@ -144,9 +144,14 @@ def _get_actual_result(
None,
)
)
- sub_expect_results[sub_expect] = DebuggerExpectMatch(
- self.expect, sub_expected, value, self.match_context
- )
+ if value is None:
+ sub_expect_results[sub_expect] = DebuggerExpectMatch(
+ self.expect, None, None, self.match_context
+ )
+ else:
+ sub_expect_results[sub_expect] = get_expect_match(
+ self.expect, sub_expected, value, self.match_context
+ )
match_result = MatchResult.from_bools(
any(
result.match_result == MatchResult.TRUE
diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/evaluation/eval_sublist_aggregates.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/evaluation/eval_sublist_aggregates.cpp
new file mode 100644
index 0000000000000..cc1cda551b268
--- /dev/null
+++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/evaluation/eval_sublist_aggregates.cpp
@@ -0,0 +1,136 @@
+// RUN: %dexter_regression_test_cxx_build %s -o %t
+// RUN: %dexter_regression_test_run --use-script --binary %t -- %s \
+// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-FORWARD
+// RUN: %dexter_regression_test_cxx_build %s -o %t -DREVERSE_TEST
+// RUN: %dexter_regression_test_run --use-script --binary %t -- %s \
+// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-REVERSE
+
+/// Check that the debugger is able to correctly evaluate lists of values for
+/// individual members of aggregates, and that doing so removes ordering
+/// constraints. in the first run P goes through the values:
+/// [{1, 2}, {3, 2}, {5, 2}, {5, 4}, {5, 6}, {7, 7}]
+/// In the second run P goes through the values:
+/// [{1, 2}, {1, 4}, {1, 6}, {3, 6}, {5, 6}, {7, 7}]
+/// Despite each run containing combinations of values that are not seen in the
+/// other, the test should pass, as we check the sequence of X and Y values
+/// individually rather than as a pair.
+
+struct Point {
+ int X;
+ int Y;
+};
+
+struct Rectangle {
+ Point TopLeft;
+ Point Size;
+};
+
+int main() {
+ Rectangle R{Point{1, 2}, Point{4, 4}};
+ // !dex_label start
+#ifndef REVERSE_TEST
+ R.TopLeft.X = 3;
+ R.TopLeft.X = 5;
+ R.Size.X += 2;
+ R.TopLeft.Y = 4;
+ R.TopLeft.Y = 6;
+#else
+ R.TopLeft.Y = 4;
+ R.TopLeft.Y = 6;
+ R.Size.X += 2;
+ R.TopLeft.X = 3;
+ R.TopLeft.X = 5;
+#endif
+ R.TopLeft = {7, 7};
+ R = {Point{0, 0}, Point{0, 0}};
+ return 0; // !dex_label end
+}
+
+// CHECK-FORWARD: Step 0:
+// CHECK-FORWARD: Matching nodes: [Value(R)={
+// CHECK-FORWARD-SAME: "TopLeft": { "X": 1, "Y": 2 },
+// CHECK-FORWARD-SAME: "Size": { "X": 4, "Y": 4 } }]
+// CHECK-FORWARD: Step 1:
+// CHECK-FORWARD: Matching nodes: [Value(R)={
+// CHECK-FORWARD-SAME: "TopLeft": { "X": 3, "Y": 2 },
+// CHECK-FORWARD-SAME: "Size": { "X": 4, "Y": 4 } }]
+// CHECK-FORWARD: Step 2:
+// CHECK-FORWARD: Matching nodes: [Value(R)={
+// CHECK-FORWARD-SAME: "TopLeft": { "X": 5, "Y": 2 },
+// CHECK-FORWARD-SAME: "Size": { "X": 4, "Y": 4 } }]
+// CHECK-FORWARD: Step 3:
+// CHECK-FORWARD: Matching nodes: [Value(R)={
+// CHECK-FORWARD-SAME: "TopLeft": { "X": 5, "Y": 2 },
+// CHECK-FORWARD-SAME: "Size": { "X": 6, "Y": 4 } }]
+// CHECK-FORWARD: Step 4:
+// CHECK-FORWARD: Matching nodes: [Value(R)={
+// CHECK-FORWARD-SAME: "TopLeft": { "X": 5, "Y": 4 },
+// CHECK-FORWARD-SAME: "Size": { "X": 6, "Y": 4 } }]
+// CHECK-FORWARD: Step 5:
+// CHECK-FORWARD: Matching nodes: [Value(R)={
+// CHECK-FORWARD-SAME: "TopLeft": { "X": 5, "Y": 6 },
+// CHECK-FORWARD-SAME: "Size": { "X": 6, "Y": 4 } }]
+// CHECK-FORWARD: Step 6:
+// CHECK-FORWARD: Matching nodes: [Value(R)={
+// CHECK-FORWARD-SAME: "TopLeft": { "X": 7, "Y": 7 },
+// CHECK-FORWARD-SAME: "Size": { "X": 6, "Y": 4 } }]
+
+// CHECK-REVERSE: Step 0:
+// CHECK-REVERSE: Matching nodes: [Value(R)={
+// CHECK-REVERSE-SAME: "TopLeft": { "X": 1, "Y": 2 },
+// CHECK-REVERSE-SAME: "Size": { "X": 4, "Y": 4 } }]
+// CHECK-REVERSE: Step 1:
+// CHECK-REVERSE: Matching nodes: [Value(R)={
+// CHECK-REVERSE-SAME: "TopLeft": { "X": 1, "Y": 4 },
+// CHECK-REVERSE-SAME: "Size": { "X": 4, "Y": 4 } }]
+// CHECK-REVERSE: Step 2:
+// CHECK-REVERSE: Matching nodes: [Value(R)={
+// CHECK-REVERSE-SAME: "TopLeft": { "X": 1, "Y": 6 },
+// CHECK-REVERSE-SAME: "Size": { "X": 4, "Y": 4 } }]
+// CHECK-REVERSE: Step 3:
+// CHECK-REVERSE: Matching nodes: [Value(R)={
+// CHECK-REVERSE-SAME: "TopLeft": { "X": 1, "Y": 6 },
+// CHECK-REVERSE-SAME: "Size": { "X": 6, "Y": 4 } }]
+// CHECK-REVERSE: Step 4:
+// CHECK-REVERSE: Matching nodes: [Value(R)={
+// CHECK-REVERSE-SAME: "TopLeft": { "X": 3, "Y": 6 },
+// CHECK-REVERSE-SAME: "Size": { "X": 6, "Y": 4 } }]
+// CHECK-REVERSE: Step 5:
+// CHECK-REVERSE: Matching nodes: [Value(R)={
+// CHECK-REVERSE-SAME: "TopLeft": { "X": 5, "Y": 6 },
+// CHECK-REVERSE-SAME: "Size": { "X": 6, "Y": 4 } }]
+// CHECK-REVERSE: Step 6:
+// CHECK-REVERSE: Matching nodes: [Value(R)={
+// CHECK-REVERSE-SAME: "TopLeft": { "X": 7, "Y": 7 },
+// CHECK-REVERSE-SAME: "Size": { "X": 6, "Y": 4 } }]
+
+// CHECK: total_watched_steps: 8
+// CHECK: correct_steps: 8
+// CHECK: incorrect_steps: 0
+// CHECK: partial_step_correctness: 8.0
+// CHECK: missing_var_steps: 0
+// CHECK: unexpected_value_steps: 0
+// CHECK: correct_step_coverage: 100.0% (8/8)
+// CHECK: seen_values: 15
+// CHECK: missing_values: 0
+
+/*
+---
+!where {lines: !range [!label start, !label end]}:
+ !value R:
+ - TopLeft:
+ - X: [1, 3, 5]
+ Y: [2, 4, 6]
+ - X: 7
+ Y: 7
+ Size:
+ - X: [4, 6]
+ Y: 4
+ - TopLeft:
+ X: 0
+ Y: 0
+ Size:
+ X: 0
+ Y: 0
+...
+*/
diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/evaluation/eval_sublist_aggregates_addresses.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/evaluation/eval_sublist_aggregates_addresses.cpp
new file mode 100644
index 0000000000000..a60a628758262
--- /dev/null
+++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/evaluation/eval_sublist_aggregates_addresses.cpp
@@ -0,0 +1,54 @@
+// RUN: %dexter_regression_test_cxx_build %s -o %t
+// RUN: %dexter_regression_test_run --use-script --binary %t -- %s \
+// RUN: | FileCheck %s
+
+/// Tests that expected value lists for aggregate members work as expected with
+/// !address expected values.
+
+/// Any of the expected values for First and Second could be selected for the
+/// first step match, so the matching order for the first step should be:
+/// 1. We attempt to match P.First->!address F, match succeeds and assigns
+/// F = &Arr[2].
+/// 2. We attempt to match P.Second->!address F, match fails as F == &Arr[2].
+/// 3. We attempt to match P.Second->!address S, match succeeds and assigns
+/// S = &Arr[4].
+/// From there, the remaining values should resolve correctly.
+
+struct PointerPair {
+ char *First;
+ char *Second;
+};
+
+void swapPtrs(char *&A, char *&B) {
+ char *Tmp = A;
+ A = B;
+ B = Tmp;
+}
+
+int main() {
+ char Arr[] = {0, 1, 2, 3, 4, 5, 6, 7};
+ PointerPair P = {&Arr[2], &Arr[4]};
+ // !dex_label start
+ P.Second += 1;
+ swapPtrs(P.First, P.Second);
+ return 0; // !dex_label end
+}
+
+// CHECK: total_watched_steps: 3
+// CHECK: correct_steps: 3
+// CHECK: incorrect_steps: 0
+// CHECK: partial_step_correctness: 3.0
+// CHECK: missing_var_steps: 0
+// CHECK: unexpected_value_steps: 0
+// CHECK: correct_step_coverage: 100.0% (3/3)
+// CHECK: seen_values: 5
+// CHECK: missing_values: 0
+
+/*
+---
+!where {lines: !range [!label start, !label end]}:
+ !value P:
+ First: [!address F, !address S + 1]
+ Second: [!address F, !address S, !address S + 1]
+...
+*/
More information about the llvm-branch-commits
mailing list