diff mbox series

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

Message ID eb7bd855a76bcf89b5b0823882a8ec0d828c7291.camel@tugraz.at
State New
Headers show
Series [1/4] c23: tag compatibility rules for struct and unions | expand

Commit Message

Martin Uecker Nov. 16, 2023, 9:38 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/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 | 23 ++++++++++
 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 ++++++++
 9 files changed, 192 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

Comments

Joseph Myers Nov. 23, 2023, 11:26 p.m. UTC | #1
On Thu, 16 Nov 2023, Martin Uecker wrote:

> +enum tt { R = 2 } TT;
> +enum tt {
> +	R = _Generic(&TT, enum tt*: 0, default: 2)
> +};

> +// incomplete during construction
> +
> +enum A { B = 7 } y;
> +enum A { B = 7 };
> +
> +enum A { B = _Generic(&y, enum A*: 1, default: 7) };

I don't follow the basis for these examples.

The multiple definitions are in the same scope, so declare the same type.  
The type "is incomplete until immediately after the closing brace of the 
list defining the content for the first time and complete thereafter.", 
which should include being complete inside the redefinition (so avoiding 
the problems discussed in earlier drafts of the proposal when a type went 
from complete to incomplete and there were corresponding issues with type 
compatibility questions inside the redefinition).  So I'd expect the "enum 
tt*" and "enum A*" cases in _Generic to match (which would make these 
definitions invalid because of the enumeration constant getting a 
different type).

> +void g(void)
> +{
> +	enum A { B = _Generic(&y, enum A*: 1, default: 7) };

In this case with an inner scope, however, "enum A" refers to the new type 
which indeed is incomplete at this point - though that also means this 
isn't an ISO C example but one using a GNU C extension ("A type specifier 
of the form enum identifier without an enumerator list shall only appear 
after the type it specifies is complete.") and so should correspondingly 
be in a gnu23-* test.
diff mbox series

Patch

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 194dd595334..e5d48c3fa56 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2114,9 +2114,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;
     }
 
@@ -3277,8 +3292,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;
@@ -9737,7 +9755,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;
@@ -9749,9 +9767,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)
 	{
@@ -9779,9 +9814,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)
@@ -10011,6 +10043,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;
@@ -10190,6 +10236,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);
@@ -10206,7 +10253,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 703f9570dbc..ff30ba198ca 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 7df4d65bf7a..a5dd9a37944 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -682,7 +682,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 dc8a16df272..8116c9b3e68 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);
@@ -6948,7 +6951,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..0b4829cdbe3
--- /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-warning "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*: 0, default: 2)
+};
+
+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..1ced39974f4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-2.c
@@ -0,0 +1,23 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// incomplete during construction
+
+enum A { B = 7 } y;
+enum A { B = 7 };
+
+enum A { B = _Generic(&y, enum A*: 1, default: 7) };
+
+void g(void)
+{
+	enum A { B = _Generic(&y, enum A*: 1, default: 7) };
+	_Static_assert(7 == B, "");
+}
+
+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..f20dc55fc6c
--- /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-warning "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" } */
+
+
+