#include <iostream> auto consteval CONSTEVAL(auto x) { return x; } constexpr bool isconstant() { return std::is_constant_evaluated(); } int main() { // Tested via godbolt.org using the following compilers/flags: // GCC x86-64 -std=c++20 (13.3.0, 14.1.0, and 15.0.1 20250306) // Clang x86-64 -std=c++20 (head) // MSVC x64 /std:c++20 (head) // Working case: // GCC (all tested versions), Clang, MSVC: prints 1 constexpr int n { CONSTEVAL(isconstant()) }; std::cout << n << '\n'; // Broken case: // Clang and MSVC: prints 1 // GCC: -Ox (when x == 0) prints 1 on GCC (all tested versions) // GCC: -Ox (when x != 0) prints 1 on GCC 13.3.0 // GCC: -Ox (when x != 0) prints 0 on GCC 14.1.0 and 15.0.1 20250306 std::cout << CONSTEVAL(isconstant()) << '\n'; return 0; }
I am not even sure this is not incorrect.
Reduced: auto consteval consteval_id(auto x) { return x; } int main() { bool b = consteval_id(__builtin_is_constant_evaluated()); if (!b) __builtin_abort(); } Changed with r14-4140 "c++: Move consteval folding to cp_fold_r"
.
As consteval bool foo (bool x) { return x; } constexpr bool bar () { if consteval { return true; } else { return false; } } int main () { bool a = false; a = foo (bar ()); if (!a) __builtin_abort (); bool b = foo (bar ()); if (!b) __builtin_abort (); } shows, the bug is cxx_eval_call_expression, when we are doing non-manifestly constant-evaluated constexpr evaluation, we set /* Make sure we fold std::is_constant_evaluated to true in an immediate function. */ if (DECL_IMMEDIATE_FUNCTION_P (fun)) call_ctx.manifestly_const_eval = mce_true; but evaluate arguments of the immediate function before that. In the a case, there is no constant evaluation done before cp_fold_function and so the evaluation is correct in that case.
Created attachment 60677 [details] gcc15-pr119150.patch Untested fix.
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:ebf6e6241f5658a3cae462b1314f4a8f2bc71760 commit r15-8014-gebf6e6241f5658a3cae462b1314f4a8f2bc71760 Author: Jakub Jelinek <jakub@redhat.com> Date: Thu Mar 13 00:42:54 2025 +0100 c++: Evaluate immediate invocation call arguments with mce_true [PR119150] Since Marek's r14-4140 which moved immediate invocation evaluation from build_over_call to cp_fold_r, the following testcase is miscompiled. The a = foo (bar ()); case is actually handled right, that is handled in cp_fold_r and the whole CALL_EXPR is at that point evaluated by cp_fold_immediate_r with cxx_constant_value (stmt, tf_none); and that uses mce_true for evaluation of the argument as well as the actual call. But in the bool b = foo (bar ()); case we actually try to evaluate this as non-manifestly constant-evaluated. And while /* Make sure we fold std::is_constant_evaluated to true in an immediate function. */ if (DECL_IMMEDIATE_FUNCTION_P (fun)) call_ctx.manifestly_const_eval = mce_true; ensures that if consteval and __builtin_is_constant_evaluated () is true inside of that call, this happens after arguments to the function have been already constant evaluated in cxx_bind_parameters_in_call. The call_ctx in that case also includes new call_ctx.call, something that shouldn't be used for the arguments, so the following patch just arranges to call cxx_bind_parameters_in_call with manifestly_constant_evaluated = mce_true. 2025-03-13 Jakub Jelinek <jakub@redhat.com> PR c++/119150 * constexpr.cc (cxx_eval_call_expression): For DECL_IMMEDIATE_FUNCTION_P (fun) set manifestly_const_eval in new_ctx and new_call to mce_true and set ctx to &new_ctx. * g++.dg/cpp2a/consteval41.C: New test.
Fixed for 15+. Unsure about backports.
The releases/gcc-14 branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:13fd7c971fb573b2f1721c3684b34a65133b7085 commit r14-11469-g13fd7c971fb573b2f1721c3684b34a65133b7085 Author: Jakub Jelinek <jakub@redhat.com> Date: Thu Mar 13 00:42:54 2025 +0100 c++: Evaluate immediate invocation call arguments with mce_true [PR119150] Since Marek's r14-4140 which moved immediate invocation evaluation from build_over_call to cp_fold_r, the following testcase is miscompiled. The a = foo (bar ()); case is actually handled right, that is handled in cp_fold_r and the whole CALL_EXPR is at that point evaluated by cp_fold_immediate_r with cxx_constant_value (stmt, tf_none); and that uses mce_true for evaluation of the argument as well as the actual call. But in the bool b = foo (bar ()); case we actually try to evaluate this as non-manifestly constant-evaluated. And while /* Make sure we fold std::is_constant_evaluated to true in an immediate function. */ if (DECL_IMMEDIATE_FUNCTION_P (fun)) call_ctx.manifestly_const_eval = mce_true; ensures that if consteval and __builtin_is_constant_evaluated () is true inside of that call, this happens after arguments to the function have been already constant evaluated in cxx_bind_parameters_in_call. The call_ctx in that case also includes new call_ctx.call, something that shouldn't be used for the arguments, so the following patch just arranges to call cxx_bind_parameters_in_call with manifestly_constant_evaluated = mce_true. 2025-03-13 Jakub Jelinek <jakub@redhat.com> PR c++/119150 * constexpr.cc (cxx_eval_call_expression): For DECL_IMMEDIATE_FUNCTION_P (fun) set manifestly_const_eval in new_ctx and new_call to mce_true and set ctx to &new_ctx. * g++.dg/cpp2a/consteval41.C: New test. (cherry picked from commit ebf6e6241f5658a3cae462b1314f4a8f2bc71760)
Fixed for 14.3+ as well.
Now that's service! :) Thanks Jakub and everyone else who contributed.