Hi!

On Mon, Nov 25, 2019 at 03:46:23PM +0100, Jakub Jelinek wrote:
> On Mon, Nov 25, 2019 at 03:39:32PM +0100, Jakub Jelinek wrote:
> > I guess the question is, shall we store the minimum precision needed
> > somewhere by finish_enum_value_list (perhaps only bother if the precision
> > of the underlying type is not the same) or compute it each time again
> > (which would mean for each bitfield of enum type walk the list of all the
> > enum values)?  And, if we should store it somewhere, any preferences where?
> > These days C++ FE has TYPE_LANG_SPECIFIC just for classes, so options are
> > just use the class TYPE_LANG_SPECIFIC and just stick the min/max values in
> > some trees in there, restore having different lang specific variants and
> > store just minimum precision in there, add hash map from tree (hashed using
> > TYPE_UID) to precision, something else?
> 
> Or yet another possibility, only compute it on demand and use a hash map as
> cache.

Here is that option implemented, bootstrapped/regtested on x86_64-linux and
i686-linux, ok for trunk?

2019-11-26  Jakub Jelinek  <ja...@redhat.com>

        PR c++/61414
        * c-attribs.c (handle_mode_attribute): Add mode attribute to
        ENUMERAL_TYPEs.

        * class.c (enum_to_min_precision): New hash_map.
        (enum_min_precision): New function.
        (check_bitfield_decl): Use it.

        * g++.dg/cpp0x/enum23.C: Remove xfail.
        * g++.dg/cpp0x/enum28.C: New test.

--- gcc/c-family/c-attribs.c.jj 2019-11-23 11:05:50.813492164 +0100
+++ gcc/c-family/c-attribs.c    2019-11-25 16:47:35.317750531 +0100
@@ -1866,6 +1866,7 @@ handle_mode_attribute (tree *node, tree
                typefm = make_signed_type (TYPE_PRECISION (typefm));
              TREE_TYPE (typefm) = type;
            }
+         *no_add_attrs = false;
        }
       else if (VECTOR_MODE_P (mode)
               ? TREE_CODE (type) != TREE_CODE (TREE_TYPE (typefm))
--- gcc/cp/class.c.jj   2019-11-16 18:13:42.844606979 +0100
+++ gcc/cp/class.c      2019-11-25 16:42:15.050633229 +0100
@@ -3265,6 +3265,60 @@ add_implicitly_declared_members (tree t,
     }
 }
 
+/* Cache of enum_min_precision values.  */
+static GTY((deletable)) hash_map<tree, int> *enum_to_min_precision;
+
+/* Return the minimum precision of a bit-field needed to store all
+   enumerators of ENUMERAL_TYPE TYPE.  */
+
+static int
+enum_min_precision (tree type)
+{
+  type = TYPE_MAIN_VARIANT (type);
+  /* For unscoped enums without fixed underlying type and without mode
+     attribute we can just use precision of the underlying type.  */
+  if (UNSCOPED_ENUM_P (type)
+      && !ENUM_FIXED_UNDERLYING_TYPE_P (type)
+      && !lookup_attribute ("mode", TYPE_ATTRIBUTES (type)))
+    return TYPE_PRECISION (ENUM_UNDERLYING_TYPE (type));
+
+  if (enum_to_min_precision == NULL)
+    enum_to_min_precision = hash_map<tree, int>::create_ggc (37);
+
+  bool existed;
+  int prec = enum_to_min_precision->get_or_insert (type, &existed);
+  if (existed)
+    return prec;
+
+  tree minnode, maxnode;
+  if (TYPE_VALUES (type))
+    {
+      minnode = maxnode = NULL_TREE;
+      for (tree values = TYPE_VALUES (type);
+          values; values = TREE_CHAIN (values))
+       {
+         tree decl = TREE_VALUE (values);
+         tree value = DECL_INITIAL (decl);
+         if (value == error_mark_node)
+           value = integer_zero_node;
+         if (!minnode)
+           minnode = maxnode = value;
+         else if (tree_int_cst_lt (maxnode, value))
+           maxnode = value;
+         else if (tree_int_cst_lt (value, minnode))
+           minnode = value;
+       }
+    }
+  else
+    minnode = maxnode = integer_zero_node;
+
+  signop sgn = tree_int_cst_sgn (minnode) >= 0 ? UNSIGNED : SIGNED;
+  int lowprec = tree_int_cst_min_precision (minnode, sgn);
+  int highprec = tree_int_cst_min_precision (maxnode, sgn);
+  prec = MAX (lowprec, highprec);
+  return prec;
+}
+
 /* FIELD is a bit-field.  We are finishing the processing for its
    enclosing type.  Issue any appropriate messages and set appropriate
    flags.  Returns false if an error has been diagnosed.  */
