Skip to content

CHECK_EQUAL: fix array != array compare under C++26#1886

Open
dale-stewart wants to merge 1 commit into
cpputest:masterfrom
dale-stewart:fix/check-equal-cpp26-array-comparison
Open

CHECK_EQUAL: fix array != array compare under C++26#1886
dale-stewart wants to merge 1 commit into
cpputest:masterfrom
dale-stewart:fix/check-equal-cpp26-array-comparison

Conversation

@dale-stewart
Copy link
Copy Markdown

Problem

C++26 [over.match.oper]/p10 makes array == array (and array != array) ill-formed. CHECK_EQUAL_LOCATION expanded its two side-effect-detection probes literally as (operand) != (operand). As soon as either operand was an array — most commonly a string literal in CHECK_EQUAL("text", x) — the inner self-compare became a forbidden array-vs-array compare.

The same pattern is also a -Wdeprecated-array-compare error under C++20+ when -Werror is on. CppUTest's existing tests don't exercise the array-operand path, so this never showed up in CI; it surfaces immediately in any consuming project that does.

Fix

A small CppUTestPrivate::checkEqualSelfNotEqual<T>(T, T) helper that takes both arguments by value, so any array decays to a pointer before the comparison runs. The two side-effect probes ((actual) != (actual) and (expected) != (expected)) route through it. Both arguments come from the same operand expression, so T is identical on both sides — -Wsign-compare cannot fire.

The outer (expected) != (actual) is intentionally left at the macro call site. Routing it through a function template would lose -Wsign-compare's constant-fits exception that lets idioms like CHECK_EQUAL(0, some_unsigned) build clean today.

Behavior is unchanged for every non-array operand. For arrays, the resulting pointer-address self-compare matches the historical C++03 behavior (always false, since the same array's decayed pointer equals itself).

Test

Added CHECK_EQUAL_compilesWithArrayOperand in TestUTestMacro.cpp. Without the fix, the test TU emits -Wdeprecated-array-compare under C++20+ with -Werror, and is ill-formed at C++26 regardless of warning flags. With the fix it compiles at every standard from C++98 through C++26.

CI coverage

Verified by the existing -Wall -Werror jobs (Make Defaults, CMake Linux GNU 17/20/23, etc.) — -Wdeprecated-array-compare is on by default at C++20+ and gates the relevant condition. A future PR could add a C++26 job once compiler C++26 support stabilizes; this fix doesn't depend on that addition.

Compatibility

Works at C++98 through C++26. No new headers included. The helper is a plain function template — no <type_traits>, no if constexpr, no forwarding refs.

C++26 [over.match.oper]/p10 makes array == array (and array != array)
ill-formed. CHECK_EQUAL_LOCATION expanded its two side-effect-detection
probes literally as (operand) != (operand). As soon as either operand
was an array (most often a string literal in CHECK_EQUAL("text", x)),
the inner self-compare became a forbidden array-vs-array compare. The
same pattern is also a -Wdeprecated-array-compare error under C++20+
when -Werror is on.

Add a CppUTestPrivate::checkEqualSelfNotEqual<T>(T, T) helper that
takes both arguments by value, so any array decays to a pointer
before the comparison runs. The two side-effect probes
((actual) != (actual) and (expected) != (expected)) are routed
through it. Both arguments come from the same operand expression, so
T is identical on both sides; -Wsign-compare cannot fire.

The outer (expected) != (actual) is intentionally left at the macro
call site. Routing it through a function template would lose
-Wsign-compare's constant-fits exception that lets idioms like
CHECK_EQUAL(0, some_unsigned) build clean today.

Behavior is unchanged for every non-array operand. For arrays, the
resulting pointer-address self-compare matches the historical C++03
behavior (always false, since the same array's decayed pointer
equals itself).

Add a regression test exercising the array-operand path. Without the
helper, the test TU is ill-formed under C++26 and emits
-Wdeprecated-array-compare under C++20+; with the helper it compiles
at every standard from C++98 through C++26.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant