diff mbox

[C] Fix up handling of enum with mode attribute (PR c/69669)

Message ID 20160204195313.GQ3017@tucnak.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek Feb. 4, 2016, 7:53 p.m. UTC
Hi!

If an enum has mode attribute, then finish_enum uses the precision
from its mode (and complains if the enumerators don't fit into that
precision), but will use TYPE_{MIN,MAX}_VALUE from int's precision instead.

Fixed thusly, bootstrapped/regtested on x86_64-linux and i686-linux, ok for
trunk?

2016-02-04  Jakub Jelinek  <jakub@redhat.com>

	PR c/69669
	* c-decl.c (finish_enum): When honoring mode attribute,
	make sure to use proper TYPE_MIN_VALUE and TYPE_MAX_VALUE.

	* c-c++-common/pr69669.c: New test.


	Jakub

Comments

Joseph Myers Feb. 4, 2016, 9:39 p.m. UTC | #1
On Thu, 4 Feb 2016, Jakub Jelinek wrote:

> Hi!
> 
> If an enum has mode attribute, then finish_enum uses the precision
> from its mode (and complains if the enumerators don't fit into that
> precision), but will use TYPE_{MIN,MAX}_VALUE from int's precision instead.
> 
> Fixed thusly, bootstrapped/regtested on x86_64-linux and i686-linux, ok for
> trunk?

OK.
diff mbox

Patch

--- gcc/c/c-decl.c.jj	2016-01-29 21:36:15.000000000 +0100
+++ gcc/c/c-decl.c	2016-02-04 16:58:03.461160993 +0100
@@ -8037,7 +8037,24 @@  finish_enum (tree enumtype, tree values,
   precision = MAX (tree_int_cst_min_precision (minnode, sign),
 		   tree_int_cst_min_precision (maxnode, sign));
 
-  if (TYPE_PACKED (enumtype) || precision > TYPE_PRECISION (integer_type_node))
+  /* If the precision of the type was specified with an attribute and it
+     was too small, give an error.  Otherwise, use it.  */
+  if (TYPE_PRECISION (enumtype) && lookup_attribute ("mode", attributes))
+    {
+      if (precision > TYPE_PRECISION (enumtype))
+	{
+	  TYPE_PRECISION (enumtype) = 0;
+	  error ("specified mode too small for enumeral values");
+	}
+      else
+	precision = TYPE_PRECISION (enumtype);
+    }
+  else
+    TYPE_PRECISION (enumtype) = 0;
+
+  if (TYPE_PACKED (enumtype)
+      || precision > TYPE_PRECISION (integer_type_node)
+      || TYPE_PRECISION (enumtype))
     {
       tem = c_common_type_for_size (precision, sign == UNSIGNED ? 1 : 0);
       if (tem == NULL)
@@ -8054,17 +8071,7 @@  finish_enum (tree enumtype, tree values,
   TYPE_UNSIGNED (enumtype) = TYPE_UNSIGNED (tem);
   TYPE_ALIGN (enumtype) = TYPE_ALIGN (tem);
   TYPE_SIZE (enumtype) = 0;
-
-  /* If the precision of the type was specified with an attribute and it
-     was too small, give an error.  Otherwise, use it.  */
-  if (TYPE_PRECISION (enumtype)
-      && lookup_attribute ("mode", attributes))
-    {
-      if (precision > TYPE_PRECISION (enumtype))
-	error ("specified mode too small for enumeral values");
-    }
-  else
-    TYPE_PRECISION (enumtype) = TYPE_PRECISION (tem);
+  TYPE_PRECISION (enumtype) = TYPE_PRECISION (tem);
 
   layout_type (enumtype);
 
--- gcc/testsuite/c-c++-common/pr69669.c.jj	2016-02-04 17:08:59.475060778 +0100
+++ gcc/testsuite/c-c++-common/pr69669.c	2016-02-04 17:08:15.000000000 +0100
@@ -0,0 +1,10 @@ 
+/* PR c/69669 */
+/* { dg-do compile } */
+
+enum __attribute__((mode(QI))) E { F = 1 };
+
+void
+foo (enum E *x, int y)
+{
+  *x = (enum E) y;
+}