c: C2x enums with fixed underlying type [PR61469]
authorJoseph Myers <joseph@codesourcery.com>
Fri, 28 Oct 2022 00:37:28 +0000 (00:37 +0000)
committerJoseph Myers <joseph@codesourcery.com>
Fri, 28 Oct 2022 00:37:28 +0000 (00:37 +0000)
C2x adds support for enums with a fixed underlying type specified
("enum e : long long;" and similar).  Implement this in the C front
end.  The same representation is used for these types as in C++, with
two macros moved from cp-tree.h to c-common.h.

Such enums can have bool as the underlying type, and various C
front-end code checking for boolean types is adjusted to use a new
C_BOOLEAN_TYPE_P to handle such enums the same way as bool.  (Note
that for C++ we have bug 96496 that enums with underlying type bool
don't work correctly there.)

There are various issues with the wording for such enums in the
current C2x working draft (including but not limited to wording in the
accepted paper that failed to make it into the working draft), which I
intend to raise in NB comments.  I think what I've implemented and
added tests for matches the intent.

Bootstrapped with no regressions for x86_64-pc-linux-gnu.

PR c/61469

gcc/c-family/
* c-common.h (ENUM_UNDERLYING_TYPE, ENUM_FIXED_UNDERLYING_TYPE_P):
New.  Moved from cp/cp-tree.h.
* c-warn.cc (warnings_for_convert_and_check): Do not consider
conversions to enum with underlying type bool to overflow.

gcc/c/
* c-convert.cc (c_convert): Handle enums with underlying boolean
type like bool.
* c-decl.cc (shadow_tag_warned): Allow shadowing declarations for
enums with enum type specifier, but give errors for storage class
specifiers, qualifiers or alignment specifiers in non-definition
declarations of such enums.
(grokdeclarator): Give error for non-definition use of type
specifier with an enum type specifier.
(parser_xref_tag): Add argument has_enum_type_specifier.  Pass it
to lookup_tag and use it to set ENUM_FIXED_UNDERLYING_TYPE_P.
(xref_tag): Update call to parser_xref_tag.
(start_enum): Add argument fixed_underlying_type.  Complete enum
type with a fixed underlying type given in the definition.  Give
error for defining without a fixed underlying type in the
definition if one was given in a prior declaration.  Do not mark
enums with fixed underlying type as packed for -fshort-enums.
Store the enum type in the_enum.
(finish_enum): Do not adjust types of values or check their range
for an enum with a fixed underlying type.  Set underlying type of
enum and variants.
(build_enumerator): Check enumeration constants for enum with
fixed underlying type against that type and convert to that type.
Increment in the underlying integer type, with handling for bool.
(c_simulate_enum_decl): Update call to start_enum.
(declspecs_add_type): Set specs->enum_type_specifier_ref_p.
* c-objc-common.cc (c_get_alias_set): Use ENUM_UNDERLYING_TYPE
rather than recomputing an underlying type based on size.
* c-parser.cc (c_parser_declspecs)
(c_parser_struct_or_union_specifier, c_parser_typeof_specifier):
Set has_enum_type_specifier for type specifiers.
(c_parser_enum_specifier): Handle enum type specifiers.
(c_parser_struct_or_union_specifier): Update call to
parser_xref_tag.
(c_parser_omp_atomic): Check for boolean increment or decrement
using C_BOOLEAN_TYPE_P.
* c-tree.h (C_BOOLEAN_TYPE_P): New.
(struct c_typespec): Add has_enum_type_specifier.
(struct c_declspecs): Add enum_type_specifier_ref_p.
(struct c_enum_contents): Add enum_type.
(start_enum, parser_xref_tag): Update prototypes.
* c-typeck.cc (composite_type): Allow for enumerated types
compatible with bool.
(common_type, comptypes_internal, perform_integral_promotions):
Use ENUM_UNDERLYING_TYPE.
(parser_build_binary_op, build_unary_op, convert_for_assignment)
(c_finish_return, c_start_switch, build_binary_op): Check for
boolean types using C_BOOLEAN_TYPE_P.

gcc/cp/
* cp-tree.h (ENUM_FIXED_UNDERLYING_TYPE_P, ENUM_UNDERLYING_TYPE):
Remove.  Moved to c-common.h.

gcc/testsuite/
* gcc.dg/c11-enum-4.c, gcc.dg/c11-enum-5.c, gcc.dg/c11-enum-6.c,
gcc.dg/c2x-enum-6.c, gcc.dg/c2x-enum-7.c, gcc.dg/c2x-enum-8.c,
gcc.dg/gnu2x-enum-1.c: New tests.

16 files changed:
gcc/c-family/c-common.h
gcc/c-family/c-warn.cc
gcc/c/c-convert.cc
gcc/c/c-decl.cc
gcc/c/c-objc-common.cc
gcc/c/c-parser.cc
gcc/c/c-tree.h
gcc/c/c-typeck.cc
gcc/cp/cp-tree.h
gcc/testsuite/gcc.dg/c11-enum-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c11-enum-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c11-enum-6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-enum-6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-enum-7.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-enum-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu2x-enum-1.c [new file with mode: 0644]

index 62ab4ba..f9d0d29 100644 (file)
@@ -1004,6 +1004,30 @@ extern void c_parse_final_cleanups (void);
 /* True iff TYPE is cv decltype(nullptr).  */
 #define NULLPTR_TYPE_P(TYPE) (TREE_CODE (TYPE) == NULLPTR_TYPE)
 
+/* Returns the underlying type of the given enumeration type. The
+   underlying type is determined in different ways, depending on the
+   properties of the enum:
+
+     - In C++0x or C2x, the underlying type can be explicitly specified, e.g.,
+
+         enum E1 : char { ... } // underlying type is char
+
+     - In a C++0x scoped enumeration, the underlying type is int
+       unless otherwises specified:
+
+         enum class E2 { ... } // underlying type is int
+
+     - Otherwise, the underlying type is determined based on the
+       values of the enumerators. In this case, the
+       ENUM_UNDERLYING_TYPE will not be set until after the definition
+       of the enumeration is completed by finish_enum.  */
+#define ENUM_UNDERLYING_TYPE(TYPE) \
+  TREE_TYPE (ENUMERAL_TYPE_CHECK (TYPE))
+
+/* Determines whether an ENUMERAL_TYPE has an explicit
+   underlying type.  */
+#define ENUM_FIXED_UNDERLYING_TYPE_P(NODE) (TYPE_LANG_FLAG_5 (NODE))
+
 extern tree do_case (location_t, tree, tree, tree);
 extern tree build_stmt (location_t, enum tree_code, ...);
 extern tree build_real_imag_expr (location_t, enum tree_code, tree);
index 6742f44..cd59c3c 100644 (file)
@@ -1415,7 +1415,8 @@ warnings_for_convert_and_check (location_t loc, tree type, tree expr,
 
   if (TREE_CODE (expr) == INTEGER_CST
       && (TREE_CODE (type) == INTEGER_TYPE
-         || TREE_CODE (type) == ENUMERAL_TYPE)
+         || (TREE_CODE (type) == ENUMERAL_TYPE
+             && TREE_CODE (ENUM_UNDERLYING_TYPE (type)) != BOOLEAN_TYPE))
       && !int_fits_type_p (expr, type))
     {
       /* Do not diagnose overflow in a constant expression merely
index 6e74913..8a43e5f 100644 (file)
@@ -110,8 +110,13 @@ c_convert (tree type, tree expr, bool init_const)
     case VOID_TYPE:
       return fold_convert_loc (loc, type, e);
 
-    case INTEGER_TYPE:
     case ENUMERAL_TYPE:
+      if (ENUM_UNDERLYING_TYPE (type) != NULL_TREE
+         && TREE_CODE (ENUM_UNDERLYING_TYPE (type)) == BOOLEAN_TYPE)
+       goto convert_to_boolean;
+      gcc_fallthrough ();
+
+    case INTEGER_TYPE:
       if (sanitize_flags_p (SANITIZE_FLOAT_CAST)
          && current_function_decl != NULL_TREE
          && TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE
@@ -129,6 +134,7 @@ c_convert (tree type, tree expr, bool init_const)
       goto maybe_fold;
 
     case BOOLEAN_TYPE:
+    convert_to_boolean:
       return fold_convert_loc
        (loc, type, c_objc_common_truthvalue_conversion (input_location, expr));
 
index 4746e31..2b83900 100644 (file)
@@ -4817,7 +4817,8 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned)
          else if (declspecs->typespec_kind != ctsk_tagdef
                   && declspecs->typespec_kind != ctsk_tagfirstref
                   && declspecs->typespec_kind != ctsk_tagfirstref_attrs
-                  && code == ENUMERAL_TYPE)
+                  && code == ENUMERAL_TYPE
+                  && !declspecs->enum_type_specifier_ref_p)
            {
              bool warned_enum = false;
              if (warned != 1)
@@ -4883,6 +4884,38 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned)
       warned = 1;
     }
 
+  if (declspecs->enum_type_specifier_ref_p && !warned)
+    {
+      if (declspecs->storage_class != csc_none)
+       {
+         error ("storage class specifier in empty declaration with %<enum%> "
+                "underlying type");
+         warned = 1;
+       }
+      else if (declspecs->thread_p)
+       {
+         error ("%qs in empty declaration with %<enum%> underlying type",
+                declspecs->thread_gnu_p ? "__thread" : "_Thread_local");
+         warned = 1;
+       }
+      else if (declspecs->const_p
+              || declspecs->volatile_p
+              || declspecs->atomic_p
+              || declspecs->restrict_p
+              || declspecs->address_space)
+       {
+         error ("type qualifier in empty declaration with %<enum%> "
+                "underlying type");
+         warned = 1;
+       }
+      else if (declspecs->alignas_p)
+       {
+         error ("%<alignas%> in empty declaration with %<enum%> "
+                "underlying type");
+         warned = 1;
+       }
+    }
+
   if (!warned && !in_system_header_at (input_location)
       && declspecs->storage_class != csc_none)
     {
@@ -6496,6 +6529,16 @@ grokdeclarator (const struct c_declarator *declarator,
       }
   }
 
+  /* An enum type specifier (": specifier-qualifier-list") may only be
+     specified when the enum is being defined or in an empty
+     declaration of the form "enum identifier enum-type-specifier;".
+     Except for the case of an empty declaration that has additional
+     declaration specifiers, all invalid contexts (declarations that
+     aren't empty, type names, parameter declarations, member
+     declarations) pass through grokdeclarator.  */
+  if (declspecs->enum_type_specifier_ref_p)
+    error_at (loc, "%<enum%> underlying type may not be specified here");
+
   /* A function definition's declarator must have the form of
      a function declarator.  */
 
@@ -8285,12 +8328,15 @@ get_parm_info (bool ellipsis, tree expr)
    Define the tag as a forward-reference with location LOC if it is
    not defined.  HAVE_STD_ATTRS says whether any standard attributes
    were present after the struct, union or enum keyword; ATTRS are the
-   standard attributes present there.  Return a c_typespec structure
-   for the type specifier.  */
+   standard attributes present there.  HAS_ENUM_TYPE_SPECIFIER says
+   whether an enum type specifier (": specifier-qualifier-list") is
+   present; if so, this is called before that specifier is parsed, so
+   that the tag is in scope for that specifier.  Return a c_typespec
+   structure for the type specifier.  */
 
 struct c_typespec
 parser_xref_tag (location_t loc, enum tree_code code, tree name,
-                bool have_std_attrs, tree attrs)
+                bool have_std_attrs, tree attrs, bool has_enum_type_specifier)
 {
   struct c_typespec ret;
   tree ref;
@@ -8298,11 +8344,13 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name,
 
   ret.expr = NULL_TREE;
   ret.expr_const_operands = true;
+  ret.has_enum_type_specifier = has_enum_type_specifier;
 
-  /* If a cross reference is requested, look up the type
-     already defined for this tag and return it.  */
+  /* If a cross reference is requested, look up the type already
+     defined for this tag and return it.  If an enum type specifier is
+     present, only a definition in the current scope is relevant.  */
 
-  ref = lookup_tag (code, name, false, &refloc);
+  ref = lookup_tag (code, name, has_enum_type_specifier, &refloc);
   /* If this is the right type of tag, return what we found.
      (This reference will be shadowed by shadow_tag later if appropriate.)
      If this is the wrong type of tag, do not return it.  If it was the
@@ -8371,6 +8419,7 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name,
       TYPE_PRECISION (ref) = TYPE_PRECISION (unsigned_type_node);
       TYPE_MIN_VALUE (ref) = TYPE_MIN_VALUE (unsigned_type_node);
       TYPE_MAX_VALUE (ref) = TYPE_MAX_VALUE (unsigned_type_node);
+      ENUM_FIXED_UNDERLYING_TYPE_P (ref) = has_enum_type_specifier;
     }
 
   pushtag (loc, name, ref);
@@ -8387,7 +8436,8 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name,
 tree
 xref_tag (enum tree_code code, tree name)
 {
-  return parser_xref_tag (input_location, code, name, false, NULL_TREE).spec;
+  return parser_xref_tag (input_location, code, name, false, NULL_TREE,
+                         false).spec;
 }
 \f
 /* Make sure that the tag NAME is defined *in the current scope*
@@ -9288,12 +9338,15 @@ layout_array_type (tree t)
 /* Begin compiling the definition of an enumeration type.
    NAME is its name (or null if anonymous).
    LOC is the enum's location.
+   FIXED_UNDERLYING_TYPE is the (C2x) underlying type specified in the
+   definition.
    Returns the type object, as yet incomplete.
    Also records info about it so that build_enumerator
    may be used to declare the individual values as they are read.  */
 
 tree
-start_enum (location_t loc, struct c_enum_contents *the_enum, tree name)
+start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
+           tree fixed_underlying_type)
 {
   tree enumtype = NULL_TREE;
   location_t enumloc = UNKNOWN_LOCATION;
@@ -9309,6 +9362,23 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name)
     {
       enumtype = make_node (ENUMERAL_TYPE);
       pushtag (loc, name, enumtype);
+      if (fixed_underlying_type != NULL_TREE)
+       {
+         /* For an enum definition with a fixed underlying type, the
+            type is complete during the definition and the
+            enumeration constants have that type.  If there was a
+            tag, the type was completed in c_parser_enum_specifier.
+            If not, it must be completed here.  */
+         ENUM_FIXED_UNDERLYING_TYPE_P (enumtype) = true;
+         TYPE_MIN_VALUE (enumtype) = TYPE_MIN_VALUE (fixed_underlying_type);
+         TYPE_MAX_VALUE (enumtype) = TYPE_MAX_VALUE (fixed_underlying_type);
+         TYPE_UNSIGNED (enumtype) = TYPE_UNSIGNED (fixed_underlying_type);
+         SET_TYPE_ALIGN (enumtype, TYPE_ALIGN (fixed_underlying_type));
+         TYPE_SIZE (enumtype) = NULL_TREE;
+         TYPE_PRECISION (enumtype) = TYPE_PRECISION (fixed_underlying_type);
+         ENUM_UNDERLYING_TYPE (enumtype) = fixed_underlying_type;
+         layout_type (enumtype);
+       }
     }
   /* Update type location to the one of the definition, instead of e.g.
      a forward declaration.  */
@@ -9336,10 +9406,16 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name)
       TYPE_VALUES (enumtype) = NULL_TREE;
     }
 
