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