diff mbox series

[C++,4/4] PR c++/30277 - int-width bit-field promotion.

Message ID 20190916043328.3739-4-jason@redhat.com
State New
Headers show
Series [C++,1/4] Handle location wrappers better in warn_logical_operator. | expand

Commit Message

Jason Merrill Sept. 16, 2019, 4:33 a.m. UTC
Here, if cp_perform_integral_promotions saw that the TREE_TYPE of a
bit-field reference was the same as the type it promotes to, it didn't do
anything.  But then decay_conversion saw that the bit-field reference was
unchanged, and converted it to its declared type.  So I needed to add
something to make it clear that promotion has been done.  But then the 33819
change caused trouble by looking through the NOP_EXPR I just added.  This
was the wrong fix for that bug; I've now fixed that better by recognizing in
cp_perform_integral_promotions that we won't promote a bit-field larger than
32 bits, so we should use the declared type.

Tested x86_64-pc-linux-gnu, applying to trunk.

	PR c++/33819 - long bit-field promotion.
	* typeck.c (cp_perform_integral_promotions): Handle large bit-fields
	properly.  Handle 32-bit non-int bit-fields properly.
	(is_bitfield_expr_with_lowered_type): Don't look through NOP_EXPR.
---
 gcc/cp/typeck.c                        | 29 ++++++++++++++++----------
 gcc/testsuite/g++.dg/expr/bitfield14.C | 17 +++++++++++++++
 gcc/cp/ChangeLog                       |  6 ++++++
 3 files changed, 41 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/expr/bitfield14.C

Comments

Jakub Jelinek Sept. 21, 2019, 6:23 a.m. UTC | #1
On Mon, Sep 16, 2019 at 12:33:28AM -0400, Jason Merrill wrote:
> Here, if cp_perform_integral_promotions saw that the TREE_TYPE of a
> bit-field reference was the same as the type it promotes to, it didn't do
> anything.  But then decay_conversion saw that the bit-field reference was
> unchanged, and converted it to its declared type.  So I needed to add
> something to make it clear that promotion has been done.  But then the 33819
> change caused trouble by looking through the NOP_EXPR I just added.  This
> was the wrong fix for that bug; I've now fixed that better by recognizing in
> cp_perform_integral_promotions that we won't promote a bit-field larger than
> 32 bits, so we should use the declared type.
> 
> Tested x86_64-pc-linux-gnu, applying to trunk.
> 
> 	PR c++/33819 - long bit-field promotion.
> 	* typeck.c (cp_perform_integral_promotions): Handle large bit-fields
> 	properly.  Handle 32-bit non-int bit-fields properly.
> 	(is_bitfield_expr_with_lowered_type): Don't look through NOP_EXPR.

> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/expr/bitfield14.C
> @@ -0,0 +1,17 @@
> +// PR c++/30277
> +// { dg-do compile { target c++11 } }
> +
> +struct S
> +{
> +  signed long l: 32;
> +};
> +
> +void foo(long) = delete;
> +void foo(int) {}
> +
> +int main()
> +{
> +  S x = {1};
> +  foo(x.l+0);
> +  return 0;
> +}

This testcase fails on all targets where int and long have the same
precision.  Is that a bug on the compiler side, or should the testcase just
use a type with a larger precision than int (i.e. long long), like following
(tested on x86_64-linux and i686-linux):

2019-09-21  Jakub Jelinek  <jakub@redhat.com>

	PR c++/30277
	* g++.dg/expr/bitfield14.C (struct S): Use signed long long instead
	of signed long.
	(foo): Use long long instead of long.

--- gcc/testsuite/g++.dg/expr/bitfield14.C.jj	2019-09-20 12:25:24.833748976 +0200
+++ gcc/testsuite/g++.dg/expr/bitfield14.C	2019-09-21 08:17:18.500234760 +0200
@@ -3,10 +3,10 @@
 
 struct S
 {
-  signed long l: 32;
+  signed long long l: 32;
 };
 
-void foo(long) = delete;
+void foo(long long) = delete;
 void foo(int) {}
 
 int main()


	Jakub