+  if (ENUM_FIXED_UNDERLYING_TYPE_P (enumtype)
+      && fixed_underlying_type == NULL_TREE)
+    error_at (loc, "%<enum%> declared with but defined without "
+             "fixed underlying type");
+
   the_enum->enum_next_value = integer_zero_node;
+  the_enum->enum_type = enumtype;
   the_enum->enum_overflow = 0;
 
-  if (flag_short_enums)
+  if (flag_short_enums && !ENUM_FIXED_UNDERLYING_TYPE_P (enumtype))
     for (tree v = TYPE_MAIN_VARIANT (enumtype); v; v = TYPE_NEXT_VARIANT (v))
       TYPE_PACKED (v) = 1;
 
@@ -9403,54 +9479,61 @@ finish_enum (tree enumtype, tree values, tree attributes)
     (tree_int_cst_lt (minnode, TYPE_MIN_VALUE (integer_type_node))
      || tree_int_cst_lt (TYPE_MAX_VALUE (integer_type_node), maxnode));
 
-  /* 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 (!ENUM_FIXED_UNDERLYING_TYPE_P (enumtype))
     {
-      if (precision > TYPE_PRECISION (enumtype))
+      /* 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))
        {
-         TYPE_PRECISION (enumtype) = 0;
-         error ("specified mode too small for enumerated values");
+         if (precision > TYPE_PRECISION (enumtype))
+           {
+             TYPE_PRECISION (enumtype) = 0;
+             error ("specified mode too small for enumerated values");
+           }
+         else
+           precision = TYPE_PRECISION (enumtype);
        }
       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)
-       {
-         /* This should only occur when both signed and unsigned
-            values of maximum precision occur among the
-            enumerators.  */
-         pedwarn (input_location, 0,
-                  "enumeration values exceed range of largest integer");
-         tem = widest_integer_literal_type_node;
-       }
-      else if (precision > TYPE_PRECISION (intmax_type_node)
-              && !tree_int_cst_lt (minnode, TYPE_MIN_VALUE (intmax_type_node))
-              && !tree_int_cst_lt (TYPE_MAX_VALUE (uintmax_type_node),
-                                   maxnode))
-       pedwarn (input_location, OPT_Wpedantic,
-                "enumeration values exceed range of %qs",
-                sign == UNSIGNED ? "uintmax_t" : "intmax_t");
-    }
-  else
-    tem = sign == UNSIGNED ? unsigned_type_node : integer_type_node;
+       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)
+           {
+             /* This should only occur when both signed and unsigned
+                values of maximum precision occur among the
+                enumerators.  */
+             pedwarn (input_location, 0,
+                      "enumeration values exceed range of largest integer");
+             tem = widest_integer_literal_type_node;
+           }
+         else if (precision > TYPE_PRECISION (intmax_type_node)
+                  && !tree_int_cst_lt (minnode,
+                                       TYPE_MIN_VALUE (intmax_type_node))
+                  && !tree_int_cst_lt (TYPE_MAX_VALUE (uintmax_type_node),
+                                       maxnode))
+           pedwarn (input_location, OPT_Wpedantic,
+                    "enumeration values exceed range of %qs",
+                    sign == UNSIGNED ? "uintmax_t" : "intmax_t");
+       }
+      else
+       tem = sign == UNSIGNED ? unsigned_type_node : integer_type_node;
 
