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 <[email protected]> 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] +... +*/ _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
