diff mbox series

[V5,C,2/4] c23: tag compatibility rules for enums

Message ID 28350926d4c9d1c44272cf1237d5326453080bf2.camel@tugraz.at
State New
Headers show
Series None | expand

Commit Message

Martin Uecker Dec. 17, 2023, 5:42 p.m. UTC
Allow redefinition of enum types and enumerators.  Diagnose
nested redefinitions including redefinitions in the enum
specifier for enum types with fixed underlying type.

gcc/c:
	* c-tree.h (c_parser_enum_specifier): Add parameter.
	* c-decl.cc (start_enum): Allow redefinition.
	(finish_enum): Diagnose conflicts.
	(build_enumerator): Set context.
	(diagnose_mismatched_decls): Diagnose conflicting enumerators.
	(push_decl): Preserve context for enumerators.
	* c-parser.cc (c_parser_enum_specifier): Remember when
	seen is from an enum type which is not yet defined.

gcc/testsuide/:
	* gcc.dg/c23-tag-enum-1.c: New test.
	* gcc.dg/c23-tag-enum-2.c: New test.
	* gcc.dg/c23-tag-enum-3.c: New test.
	* gcc.dg/c23-tag-enum-4.c: New test.
	* gcc.dg/c23-tag-enum-5.c: New test.
	* gcc.dg/gnu23-tag-enum-1.c: Mew test.
---
 gcc/c/c-decl.cc                         | 65 +++++++++++++++++++++----
 gcc/c/c-parser.cc                       |  5 +-
 gcc/c/c-tree.h                          |  3 +-
 gcc/c/c-typeck.cc                       |  5 +-
 gcc/testsuite/gcc.dg/c23-tag-enum-1.c   | 56 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-2.c   | 17 +++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-3.c   |  7 +++
 gcc/testsuite/gcc.dg/c23-tag-enum-4.c   | 22 +++++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-5.c   | 18 +++++++
 gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c | 19 ++++++++
 10 files changed, 205 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c

Comments

Joseph Myers Dec. 19, 2023, 9:50 p.m. UTC | #1
On Sun, 17 Dec 2023, Martin Uecker wrote:

> diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-1.c b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c
> new file mode 100644
> index 00000000000..a81a5afc456
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c