-  TYPE_MIN_VALUE (enumtype) = TYPE_MIN_VALUE (tem);
-  TYPE_MAX_VALUE (enumtype) = TYPE_MAX_VALUE (tem);
-  TYPE_UNSIGNED (enumtype) = TYPE_UNSIGNED (tem);
-  SET_TYPE_ALIGN (enumtype, TYPE_ALIGN (tem));
-  TYPE_SIZE (enumtype) = NULL_TREE;
-  TYPE_PRECISION (enumtype) = TYPE_PRECISION (tem);
+      TYPE_MIN_VALUE (enumtype) = TYPE_MIN_VALUE (tem);
+      TYPE_MAX_VALUE (enumtype) = TYPE_MAX_VALUE (tem);
+      TYPE_UNSIGNED (enumtype) = TYPE_UNSIGNED (tem);
+      SET_TYPE_ALIGN (enumtype, TYPE_ALIGN (tem));
+      TYPE_SIZE (enumtype) = NULL_TREE;
+      TYPE_PRECISION (enumtype) = TYPE_PRECISION (tem);
+      ENUM_UNDERLYING_TYPE (enumtype) =
+       c_common_type_for_size (TYPE_PRECISION (tem), TYPE_UNSIGNED (tem));
 
-  layout_type (enumtype);
+      layout_type (enumtype);
+    }
 
   if (values != error_mark_node)
     {
@@ -9477,8 +9560,10 @@ finish_enum (tree enumtype, tree values, tree attributes)
             fit in int are given type int in build_enumerator (which
             is the correct type while the enumeration is being
             parsed), so no conversions are needed here if all
-            enumerators fit in int.  */
-         if (wider_than_int)
+            enumerators fit in int.  If the enum has a fixed
+            underlying type, the correct type was also given in
+            build_enumerator.  */
+         if (!ENUM_FIXED_UNDERLYING_TYPE_P (enumtype) && wider_than_int)
            ini = convert (enumtype, ini);
 
          DECL_INITIAL (enu) = ini;
@@ -9516,6 +9601,7 @@ finish_enum (tree enumtype, tree values, tree attributes)
       TYPE_USER_ALIGN (tem) = TYPE_USER_ALIGN (enumtype);
       TYPE_UNSIGNED (tem) = TYPE_UNSIGNED (enumtype);
       TYPE_LANG_SPECIFIC (tem) = TYPE_LANG_SPECIFIC (enumtype);
+      ENUM_UNDERLYING_TYPE (tem) = ENUM_UNDERLYING_TYPE (enumtype);
     }
 
   /* Finish debugging output for this type.  */
@@ -9595,56 +9681,89 @@ build_enumerator (location_t decl_loc, location_t loc,
       if (the_enum->enum_overflow)
        error_at (loc, "overflow in enumeration values");
     }
-  /* Even though the underlying type of an enum is unspecified, the
-     type of enumeration constants is explicitly defined as int
-     (6.4.4.3/2 in the C99 Standard).  C2X allows any integer type, and
-     GCC allows such types for older standards as an extension.  */
-  bool warned_range = false;
-  if (!int_fits_type_p (value,
-                       (TYPE_UNSIGNED (TREE_TYPE (value))
-                        ? uintmax_type_node
-                        : intmax_type_node)))
-    /* GCC does not consider its types larger than intmax_t to be
-       extended integer types (although C2X would permit such types to
-       be considered extended integer types if all the features
-       required by <stdint.h> and <inttypes.h> macros, such as support
-       for integer constants and I/O, were present), so diagnose if
-       such a wider type is used.  (If the wider type arose from a
-       constant of such a type, that will also have been diagnosed,
-       but this is the only diagnostic in the case where it arises
-       from choosing a wider type automatically when adding 1
-       overflows.)  */
-    warned_range = pedwarn (loc, OPT_Wpedantic,
-                           "enumerator value outside the range of %qs",
+  if (ENUM_FIXED_UNDERLYING_TYPE_P (the_enum->enum_type))
+    {
+      /* Enumeration constants must fit in the fixed underlying type.  */
+      if (!int_fits_type_p (value, ENUM_UNDERLYING_TYPE (the_enum->enum_type)))
+       error_at (loc,
+                 "enumerator value outside the range of underlying type");
+      /* Enumeration constants for an enum with fixed underlying type
+        have the enum type, both inside and outside the
+        definition.  */
+      value = convert (the_enum->enum_type, value);
+    }
+  else
+    {
+      /* Even though the underlying type of an enum is unspecified, the
+        type of enumeration constants is explicitly defined as int
+        (6.4.4.3/2 in the C99 Standard).  C2X allows any integer type, and
+        GCC allows such types for older standards as an extension.  */
+      bool warned_range = false;
+      if (!int_fits_type_p (value,
                            (TYPE_UNSIGNED (TREE_TYPE (value))
-                            ? "uintmax_t"
-                            : "intmax_t"));
-  if (!warned_range && !int_fits_type_p (value, integer_type_node))
-    pedwarn_c11 (loc, OPT_Wpedantic,
-                "ISO C restricts enumerator values to range of %<int%> "
-                "before C2X");
-
-  /* The ISO C Standard mandates enumerators to have type int before
-     C2X, even though the underlying type of an enum type is
-     unspecified.  C2X allows enumerators of any integer type.  During
-     the parsing of the enumeration, C2X specifies that constants
-     representable in int have type int, constants not representable
-     in int have the type of the given expression if any, and
-     constants not representable in int and derived by adding 1 to the
-     previous constant have the type of that constant unless the
-     addition would overflow or wraparound, in which case a wider type
-     of the same signedness is chosen automatically; after the
-     enumeration is parsed, all the constants have the type of the
-     enumeration if any do not fit in int.  */
-  if (int_fits_type_p (value, integer_type_node))
-    value = convert (integer_type_node, value);
+                            ? uintmax_type_node
+                            : intmax_type_node)))
+       /* GCC does not consider its types larger than intmax_t to be
+          extended integer types (although C2X would permit such types to
+          be considered extended integer types if all the features
+          required by <stdint.h> and <inttypes.h> macros, such as support
+          for integer constants and I/O, were present), so diagnose if
+          such a wider type is used.  (If the wider type arose from a
+          constant of such a type, that will also have been diagnosed,
+          but this is the only diagnostic in the case where it arises
+          from choosing a wider type automatically when adding 1
+          overflows.)  */
+       warned_range = pedwarn (loc, OPT_Wpedantic,
+                               "enumerator value outside the range of %qs",
+                               (TYPE_UNSIGNED (TREE_TYPE (value))
+                                ? "uintmax_t"
+                                : "intmax_t"));
+      if (!warned_range && !int_fits_type_p (value, integer_type_node))
+       pedwarn_c11 (loc, OPT_Wpedantic,
+                    "ISO C restricts enumerator values to range of %<int%> "
+                    "before C2X");
+
+      /* The ISO C Standard mandates enumerators to have type int before
+        C2X, even though the underlying type of an enum type is
+        unspecified.  C2X allows enumerators of any integer type.  During
+        the parsing of the enumeration, C2X specifies that constants
+        representable in int have type int, constants not representable
+        in int have the type of the given expression if any, and
+        constants not representable in int and derived by adding 1 to the
+        previous constant have the type of that constant unless the
+        addition would overflow or wraparound, in which case a wider type
+        of the same signedness is chosen automatically; after the
+        enumeration is parsed, all the constants have the type of the
+        enumeration if any do not fit in int.  */
+      if (int_fits_type_p (value, integer_type_node))
+       value = convert (integer_type_node, value);
+    }
 
   /* Set basis for default for next value.  */
