c++g++c++23compiler-bug

consteval influences how an expression in a function is classified/evaluated?


I have the following code:

#include <string_view>
#include <iostream>
#include <cstring>

consteval const char* compile_time_trim(const std::string_view& s) noexcept{
    return s.data() + s.find("/app/") + strlen("/app/");
}

constexpr const char* best_effort_trim(const std::string_view& s) noexcept{
    if consteval {
        return compile_time_trim(s);
    }
    else {
        return s.data();
    }
}

int main() {
    std::cout << best_effort_trim(__FILE__) << std::endl;
}

And GCC 14.1 emitted error:

<source>: In function 'int main()':
<source>:19:34:   in 'constexpr' expansion of 'best_effort_trim(std::basic_string_view<char>(((const char*)"<source>")))'
<source>:11:33: error: call to consteval function 'compile_time_trim((* & s))' is not a constant expression
   11 |         return compile_time_trim(s);
      |                ~~~~~~~~~~~~~~~~~^~~
In file included from <source>:1:
<source>:19:34:   in 'constexpr' expansion of 'best_effort_trim(std::basic_string_view<char>(((const char*)"<source>")))'
<source>:11:33:   in 'constexpr' expansion of 'compile_time_trim((* & s))'
<source>:6:18:   in 'constexpr' expansion of '(& s)->std::basic_string_view<char>::data()'
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/string_view:290:22: error: '*(const std::basic_string_view<char>*)this' is not a constant expression
  290 |       { return this->_M_str; }
      |                ~~~~~~^~~~~~
Compiler returned: 1

However, if I change constexpr const char* best_effort_trim( to consteval const char* best_effort_trim(, GCC has no error.

Although the macro __FILE__ is a compile time constant, but it seems like if it is passed into a constexpr function, s would be treated as a non-constant (even thought it could be constructed at compile time), but why?

But then if s is considered a non-constant expression, I thought return compile_time_trim(s); won't even be evaluated. But the error suggests otherwise. Why?

Anyone can explain in detail what's going on?


Solution

  • This is a confirmed GCC bug. return compile_time_trim(s); should not be executed in your example.

    GCC is wrong because an if consteval block is executed when it shouldn't be. [stmt.if] p5 states:

    If a consteval if statement is evaluated in a context that is manifestly constant-evaluated, the first substatement is executed.

    This term is defined as follows:

    An expression or conversion is manifestly constant-evaluated if it is:

    • a constant-expression, or
    • the condition of a constexpr if statement ([stmt.if]), or
    • an immediate invocation, or
    • the result of substitution into an atomic constraint expression to determine whether it is satisfied ([temp.constr.atomic]), or
    • the initializer of a variable that is usable in constant expressions or has constant initialization ([basic.start.static]).

    In the statement std::cout << best_effort_trim(__FILE__) << std::endl;, none of these conditions hold true, so no constant evaluation takes place. The bug seems to be related to GCC falsely doing constant evaluation when optimizations are enabled; the standard does not permit arbitrary constant evaluation like that.

    When you change best_effort_trim to consteval, the call is an immediate invocation, and is getting constant-evaluated, as it should.