From b7ced159dbb789c70d4c502393e06ffac8c0e23d Mon Sep 17 00:00:00 2001 From: Niclas Larsson Date: Thu, 4 Jun 2026 19:45:38 +0200 Subject: [PATCH] Avoid is_constructible recursion in OnceAction Both OnceAction compatibility traits, IsDirectlyCompatible and IsCompatibleAfterIgnoringArguments, check std::is_constructible before is_callable_r. On libc++ that order breaks. Evaluating is_constructible, ...> drives libc++'s tuple constructor SFINAE, which calls back into OnceAction's converting constructor and re-enters the conjunction while it is still incomplete. GCC <= 8 (e.g. QNX 7.1's gcc 8.3) rejects this with: incomplete type ... used in nested name specifier so EXPECT_CALL(m, f()).WillOnce(Return(...)) fails to compile. GCC >= 9 and every Clang accept the original order; libstdc++ is unaffected. Swapping the two checks fixes it: is_callable_r is cheap and non-recursive, so the conjunction short-circuits to false for the non-callable tuple before is_constructible is instantiated. Fixes #3947. --- googlemock/include/gmock/gmock-actions.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/googlemock/include/gmock/gmock-actions.h b/googlemock/include/gmock/gmock-actions.h index 94552cea2..4a3904f64 100644 --- a/googlemock/include/gmock/gmock-actions.h +++ b/googlemock/include/gmock/gmock-actions.h @@ -427,21 +427,21 @@ class [[nodiscard]] OnceAction final { // via StdFunctionAdaptor. template using IsDirectlyCompatible = internal::conjunction< - // It must be possible to capture the callable in StdFunctionAdaptor. - std::is_constructible::type, Callable>, // The callable must be compatible with our signature. internal::is_callable_r::type, - Args...>>; + Args...>, + // It must be possible to capture the callable in StdFunctionAdaptor. + std::is_constructible::type, Callable>>; // True iff we can use the given callable type via StdFunctionAdaptor once we // ignore incoming arguments. template using IsCompatibleAfterIgnoringArguments = internal::conjunction< - // It must be possible to capture the callable in a lambda. - std::is_constructible::type, Callable>, // The callable must be invocable with zero arguments, returning something // convertible to Result. - internal::is_callable_r::type>>; + internal::is_callable_r::type>, + // It must be possible to capture the callable in a lambda. + std::is_constructible::type, Callable>>; public: // Construct from a callable that is directly compatible with our mocked