Details

The integer promotions apply to bitfields as well, but
rather oddly. If you have a bitfield with a lower width
than int, then the type of the member expression will
be int regardless of the type of the bitfield member.
This means that you can effectively get 'type demotion'
in a bitfield member expression.

However, when analyzing format string types, we
would look through argument promotions like integer
promotion. Looking through bitfield demotion means
that we would get the wrong type when analyzing,
hiding -Wformat issues.

This patch fixes this so that we only explicitly look
through integer and floating point promotion where
the result type is actually a promotion.

The test currently does not have a target triple, so this is likely to fail on some bots. Rather than tie this entire test to one architecture, I'd split the bit-field tests out into a separate file and pick an explicit triple that meets this assumption.

I'm not entirely sure what you mean. Are you referring to the type produced by isPromotableBitField? The source type of that promotion is what we don't want to see in these implicit casts.

We don't want to look through promotions here if we promoted from a type which was the result of a bitfield promotion, and that bitfield promotion was from a higher ranked type to a lower ranked type. so, if we have a bitfield of type short, then promoting that to int is fine, and we will give a warning for the short. But if the type of the bitfield is long, it could be promoted to int. However, the format specifier warning will look through these promotions and think that we passed an expression of long to the function even though it was int.

That gcc godbolt is a bit odd. The type of the bitfield expression in the C++ example is long and not int, but in Clang, it's clearly being converted. If I change the example a bit, we get this warning:

C++ disallows the rank conversion from int to long as well. [conv.prom]p1 does not apply because long int has a higher rank than int, but [conv.prom]p5 allows the promotion if the range of values is identical between the two types.

C makes this UB in several ways -- you can't have a bit-field whose type is something other than int, unsigned int, or _Bool (6.7.2.1p5) or promoting from types other than those (6.3.1.1p2), but otherwise matches the C++ behavior in terms of promotion (including the rank conversion).

You may have to dig further into what Clang is doing, but I would guess that the diagnostics should be triggered in both C and C++ similarly.

Ultimately, I'd like to see tests for cases where sizeof(int) == sizeof(long), sizeof(int) != sizeof(long), and variants for C and C++ of each.

I'm not sure the warning should trigger in C++; the behavior is correct there. The expression in those cases should be of type long, not int. The bitfield promotions in C++ say that values _can_ be promoted if the value fits in int, but the rules in C say that the value _is_ promoted.

The strange promotion behavior only occurs in C because of the issue with bitfields larger than int. It's not really permitted according to the standard, but it's supported anyway to match C++. Though, it ends up not matching C++ due to these promotion differences.

I'll add tests for the different int and long sizes, though the only case where it would make a difference would be if int was larger than 32 bits, which it isn't on any target.