Bug 119150 - [14 Regression] Optimization causes function call arguments to consteval functions to not be manifestly constant-evaluated since r14-4140
Summary: [14 Regression] Optimization causes function call arguments to consteval func...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 14.1.0
: P2 normal
Target Milestone: 14.3
Assignee: Jakub Jelinek
URL:
Keywords: wrong-code
Depends on:
Blocks: constexpr
  Show dependency treegraph
 
Reported: 2025-03-06 20:13 UTC by Alex Pomeranz
Modified: 2025-03-31 22:07 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work: 13.3.0
Known to fail:
Last reconfirmed: 2025-03-07 00:00:00


Attachments
gcc15-pr119150.patch (913 bytes, patch)
2025-03-07 12:50 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Alex Pomeranz 2025-03-06 20:13:36 UTC
#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;
}
Comment 1 Drea Pinski 2025-03-06 22:15:15 UTC
I am not even sure this is not incorrect.
Comment 2 Patrick Palka 2025-03-06 23:00:07 UTC
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"
Comment 3 Drea Pinski 2025-03-07 00:21:52 UTC
.
Comment 4 Jakub Jelinek 2025-03-07 11:53:47 UTC
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.
Comment 5 Jakub Jelinek 2025-03-07 12:50:42 UTC
Created attachment 60677 [details]
gcc15-pr119150.patch

Untested fix.
Comment 6 GCC Commits 2025-03-12 23:44:37 UTC
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.
Comment 7 Jakub Jelinek 2025-03-25 17:32:31 UTC
Fixed for 15+.  Unsure about backports.
Comment 8 GCC Commits 2025-03-27 23:41:48 UTC
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)
Comment 9 Jakub Jelinek 2025-03-28 09:53:59 UTC
Fixed for 14.3+ as well.
Comment 10 Alex Pomeranz 2025-03-31 22:07:45 UTC
Now that's service! :)  Thanks Jakub and everyone else who contributed.