-  the_enum->enum_next_value
-    = build_binary_op (EXPR_LOC_OR_LOC (value, input_location),
-                      PLUS_EXPR, value, integer_one_node, false);
+  if (ENUM_FIXED_UNDERLYING_TYPE_P (the_enum->enum_type))
+    {
+      tree underlying_type = ENUM_UNDERLYING_TYPE (the_enum->enum_type);
+      if (TREE_CODE (underlying_type) == BOOLEAN_TYPE)
+       /* A value of 2 following a value of 1 overflows bool, but we
+          cannot carry out addition directly on bool without
+          promotion, and converting the result of arithmetic in a
+          wider type back to bool would not produce the right result
+          for this overflow check.  */
+       the_enum->enum_next_value = invert_truthvalue_loc (loc, value);
+      else
+       the_enum->enum_next_value
+         = build_binary_op (EXPR_LOC_OR_LOC (value, input_location),
+                            PLUS_EXPR, convert (underlying_type, value),
+                            convert (underlying_type, integer_one_node),
+                            false);
+    }
+  else
+    the_enum->enum_next_value
+      = build_binary_op (EXPR_LOC_OR_LOC (value, input_location),
+                        PLUS_EXPR, value, integer_one_node, false);
   the_enum->enum_overflow = tree_int_cst_lt (the_enum->enum_next_value, value);
-  if (the_enum->enum_overflow)
+  if (the_enum->enum_overflow
+      && !ENUM_FIXED_UNDERLYING_TYPE_P (the_enum->enum_type))
     {
       /* Choose a wider type with the same signedness if
         available.  */
@@ -9691,7 +9810,8 @@ c_simulate_enum_decl (location_t loc, const char *name,
   input_location = loc;
 
   struct c_enum_contents the_enum;
-  tree enumtype = start_enum (loc, &the_enum, get_identifier (name));
+  tree enumtype = start_enum (loc, &the_enum, get_identifier (name),
+                             NULL_TREE);
 
   tree value_chain = NULL_TREE;
   string_int_pair *value;
@@ -11980,6 +12100,9 @@ declspecs_add_type (location_t loc, struct c_declspecs *specs,
            }
        }
       specs->type = type;
+      if (spec.has_enum_type_specifier
+         && spec.kind != ctsk_tagdef)
+       specs->enum_type_specifier_ref_p = true;
     }
 
   return specs;
index b468091..1a8b162 100644 (file)
@@ -387,13 +387,7 @@ c_get_alias_set (tree t)
   /* Allow aliasing between enumeral types and the underlying
      integer type.  This is required since those are compatible types.  */
   if (TREE_CODE (t) == ENUMERAL_TYPE)
-    {
-      tree t1 = c_common_type_for_size (tree_to_uhwi (TYPE_SIZE (t)),
-                                       /* short-cut commoning to signed
-                                          type.  */
-                                       false);
-      return get_alias_set (t1);
-    }
+    return get_alias_set (ENUM_UNDERLYING_TYPE (t));
 
   return c_common_get_alias_set (t);
 }
index 602e023..5bdcd93 100644 (file)
@@ -3011,6 +3011,7 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
            }
          t.expr = NULL_TREE;
          t.expr_const_operands = true;
+         t.has_enum_type_specifier = false;
          declspecs_add_type (name_token->location, specs, t);
          continue;
        }
@@ -3027,6 +3028,7 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
          t.spec = objc_get_protocol_qualified_type (NULL_TREE, proto);
          t.expr = NULL_TREE;
          t.expr_const_operands = true;
+         t.has_enum_type_specifier = false;
          declspecs_add_type (loc, specs, t);
          continue;
        }
@@ -3087,6 +3089,7 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
          t.spec = c_parser_peek_token (parser)->value;
          t.expr = NULL_TREE;
          t.expr_const_operands = true;
+         t.has_enum_type_specifier = false;
          declspecs_add_type (loc, specs, t);
          c_parser_consume_token (parser);
          break;
@@ -3151,6 +3154,7 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
              t.spec = error_mark_node;
              t.expr = NULL_TREE;
              t.expr_const_operands = true;
+             t.has_enum_type_specifier = false;
              if (type != NULL)
                t.spec = groktypename (type, &t.expr,
                                       &t.expr_const_operands);
@@ -3218,17 +3222,20 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
 /* Parse an enum specifier (C90 6.5.2.2, C99 6.7.2.2, C11 6.7.2.2).
 
    enum-specifier:
-     enum gnu-attributes[opt] identifier[opt] { enumerator-list }
-       gnu-attributes[opt]
-     enum gnu-attributes[opt] identifier[opt] { enumerator-list , }
-       gnu-attributes[opt]
+     enum gnu-attributes[opt] identifier[opt] enum-type-specifier[opt]
+       { enumerator-list } gnu-attributes[opt]
+     enum gnu-attributes[opt] identifier[opt] enum-type-specifier[opt]
+       { enumerator-list , } gnu-attributes[opt] enum-type-specifier[opt]
      enum gnu-attributes[opt] identifier
 
-   The form with trailing comma is new in C99.  The forms with
-   gnu-attributes are GNU extensions.  In GNU C, we accept any expression
-   without commas in the syntax (assignment expressions, not just
-   conditional expressions); assignment expressions will be diagnosed
-   as non-constant.
+   The form with trailing comma is new in C99; enum-type-specifiers
+   are new in C2x.  The forms with gnu-attributes are GNU extensions.
+   In GNU C, we accept any expression without commas in the syntax
+   (assignment expressions, not just conditional expressions);
+   assignment expressions will be diagnosed as non-constant.
+
+   enum-type-specifier:
+     : specifier-qualifier-list
 
    enumerator-list:
      enumerator
@@ -3256,6 +3263,7 @@ c_parser_enum_specifier (c_parser *parser)
   tree std_attrs = NULL_TREE;
   tree attrs;
   tree ident = NULL_TREE;
+  tree fixed_underlying_type = NULL_TREE;
   location_t enum_loc;
   location_t ident_loc = UNKNOWN_LOCATION;  /* Quiet warning.  */
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_ENUM));
@@ -3274,6 +3282,83 @@ c_parser_enum_specifier (c_parser *parser)
       enum_loc = ident_loc;
       c_parser_consume_token (parser);
     }
+  if (c_parser_next_token_is (parser, CPP_COLON)
+      /* Distinguish an enum-type-specifier from a bit-field
+        declaration of the form "enum e : constant-expression;".  */
+      && c_token_starts_typename (c_parser_peek_2nd_token (parser)))
+    {
+      pedwarn_c11 (enum_loc, OPT_Wpedantic,
+                  "ISO C does not support specifying %<enum%> underlying "
+                  "types before C2X");
+      if (ident)
+       {
+         /* The tag is in scope during the enum-type-specifier (which
+            may refer to the tag inside typeof).  */
+         ret = parser_xref_tag (ident_loc, ENUMERAL_TYPE, ident,
+                                have_std_attrs, std_attrs, true);
+         if (!ENUM_FIXED_UNDERLYING_TYPE_P (ret.spec))
+           error_at (enum_loc, "%<enum%> declared both with and without "
+                     "fixed underlying type");
+       }
+      else
+       {
+         /* There must be an enum definition, so this initialization
+            (to avoid possible warnings about uninitialized data)
+            will be replaced later (either with the results of that
+            definition, or with the results of error handling for the
+            case of no tag and no definition).  */
+         ret.spec = NULL_TREE;
+         ret.kind = ctsk_tagdef;
+         ret.expr = NULL_TREE;
+         ret.expr_const_operands = true;
+         ret.has_enum_type_specifier = true;
+       }
+      c_parser_consume_token (parser);
+      struct c_declspecs *specs = build_null_declspecs ();
+      c_parser_declspecs (parser, specs, false, true, false, false, false,
+                         false, true, cla_prefer_id);
+      finish_declspecs (specs);
+      if (specs->default_int_p)
+       error_at (enum_loc, "no %<enum%> underlying type specified");
+      else if (TREE_CODE (specs->type) != INTEGER_TYPE
+              && TREE_CODE (specs->type) != BOOLEAN_TYPE)
+       {
+         error_at (enum_loc, "invalid %<enum%> underlying type");
+         specs->type = integer_type_node;
+       }
+      else if (specs->restrict_p)
+       error_at (enum_loc, "invalid use of %<restrict%>");
+      fixed_underlying_type = TYPE_MAIN_VARIANT (specs->type);
+      if (ident)
+       {
+         /* The type specified must be consistent with any previously
+            specified underlying type.  If this is a newly declared
+            type, it is now a complete type.  */
+         if (ENUM_FIXED_UNDERLYING_TYPE_P (ret.spec)
+             && ENUM_UNDERLYING_TYPE (ret.spec) == NULL_TREE)
+           {
+             TYPE_MIN_VALUE (ret.spec) =
+               TYPE_MIN_VALUE (fixed_underlying_type);
+             TYPE_MAX_VALUE (ret.spec) =
+               TYPE_MAX_VALUE (fixed_underlying_type);
+             TYPE_UNSIGNED (ret.spec) = TYPE_UNSIGNED (fixed_underlying_type);
+             SET_TYPE_ALIGN (ret.spec, TYPE_ALIGN (fixed_underlying_type));
+             TYPE_SIZE (ret.spec) = NULL_TREE;
+             TYPE_PRECISION (ret.spec) =
+               TYPE_PRECISION (fixed_underlying_type);
+             ENUM_UNDERLYING_TYPE (ret.spec) = fixed_underlying_type;
+             layout_type (ret.spec);
+           }
+         else if (ENUM_FIXED_UNDERLYING_TYPE_P (ret.spec)
+                  && !comptypes (fixed_underlying_type,
+                                 ENUM_UNDERLYING_TYPE (ret.spec)))
+           {
+             error_at (enum_loc, "%<enum%> underlying type incompatible with "
+                       "previous declaration");
+             fixed_underlying_type = ENUM_UNDERLYING_TYPE (ret.spec);
+           }
+       }
+    }
   if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
     {
       /* Parse an enum definition.  */
@@ -3284,7 +3369,7 @@ c_parser_enum_specifier (c_parser *parser)
         forward order at the end.  */
       tree values;
       timevar_push (TV_PARSE_ENUM);
-      type = start_enum (enum_loc, &the_enum, ident);
+      type = start_enum (enum_loc, &the_enum, ident, fixed_underlying_type);
       values = NULL_TREE;
       c_parser_consume_token (parser);
       while (true)
@@ -3368,6 +3453,7 @@ c_parser_enum_specifier (c_parser *parser)
       ret.kind = ctsk_tagdef;
       ret.expr = NULL_TREE;
       ret.expr_const_operands = true;
+      ret.has_enum_type_specifier = fixed_underlying_type != NULL_TREE;
       timevar_pop (TV_PARSE_ENUM);
       return ret;
     }
