Code to reproduce ================= static_assert(__builtin_clz(0)); Actual output (https://godbolt.org/z/v6v5nGxv8) =============================================== None. Expected output (Clang) ======================= <source>:1:15: error: static assertion expression is not an integral constant expression 1 | static_assert(__builtin_clz(0)); | ^~~~~~~~~~~~~~~~
This should fix it (but I am have not tested it either and I am not 100% sure we want/need this): ``` [apinski@xeond2 gcc]$ git diff fold-const-call.cc diff --git a/gcc/fold-const-call.cc b/gcc/fold-const-call.cc index 47bf8d64391..c9fa51e35ee 100644 --- a/gcc/fold-const-call.cc +++ b/gcc/fold-const-call.cc @@ -1031,7 +1031,7 @@ fold_const_call_ss (wide_int *result, combined_fn fn, const wide_int_ref &arg, tmp = TYPE_PRECISION (arg_type); else if (!CLZ_DEFINED_VALUE_AT_ZERO (SCALAR_INT_TYPE_MODE (arg_type), tmp)) - tmp = TYPE_PRECISION (arg_type); + return false; *result = wi::shwi (tmp, precision); return true; } @@ -1046,7 +1046,7 @@ fold_const_call_ss (wide_int *result, combined_fn fn, const wide_int_ref &arg, tmp = TYPE_PRECISION (arg_type); else if (!CTZ_DEFINED_VALUE_AT_ZERO (SCALAR_INT_TYPE_MODE (arg_type), tmp)) - tmp = TYPE_PRECISION (arg_type); + return false; *result = wi::shwi (tmp, precision); return true; } ``` Confirmed.
Note starting in GCC 14, it is better to use __builtin_clzg with the 2 arguments anyways so you can control exactly what value you want/need for 0.
I'm not sure it's reasonable to expect an error here. The C++ standard says nothing about whether __builtin_clz is a constant expression, obviously. So I'm changing this to severity=enhancement.
I would expect an error here because things that are undefined behavior are generally supposed to fail in constant expressions. I don't see a good reason why builtins should be exempt from that rule. The lack of diagnostic has cost me a few minutes of debugging yesterday. I had a static_assert: > static_assert(my_function(0u) == 32); This succeeded at compile time, but my_function(0) would return 0 at run-time as a result of passing through to __builtin_clz. UBSan may have caught it, but regardless, it's not sane to have different results inside/outside constant expressions like that.
You can reproduce this as follows: > static_assert(__builtin_clz(0u) == 32); > > unsigned x = 0; > > int main() { > return __builtin_clz(x); > } For base x86_64, GCC emits: (https://godbolt.org/z/nqzYrTWd1) > main: > bsr eax, DWORD PTR x[rip] > xor eax, 31 > ret > x: > .zero 4 Even though __builtin_clz(0u) == 32 passes, this program returns 63. This is obviously not in the interest of the developer, regardless of what the standard mandates.