diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 62ab4ba437b..f9d0d2945a5 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -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);
diff --git a/gcc/c-family/c-warn.cc b/gcc/c-family/c-warn.cc
index 6742f447ff5..cd59c3cafa9 100644
--- a/gcc/c-family/c-warn.cc
+++ b/gcc/c-family/c-warn.cc
@@ -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
diff --git a/gcc/c/c-convert.cc b/gcc/c/c-convert.cc
index 6e7491339d4..8a43e5f6f33 100644
--- a/gcc/c/c-convert.cc
+++ b/gcc/c/c-convert.cc
@@ -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));
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 4746e310d2d..2b83900e3f0 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -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;
 }
 
 /* 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;
diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
index b4680912547..1a8b1628218 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -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);
 }
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 602e0235f2d..5bdcd93b5c7 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -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)))
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index e7cdd2f11dc..d787dd4267b 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -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,
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 92f3afc5857..6c16647cbe3 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -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;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 40f5bf802c3..6d84514e4c0 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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
index 00000000000..57dd92a687a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c11-enum-4.c
@@ -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
index 00000000000..91d681f21fe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c11-enum-5.c
@@ -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
index 00000000000..cd708bd7c58
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c11-enum-6.c
@@ -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
index 00000000000..5ab9694302f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-enum-6.c
@@ -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
index 00000000000..08bae31d82c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-enum-7.c
@@ -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
index 00000000000..f7757fc3326
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-enum-8.c
@@ -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
index 00000000000..b72ed7380e9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu2x-enum-1.c
@@ -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" } */
