Aykhan Hagverdili
Aykhan Hagverdili

Reputation: 29985

static_assert expression is not an integral constant expression with __builtin_clz

I have the following code:

typedef unsigned char uchar;

constexpr int leaing_ones(uchar const u8) noexcept {
  return __builtin_clz(static_cast<uchar>(~static_cast<unsigned>(u8))) +
         sizeof(uchar) * 8 - sizeof(int) * 8;
}

int main() {
  static_assert(leaing_ones(0b0000'0000) == 0);
  static_assert(leaing_ones(0b1000'0000) == 1);
  static_assert(leaing_ones(0b1100'0000) == 2);
  static_assert(leaing_ones(0b1110'0000) == 3);
  static_assert(leaing_ones(0b1111'0000) == 4);
  static_assert(leaing_ones(0b1111'1000) == 5);
  static_assert(leaing_ones(0b1111'1000) == 5);
  static_assert(leaing_ones(0b1111'1100) == 6);
  static_assert(leaing_ones(0b1111'1110) == 7);
  static_assert(leaing_ones(0b1111'1111) == 8);
}

I tested these with clang 12 and GCC 11.1. The only problem is that clang specifically doesn't like the last assertation:

<source>:16:17: error: static_assert expression is not an integral constant expression
  static_assert(leaing_ones(0b1111'1111) == 8);
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As far as I can tell, I am not causing any kind of overflow, and GCC works fine with all of them. Is this a clang bug, or am I overlooking something?

Upvotes: 0

Views: 3653

Answers (2)

HolyBlackCat
HolyBlackCat

Reputation: 96699

Passing 0 to __builtin_clz is undefined behavior.

From GCC manual:

Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.

(bold mine)

Upvotes: 1

ShadowRanger
ShadowRanger

Reputation: 155516

__builtin_clz is undefined for an argument with value zero, and since you invert all the bits, 0b1111'1111 becomes the pattern of all zeroes. Your function will need to special case 0 to use an appropriate value.

Upvotes: 1

Related Questions