Message ID | 20240628230052.446907-1-polacek@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: DR2627, Bit-fields and narrowing conversions [PR94058] | expand |
On 6/28/24 7:00 PM, Marek Polacek wrote: > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? OK. > -- >8 -- > This DR (https://cplusplus.github.io/CWG/issues/2627.html) says that > even if we are converting from an integer type or unscoped enumeration type > to an integer type that cannot represent all the values of the original > type, it's not narrowing if "the source is a bit-field whose width w is > less than that of its type (or, for an enumeration type, its underlying > type) and the target type can represent all the values of a hypothetical > extended integer type with width w and with the same signedness as the > original type". > > DR 2627 > PR c++/94058 > PR c++/104392 > > gcc/cp/ChangeLog: > > * typeck2.cc (check_narrowing): Don't warn if the conversion isn't > narrowing as per DR 2627. > > gcc/testsuite/ChangeLog: > > * g++.dg/DRs/dr2627.C: New test. > * g++.dg/cpp0x/Wnarrowing22.C: New test. > * g++.dg/cpp2a/spaceship-narrowing1.C: New test. > * g++.dg/cpp2a/spaceship-narrowing2.C: New test. > --- > gcc/cp/typeck2.cc | 12 +++++ > gcc/testsuite/g++.dg/DRs/dr2627.C | 13 +++++ > gcc/testsuite/g++.dg/cpp0x/Wnarrowing22.C | 49 +++++++++++++++++++ > .../g++.dg/cpp2a/spaceship-narrowing1.C | 34 +++++++++++++ > .../g++.dg/cpp2a/spaceship-narrowing2.C | 26 ++++++++++ > 5 files changed, 134 insertions(+) > create mode 100644 gcc/testsuite/g++.dg/DRs/dr2627.C > create mode 100644 gcc/testsuite/g++.dg/cpp0x/Wnarrowing22.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing1.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing2.C > > diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc > index 7782f38da43..30a6fbe95c9 100644 > --- a/gcc/cp/typeck2.cc > +++ b/gcc/cp/typeck2.cc > @@ -1012,6 +1012,18 @@ check_narrowing (tree type, tree init, tsubst_flags_t complain, > if (TREE_CODE (ftype) == ENUMERAL_TYPE) > /* Check for narrowing based on the values of the enumeration. */ > ftype = ENUM_UNDERLYING_TYPE (ftype); > + /* Undo convert_bitfield_to_declared_type (STRIP_NOPS isn't enough). */ > + tree op = init; > + while (CONVERT_EXPR_P (op)) > + op = TREE_OPERAND (op, 0); > + /* Core 2627 says that we shouldn't warn when "the source is a bit-field > + whose width w is less than that of its type (or, for an enumeration > + type, its underlying type) and the target type can represent all the > + values of a hypothetical extended integer type with width w and with > + the same signedness as the original type". */ > + if (is_bitfield_expr_with_lowered_type (op) > + && TYPE_PRECISION (TREE_TYPE (op)) < TYPE_PRECISION (ftype)) > + ftype = TREE_TYPE (op); > if ((tree_int_cst_lt (TYPE_MAX_VALUE (type), > TYPE_MAX_VALUE (ftype)) > || tree_int_cst_lt (TYPE_MIN_VALUE (ftype), > diff --git a/gcc/testsuite/g++.dg/DRs/dr2627.C b/gcc/testsuite/g++.dg/DRs/dr2627.C > new file mode 100644 > index 00000000000..fe7f28613ca > --- /dev/null > +++ b/gcc/testsuite/g++.dg/DRs/dr2627.C > @@ -0,0 +1,13 @@ > +// DR 2627 - Bit-fields and narrowing conversions > +// { dg-do compile { target c++20 } } > + > +#include <compare> > + > +struct C { > + long long i : 8; > +}; > + > +void f() { > + C x{1}, y{2}; > + x.i <=> y.i; > +} > diff --git a/gcc/testsuite/g++.dg/cpp0x/Wnarrowing22.C b/gcc/testsuite/g++.dg/cpp0x/Wnarrowing22.C > new file mode 100644 > index 00000000000..dd30451a7cc > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp0x/Wnarrowing22.C > @@ -0,0 +1,49 @@ > +// DR 2627 - Bit-fields and narrowing conversions > +// PR c++/94058 > +// { dg-do compile { target c++11 } } > +// { dg-options "-Wno-error=narrowing" } > + > +using int64_t = __INT64_TYPE__; > +using int32_t = __INT32_TYPE__; > + > +struct A { > + int64_t i1 : __CHAR_BIT__; > + int64_t i2 : sizeof (int32_t) * __CHAR_BIT__ - 1; > + int64_t i3 : sizeof (int32_t) * __CHAR_BIT__; > + int64_t i4 : sizeof (int32_t) * __CHAR_BIT__ + 1; > + int64_t i5 : sizeof (int64_t) * __CHAR_BIT__ - 1; > + int64_t i6 : sizeof (int64_t) * __CHAR_BIT__; > +} a; > + > +int32_t i1{a.i1}; > +int32_t i2{a.i2}; > +int32_t i3{a.i3}; > +int32_t i4{a.i4}; // { dg-warning "narrowing conversion" } > +int32_t i5{a.i5}; // { dg-warning "narrowing conversion" } > +int32_t i6{a.i6}; // { dg-warning "narrowing conversion" } > + > +struct B { > + bool b1 : sizeof (bool) * __CHAR_BIT__; > + bool b2 : sizeof (bool); > +} b; > + > +signed char b1{b.b1}; > +signed char b2{b.b2}; > + > +enum E : int64_t { E1 }; > + > +struct C { > + E e1 : __CHAR_BIT__; > + E e2 : sizeof (int32_t) * __CHAR_BIT__ - 1; > + E e3 : sizeof (int32_t) * __CHAR_BIT__; > + E e4 : sizeof (int32_t) * __CHAR_BIT__ + 1; > + E e5 : sizeof (int64_t) * __CHAR_BIT__ - 1; > + E e6 : sizeof (int64_t) * __CHAR_BIT__; > +} c; > + > +int32_t e1{c.e1}; > +int32_t e2{c.e2}; > +int32_t e3{c.e3}; > +int32_t e4{c.e4}; // { dg-warning "narrowing conversion" } > +int32_t e5{c.e5}; // { dg-warning "narrowing conversion" } > +int32_t e6{c.e6}; // { dg-warning "narrowing conversion" } > diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing1.C > new file mode 100644 > index 00000000000..7769f950bed > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing1.C > @@ -0,0 +1,34 @@ > +// PR c++/94058 > +// { dg-do compile { target c++20 } } > + > +namespace std { > +struct strong_ordering { > + int _v; > + constexpr strong_ordering (int v) :_v(v) {} > + constexpr operator int (void) const { return _v; } > + static const strong_ordering less; > + static const strong_ordering equal; > + static const strong_ordering greater; > +}; > +constexpr strong_ordering strong_ordering::less = -1; > +constexpr strong_ordering strong_ordering::equal = 0; > +constexpr strong_ordering strong_ordering::greater = 1; > +} > + > +struct A { > + long i : 48; > + auto operator <=> (const A&) const = default; > +}; > + > +struct B { > + long i : 8; > + auto operator <=> (const B&) const = default; > +}; > + > +void > +f (B b) > +{ > + (void) int{b.i}; // Not narrowing anymore > + b.i <=> b.i; // Not narrowing anymore > + b <=> b; // Not deleted anymore > +} > diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing2.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing2.C > new file mode 100644 > index 00000000000..cfecce69405 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing2.C > @@ -0,0 +1,26 @@ > +// PR c++/104392 > +// { dg-do compile { target c++20 } } > + > +namespace std { > +struct strong_ordering { > + int _v; > + constexpr strong_ordering (int v) :_v(v) {} > + constexpr operator int (void) const { return _v; } > + static const strong_ordering less; > + static const strong_ordering equal; > + static const strong_ordering greater; > +}; > +constexpr strong_ordering strong_ordering::less = -1; > +constexpr strong_ordering strong_ordering::equal = 0; > +constexpr strong_ordering strong_ordering::greater = 1; > +} > + > +struct A { > + unsigned int a:5; > +}; > + > +constexpr std::strong_ordering > +operator<=>(const A & left, const A & right) > +{ > + return left.a <=> right.a; > +} > > base-commit: 52370c839edd04df86d3ff2b71fcdca0c7376a7f
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc index 7782f38da43..30a6fbe95c9 100644 --- a/gcc/cp/typeck2.cc +++ b/gcc/cp/typeck2.cc @@ -1012,6 +1012,18 @@ check_narrowing (tree type, tree init, tsubst_flags_t complain, if (TREE_CODE (ftype) == ENUMERAL_TYPE) /* Check for narrowing based on the values of the enumeration. */ ftype = ENUM_UNDERLYING_TYPE (ftype); + /* Undo convert_bitfield_to_declared_type (STRIP_NOPS isn't enough). */ + tree op = init; + while (CONVERT_EXPR_P (op)) + op = TREE_OPERAND (op, 0); + /* Core 2627 says that we shouldn't warn when "the source is a bit-field + whose width w is less than that of its type (or, for an enumeration + type, its underlying type) and the target type can represent all the + values of a hypothetical extended integer type with width w and with + the same signedness as the original type". */ + if (is_bitfield_expr_with_lowered_type (op) + && TYPE_PRECISION (TREE_TYPE (op)) < TYPE_PRECISION (ftype)) + ftype = TREE_TYPE (op); if ((tree_int_cst_lt (TYPE_MAX_VALUE (type), TYPE_MAX_VALUE (ftype)) || tree_int_cst_lt (TYPE_MIN_VALUE (ftype), diff --git a/gcc/testsuite/g++.dg/DRs/dr2627.C b/gcc/testsuite/g++.dg/DRs/dr2627.C new file mode 100644 index 00000000000..fe7f28613ca --- /dev/null +++ b/gcc/testsuite/g++.dg/DRs/dr2627.C @@ -0,0 +1,13 @@ +// DR 2627 - Bit-fields and narrowing conversions +// { dg-do compile { target c++20 } } + +#include <compare> + +struct C { + long long i : 8; +}; + +void f() { + C x{1}, y{2}; + x.i <=> y.i; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/Wnarrowing22.C b/gcc/testsuite/g++.dg/cpp0x/Wnarrowing22.C new file mode 100644 index 00000000000..dd30451a7cc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/Wnarrowing22.C @@ -0,0 +1,49 @@ +// DR 2627 - Bit-fields and narrowing conversions +// PR c++/94058 +// { dg-do compile { target c++11 } } +// { dg-options "-Wno-error=narrowing" } + +using int64_t = __INT64_TYPE__; +using int32_t = __INT32_TYPE__; + +struct A { + int64_t i1 : __CHAR_BIT__; + int64_t i2 : sizeof (int32_t) * __CHAR_BIT__ - 1; + int64_t i3 : sizeof (int32_t) * __CHAR_BIT__; + int64_t i4 : sizeof (int32_t) * __CHAR_BIT__ + 1; + int64_t i5 : sizeof (int64_t) * __CHAR_BIT__ - 1; + int64_t i6 : sizeof (int64_t) * __CHAR_BIT__; +} a; + +int32_t i1{a.i1}; +int32_t i2{a.i2}; +int32_t i3{a.i3}; +int32_t i4{a.i4}; // { dg-warning "narrowing conversion" } +int32_t i5{a.i5}; // { dg-warning "narrowing conversion" } +int32_t i6{a.i6}; // { dg-warning "narrowing conversion" } + +struct B { + bool b1 : sizeof (bool) * __CHAR_BIT__; + bool b2 : sizeof (bool); +} b; + +signed char b1{b.b1}; +signed char b2{b.b2}; + +enum E : int64_t { E1 }; + +struct C { + E e1 : __CHAR_BIT__; + E e2 : sizeof (int32_t) * __CHAR_BIT__ - 1; + E e3 : sizeof (int32_t) * __CHAR_BIT__; + E e4 : sizeof (int32_t) * __CHAR_BIT__ + 1; + E e5 : sizeof (int64_t) * __CHAR_BIT__ - 1; + E e6 : sizeof (int64_t) * __CHAR_BIT__; +} c; + +int32_t e1{c.e1}; +int32_t e2{c.e2}; +int32_t e3{c.e3}; +int32_t e4{c.e4}; // { dg-warning "narrowing conversion" } +int32_t e5{c.e5}; // { dg-warning "narrowing conversion" } +int32_t e6{c.e6}; // { dg-warning "narrowing conversion" } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing1.C new file mode 100644 index 00000000000..7769f950bed --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing1.C @@ -0,0 +1,34 @@ +// PR c++/94058 +// { dg-do compile { target c++20 } } + +namespace std { +struct strong_ordering { + int _v; + constexpr strong_ordering (int v) :_v(v) {} + constexpr operator int (void) const { return _v; } + static const strong_ordering less; + static const strong_ordering equal; + static const strong_ordering greater; +}; +constexpr strong_ordering strong_ordering::less = -1; +constexpr strong_ordering strong_ordering::equal = 0; +constexpr strong_ordering strong_ordering::greater = 1; +} + +struct A { + long i : 48; + auto operator <=> (const A&) const = default; +}; + +struct B { + long i : 8; + auto operator <=> (const B&) const = default; +}; + +void +f (B b) +{ + (void) int{b.i}; // Not narrowing anymore + b.i <=> b.i; // Not narrowing anymore + b <=> b; // Not deleted anymore +} diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing2.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing2.C new file mode 100644 index 00000000000..cfecce69405 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-narrowing2.C @@ -0,0 +1,26 @@ +// PR c++/104392 +// { dg-do compile { target c++20 } } + +namespace std { +struct strong_ordering { + int _v; + constexpr strong_ordering (int v) :_v(v) {} + constexpr operator int (void) const { return _v; } + static const strong_ordering less; + static const strong_ordering equal; + static const strong_ordering greater; +}; +constexpr strong_ordering strong_ordering::less = -1; +constexpr strong_ordering strong_ordering::equal = 0; +constexpr strong_ordering strong_ordering::greater = 1; +} + +struct A { + unsigned int a:5; +}; + +constexpr std::strong_ordering +operator<=>(const A & left, const A & right) +{ + return left.a <=> right.a; +}