> +void test2(void)
> +{
> +  enum ee *a;
> +  enum ee { F = 2 } *b;

There's no prior declaration of enum ee, so this is using the GNU 
extension of forward enum declarations, and should move to a gnu23-* test.

Patch 2 is OK with that change.
diff mbox series

Patch

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 0e6b4a5248b..26188aa225e 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2112,9 +2112,24 @@  diagnose_mismatched_decls (tree newdecl, tree olddecl,
      given scope.  */
   if (TREE_CODE (olddecl) == CONST_DECL)
     {
-      auto_diagnostic_group d;
-      error ("redeclaration of enumerator %q+D", newdecl);
-      locate_old_decl (olddecl);
+      if (flag_isoc23
+	  && TYPE_NAME (DECL_CONTEXT (newdecl))
+	  && DECL_CONTEXT (newdecl) != DECL_CONTEXT (olddecl)
+	  && TYPE_NAME (DECL_CONTEXT (newdecl)) == TYPE_NAME (DECL_CONTEXT (olddecl)))
+	{
+	  if (!simple_cst_equal (DECL_INITIAL (olddecl), DECL_INITIAL (newdecl)))
+	    {
+	      auto_diagnostic_group d;
+	      error ("conflicting redeclaration of enumerator %q+D", newdecl);
+	      locate_old_decl (olddecl);
+	    }
+	}
+      else
+	{
+	  auto_diagnostic_group d;
+	  error ("redeclaration of enumerator %q+D", newdecl);
+	  locate_old_decl (olddecl);
+	}
       return false;
     }
 
@@ -3275,8 +3290,11 @@  pushdecl (tree x)
 
   /* Must set DECL_CONTEXT for everything not at file scope or
      DECL_FILE_SCOPE_P won't work.  Local externs don't count
-     unless they have initializers (which generate code).  */
+     unless they have initializers (which generate code).  We
+     also exclude CONST_DECLs because enumerators will get the
+     type of the enum as context.  */
   if (current_function_decl
+      && TREE_CODE (x) != CONST_DECL
       && (!VAR_OR_FUNCTION_DECL_P (x)
 	  || DECL_INITIAL (x) || !TREE_PUBLIC (x)))
     DECL_CONTEXT (x) = current_function_decl;
@@ -9759,7 +9777,7 @@  layout_array_type (tree t)
 
 tree
 start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
-	    tree fixed_underlying_type)
+	    tree fixed_underlying_type, bool potential_nesting_p)
 {
   tree enumtype = NULL_TREE;
   location_t enumloc = UNKNOWN_LOCATION;
@@ -9771,9 +9789,26 @@  start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
   if (name != NULL_TREE)
     enumtype = lookup_tag (ENUMERAL_TYPE, name, true, &enumloc);
 
+  if (enumtype != NULL_TREE && TREE_CODE (enumtype) == ENUMERAL_TYPE)
+    {
+      /* If the type is currently being defined or if we have seen an
+	 incomplete version which is now complete, this is a nested
+	 redefinition.  The later happens if the redefinition occurs
+	 inside the enum specifier itself.  */
+      if (C_TYPE_BEING_DEFINED (enumtype)
+	  || (potential_nesting_p && TYPE_VALUES (enumtype) != NULL_TREE))
+	error_at (loc, "nested redefinition of %<enum %E%>", name);
+
+      /* For C23 we allow redefinitions.  We set to zero and check for
+	 consistency later.  */
+      if (flag_isoc23 && TYPE_VALUES (enumtype) != NULL_TREE)
+	enumtype = NULL_TREE;
+    }
+
   if (enumtype == NULL_TREE || TREE_CODE (enumtype) != ENUMERAL_TYPE)
     {
       enumtype = make_node (ENUMERAL_TYPE);
+      TYPE_SIZE (enumtype) = NULL_TREE;
       pushtag (loc, name, enumtype);
       if (fixed_underlying_type != NULL_TREE)
 	{
@@ -9801,9 +9836,6 @@  start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
       DECL_SOURCE_LOCATION (TYPE_STUB_DECL (enumtype)) = loc;
     }
 
-  if (C_TYPE_BEING_DEFINED (enumtype))
-    error_at (loc, "nested redefinition of %<enum %E%>", name);
-
   C_TYPE_BEING_DEFINED (enumtype) = 1;
 
   if (TYPE_VALUES (enumtype) != NULL_TREE)
@@ -10033,6 +10065,20 @@  finish_enum (tree enumtype, tree values, tree attributes)
       && !in_sizeof && !in_typeof && !in_alignof)
     struct_parse_info->struct_types.safe_push (enumtype);
 
+  /* Check for consistency with previous definition */
+  if (flag_isoc23)
+    {
+      tree vistype = previous_tag (enumtype);
+      if (vistype
+	  && TREE_CODE (vistype) == TREE_CODE (enumtype)
+	  && !C_TYPE_BEING_DEFINED (vistype))
+	{
+	  TYPE_STUB_DECL (vistype) = TYPE_STUB_DECL (enumtype);
+	  if (!comptypes_same_p (enumtype, vistype))
+	    error("conflicting redefinition of enum %qT", enumtype);
+	}
+    }
+
   C_TYPE_BEING_DEFINED (enumtype) = 0;
 
   return enumtype;
@@ -10212,6 +10258,7 @@  build_enumerator (location_t decl_loc, location_t loc,
 
   decl = build_decl (decl_loc, CONST_DECL, name, TREE_TYPE (value));
   DECL_INITIAL (decl) = value;
+  DECL_CONTEXT (decl) = the_enum->enum_type;
   pushdecl (decl);
 
   return tree_cons (decl, value, NULL_TREE);
@@ -10228,7 +10275,7 @@  c_simulate_enum_decl (location_t loc, const char *name,
 
   struct c_enum_contents the_enum;
   tree enumtype = start_enum (loc, &the_enum, get_identifier (name),
-			      NULL_TREE);
+			      NULL_TREE, false);
 
   tree value_chain = NULL_TREE;
   string_int_pair *value;
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index a7dc096011f..5e1b56665d2 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -3667,6 +3667,7 @@  c_parser_enum_specifier (c_parser *parser)
 {
   struct c_typespec ret;
   bool have_std_attrs;
+  bool potential_nesting_p = false;
   tree std_attrs = NULL_TREE;
   tree attrs;
   tree ident = NULL_TREE;
@@ -3706,6 +3707,7 @@  c_parser_enum_specifier (c_parser *parser)
 	  if (!ENUM_FIXED_UNDERLYING_TYPE_P (ret.spec))
 	    error_at (enum_loc, "%<enum%> declared both with and without "
 		      "fixed underlying type");
+	  potential_nesting_p = NULL_TREE == TYPE_VALUES (ret.spec);
 	}
       else
 	{
@@ -3776,7 +3778,8 @@  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, fixed_underlying_type);
+      type = start_enum (enum_loc, &the_enum, ident, fixed_underlying_type,
+			 potential_nesting_p);
       values = NULL_TREE;
       c_parser_consume_token (parser);
       while (true)
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index e53948f21f3..54f1353ad34 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -683,7 +683,8 @@  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, tree);
+extern tree start_enum (location_t, struct c_enum_contents *, tree, tree,
+			bool potential_nesting_p);
 extern bool start_function (struct c_declspecs *, struct c_declarator *, tree);
 extern tree start_decl (struct c_declarator *, struct c_declspecs *, bool,
 			tree, bool = true, location_t * = NULL);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 0d514ec3570..0a9e8980c79 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1419,6 +1419,9 @@  tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
     {
     case ENUMERAL_TYPE:
       {
+	if (!comptypes (ENUM_UNDERLYING_TYPE (t1), ENUM_UNDERLYING_TYPE (t2)))
+	  return false;
+
 	/* Speed up the case where the type values are in the same order.  */
 	tree tv1 = TYPE_VALUES (t1);
 	tree tv2 = TYPE_VALUES (t2);
@@ -7006,7 +7009,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
       if (checktype != error_mark_node
 	  && TREE_CODE (checktype) == ENUMERAL_TYPE
 	  && TREE_CODE (type) == ENUMERAL_TYPE
-	  && TYPE_MAIN_VARIANT (checktype) != TYPE_MAIN_VARIANT (type))
+	  && !comptypes (TYPE_MAIN_VARIANT (checktype), TYPE_MAIN_VARIANT (type)))
        {
 	  gcc_rich_location loc (location);
 	  warning_at (&loc, OPT_Wenum_conversion,
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-1.c b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c
new file mode 100644
index 00000000000..a81a5afc456
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c
@@ -0,0 +1,56 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// incompatible redeclarations, conflicing redefinitions
+
+
+enum aa { A = 1 } *a;
+enum bb { B = 1 } *b;
+
+void test(void)
+{
+  enum aa { A = 1 } *c = a;
+  enum bb { B = 2 } *d = b;	/* { dg-error "incompatible pointer type" } */
+}
+
+enum cc { C = 1 };
+enum cc { D = 1 };		/* { dg-error "conflicting redefinition" } */	
+
+enum dd { E = 1 };
+enum dd { E = 2 };		/* { dg-error "conflicting redefinition" } */	
+				/* { dg-error "redeclaration of enumerator" "" { target *-*-* } .-1 } */	
+
+
+
+void test2(void)
+{
+  enum ee *a;
+  enum ee { F = 2 } *b;
+  b = a;
+}
+
+
+enum ff { G = 2 };
+enum gg { G = 2 };		/* { dg-error "redeclaration of enumerator" } */
+enum g2 { G = 3 };		/* { dg-error "redeclaration of enumerator" } */
+
+enum hh { H = 1, H = 1 };	/* { dg-error "redeclaration of enumerator" } */
+
+enum ss { K = 2 };
+enum ss { K = 2 };
+
+enum tt { R = 2 } TT;
+enum tt {
+	R = _Generic(&TT, enum tt*: 2, default: 0)
+};
+
+enum { U = 1 };
+enum { U = 1 };			/* { dg-error "redeclaration of enumerator" } */
+
+enum { V = 1 };
+enum { V = 2 };			/* { dg-error "redeclaration of enumerator" } */
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-2.c b/gcc/testsuite/gcc.dg/c23-tag-enum-2.c
new file mode 100644
index 00000000000..2bf7255b844
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-2.c
@@ -0,0 +1,17 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+enum A { B = 7 } y;
+
+// complete during later construction
+
+enum A { B = _Generic(&y, enum A*: 7, default: 1) };
+
+
+enum X { E = 1, F = 1 + 1 };
+enum X { F = 2, E = 1 };
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-3.c b/gcc/testsuite/gcc.dg/c23-tag-enum-3.c
new file mode 100644
index 00000000000..12218a5b911
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-3.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+enum A { N = 0 * sizeof(enum A { M = 1 }) }; 	/* { dg-error "nested" } */
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-4.c b/gcc/testsuite/gcc.dg/c23-tag-enum-4.c
new file mode 100644
index 00000000000..e6bc167a2f7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-4.c
@@ -0,0 +1,22 @@ 
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// fixed underlying types
+
+enum A : int { N = 1 } x1 = { };
+enum B : int { M = 1 } x2 = { };
+enum C { U = 1 } x3 = { };
+
+void f(void)
+{
+	enum A : int { N = 1 } y1 = x1;
+	enum B : short { M = 1 } y2;
+        y2 = x2;
+	enum B : short { M = 1 } y2b;
+	enum Bb : short { V = 1 } y2d = x2;
+	enum B : short { M = 1 } *y2e = &x2;	/* { dg-error "incompatible" } */
+	enum B : short { M = 1 } y2c = x2;
+	enum C { U = 1 } y3 = x3;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-5.c b/gcc/testsuite/gcc.dg/c23-tag-enum-5.c
new file mode 100644
index 00000000000..22ce06fb80d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-5.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile }
+ * { dg-options "-std=c23" } */
+
+// test for nested redefinitions of enums
+
+void foo(void)
+{
+	enum e { A = 1 };
+	enum e { A = 1 					/* { dg-error "redeclaration" } */
+		+ 0 * sizeof(enum e { A = 1 }) };	/* { dg-error "nested redefinition" } */
+							
+}
+
+typedef __SIZE_TYPE__ size_t;
+enum f : typeof (sizeof (enum f : size_t { B })) { B };	/* { dg-error "nested redefinition" } */
+
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c b/gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c
new file mode 100644
index 00000000000..6b346ed35a0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c
@@ -0,0 +1,19 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=gnu23" }
+ */
+
+
+enum A { B = 7 } y;
+
+void g(void)
+{
+	// incomplete during construction
+	// this is a GNU extension because enum A is used
+	// before the type is completed.
+
+	enum A { B = _Generic(&y, enum A*: 1, default: 7) };
+	_Static_assert(7 == B, "");
+}
+
+