Bug 84516 - bitfield temporaries > 32-bits have wrong type
Summary: bitfield temporaries > 32-bits have wrong type
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 7.3.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: rejects-valid, wrong-code
Depends on:
Blocks:
 
Reported: 2018-02-22 15:07 UTC by Greg Miller
Modified: 2018-10-08 10:44 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2018-10-08 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
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 joseph@codesourcery.com 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.