Message ID | 20231024161843.20031-1-polacek@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: error with bit-fields and scoped enums [PR111895] | expand |
On 10/24/23 12:18, Marek Polacek wrote: > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > -- >8 -- > Here we issue a bogus error: invalid operands of types 'unsigned char:2' > and 'int' to binary 'operator!=' when casting a bit-field of scoped enum > type to bool. > > In build_static_cast_1, perform_direct_initialization_if_possible returns > NULL_TREE, because the invented declaration T t(e) fails, which is > correct. So we go down to ocp_convert, which has code to deal with this > case: > /* We can't implicitly convert a scoped enum to bool, so convert > to the underlying type first. */ > if (SCOPED_ENUM_P (intype) && (convtype & CONV_STATIC)) > e = build_nop (ENUM_UNDERLYING_TYPE (intype), e); > but the SCOPED_ENUM_P is false since intype is <unnamed-unsigned:2>. > This could be fixed by using unlowered_expr_type. But then > c_common_truthvalue_conversion/CASE_CONVERT has a similar problem, and > unlowered_expr_type is a C++-only function. > > Rather than adding a dummy unlowered_expr_type to C, I think we should > follow [expr.static.cast]p3: "the lvalue-to-rvalue conversion is applied > to the bit-field and the resulting prvalue is used as the operand of the > static_cast." There are no prvalue bit-fields, so the l-to-r conversion > will get us an expression whose type is the enum. (I thought we didn't > need decay_conversion because that does a whole lot more but using it > would make sense to me too.) It's possible that we might want some of that more, particularly mark_rvalue_use; decay_conversion seems like the right answer. OK with that change. rvalue() would also make sense, though that seems to be missing a call to unlowered_expr_type at the moment. In fact, after "otherwise, it's the lvalue-to-rvalue conversion" in decay_conv should probably just be a call to rvalue, with missing bits added to the latter function. Jason
On Tue, Oct 24, 2023 at 04:46:02PM -0400, Jason Merrill wrote: > On 10/24/23 12:18, Marek Polacek wrote: > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > -- >8 -- > > Here we issue a bogus error: invalid operands of types 'unsigned char:2' > > and 'int' to binary 'operator!=' when casting a bit-field of scoped enum > > type to bool. > > > > In build_static_cast_1, perform_direct_initialization_if_possible returns > > NULL_TREE, because the invented declaration T t(e) fails, which is > > correct. So we go down to ocp_convert, which has code to deal with this > > case: > > /* We can't implicitly convert a scoped enum to bool, so convert > > to the underlying type first. */ > > if (SCOPED_ENUM_P (intype) && (convtype & CONV_STATIC)) > > e = build_nop (ENUM_UNDERLYING_TYPE (intype), e); > > but the SCOPED_ENUM_P is false since intype is <unnamed-unsigned:2>. > > This could be fixed by using unlowered_expr_type. But then > > c_common_truthvalue_conversion/CASE_CONVERT has a similar problem, and > > unlowered_expr_type is a C++-only function. > > > > Rather than adding a dummy unlowered_expr_type to C, I think we should > > follow [expr.static.cast]p3: "the lvalue-to-rvalue conversion is applied > > to the bit-field and the resulting prvalue is used as the operand of the > > static_cast." There are no prvalue bit-fields, so the l-to-r conversion > > will get us an expression whose type is the enum. (I thought we didn't > > need decay_conversion because that does a whole lot more but using it > > would make sense to me too.) > > It's possible that we might want some of that more, particularly > mark_rvalue_use; decay_conversion seems like the right answer. OK with that > change. Makes total sense, thank you. (I'd tested the version with decay_conversion and it worked fine.) > rvalue() would also make sense, though that seems to be missing a call to > unlowered_expr_type at the moment. In fact, after "otherwise, it's the > lvalue-to-rvalue conversion" in decay_conv should probably just be a call to > rvalue, with missing bits added to the latter function. Sounds good; I hope I'll get to it next week. I'm not going to make it part of this patch so that I can backport this one to 13 and leave the cleanup for trunk only. Marek
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index f3dc80c40cf..50427090e5d 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -8405,6 +8405,15 @@ build_static_cast_1 (location_t loc, tree type, tree expr, bool c_cast_p, return expr; if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR) expr = TREE_OPERAND (expr, 0); + /* [expr.static.cast]: "If the value is not a bit-field, the result + refers to the object or the specified base class subobject thereof; + otherwise, the lvalue-to-rvalue conversion is applied to the + bit-field and the resulting prvalue is used as the operand of the + static_cast." There are no prvalue bit-fields; the l-to-r conversion + will give us an object of the underlying type of the bit-field. We + can let convert_bitfield_to_declared_type convert EXPR to the desired + type. */ + expr = convert_bitfield_to_declared_type (expr); return ocp_convert (type, expr, CONV_C_CAST, LOOKUP_NORMAL, complain); } diff --git a/gcc/testsuite/g++.dg/cpp0x/scoped_enum12.C b/gcc/testsuite/g++.dg/cpp0x/scoped_enum12.C new file mode 100644 index 00000000000..1d10431e6dc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/scoped_enum12.C @@ -0,0 +1,8 @@ +// PR c++/111895 +// { dg-do compile { target c++11 } } + +enum class o_field : unsigned char { no, yes, different_from_s }; +struct fields { + o_field o : 2; +}; +bool func(fields f) { return static_cast<bool>(f.o); }