/* 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);
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
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
goto maybe_fold;
case BOOLEAN_TYPE:
+ convert_to_boolean:
return fold_convert_loc
(loc, type, c_objc_common_truthvalue_conversion (input_location, expr));
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)
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)
{
}
}
+ /* 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. */
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;
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
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);
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*
/* 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;
{
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. */
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;
(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)
{
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;
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. */
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. */
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;
}
}
specs->type = type;
+ if (spec.has_enum_type_specifier
+ && spec.kind != ctsk_tagdef)
+ specs->enum_type_specifier_ref_p = true;
}
return specs;
/* 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);
}
}
t.expr = NULL_TREE;
t.expr_const_operands = true;
+ t.has_enum_type_specifier = false;
declspecs_add_type (name_token->location, specs, t);
continue;
}
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;
}
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;
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);
/* 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
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));
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. */
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)
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;
}
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
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;
}
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;
}
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
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;
}
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;
&& 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)))
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
/* 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
/* 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;
};
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;
};
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);
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,
(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);
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
&& 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)
&& 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)
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);
}
}
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);
}
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;
"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,
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);
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)))
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))
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
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
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;
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;
#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
--- /dev/null
+/* 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" } */
--- /dev/null
+/* 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" } */
--- /dev/null
+/* 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 };
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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'" } */
--- /dev/null
+/* 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" } */
--- /dev/null
+/* 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" } */