Jason Merrill Sept. 21, 2019, 9:51 p.m. UTC | #2
On Sat, Sep 21, 2019 at 2:23 AM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Mon, Sep 16, 2019 at 12:33:28AM -0400, Jason Merrill wrote:
> > Here, if cp_perform_integral_promotions saw that the TREE_TYPE of a
> > bit-field reference was the same as the type it promotes to, it didn't do
> > anything.  But then decay_conversion saw that the bit-field reference was
> > unchanged, and converted it to its declared type.  So I needed to add
> > something to make it clear that promotion has been done.  But then the 33819
> > change caused trouble by looking through the NOP_EXPR I just added.  This
> > was the wrong fix for that bug; I've now fixed that better by recognizing in
> > cp_perform_integral_promotions that we won't promote a bit-field larger than
> > 32 bits, so we should use the declared type.
> >
> > Tested x86_64-pc-linux-gnu, applying to trunk.
> >
> >       PR c++/33819 - long bit-field promotion.
> >       * typeck.c (cp_perform_integral_promotions): Handle large bit-fields
> >       properly.  Handle 32-bit non-int bit-fields properly.
> >       (is_bitfield_expr_with_lowered_type): Don't look through NOP_EXPR.
>
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/expr/bitfield14.C
> > @@ -0,0 +1,17 @@
> > +// PR c++/30277
> > +// { dg-do compile { target c++11 } }
> > +
> > +struct S
> > +{
> > +  signed long l: 32;
> > +};
> > +
> > +void foo(long) = delete;
> > +void foo(int) {}
> > +
> > +int main()
> > +{
> > +  S x = {1};
> > +  foo(x.l+0);
> > +  return 0;
> > +}
>
> This testcase fails on all targets where int and long have the same
> precision.  Is that a bug on the compiler side, or should the testcase just
> use a type with a larger precision than int (i.e. long long), like following
> (tested on x86_64-linux and i686-linux):

It's a testcase bug.

> 2019-09-21  Jakub Jelinek  <jakub@redhat.com>
>
>         PR c++/30277
>         * g++.dg/expr/bitfield14.C (struct S): Use signed long long instead
>         of signed long.
>         (foo): Use long long instead of long.

OK, thanks.

Jason
diff mbox series

Patch

diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 620f2c9afdf..c6bf41ee7a4 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -1971,12 +1971,6 @@  is_bitfield_expr_with_lowered_type (const_tree exp)
       else
 	return NULL_TREE;
 
-    CASE_CONVERT:
-      if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (exp, 0)))
-	  == TYPE_MAIN_VARIANT (TREE_TYPE (exp)))
-	return is_bitfield_expr_with_lowered_type (TREE_OPERAND (exp, 0));
-      /* Fallthrough.  */
-
     default:
       return NULL_TREE;
     }
@@ -2189,13 +2183,23 @@  cp_perform_integral_promotions (tree expr, tsubst_flags_t complain)
   if (error_operand_p (expr))
     return error_mark_node;
 
+  type = TREE_TYPE (expr);
+
   /* [conv.prom]
 
-     If the bitfield has an enumerated type, it is treated as any
-     other value of that type for promotion purposes.  */
-  type = is_bitfield_expr_with_lowered_type (expr);
-  if (!type || TREE_CODE (type) != ENUMERAL_TYPE)
-    type = TREE_TYPE (expr);
+     A prvalue for an integral bit-field (11.3.9) 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. If the bit-field has an enumerated
+     type, it is treated as any other value of that type for promotion
+     purposes.  */
+  tree bitfield_type = is_bitfield_expr_with_lowered_type (expr);
+  if (bitfield_type
+      && (TREE_CODE (bitfield_type) == ENUMERAL_TYPE
+	  || TYPE_PRECISION (type) > TYPE_PRECISION (integer_type_node)))
+    type = bitfield_type;
+
   gcc_assert (INTEGRAL_OR_ENUMERATION_TYPE_P (type));
   /* Scoped enums don't promote.  */
   if (SCOPED_ENUM_P (type))
@@ -2203,6 +2207,9 @@  cp_perform_integral_promotions (tree expr, tsubst_flags_t complain)
   promoted_type = type_promotes_to (type);
   if (type != promoted_type)
     expr = cp_convert (promoted_type, expr, complain);
+  else if (bitfield_type && bitfield_type != type)
+    /* Prevent decay_conversion from converting to bitfield_type.  */
+    expr = build_nop (type, expr);
   return expr;
 }
 
diff --git a/gcc/testsuite/g++.dg/expr/bitfield14.C b/gcc/testsuite/g++.dg/expr/bitfield14.C
new file mode 100644
index 00000000000..546af85ba10
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/bitfield14.C
@@ -0,0 +1,17 @@ 
+// PR c++/30277
+// { dg-do compile { target c++11 } }
+
+struct S
+{
+  signed long l: 32;
+};
+
+void foo(long) = delete;
+void foo(int) {}
+
+int main()
+{
+  S x = {1};
+  foo(x.l+0);
+  return 0;
+}
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index bed72c90a2e..7bf28f81fae 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,11 @@ 
 2019-09-15  Jason Merrill  <jason@redhat.com>
 
+	PR c++/30277 - int-width bit-field promotion.
+	PR c++/33819 - long bit-field promotion.
+	* typeck.c (cp_perform_integral_promotions): Handle large bit-fields
+	properly.  Handle 32-bit non-int bit-fields properly.
+	(is_bitfield_expr_with_lowered_type): Don't look through NOP_EXPR.
+
 	PR c++/82165 - enum bitfields and operator overloading.
 	* call.c (build_new_op_1): Use unlowered_expr_type.