[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
Wed Jun 24 07:50:47 PDT 2026
https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/204160
>From c53475c5753e069df47a1a6d2b48422d4bee43fb 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 | 12 +-
.../evaluation/eval_sublist_aggregates.cpp | 136 ++++++++++++++++++
.../eval_sublist_aggregates_addresses.cpp | 54 +++++++
3 files changed, 198 insertions(+), 4 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 c15313ac54c72..805a696ae65b7 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectMatch.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectMatch.py
@@ -193,9 +193,14 @@ def _get_dict_actual_result(
None,
)
)
- sub_expect_results[sub_expect] = DebuggerExpectMatch(
- self.expect, sub_expected, value, self.provisional_match_context
- )
+ if value is None:
+ sub_expect_results[sub_expect] = DebuggerExpectMatch(
+ self.expect, None, None, self.provisional_match_context
+ )
+ else:
+ sub_expect_results[sub_expect] = get_expect_match(
+ self.expect, sub_expected, value, self.provisional_match_context
+ )
match_result = MatchResult.from_bools(
any(
result.match_result == MatchResult.TRUE
@@ -208,7 +213,6 @@ def _get_dict_actual_result(
)
return sub_expect_results, match_result
-
def _get_match_distance(self) -> float:
if self.match_result == MatchResult.TRUE:
return 0.0
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