Bug 84516

Summary: bitfield temporaries > 32-bits have wrong type
Product: gcc Reporter: Greg Miller <gnu>
Component: c++Assignee: Not yet assigned to anyone <unassigned>
Status: NEW ---    
Severity: normal CC: webrown.cpp
Priority: P3 Keywords: rejects-valid, wrong-code
Version: 7.3.1   
Target Milestone: ---   
See Also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70733
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87547
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95009
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed: 2018-10-08 00:00:00
Bug Depends on: 95009    
Bug Blocks: 94058    

Description Greg Miller 2018-02-22 15:07:32 UTC
(Disclaimer: I'm not a compiler guy, so I may accidentally use the wrong terminology)

Example: https://godbolt.org/g/brhTMw


#include <iostream>
struct A {
    long x : 32;
    long y : 33;
};
int main() {
    A a;
    std::cout << a.x;  // OK
    std::cout << a.y;  // OK
    std::cout << +a.x;  // OK
    std::cout << +a.y;  // BREAKS on gcc
}

The problem is the last line. The type of the expression `+a.y` is 'long int:33', which is not a type that operator<<() has an overload for. The type of the expression `a.y` is 'long int' and so op<<() has an overload and works.

This issue seems to be affected by

(a) The size of the bit field; <= 32 bits works, > 32 bits breaks.
(b) Whether the bitfield is used in an expression producing a temporary; >32-bit size AND a temporary breaks, <= 32-bit size and an lvalue works.

I confirmed this behavior on x86-64 gcc trunk using godbolt.org. (link again: https://godbolt.org/g/brhTMw)
Comment 1 Greg Miller 2018-02-22 15:39:42 UTC
The issue is not related to iostream. So, here's perhaps a simpler reproduction example that may focus more on the issue at hand.

Link: https://godbolt.org/g/vA2rPN

struct A {
    long x : 32;
    long y : 33;
};

void F(int) {}
void F(long) {}
template <typename T>
void F(T) = delete;

int main() {
    A a;
    F(a.x);   // Calls F(long)
    F(+a.x);  // Calls F(int)
    F(a.y);   // Calls F(long)
    F(+a.y);  // error: use of deleted function 'void F(T) [with T = long int:33]'
}

Here we can see that the type of the expression `+a.y` is `long int:33`, which I suspect is a problem. I think the type of that expression should be `long int`.
Comment 2 jsm-csl@polyomino.org.uk 2018-02-22 21:25:07 UTC
See also bug 70733, another bug with these types being user-exposed for 
bit-fields for C++.  For C++ (unlike C), the existence of these types 
internally in the compiler should never be user-visible, because bit-field 
width is explicitly not part of the type for C++.
Comment 3 Jonathan Wakely 2018-10-08 10:42:47 UTC
This seems to be related to the following rule in [conv.prom]:

> A prvalue for an integral bit-field (10.3.10) can be converted to a prvalue of type int if int can represent all the values of the bit-field; otherwise, it can be converted to unsigned int if unsigned int can represent all the values of the bit-field. If the bit-field is larger yet, no integral promotion applies to it.

But the fact no promotion applies should mean the type is unchanged, so is just long, not long:33.
Comment 4 Andrew Pinski 2021-12-03 08:23:02 UTC
There is most likely a missing call to unlowered_expr_type somewhere, most likely in finish_unary_op_expr.