First version was flawed, as it used the wrong type.
Here is another iteration.
Bootstrapped and regression tested on x86_64.
c23: Fix for redeclared enumerator initialized with different type
[PR115109]
c23 specifies that the type of a redeclared enumerator is the one of the
previous declaration. Convert initializers with different type accordingly
and add -Woverflow warning.
2024-05-18 Martin Uecker <uec...@tugraz.at>
PR c/115109
gcc/c/
* c-decl.cc (build_enumerator): When redeclaring an
enumerator convert value to previous type. For redeclared
enumerators use underlying type for computing the next value.
gcc/testsuite/
* gcc.dg/pr115109.c: New test.
* gcc.dg/c23-tag-enum-6.c: New test.
* gcc.dg/c23-tag-enum-7.c: New test.
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index b691b91b3db..540927a8df6 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -10209,6 +10209,7 @@ build_enumerator (location_t decl_loc, location_t loc,
struct c_enum_contents *the_enum, tree name, tree value)
{
tree decl;
+ tree old_decl;
/* Validate and default VALUE. */
@@ -10268,6 +10269,24 @@ build_enumerator (location_t decl_loc, location_t loc,
definition. */
value = convert (the_enum->enum_type, value);
}
+ else if (flag_isoc23
+ && (old_decl = lookup_name_in_scope (name, current_scope))
+ && old_decl != error_mark_node
+ && TREE_TYPE (old_decl)
+ && TREE_TYPE (TREE_TYPE (old_decl))
+ && TREE_CODE (old_decl) == CONST_DECL)
+ {
+ /* Enumeration constants in a redeclaration have the previous type. */
+ tree previous_type = TREE_TYPE (DECL_INITIAL (old_decl));
+ if (!int_fits_type_p (value, previous_type))
+ {
+ warning_at (loc, OPT_Woverflow,
+ "value of redeclared enumerator outside the range of "
+ "the previous type %qT", previous_type);
+ locate_old_decl (old_decl);
+ }
+ value = convert (previous_type, value);
+ }
else
{
/* Even though the underlying type of an enum is unspecified, the
@@ -10334,9 +10353,14 @@ build_enumerator (location_t decl_loc, location_t loc,
false);
}
else
- the_enum->enum_next_value
- = build_binary_op (EXPR_LOC_OR_LOC (value, input_location),
- PLUS_EXPR, value, integer_one_node, false);
+ {
+ /* In a redeclaration the type can already be the enumeral type. */
+ if (TREE_CODE (TREE_TYPE (value)) == ENUMERAL_TYPE)
+ value = convert (ENUM_UNDERLYING_TYPE (TREE_TYPE (value)), value);
+ 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
&& !ENUM_FIXED_UNDERLYING_TYPE_P (the_enum->enum_type))
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-6.c
b/gcc/testsuite/gcc.dg/c23-tag-enum-6.c
new file mode 100644
index 00000000000..ff9ec89775e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-6.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+#include <limits.h>
+
+enum E : int { a = 1, b = 2 };
+enum E : int { b = _Generic(a, enum E: 2), a = 1 };
+
+enum H { x = 1 };
+enum H { x = 2UL + UINT_MAX }; /* { dg-warning "outside the range" } */
+
+enum K : int { z = 1 };
+enum K : int { z = 2UL + UINT_MAX }; /* { dg-error "outside the range" } */
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-7.c
b/gcc/testsuite/gcc.dg/c23-tag-enum-7.c
new file mode 100644
index 00000000000..4a5b4bc63f6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-7.c
@@ -0,0 +1,41 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" } */
+
+#include <limits.h>
+
+// enumerators are all representable in int
+enum E { a = 1UL, b = _Generic(a, int: 2) };
+static_assert(_Generic(a, int: 1));
+static_assert(_Generic(b, int: 1));
+enum E { a = 1UL, b = _Generic(a, int: 2) };
+static_assert(_Generic(a, int: 1));
+static_assert(_Generic(b, int: 1));
+
+// enumerators are not representable in int
+enum H { c = 1UL << (UINT_WIDTH + 1), d = 2 };
+static_assert(_Generic(c, enum H: 1));
+static_assert(_Generic(d, enum H: 1));
+enum H { c = 1UL << (UINT_WIDTH + 1), d = _Generic(c, enum H: 2) };
+static_assert(_Generic(c, enum H: 1));
+static_assert(_Generic(d, enum H: 1));
+
+// there is an overflow in the first redeclaration
+enum K { e = UINT_MAX, f, g = _Generic(e, unsigned int: 0) + _Generic(f,
unsigned long: 1) };
+static_assert(_Generic(e, enum K: 1));
+static_assert(_Generic(f, enum K: 1));
+static_assert(_Generic(g, enum K: 1));
+enum K { e = UINT_MAX, f, g = _Generic(e, enum K: 0) + _Generic(f, enum K: 1)
};
+static_assert(_Generic(e, enum K: 1));
+static_assert(_Generic(f, enum K: 1));
+static_assert(_Generic(g, enum K: 1));
+
+// there is an overflow in the first redeclaration
+enum U { k = INT_MAX, l, m = _Generic(k, int: 0) + _Generic(l, long: 1) };
+static_assert(_Generic(k, enum U: 1));
+static_assert(_Generic(l, enum U: 1));
+static_assert(_Generic(m, enum U: 1));
+enum U { k = INT_MAX, l, m = _Generic(k, enum U: 0) + _Generic(l, enum U: 1) };
+static_assert(_Generic(k, enum U: 1));
+static_assert(_Generic(l, enum U: 1));
+static_assert(_Generic(m, enum U: 1));
+
diff --git a/gcc/testsuite/gcc.dg/pr115109.c b/gcc/testsuite/gcc.dg/pr115109.c
new file mode 100644
index 00000000000..961197d0c71
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr115109.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+#include <limits.h>
+
+enum E { a = 1UL << (ULONG_WIDTH - 5), b = 2 };
+enum E { a = 1ULL << (ULONG_WIDTH - 5), b = _Generic(a, enum E: 2) };
+