@@ -3326,7 +3380,7 @@ check_bitfield_decl (tree field)
                    "width of %qD exceeds its type", field);
       else if (TREE_CODE (type) == ENUMERAL_TYPE)
        {
-         int prec = TYPE_PRECISION (ENUM_UNDERLYING_TYPE (type));
+         int prec = enum_min_precision (type);
          if (compare_tree_int (w, prec) < 0)
            warning_at (DECL_SOURCE_LOCATION (field), 0,
                        "%qD is too small to hold all values of %q#T",
--- gcc/testsuite/g++.dg/cpp0x/enum23.C.jj      2013-02-16 09:31:06.400506406 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/enum23.C 2019-11-25 16:51:19.532332223 +0100
@@ -5,5 +5,5 @@ enum class MyEnum { A = 1 };
 
 struct MyClass
 {
-  MyEnum Field1 : 3; // { dg-bogus "warning: 'MyClass::Field1' is too small" 
"" { xfail *-*-* } }
+  MyEnum Field1 : 3; // { dg-bogus "warning: 'MyClass::Field1' is too small" }
 };
--- gcc/testsuite/g++.dg/cpp0x/enum38.C.jj      2019-11-25 16:50:56.323686060 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/enum38.C 2019-11-25 16:52:28.466281278 +0100
@@ -0,0 +1,25 @@
+// PR c++/61414
+// { dg-do compile { target c++11 } }
+
+enum C { C0 = -4, C1 = 3 };
+enum D { D0 = 0, D1 = 15 };
+enum class E { E0 = -4, E1 = 3 };
+enum F : unsigned { F0 = 0, F1 = 15 };
+enum __attribute__((__mode__ (__QI__))) G { G0 = -4, G1 = 3 };
+enum __attribute__((__mode__ (__HI__))) H { H0 = 0, H1 = 15 };
+
+struct S
+{
+  C a : 2;     // { dg-warning "'S::a' is too small to hold all values of 
'enum C'" }
+  C b : 3;     // { dg-bogus "'S::b' is too small to hold all values of 'enum 
C'" }
+  D c : 3;     // { dg-warning "'S::c' is too small to hold all values of 
'enum D'" }
+  D d : 4;     // { dg-bogus "'S::d' is too small to hold all values of 'enum 
D'" }
+  E e : 2;     // { dg-warning "'S::e' is too small to hold all values of 
'enum class E'" }
+  E f : 3;     // { dg-bogus "'S::f' is too small to hold all values of 'enum 
class E'" }
+  F g : 3;     // { dg-warning "'S::g' is too small to hold all values of 
'enum F'" }
+  F h : 4;     // { dg-bogus "'S::h' is too small to hold all values of 'enum 
F'" }
+  G i : 2;     // { dg-warning "'S::i' is too small to hold all values of 
'enum G'" }
+  G j : 3;     // { dg-bogus "'S::j' is too small to hold all values of 'enum 
G'" }
+  H k : 3;     // { dg-warning "'S::k' is too small to hold all values of 
'enum H'" }
+  H l : 4;     // { dg-bogus "'S::l' is too small to hold all values of 'enum 
H'" }
+};


        Jakub

Reply via email to