For context, I on a 64-bit (AMD64) little-endian system and am using g++ -std=gnu++23 -O3 -Wall
to compile my code, if it makes any difference.
The following code snippet all shows some seemingly pathological behaviour: the warning "left shift count >= width of type" kicks in later than I would expect (partially explained here), but does not seem to do full integer promotion as I would expect either.
#include <cstdint>
#include <iostream>
int main()
{
// any value will cause the confusing behaviour, of course
uint8_t a = 5;
a <<= 7;
// 128, as expected
std::cout << static_cast<int>(a) << '\n';
uint8_t b = 5;
b <<= 8;
// 0, as expected, if slightly dubious (8 = width of type, right?)
std::cout << static_cast<int>(b) << '\n';
uint8_t c = 5;
c <<= 16;
// 0, as expected, assuming a promotion to a larger unsigned int type
std::cout << static_cast<int>(c) << '\n';
uint8_t d = 5;
d <<= 31;
// 0, as expected, assuming a promotion to a larger unsigned int type
std::cout << static_cast<int>(d) << '\n';
uint8_t e = 5;
e <<= 64;
/*
raises a warning, rather understandably
(I suppose it isn't aware of __uint128_t?)
*/
// still outputs 0, although I assume this is undefined behaviour
std::cout << static_cast<int>(e) << '\n';
uint8_t f = 5;
f <<= 32;
/*
raises a warning for some reason
(shouldn't it just promote to uint64_t?)
*/
// still outputs 0, although I assume this is undefined behaviour
std::cout << static_cast<int>(f) << '\n';
}
What is going on?
Why does "left shift count >= width of type" seemingly kick in late?
It does not "kick in late". It kicks in exactly when it intends to.
I suppose it isn't aware of __uint128_t?
It is aware of __uint128_t
. Integer promotion does not promote to __int128_t
.
shouldn't it just promote to uint64_t?
No.
The rules of integral promotions are https://en.cppreference.com/w/cpp/language/implicit_conversion#Promotion_from_integral_types .
[...] val can be converted to a prvalue of the first of the following types that can represent all the values of its underlying type:
int unsigned int long unsigned long long long unsigned long long the underlying type of T
Assuming normal system, uint8_t
is unsigned char
and int
has 32 bits. uint8_t
is promoted to int
, as int
can represent all values. And int
on your system has width 32 bits, thus the warning on 32 and 64. It is not promoted to uint64_t
, but to int
.