@@ -3378,6 +3464,7 @@ c_parser_enum_specifier (c_parser *parser)
       ret.kind = ctsk_tagref;
       ret.expr = NULL_TREE;
       ret.expr_const_operands = true;
+      ret.has_enum_type_specifier = false;
       return ret;
     }
   /* Attributes may only appear when the members are defined or in
@@ -3386,15 +3473,18 @@ c_parser_enum_specifier (c_parser *parser)
      standard C).  */
   if (have_std_attrs && c_parser_next_token_is_not (parser, CPP_SEMICOLON))
     c_parser_error (parser, "expected %<;%>");
-  ret = parser_xref_tag (ident_loc, ENUMERAL_TYPE, ident, have_std_attrs,
-                        std_attrs);
-  /* In ISO C, enumerated types can be referred to only if already
-     defined.  */
-  if (pedantic && !COMPLETE_TYPE_P (ret.spec))
+  if (fixed_underlying_type == NULL_TREE)
     {
-      gcc_assert (ident);
-      pedwarn (enum_loc, OPT_Wpedantic,
-              "ISO C forbids forward references to %<enum%> types");
+      ret = parser_xref_tag (ident_loc, ENUMERAL_TYPE, ident, have_std_attrs,
+                            std_attrs, false);
+      /* In ISO C, enumerated types without a fixed underlying type
+        can be referred to only if already defined.  */
+      if (pedantic && !COMPLETE_TYPE_P (ret.spec))
+       {
+         gcc_assert (ident);
+         pedwarn (enum_loc, OPT_Wpedantic,
+                  "ISO C forbids forward references to %<enum%> types");
+       }
     }
   return ret;
 }
@@ -3590,6 +3680,7 @@ c_parser_struct_or_union_specifier (c_parser *parser)
       ret.kind = ctsk_tagdef;
       ret.expr = NULL_TREE;
       ret.expr_const_operands = true;
+      ret.has_enum_type_specifier = false;
       timevar_pop (TV_PARSE_STRUCT);
       return ret;
     }
@@ -3600,6 +3691,7 @@ c_parser_struct_or_union_specifier (c_parser *parser)
       ret.kind = ctsk_tagref;
       ret.expr = NULL_TREE;
       ret.expr_const_operands = true;
+      ret.has_enum_type_specifier = false;
       return ret;
     }
   /* Attributes may only appear when the members are defined or in
@@ -3608,7 +3700,8 @@ c_parser_struct_or_union_specifier (c_parser *parser)
     c_parser_error (parser, "expected %<;%>");
   /* ??? Existing practice is that GNU attributes are ignored after
      the struct or union keyword when not defining the members.  */
-  ret = parser_xref_tag (ident_loc, code, ident, have_std_attrs, std_attrs);
+  ret = parser_xref_tag (ident_loc, code, ident, have_std_attrs, std_attrs,
+                        false);
   return ret;
 }
 
@@ -3817,6 +3910,7 @@ c_parser_typeof_specifier (c_parser *parser)
   ret.spec = error_mark_node;
   ret.expr = NULL_TREE;
   ret.expr_const_operands = true;
+  ret.has_enum_type_specifier = false;
   if (c_parser_next_token_is_keyword (parser, RID_TYPEOF))
     {
       is_unqual = false;
@@ -19153,15 +19247,14 @@ restart:
          && TREE_CODE (TREE_OPERAND (lhs, 1)) == COMPOUND_EXPR
          && TREE_CODE (TREE_OPERAND (TREE_OPERAND (lhs, 1), 0)) == MODIFY_EXPR
          && TREE_OPERAND (TREE_OPERAND (lhs, 1), 1) == TREE_OPERAND (lhs, 0)
-         && TREE_CODE (TREE_TYPE (TREE_OPERAND (TREE_OPERAND
-                                             (TREE_OPERAND (lhs, 1), 0), 0)))
-            == BOOLEAN_TYPE)
+         && C_BOOLEAN_TYPE_P (TREE_TYPE (TREE_OPERAND (TREE_OPERAND
+                                             (TREE_OPERAND (lhs, 1), 0), 0))))
        /* Undo effects of boolean_increment for post {in,de}crement.  */
        lhs = TREE_OPERAND (TREE_OPERAND (lhs, 1), 0);
       /* FALLTHRU */
     case MODIFY_EXPR:
       if (TREE_CODE (lhs) == MODIFY_EXPR
-         && TREE_CODE (TREE_TYPE (TREE_OPERAND (lhs, 0))) == BOOLEAN_TYPE)
+         && C_BOOLEAN_TYPE_P (TREE_TYPE (TREE_OPERAND (lhs, 0))))
        {
          /* Undo effects of boolean_increment.  */
          if (integer_onep (TREE_OPERAND (lhs, 1)))
index e7cdd2f..d787dd4 100644 (file)
@@ -122,6 +122,14 @@ along with GCC; see the file COPYING3.  If not see
    been folded.  */
 #define SAVE_EXPR_FOLDED_P(EXP)        TREE_LANG_FLAG_1 (SAVE_EXPR_CHECK (EXP))
 
+/* Whether a type has boolean semantics: either a boolean type or an
+   enumeration type with a boolean type as its underlying type.  */
+#define C_BOOLEAN_TYPE_P(TYPE)                                         \
+  (TREE_CODE (TYPE) == BOOLEAN_TYPE                                    \
+   || (TREE_CODE (TYPE) == ENUMERAL_TYPE                               \
+       && ENUM_UNDERLYING_TYPE (TYPE) != NULL_TREE                     \
+       && TREE_CODE (ENUM_UNDERLYING_TYPE (TYPE)) == BOOLEAN_TYPE))
+
 /* Record parser information about an expression that is irrelevant
    for code generation alongside a tree representing its value.  */
 struct c_expr
@@ -216,6 +224,10 @@ struct c_typespec {
   /* Whether the expression has operands suitable for use in constant
      expressions.  */
   bool expr_const_operands;
+  /* Whether the type specifier includes an enum type specifier (that
+     is, ": specifier-qualifier-list" in a declaration using
+     "enum").  */
+  bool has_enum_type_specifier;
   /* The specifier itself.  */
   tree spec;
   /* An expression to be evaluated before the type specifier, in the
@@ -412,6 +424,12 @@ struct c_declspecs {
   /* Whether any alignment specifier (even with zero alignment) was
      specified.  */
   BOOL_BITFIELD alignas_p : 1;
+  /* Whether an enum type specifier (": specifier-qualifier-list") was
+     specified other than in a definition of that enum (if so, this is
+     invalid unless it is an empty declaration "enum identifier
+     enum-type-specifier;", but such an empty declaration is valid in
+     C2x when "enum identifier;" would not be).  */
+  BOOL_BITFIELD enum_type_specifier_ref_p : 1;
   /* The address space that the declaration belongs to.  */
   addr_space_t address_space;
 };
@@ -525,6 +543,9 @@ struct c_enum_contents
      constant value.  */
   tree enum_next_value;
 
+  /* The enumeration type itself.  */
+  tree enum_type;
+
   /* Nonzero means that there was overflow computing enum_next_value.  */
   int enum_overflow;
 };
@@ -625,7 +646,7 @@ extern void c_warn_unused_attributes (tree);
 extern tree c_warn_type_attributes (tree);
 extern void shadow_tag (const struct c_declspecs *);
 extern void shadow_tag_warned (const struct c_declspecs *, int);
