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?
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.