diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index 9ec9100cc90..cd54c517b18 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -2298,6 +2298,10 @@ c_common_type_for_size (unsigned int bits, int unsignedp) if (bits <= TYPE_PRECISION (intDI_type_node)) return unsignedp ? unsigned_intDI_type_node : intDI_type_node; + if (bits <= TYPE_PRECISION (widest_integer_literal_type_node)) + return (unsignedp ? widest_unsigned_literal_type_node + : widest_integer_literal_type_node); + return NULL_TREE; } diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index a7571cc7542..bcb4d7b66fe 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -9385,6 +9385,10 @@ finish_enum (tree enumtype, tree values, tree attributes) precision = MAX (tree_int_cst_min_precision (minnode, sign), tree_int_cst_min_precision (maxnode, sign)); + bool wider_than_int = + (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)) @@ -9407,9 +9411,20 @@ finish_enum (tree enumtype, tree values, tree attributes) tem = c_common_type_for_size (precision, sign == UNSIGNED ? 1 : 0); if (tem == NULL) { - warning (0, "enumeration values exceed range of largest integer"); - tem = long_long_integer_type_node; + /* 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; @@ -9439,17 +9454,17 @@ finish_enum (tree enumtype, tree values, tree attributes) TREE_TYPE (enu) = enumtype; - /* The ISO C Standard mandates enumerators to have type int, - even though the underlying type of an enum type is - unspecified. However, GCC allows enumerators of any - integer type as an extensions. build_enumerator() - converts any enumerators that fit in an int to type int, - to avoid promotions to unsigned types when comparing - integers with enumerators that fit in the int range. - When -pedantic is given, build_enumerator() would have - already warned about those that don't fit. Here we - convert the rest to the enumerator type. */ - if (TREE_TYPE (ini) != integer_type_node) + /* Before C2X, the ISO C Standard mandates enumerators to + have type int, even though the underlying type of an enum + type is unspecified. However, C2X allows enumerators of + any integer type, and if an enumeration has any + enumerators wider than int, all enumerators have the + enumerated type after it is parsed. Any enumerators that + 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) ini = convert (enumtype, ini); DECL_INITIAL (enu) = ini; @@ -9517,7 +9532,7 @@ tree build_enumerator (location_t decl_loc, location_t loc, struct c_enum_contents *the_enum, tree name, tree value) { - tree decl, type; + tree decl; /* Validate and default VALUE. */ @@ -9568,21 +9583,45 @@ build_enumerator (location_t decl_loc, location_t loc, } /* 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). GCC allows any integer type as - an extension. */ - else if (!int_fits_type_p (value, integer_type_node)) - pedwarn (loc, OPT_Wpedantic, - "ISO C restricts enumerator values to range of %"); + (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 and 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 % " + "before C2X"); - /* The ISO C Standard mandates enumerators to have type int, even - though the underlying type of an enum type is unspecified. - However, GCC allows enumerators of any integer type as an - extensions. Here we convert any enumerators that fit in an int - to type int, to avoid promotions to unsigned types when comparing - integers with enumerators that fit in the int range. When - -pedantic is given, we would have already warned about those that - don't fit. We have to do this here rather than in finish_enum - because this value may be used to define more enumerators. */ + /* 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); @@ -9591,18 +9630,38 @@ build_enumerator (location_t decl_loc, location_t loc, = 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) + { + /* Choose a wider type with the same signedness if + available. */ + int prec = TYPE_PRECISION (TREE_TYPE (value)) + 1; + bool unsignedp = TYPE_UNSIGNED (TREE_TYPE (value)); + tree new_type = (unsignedp + ? long_unsigned_type_node + : long_integer_type_node); + if (prec > TYPE_PRECISION (new_type)) + new_type = (unsignedp + ? long_long_unsigned_type_node + : long_long_integer_type_node); + if (prec > TYPE_PRECISION (new_type)) + new_type = (unsignedp + ? widest_unsigned_literal_type_node + : widest_integer_literal_type_node); + if (prec <= TYPE_PRECISION (new_type)) + { + the_enum->enum_overflow = false; + the_enum->enum_next_value + = build_binary_op (EXPR_LOC_OR_LOC (value, input_location), + PLUS_EXPR, convert (new_type, value), + integer_one_node, false); + gcc_assert (!tree_int_cst_lt (the_enum->enum_next_value, value)); + } + } /* Now create a declaration for the enum value name. */ - type = TREE_TYPE (value); - 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))); - - decl = build_decl (decl_loc, CONST_DECL, name, type); - DECL_INITIAL (decl) = convert (type, value); + decl = build_decl (decl_loc, CONST_DECL, name, TREE_TYPE (value)); + DECL_INITIAL (decl) = value; pushdecl (decl); return tree_cons (decl, value, NULL_TREE); diff --git a/gcc/testsuite/gcc.dg/c11-enum-1.c b/gcc/testsuite/gcc.dg/c11-enum-1.c new file mode 100644 index 00000000000..571041d5962 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-enum-1.c @@ -0,0 +1,14 @@ +/* Test C2x enumerations with values not representable in int are diagnosed for + C11. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +enum e1 { e1a = -__LONG_LONG_MAX__ - 1 }; /* { dg-error "ISO C restricts enumerator values" } */ + +enum e2 { e2a = __LONG_LONG_MAX__ }; /* { dg-error "ISO C restricts enumerator values" } */ + +enum e3 { e3a = (unsigned int) -1 }; /* { dg-error "ISO C restricts enumerator values" } */ + +enum e4 { e4a = (long long) -__INT_MAX__ - 1, e4b = (unsigned int) __INT_MAX__ }; + +enum e5 { e5a = __INT_MAX__, e5b }; /* { dg-error "ISO C restricts enumerator values" } */ diff --git a/gcc/testsuite/gcc.dg/c11-enum-2.c b/gcc/testsuite/gcc.dg/c11-enum-2.c new file mode 100644 index 00000000000..5b07c8d4b12 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-enum-2.c @@ -0,0 +1,14 @@ +/* Test C2x enumerations with values not representable in int are diagnosed for + C11. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic" } */ + +enum e1 { e1a = -__LONG_LONG_MAX__ - 1 }; /* { dg-warning "ISO C restricts enumerator values" } */ + +enum e2 { e2a = __LONG_LONG_MAX__ }; /* { dg-warning "ISO C restricts enumerator values" } */ + +enum e3 { e3a = (unsigned int) -1 }; /* { dg-warning "ISO C restricts enumerator values" } */ + +enum e4 { e4a = (long long) -__INT_MAX__ - 1, e4b = (unsigned int) __INT_MAX__ }; + +enum e5 { e5a = __INT_MAX__, e5b }; /* { dg-warning "ISO C restricts enumerator values" } */ diff --git a/gcc/testsuite/gcc.dg/c11-enum-3.c b/gcc/testsuite/gcc.dg/c11-enum-3.c new file mode 100644 index 00000000000..8266d4ea347 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-enum-3.c @@ -0,0 +1,14 @@ +/* Test C2x enumerations with values not representable in int 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 { e1a = -__LONG_LONG_MAX__ - 1 }; + +enum e2 { e2a = __LONG_LONG_MAX__ }; + +enum e3 { e3a = (unsigned int) -1 }; + +enum e4 { e4a = (long long) -__INT_MAX__ - 1, e4b = (unsigned int) __INT_MAX__ }; + +enum e5 { e5a = __INT_MAX__, e5b }; diff --git a/gcc/testsuite/gcc.dg/c2x-enum-1.c b/gcc/testsuite/gcc.dg/c2x-enum-1.c new file mode 100644 index 00000000000..c4371faacee --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-enum-1.c @@ -0,0 +1,104 @@ +/* Test C2x enumerations with values not representable in int. */ +/* { dg-do compile } */ +/* { 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) + +/* Test various explicit values not representable in int. */ + +enum e1 { e1a = -__LONG_LONG_MAX__ - 1, TYPE_CHECK (e1a, long long), + e1b = 0, TYPE_CHECK (e1b, int), + e1c = __LONG_LONG_MAX__, TYPE_CHECK (e1c, long long), + e1d = 1, TYPE_CHECK (e1d, int) }; +extern enum e1 e1v; +extern typeof (e1a) e1v; +extern typeof (e1b) e1v; +extern typeof (e1c) e1v; +extern typeof (e1d) e1v; +static_assert (sizeof (enum e1) >= sizeof (long long)); +static_assert (e1a == -__LONG_LONG_MAX__ - 1); +static_assert (e1b == 0); +static_assert (e1c == __LONG_LONG_MAX__); +static_assert (e1d == 1); +static_assert (e1a < 0); +static_assert (e1c > 0); + +/* This is a test where values are representable in int. */ +enum e2 { e2a = (long long) -__INT_MAX__ - 1, TYPE_CHECK (e2a, int), + e2b = (unsigned int) __INT_MAX__, TYPE_CHECK (e2b, int), + e2c = 2, TYPE_CHECK (e2c, int) }; +extern int e2v; +extern typeof (e2a) e2v; +extern typeof (e2b) e2v; +extern typeof (e2c) e2v; +static_assert (e2a == -__INT_MAX__ - 1); +static_assert (e2b == __INT_MAX__); +static_assert (e2c == 2); +static_assert (e2a < 0); +static_assert (e2b > 0); + +enum e3 { e3a = 0, TYPE_CHECK (e3a, int), + e3b = (unsigned int) -1, TYPE_CHECK (e3b, unsigned int) }; +extern enum e3 e3v; +extern typeof (e3a) e3v; +extern typeof (e3b) e3v; +static_assert (e3a == 0u); +static_assert (e3b == (unsigned int) -1); +static_assert (e3b > 0); + +/* Test handling of overflow and wraparound (choosing a wider type). */ +#if __LONG_LONG_MAX__ > __INT_MAX__ +enum e4 { e4a = __INT_MAX__, + e4b, e4c, e4d = ((typeof (e4b)) -1) < 0, + e4e = (unsigned int) -1, + e4f, e4g = ((typeof (e4e)) -1) > 0, + TYPE_CHECK (e4a, int), TYPE_CHECK (e4e, unsigned int) }; +extern enum e4 e4v; +extern typeof (e4a) e4v; +extern typeof (e4b) e4v; +extern typeof (e4c) e4v; +extern typeof (e4d) e4v; +extern typeof (e4e) e4v; +extern typeof (e4f) e4v; +extern typeof (e4g) e4v; +static_assert (e4a == __INT_MAX__); +static_assert (e4b == (long long) __INT_MAX__ + 1); +static_assert (e4c == (long long) __INT_MAX__ + 2); +static_assert (e4f == (unsigned long long) (unsigned int) -1 + 1); +/* Verify the type chosen on overflow of a signed type while parsing was + signed. */ +static_assert (e4d == 1); +/* Verify the type chosen on wraparound of an unsigned type while parsing was + unsigned. */ +static_assert (e4g == 1); +#endif + +/* Likewise, for overflow from long to long long. */ +#if __LONG_LONG_MAX__ > __LONG_MAX__ +enum e5 { e5a = __LONG_MAX__, + e5b, e5c, e5d = ((typeof (e5b)) -1) < 0, + e5e = (unsigned long) -1, + e5f, e5g = ((typeof (e5e)) -1) > 0, + TYPE_CHECK (e5a, long), TYPE_CHECK (e5e, unsigned long) }; +extern enum e5 e5v; +extern typeof (e5a) e5v; +extern typeof (e5b) e5v; +extern typeof (e5c) e5v; +extern typeof (e5d) e5v; +extern typeof (e5e) e5v; +extern typeof (e5f) e5v; +extern typeof (e5g) e5v; +static_assert (e5a == __LONG_MAX__); +static_assert (e5b == (long long) __LONG_MAX__ + 1); +static_assert (e5c == (long long) __LONG_MAX__ + 2); +static_assert (e5f == (unsigned long long) (unsigned long) -1 + 1); +/* Verify the type chosen on overflow of a signed type while parsing was + signed. */ +static_assert (e5d == 1); +/* Verify the type chosen on wraparound of an unsigned type while parsing was + unsigned. */ +static_assert (e5g == 1); +#endif diff --git a/gcc/testsuite/gcc.dg/c2x-enum-2.c b/gcc/testsuite/gcc.dg/c2x-enum-2.c new file mode 100644 index 00000000000..15dcf9ac779 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-enum-2.c @@ -0,0 +1,14 @@ +/* Test C2x enumerations with values not representable in int. Test values + outside the range of standard or extended integer types are diagnosed, even + when they can be represented in __int128. */ +/* { dg-do compile { target int128 } } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +enum e1 { e1a = __LONG_LONG_MAX__, e1b }; /* { dg-error "enumerator value outside the range" } */ + +enum e2 { e2a = __LONG_LONG_MAX__ * 2ULL + 1ULL, e2b }; /* { dg-error "enumerator value outside the range" } */ + +/* Likewise, when it's the enum as a whole that can't fit in any standard or + extended type, but the individual enumerators fit (some fitting a signed + type and some fitting an unsigned type). */ +enum e3 { e3a = -__LONG_LONG_MAX__ - 1, e3b = __LONG_LONG_MAX__ * 2ULL + 1ULL }; /* { dg-error "enumeration values exceed range of 'intmax_t'" } */ diff --git a/gcc/testsuite/gcc.dg/c2x-enum-3.c b/gcc/testsuite/gcc.dg/c2x-enum-3.c new file mode 100644 index 00000000000..532d9776d3a --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-enum-3.c @@ -0,0 +1,14 @@ +/* Test C2x enumerations with values not representable in int. Test values + outside the range of standard or extended integer types are diagnosed, + when __int128 is unsupported. */ +/* { dg-do compile { target { ! int128 } } } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +enum e1 { e1a = __LONG_LONG_MAX__, e1b }; /* { dg-error "overflow in enumeration values" } */ + +enum e2 { e2a = __LONG_LONG_MAX__ * 2ULL + 1ULL, e2b }; /* { dg-error "overflow in enumeration values" } */ + +/* Likewise, when it's the enum as a whole that can't fit in any standard or + extended type, but the individual enumerators fit (some fitting a signed + type and some fitting an unsigned type). */ +enum e3 { e3a = -__LONG_LONG_MAX__ - 1, e3b = __LONG_LONG_MAX__ * 2ULL + 1ULL }; /* { dg-error "enumeration values exceed range of largest integer" } */ diff --git a/gcc/testsuite/gcc.dg/c2x-enum-4.c b/gcc/testsuite/gcc.dg/c2x-enum-4.c new file mode 100644 index 00000000000..c2e58bfe750 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-enum-4.c @@ -0,0 +1,14 @@ +/* Test C2x enumerations with values not representable in int. Test overflow + of __int128 is diagnosed. */ +/* { dg-do compile { target { int128 } } } */ +/* { dg-options "-std=c2x" } */ + +enum e1 { e1a = (__int128) (((unsigned __int128) -1) >> 1), e1b }; /* { dg-error "overflow in enumeration values" } */ + +enum e2 { e2a = (unsigned __int128) -1, e2b }; /* { dg-error "overflow in enumeration values" } */ + +/* Likewise, when it's the enum as a whole that can't fit in __int128 or + unsigned __int128, but the individual enumerators fit (some fitting __int128 + and some fitting unsigned __int128). */ +enum e3 { e3a = -(__int128) (((unsigned __int128) -1) >> 1) - 1, + e3b = (unsigned __int128) -1 }; /* { dg-warning "enumeration values exceed range of largest integer" } */ diff --git a/gcc/testsuite/gcc.dg/c2x-enum-5.c b/gcc/testsuite/gcc.dg/c2x-enum-5.c new file mode 100644 index 00000000000..a4290f04525 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-enum-5.c @@ -0,0 +1,12 @@ +/* Test C2x enumerations with values not representable in int. Test + -Wc11-c2x-compat warnings. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -pedantic-errors -Wc11-c2x-compat" } */ + +enum e1 { e1a = -__LONG_LONG_MAX__ - 1 }; /* { dg-warning "ISO C restricts enumerator values" } */ + +enum e2 { e2a = __LONG_LONG_MAX__ }; /* { dg-warning "ISO C restricts enumerator values" } */ + +enum e3 { e3a = (unsigned int) -1 }; /* { dg-warning "ISO C restricts enumerator values" } */ + +enum e4 { e4a = (long long) -__INT_MAX__ - 1, e4b = (unsigned int) __INT_MAX__ }; diff --git a/gcc/testsuite/gcc.dg/pr30260.c b/gcc/testsuite/gcc.dg/pr30260.c index e04a8beb642..3fac38ed7f3 100644 --- a/gcc/testsuite/gcc.dg/pr30260.c +++ b/gcc/testsuite/gcc.dg/pr30260.c @@ -1,6 +1,6 @@ /* PR 30260 */ /* { dg-do link } */ -/* { dg-options "-pedantic -O" } */ +/* { dg-options "-std=gnu11 -pedantic -O" } */ #include void link_error (void); @@ -30,5 +30,5 @@ int main(void) return 0; } -enum E1 { e10 = INT_MAX, e11 }; /* { dg-error "overflow in enumeration values" } */ -enum E2 { e20 = (unsigned) INT_MAX, e21 }; /* { dg-error "overflow in enumeration values" } */ +enum E1 { e10 = INT_MAX, e11 }; /* { dg-warning "ISO C restricts enumerator values to range of 'int' before C2X" } */ +enum E2 { e20 = (unsigned) INT_MAX, e21 }; /* { dg-warning "ISO C restricts enumerator values to range of 'int' before C2X" } */ diff --git a/gcc/testsuite/gcc.dg/torture/pr25183.c b/gcc/testsuite/gcc.dg/torture/pr25183.c index 0157b806c7d..84b4c8f8d44 100644 --- a/gcc/testsuite/gcc.dg/torture/pr25183.c +++ b/gcc/testsuite/gcc.dg/torture/pr25183.c @@ -3,10 +3,10 @@ enum err { err_IO = 0x8a450000, /* { dg-warning "int" } */ - err_NM, - err_EOF, - err_SE, - err_PT + err_NM, /* { dg-warning "int" } */ + err_EOF, /* { dg-warning "int" } */ + err_SE, /* { dg-warning "int" } */ + err_PT /* { dg-warning "int" } */ }; static enum err E_; int error()