-extern tree start_enum (location_t, struct c_enum_contents *, tree);
+extern tree start_enum (location_t, struct c_enum_contents *, tree, tree);
 extern bool start_function (struct c_declspecs *, struct c_declarator *, tree);
 extern tree start_decl (struct c_declarator *, struct c_declspecs *, bool,
                        tree, location_t * = NULL);
@@ -637,7 +658,7 @@ extern void temp_store_parm_decls (tree, tree);
 extern void temp_pop_parm_decls (void);
 extern tree xref_tag (enum tree_code, tree);
 extern struct c_typespec parser_xref_tag (location_t, enum tree_code, tree,
-                                         bool, tree);
+                                         bool, tree, bool);
 extern struct c_parm *build_c_parm (struct c_declspecs *, tree,
                                    struct c_declarator *, location_t);
 extern struct c_declarator *build_attrs_declarator (tree,
index 92f3afc..6c16647 100644 (file)
@@ -420,9 +420,11 @@ composite_type (tree t1, tree t2)
      (DR#013 question 3).  For consistency, use the enumerated type as
      the composite type.  */
 
-  if (code1 == ENUMERAL_TYPE && code2 == INTEGER_TYPE)
+  if (code1 == ENUMERAL_TYPE
+      && (code2 == INTEGER_TYPE || code2 == BOOLEAN_TYPE))
     return t1;
-  if (code2 == ENUMERAL_TYPE && code1 == INTEGER_TYPE)
+  if (code2 == ENUMERAL_TYPE
+      && (code1 == INTEGER_TYPE || code1 == BOOLEAN_TYPE))
     return t2;
 
   gcc_assert (code1 == code2);
@@ -1025,9 +1027,9 @@ tree
 common_type (tree t1, tree t2)
 {
   if (TREE_CODE (t1) == ENUMERAL_TYPE)
-    t1 = c_common_type_for_size (TYPE_PRECISION (t1), 1);
+    t1 = ENUM_UNDERLYING_TYPE (t1);
   if (TREE_CODE (t2) == ENUMERAL_TYPE)
-    t2 = c_common_type_for_size (TYPE_PRECISION (t2), 1);
+    t2 = ENUM_UNDERLYING_TYPE (t2);
 
   /* If both types are BOOLEAN_TYPE, then return boolean_type_node.  */
   if (TREE_CODE (t1) == BOOLEAN_TYPE
@@ -1125,7 +1127,7 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
       && COMPLETE_TYPE_P (t1)
       && TREE_CODE (t2) != ENUMERAL_TYPE)
     {
-      t1 = c_common_type_for_size (TYPE_PRECISION (t1), TYPE_UNSIGNED (t1));
+      t1 = ENUM_UNDERLYING_TYPE (t1);
       if (TREE_CODE (t2) != VOID_TYPE)
        {
          if (enum_and_int_p != NULL)
@@ -1138,7 +1140,7 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
           && COMPLETE_TYPE_P (t2)
           && TREE_CODE (t1) != ENUMERAL_TYPE)
     {
-      t2 = c_common_type_for_size (TYPE_PRECISION (t2), TYPE_UNSIGNED (t2));
+      t2 = ENUM_UNDERLYING_TYPE (t2);
       if (TREE_CODE (t1) != VOID_TYPE)
        {
          if (enum_and_int_p != NULL)
@@ -2193,15 +2195,19 @@ perform_integral_promotions (tree exp)
 
   gcc_assert (INTEGRAL_TYPE_P (type));
 
-  /* Normally convert enums to int,
-     but convert wide enums to something wider.  */
+  /* Convert enums to the result of applying the integer promotions to
+     their underlying type.  */
   if (code == ENUMERAL_TYPE)
     {
-      type = c_common_type_for_size (MAX (TYPE_PRECISION (type),
-                                         TYPE_PRECISION (integer_type_node)),
-                                    ((TYPE_PRECISION (type)
-                                      >= TYPE_PRECISION (integer_type_node))
-                                     && TYPE_UNSIGNED (type)));
+      type = ENUM_UNDERLYING_TYPE (type);
+      if (c_promoting_integer_type_p (type))
+       {
+         if (TYPE_UNSIGNED (type)
+             && TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node))
+           type = unsigned_type_node;
+         else
+           type = integer_type_node;
+       }
 
       return convert (type, exp);
     }
@@ -3932,7 +3938,7 @@ parser_build_binary_op (location_t location, enum tree_code code,
            }
          while (1);
        }
-      if (TREE_CODE (TREE_TYPE (t)) != BOOLEAN_TYPE)
+      if (!C_BOOLEAN_TYPE_P (TREE_TYPE (t)))
        warn_logical_not_parentheses (location, code, arg1.value, arg2.value);
     }
 
@@ -4537,7 +4543,7 @@ build_unary_op (location_t location, enum tree_code code, tree xarg,
          while (TREE_CODE (e) == COMPOUND_EXPR)
            e = TREE_OPERAND (e, 1);
 
-         if ((TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE
+         if ((C_BOOLEAN_TYPE_P (TREE_TYPE (arg))
               || truth_value_p (TREE_CODE (e))))
            {
              auto_diagnostic_group d;
@@ -4669,7 +4675,7 @@ build_unary_op (location_t location, enum tree_code code, tree xarg,
                        "decrement of enumeration value is invalid in C++");
        }
 
-      if (TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE)
+      if (C_BOOLEAN_TYPE_P (TREE_TYPE (arg)))
        {
          if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR)
            warning_at (location, OPT_Wbool_operation,
@@ -4831,7 +4837,7 @@ build_unary_op (location_t location, enum tree_code code, tree xarg,
            goto return_build_unary_op;
          }
 
-       if (TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE)
+       if (C_BOOLEAN_TYPE_P (TREE_TYPE (arg)))
          val = boolean_increment (code, arg);
        else
          val = build2 (code, TREE_TYPE (arg), arg, inc);
@@ -7087,7 +7093,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
                                         rhstype);
 
       bool save = in_late_binary_op;
-      if (codel == BOOLEAN_TYPE || codel == COMPLEX_TYPE
+      if (C_BOOLEAN_TYPE_P (type) || codel == COMPLEX_TYPE
          || (coder == REAL_TYPE
              && (codel == INTEGER_TYPE || codel == ENUMERAL_TYPE)
              && sanitize_flags_p (SANITIZE_FLOAT_CAST)))
@@ -7734,7 +7740,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 
       return convert (type, rhs);
     }
-  else if (codel == BOOLEAN_TYPE
+  else if (C_BOOLEAN_TYPE_P (type)
           /* The type nullptr_t may be converted to bool.  The
              result is false.  */
           && (coder == POINTER_TYPE || coder == NULLPTR_TYPE))
@@ -11002,7 +11008,7 @@ c_finish_return (location_t loc, tree retval, tree origtype)
        return NULL_TREE;
 
       save = in_late_binary_op;
-      if (TREE_CODE (TREE_TYPE (res)) == BOOLEAN_TYPE
+      if (C_BOOLEAN_TYPE_P (TREE_TYPE (res))
          || TREE_CODE (TREE_TYPE (res)) == COMPLEX_TYPE
          || (TREE_CODE (TREE_TYPE (t)) == REAL_TYPE
              && (TREE_CODE (TREE_TYPE (res)) == INTEGER_TYPE
@@ -11164,7 +11170,7 @@ c_start_switch (location_t switch_loc,
          while (TREE_CODE (e) == COMPOUND_EXPR)
            e = TREE_OPERAND (e, 1);
 
-         if ((TREE_CODE (type) == BOOLEAN_TYPE
+         if ((C_BOOLEAN_TYPE_P (type)
               || truth_value_p (TREE_CODE (e)))
              /* Explicit cast to int suppresses this warning.  */
              && !(TREE_CODE (type) == INTEGER_TYPE
@@ -12493,9 +12499,9 @@ build_binary_op (location_t location, enum tree_code code,
       else if (code1 == NULLPTR_TYPE && null_pointer_constant_p (orig_op0))
        result_type = (INTEGRAL_TYPE_P (type0)
                       ? build_pointer_type (type0) : type0);
-      if ((TREE_CODE (TREE_TYPE (orig_op0)) == BOOLEAN_TYPE
+      if ((C_BOOLEAN_TYPE_P (TREE_TYPE (orig_op0))
           || truth_value_p (TREE_CODE (orig_op0)))
-         ^ (TREE_CODE (TREE_TYPE (orig_op1)) == BOOLEAN_TYPE
+         ^ (C_BOOLEAN_TYPE_P (TREE_TYPE (orig_op1))
             || truth_value_p (TREE_CODE (orig_op1))))
        maybe_warn_bool_compare (location, code, orig_op0, orig_op1);
       break;
@@ -12638,9 +12644,9 @@ build_binary_op (location_t location, enum tree_code code,
          instrument_expr = build_call_expr_loc (location, tt, 2, op0, op1);
        }
 
-      if ((TREE_CODE (TREE_TYPE (orig_op0)) == BOOLEAN_TYPE
+      if ((C_BOOLEAN_TYPE_P (TREE_TYPE (orig_op0))
           || truth_value_p (TREE_CODE (orig_op0)))
-         ^ (TREE_CODE (TREE_TYPE (orig_op1)) == BOOLEAN_TYPE
+         ^ (C_BOOLEAN_TYPE_P (TREE_TYPE (orig_op1))
             || truth_value_p (TREE_CODE (orig_op1))))
        maybe_warn_bool_compare (location, code, orig_op0, orig_op1);
       break;
index 40f5bf8..6d84514 100644 (file)
@@ -4507,30 +4507,6 @@ get_vec_init_expr (tree t)
 #define OPAQUE_ENUM_P(TYPE)                            \
   (TREE_CODE (TYPE) == ENUMERAL_TYPE && ENUM_IS_OPAQUE (TYPE))
 
-/* Determines whether an ENUMERAL_TYPE has an explicit
-   underlying type.  */
-#define ENUM_FIXED_UNDERLYING_TYPE_P(NODE) (TYPE_LANG_FLAG_5 (NODE))
-
-/* Returns the underlying type of the given enumeration type. The
-   underlying type is determined in different ways, depending on the
-   properties of the enum:
-
-     - In C++0x, the underlying type can be explicitly specified, e.g.,
-
-         enum E1 : char { ... } // underlying type is char
-
-     - In a C++0x scoped enumeration, the underlying type is int
-       unless otherwises specified:
-
-         enum class E2 { ... } // underlying type is int
-
-     - Otherwise, the underlying type is determined based on the
-       values of the enumerators. In this case, the
-       ENUM_UNDERLYING_TYPE will not be set until after the definition
-       of the enumeration is completed by finish_enum.  */
-#define ENUM_UNDERLYING_TYPE(TYPE) \
-  TREE_TYPE (ENUMERAL_TYPE_CHECK (TYPE))
-
 /* [dcl.init.aggr]
 
    An aggregate is an array or a class with no user-provided
diff --git a/gcc/testsuite/gcc.dg/c11-enum-4.c b/gcc/testsuite/gcc.dg/c11-enum-4.c
new file mode 100644 (file)
index 0000000..57dd92a
--- /dev/null
@@ -0,0 +1,7 @@
+/* Test C2x enumerations with fixed underlying type are diagnosed for C11.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+enum e1 : int; /* { dg-error "ISO C does not support specifying 'enum' underlying types" } */
+enum e2 : short { A }; /* { dg-error "ISO C does not support specifying 'enum' underlying types" } */
+enum : short { B }; /* { dg-error "ISO C does not support specifying 'enum' underlying types" } */
diff --git a/gcc/testsuite/gcc.dg/c11-enum-5.c b/gcc/testsuite/gcc.dg/c11-enum-5.c
new file mode 100644 (file)
index 0000000..91d681f
--- /dev/null
@@ -0,0 +1,7 @@
+/* Test C2x enumerations with fixed underlying type are diagnosed for C11.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic" } */
+
+enum e1 : int; /* { dg-warning "ISO C does not support specifying 'enum' underlying types" } */
+enum e2 : short { A }; /* { dg-warning "ISO C does not support specifying 'enum' underlying types" } */
+enum : short { B }; /* { dg-warning "ISO C does not support specifying 'enum' underlying types" } */
diff --git a/gcc/testsuite/gcc.dg/c11-enum-6.c b/gcc/testsuite/gcc.dg/c11-enum-6.c
new file mode 100644 (file)
index 0000000..cd708bd
--- /dev/null
@@ -0,0 +1,8 @@
+/* Test C2x enumerations with fixed underlying type are not diagnosed for C11
+   with -pedantic-errors -Wno-c11-c2x-compat.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors -Wno-c11-c2x-compat" } */
+
+enum e1 : int;
+enum e2 : short { A };
+enum : short { B };
diff --git a/gcc/testsuite/gcc.dg/c2x-enum-6.c b/gcc/testsuite/gcc.dg/c2x-enum-6.c
new file mode 100644 (file)
index 0000000..5ab9694
--- /dev/null
@@ -0,0 +1,167 @@
+/* Test C2x enumerations with fixed underlying type.  Valid code.  */
+/* { dg-do run } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+/* Check a type while defining an enum (via a diagnostic for incompatible
+   pointer types if the wrong type was chosen).  */
+#define TYPE_CHECK(cst, type)                                          \
+  cst ## _type_check = sizeof (1 ? (type *) 0 : (typeof (cst) *) 0)
+
+extern int i;
+
+enum e1 : short { e1a = __SHRT_MAX__,
+    TYPE_CHECK (e1a, short),
+    e1z = (long long) 0,
+    TYPE_CHECK (e1z, enum e1),
+    e1b = -__SHRT_MAX__ - 1,
+    e1c,
+    TYPE_CHECK (e1c, enum e1) };
+extern enum e1 e1v;
+extern typeof (e1a) e1v;
+extern typeof (e1b) e1v;
+extern typeof (e1c) e1v;
+extern typeof (e1z) e1v;
+extern short e1v;
+static_assert (e1a == __SHRT_MAX__);
+static_assert (e1b == -__SHRT_MAX__ - 1);
+static_assert (e1c == -__SHRT_MAX__);
+static_assert (e1a > 0);
+static_assert (e1b < 0);
+static_assert (e1c < 0);
+static_assert (e1z == 0);
+extern typeof (+e1v) i;
+extern typeof (+e1a) i;
+extern typeof (e1a + e1b) i;
+enum e1 : short;
+enum e1 : volatile short;
+enum e1 : _Atomic short;
+enum e1 : typeof (short);
+
+enum e2 : bool { b0, b1, b0a = 0, b1a = 1 };
+extern enum e2 e2v;
+extern typeof (b0) e2v;
+extern typeof (b0a) e2v;
+extern typeof (b1) e2v;
+extern typeof (b1a) e2v;
+extern bool e2v;
+extern typeof (+e2v) i;
+extern typeof (+b0) i;
+static_assert (b0 == 0);
+static_assert (b1 == 1);
+static_assert (b0a == 0);
+static_assert (b1a == 1);
+
+enum e3 : volatile const _Atomic unsigned short;
+enum e3 : unsigned short { e3a, e3b };
+extern enum e3 e3v;
+extern typeof (e3a) e3v;
+extern typeof (e3b) e3v;
+extern unsigned short e3v;
+
+/* The enum type is complete from the end of the first enum type specifier
+   (which is nested inside another enum type specifier in this example).  */
+enum e4 : typeof ((enum e4 : long { e4a = sizeof (enum e4) })0, 0L);
+extern enum e4 e4v;
+extern typeof (e4a) e4v;
+extern long e4v;
+
+enum e5 : unsigned int;
+extern enum e5 e5v;
+extern typeof (e5v + e5v) e5v;
+extern unsigned int e5v;
+
+enum : unsigned short { e6a, e6b, TYPE_CHECK (e6a, unsigned short) } e6v;
+extern typeof (e6a) e6v;
+extern typeof (e6b) e6v;
+extern unsigned short e6v;
+
+struct s1;
+struct s2 { int a; };
+union u1;
+union u2 { int a; };
+enum xe1 { XE1 };
+enum xe2 : long long { XE2 };
+enum xe3 : unsigned long;
+
+void
+f ()
+{
+  /* Tags can be redeclared in an inner scope.  */
+  enum s1 : char;
+  enum s2 : int { S2 };
+  enum u1 : long { U1 };
+  enum u2 : unsigned char;
+  enum xe1 : long long;
+  enum xe2 : short;
+  enum xe3 : char { XE3 };
+  static_assert (sizeof (enum xe3) == 1);
+  static_assert (sizeof (enum xe2) == sizeof (short));
+  static_assert (sizeof (enum xe1) == sizeof (long long));
+}
+
+void *p;
+typeof (nullptr) np;
+
+extern void abort (void);
+extern void exit (int);
+
+int
+main ()
+{
+  /* Conversions to enums with fixed underlying type have the same semantics as
+     converting to the underlying type.  */
+  volatile enum e1 e1vm;
+  volatile enum e2 e2vm;
+  e1vm = __LONG_LONG_MAX__; /* { dg-warning "overflow" } */
+  if (e1vm != (short) __LONG_LONG_MAX__)
+    abort ();
+  e2vm = 10;
+  if (e2vm != 1)
+    abort ();
+  e2vm = 0;
+  if (e2vm != 0)
+    abort ();
+  /* Arithmetic on enums with fixed underlying type has the same semantics as
+     arithmetic on the underlying type; in particular, the special semantics
+     for bool apply to enums with bool as fixed underlying type.  */
+  if (e2vm++ != 0)
+    abort ();
+  if (e2vm != 1)
+    abort ();
+  if (e2vm++ != 1)
+    abort ();
+  if (e2vm != 1)
+    abort ();
+  if (e2vm-- != 1)
+    abort ();
+  if (e2vm != 0)
+    abort ();
+  if (e2vm-- != 0)
+    abort ();
+  if (e2vm != 1)
+    abort ();
+  if (++e2vm != 1)
+    abort ();
+  if (e2vm != 1)
+    abort ();
+  e2vm = 0;
+  if (++e2vm != 1)
+    abort ();
+  if (e2vm != 1)
+    abort ();
+  if (--e2vm != 0)
+    abort ();
+  if (e2vm != 0)
+    abort ();
+  if (--e2vm != 1)
+    abort ();
+  if (e2vm != 1)
+    abort ();
+  e2vm = p;
+  e2vm = np;
+  e2vm = (bool) p;
+  e2vm = (bool) np;
+  if (e2vm != 0)
+    abort ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/c2x-enum-7.c b/gcc/testsuite/gcc.dg/c2x-enum-7.c
new file mode 100644 (file)
index 0000000..08bae31
--- /dev/null
@@ -0,0 +1,97 @@
+/* Test C2x enumerations with fixed underlying type.  Invalid code.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+/* An enum type specifier may only be used when the enum is defined, or in a
+   declaration of the form "enum name enum-type-specifier;".  */
+extern enum e1 : int; /* { dg-error "storage class specifier in empty declaration with 'enum' underlying type" } */
+_Thread_local enum e2 : short; /* { dg-error "'_Thread_local' in empty declaration with 'enum' underlying type" } */
+const enum e3 : long; /* { dg-error "type qualifier in empty declaration with 'enum' underlying type" } */
+alignas (8) enum e4 : long; /* { dg-error "'alignas' in empty declaration with 'enum' underlying type" } */
+inline enum e5 : unsigned; /* { dg-error "'inline' in empty declaration" } */
+_Noreturn enum e6 : unsigned; /* { dg-error "'_Noreturn' in empty declaration" } */
+auto enum e7 : unsigned; /* { dg-error "'auto' in file-scope empty declaration" } */
+register enum e8 : unsigned; /* { dg-error "'register' in file-scope empty declaration" } */
+
+/* When the enum is defined, some extra declaration specifiers are permitted,
+   but diagnosed as useless.  */
+extern enum e9 : int { E9 }; /* { dg-warning "useless storage class specifier in empty declaration" } */
+_Thread_local enum e10 : short { E10 }; /* { dg-warning "useless '_Thread_local' in empty declaration" } */
+const enum e11 : long { E11 }; /* { dg-warning "useless type qualifier in empty declaration" } */
+alignas (8) enum e12 : long { E12 }; /* { dg-warning "useless '_Alignas' in empty declaration" } */
+
+/* Nothing else may be declared with an enum type specifier for an enum not
+   being defined in that declaration.  */
+enum e13 : short x13; /* { dg-error "'enum' underlying type may not be specified here" } */
+enum e14 : short f14 (); /* { dg-error "'enum' underlying type may not be specified here" } */
+typeof (enum e15 : long) x15; /* { dg-error "'enum' underlying type may not be specified here" } */
+int f16 (enum e16 : char p); /* { dg-error "'enum' underlying type may not be specified here" } */
+/* { dg-warning "will not be visible outside of this definition or declaration" "warning" { target *-*-* } .-1 } */
+int f17 (enum e17 : char); /* { dg-error "'enum' underlying type may not be specified here" } */
+/* { dg-warning "will not be visible outside of this definition or declaration" "warning" { target *-*-* } .-1 } */
+struct s18 { enum e18 : int x; }; /* { dg-error "'enum' underlying type may not be specified here" } */
+
+/* But those are OK if the enum content is defined.  */
+enum e19 : short { E19 } x19;
+enum e20 : long { E20 } f20 ();
+typeof (enum e21 : long { E21 }) x21;
+int f22 (enum e22 : long long { E22 } p); /* { dg-warning "will not be visible outside of this definition or declaration" } */
+int f23 (enum e23 : long long { E23 } p); /* { dg-warning "will not be visible outside of this definition or declaration" } */
+struct s24 { enum e24 : int { E24 } x; };
+
+/* Incompatible kinds of tags in the same scope are errors.  */
+struct s25;
+enum s25 : int; /* { dg-error "wrong kind of tag" } */
+struct s26;
+enum s26 : int { E26 }; /* { dg-error "wrong kind of tag" } */
+struct s27 { int x; };
+enum s27 : int; /* { dg-error "wrong kind of tag" } */
+struct s28 { int x; };
+enum s28 : int { E28 }; /* { dg-error "wrong kind of tag" } */
+union u29;
+enum u29 : int; /* { dg-error "wrong kind of tag" } */
+union u30;
+enum u30 : int { E30 }; /* { dg-error "wrong kind of tag" } */
+union u31 { int x; };
+enum u31 : int; /* { dg-error "wrong kind of tag" } */
+union u32 { int x; };
+enum u32 : int { E32 }; /* { dg-error "wrong kind of tag" } */
+
+/* If an enum has a fixed underlying type, that must be given when defining the
+   enum.  */
+enum e33 : short;
+enum e33 { E33 }; /* { dg-error "'enum' declared with but defined without fixed underlying type" } */
+
+/* An enum defined without a fixed underlying type cannot then be declared with
+   one.  */
+enum e34 { E34A = -__INT_MAX__, E34B = __INT_MAX__ };
+enum e34 : int; /* { dg-error "'enum' declared both with and without fixed underlying type" } */
+
+/* An enum with a fixed underlying type cannot be declared with an incompatible
+   fixed underlying type.  */
+enum e35 : int;
+enum e35 : unsigned int; /* { dg-error "'enum' underlying type incompatible with previous declaration" } */
+enum e36 : int;
+enum e36 : unsigned int { E36 }; /* { dg-error "'enum' underlying type incompatible with previous declaration" } */
+enum e37 : unsigned int { E37 };
+enum e37 : int; /* { dg-error "'enum' underlying type incompatible with previous declaration" } */
+
+/* Enumeration constants must fit in the fixed underlying type.  */
+enum e38 : unsigned char { E38 = (unsigned long long)((unsigned char) -1) + 1 }; /* { dg-error "enumerator value outside the range of underlying type" } */
+enum e39 : unsigned int { E39 = -1 }; /* { dg-error "enumerator value outside the range of underlying type" } */
+enum e40 : int { E40 = __INT_MAX__, E40A }; /* { dg-error "overflow in enumeration values" } */
+enum e41 : unsigned int { E41 = (unsigned int) -1, E41A }; /* { dg-error "overflow in enumeration values" } */
+enum e42 : bool { E42 = 2 }; /* { dg-error "enumerator value outside the range of underlying type" } */
+enum e43 : bool { E43 = 1, E43A }; /* { dg-error "overflow in enumeration values" } */
+
+/* The underlying type must be an integer type, not itself an enum (or
+   bit-precise) type.  */
+enum e44 : double; /* { dg-error "invalid 'enum' underlying type" } */
+typedef int T;
+enum e45 : T;
+typedef int *TP;
+enum e46 : TP; /* { dg-error "invalid 'enum' underlying type" } */
+enum e47 : enum e45; /* { dg-error "invalid 'enum' underlying type" } */
+enum e48 : const; /* { dg-error "no 'enum' underlying type specified" } */
+/* 'restrict' is not valid on integer types.  */
+enum e49 : int restrict; /* { dg-error "invalid use of 'restrict'" } */
diff --git a/gcc/testsuite/gcc.dg/c2x-enum-8.c b/gcc/testsuite/gcc.dg/c2x-enum-8.c
new file mode 100644 (file)
index 0000000..f7757fc
--- /dev/null
@@ -0,0 +1,7 @@
+/* Test C2x enumerations with fixed underlying type.  Test -Wc11-c2x-compat
+   warnings.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors -Wc11-c2x-compat" } */
+
+enum e1 : int; /* { dg-warning "ISO C does not support specifying 'enum' underlying types before" } */
+enum e2 : short { E2 }; /* { dg-warning "ISO C does not support specifying 'enum' underlying types before" } */
diff --git a/gcc/testsuite/gcc.dg/gnu2x-enum-1.c b/gcc/testsuite/gcc.dg/gnu2x-enum-1.c
new file mode 100644 (file)
index 0000000..b72ed73
--- /dev/null
@@ -0,0 +1,11 @@
+/* Test C2x enumerations with fixed underlying type together with GNU
+   extensions: an enum cannot be forward declared without a fixed underlying
+   type and then declared or defined with one.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2x" } */
+
+enum e1;
+enum e1 : int; /* { dg-error "'enum' declared both with and without fixed underlying type" } */
+
+enum e2;
+enum e2 : long { A }; /* { dg-error "'enum' declared both with and without fixed underlying type" } */