public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* c23 type compatibility rules, v2
@ 2023-08-26 16:19 Martin Uecker
  2023-08-26 16:20 ` [C PATCH 1/6] c: reorganize recursive type checking Martin Uecker
                   ` (6 more replies)
  0 siblings, 7 replies; 15+ messages in thread
From: Martin Uecker @ 2023-08-26 16:19 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



This is a revised series for the C23 rules for type
compatibility.

1/6 c: reorganize recursive type checking
2/6 c23: recursive type checking of tagged type
3/6 c23: tag compatibility rules for struct and unions
4/6 c23: tag compatibility rules for enums
5/6 c23: aliasing of compatible tagged types
6/6 c23: construct composite type for tagged types
x/x c: flag for tag compatibility rules


1. simplifies type checking without functionality changes
as a preparation step. (This is based on a similar preparatory
patch I posted before for checking size expressions).

2. implements the new rules in comptypes for tagged types but 
the code still remains unused. This removes a lot of old
code because we now require union members to have the same
order and merges the code for structs and unions.

3. implements the rules for structs and unions.

4. does the same for enum types and enumerators. 

5. sets TYPE_CANONICAL based on a equivalence class of types
which makes aliasing work correctly. For this there is a new
comptypes_equiv_p that does relaxed checking (ignoring size
expressions in nested types but not for fields).

6. adds support for the composite type.

There is an extra patch that adds the a flag to activate
the compatibility rules independently from language mode
and activates it by default.

1-2 should cause no change in function. 3-6 implement the
new semantics for C23.

Bootstrapped and regression tested on x86_64 (also with the
extra patch).

Martin






^ permalink raw reply	[flat|nested] 15+ messages in thread

* [C PATCH 1/6]  c: reorganize recursive type checking
  2023-08-26 16:19 c23 type compatibility rules, v2 Martin Uecker
@ 2023-08-26 16:20 ` Martin Uecker
  2023-09-06 20:59   ` Joseph Myers
  2023-08-26 16:22 ` [C PATCH 2/6] c23: recursive type checking of tagged type Martin Uecker
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 15+ messages in thread
From: Martin Uecker @ 2023-08-26 16:20 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers




Reorganize recursive type checking to use a structure to
store information collected during the recursion and
returned to the caller (warning_needed, enum_and_init_p,
different_types_p).

gcc/c:
	* c-typeck.cc (struct comptypes_data): Add structure.
	(tagged_types_tu_compatible_p,
	function_types_compatible_p, type_lists_compatible_p,
	comptypes_internal): Add structure to interface, change
	return type to bool, and adapt calls.
	(comptarget_types): Change return type too bool.
	(comptypes, comptypes_check_enum_int,
	comptypes_check_different_types): Adapt calls.
---
 gcc/c/c-typeck.cc | 266 ++++++++++++++++++++--------------------------
 1 file changed, 114 insertions(+), 152 deletions(-)

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index e6ddf37d412..ed1520ed6ba 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -90,12 +90,14 @@ static bool require_constant_elements;
 static bool require_constexpr_value;
 
 static tree qualify_type (tree, tree);
-static int tagged_types_tu_compatible_p (const_tree, const_tree, bool *,
-					 bool *);
-static int comp_target_types (location_t, tree, tree);
-static int function_types_compatible_p (const_tree, const_tree, bool *,
-					bool *);
-static int type_lists_compatible_p (const_tree, const_tree, bool *, bool *);
+struct comptypes_data;
+static bool tagged_types_tu_compatible_p (const_tree, const_tree,
+					  struct comptypes_data *);
+static bool comp_target_types (location_t, tree, tree);
+static bool function_types_compatible_p (const_tree, const_tree,
+					 struct comptypes_data *);
+static bool type_lists_compatible_p (const_tree, const_tree,
+				     struct comptypes_data *);
 static tree lookup_field (tree, tree);
 static int convert_arguments (location_t, vec<location_t>, tree,
 			      vec<tree, va_gc> *, vec<tree, va_gc> *, tree,
@@ -125,7 +127,8 @@ static tree find_init_member (tree, struct obstack *);
 static void readonly_warning (tree, enum lvalue_use);
 static int lvalue_or_else (location_t, const_tree, enum lvalue_use);
 static void record_maybe_used_decl (tree);
-static int comptypes_internal (const_tree, const_tree, bool *, bool *);
+static bool comptypes_internal (const_tree, const_tree,
+				struct comptypes_data *data);
 \f
 /* Return true if EXP is a null pointer constant, false otherwise.  */
 
@@ -1039,6 +1042,13 @@ common_type (tree t1, tree t2)
   return c_common_type (t1, t2);
 }
 
+struct comptypes_data {
+
+  bool enum_and_int_p;
+  bool different_types_p;
+  bool warning_needed;
+};
+
 /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
    or various other operations.  Return 2 if they are compatible
    but a warning may be needed if you use them together.  */
@@ -1047,12 +1057,13 @@ int
 comptypes (tree type1, tree type2)
 {
   const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;
-  int val;
 
-  val = comptypes_internal (type1, type2, NULL, NULL);
+  struct comptypes_data data = { };
+  bool ret = comptypes_internal (type1, type2, &data);
+
   free_all_tagged_tu_seen_up_to (tagged_tu_seen_base1);
 
-  return val;
+  return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
 /* Like comptypes, but if it returns non-zero because enum and int are
@@ -1062,12 +1073,14 @@ int
 comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
 {
   const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;
-  int val;
 
-  val = comptypes_internal (type1, type2, enum_and_int_p, NULL);
+  struct comptypes_data data = { };
+  bool ret = comptypes_internal (type1, type2, &data);
+  *enum_and_int_p = data.enum_and_int_p;
+
   free_all_tagged_tu_seen_up_to (tagged_tu_seen_base1);
 
-  return val;
+  return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
 /* Like comptypes, but if it returns nonzero for different types, it
@@ -1078,40 +1091,40 @@ comptypes_check_different_types (tree type1, tree type2,
 				 bool *different_types_p)
 {
   const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;
-  int val;
 
-  val = comptypes_internal (type1, type2, NULL, different_types_p);
+  struct comptypes_data data = { };
+  bool ret = comptypes_internal (type1, type2, &data);
+  *different_types_p = data.different_types_p;
+
   free_all_tagged_tu_seen_up_to (tagged_tu_seen_base1);
 
-  return val;
+  return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 \f
-/* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
-   or various other operations.  Return 2 if they are compatible
-   but a warning may be needed if you use them together.  If
-   ENUM_AND_INT_P is not NULL, and one type is an enum and the other a
-   compatible integer type, then this sets *ENUM_AND_INT_P to true;
-   *ENUM_AND_INT_P is never set to false.  If DIFFERENT_TYPES_P is not
-   NULL, and the types are compatible but different enough not to be
+/* Return true if TYPE1 and TYPE2 are compatible types for assignment
+   or various other operations.  If they are compatible but a warning may
+   be needed if you use them together, 'warning_needed' in DATA is set.
+   If one type is an enum and the other a compatible integer type, then
+   this sets 'enum_and_int_p' in DATA to true (it is never set to
+   false).  If the types are compatible but different enough not to be
    permitted in C11 typedef redeclarations, then this sets
-   *DIFFERENT_TYPES_P to true; *DIFFERENT_TYPES_P is never set to
+   'different_types_p' in DATA to true; it is never set to
    false, but may or may not be set if the types are incompatible.
    This differs from comptypes, in that we don't free the seen
    types.  */
 
-static int
-comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
-		    bool *different_types_p)
+static bool
+comptypes_internal (const_tree type1, const_tree type2,
+		    struct comptypes_data *data)
 {
   const_tree t1 = type1;
   const_tree t2 = type2;
-  int attrval, val;
 
   /* Suppress errors caused by previously reported errors.  */
 
   if (t1 == t2 || !t1 || !t2
       || TREE_CODE (t1) == ERROR_MARK || TREE_CODE (t2) == ERROR_MARK)
-    return 1;
+    return true;
 
   /* Enumerated types are compatible with integer types, but this is
      not transitive: two enumerated types in the same translation unit
@@ -1124,10 +1137,8 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
       t1 = ENUM_UNDERLYING_TYPE (t1);
       if (TREE_CODE (t2) != VOID_TYPE)
 	{
-	  if (enum_and_int_p != NULL)
-	    *enum_and_int_p = true;
-	  if (different_types_p != NULL)
-	    *different_types_p = true;
+	  data->enum_and_int_p = true;
+	  data->different_types_p = true;
 	}
     }
   else if (TREE_CODE (t2) == ENUMERAL_TYPE
@@ -1137,25 +1148,23 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
       t2 = ENUM_UNDERLYING_TYPE (t2);
       if (TREE_CODE (t1) != VOID_TYPE)
 	{
-	  if (enum_and_int_p != NULL)
-	    *enum_and_int_p = true;
-	  if (different_types_p != NULL)
-	    *different_types_p = true;
+	  data->enum_and_int_p = true;
+	  data->different_types_p = true;
 	}
     }
 
   if (t1 == t2)
-    return 1;
+    return true;
 
   /* Different classes of types can't be compatible.  */
 
   if (TREE_CODE (t1) != TREE_CODE (t2))
-    return 0;
+    return false;
 
   /* Qualifiers must match. C99 6.7.3p9 */
 
   if (TYPE_QUALS (t1) != TYPE_QUALS (t2))
-    return 0;
+    return false;
 
   /* Allow for two different type nodes which have essentially the same
      definition.  Note that we already checked for equality of the type
@@ -1163,14 +1172,16 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
 
   if (TREE_CODE (t1) != ARRAY_TYPE
       && TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2))
-    return 1;
+    return true;
+
+  int attrval;
 
   /* 1 if no need for warning yet, 2 if warning cause has been seen.  */
   if (!(attrval = comp_type_attributes (t1, t2)))
-     return 0;
+     return false;
 
-  /* 1 if no need for warning yet, 2 if warning cause has been seen.  */
-  val = 0;
+  if (2 == attrval)
+    data->warning_needed = true;
 
   switch (TREE_CODE (t1))
     {
@@ -1196,16 +1207,11 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
     case POINTER_TYPE:
       /* Do not remove mode information.  */
       if (TYPE_MODE (t1) != TYPE_MODE (t2))
-	break;
-      val = (TREE_TYPE (t1) == TREE_TYPE (t2)
-	     ? 1 : comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2),
-				       enum_and_int_p, different_types_p));
-      break;
+	return false;
+      return comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data);
 
     case FUNCTION_TYPE:
-      val = function_types_compatible_p (t1, t2, enum_and_int_p,
-					 different_types_p);
-      break;
+      return function_types_compatible_p (t1, t2, data);
 
     case ARRAY_TYPE:
       {
@@ -1213,21 +1219,16 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
 	tree d2 = TYPE_DOMAIN (t2);
 	bool d1_variable, d2_variable;
 	bool d1_zero, d2_zero;
-	val = 1;
 
 	/* Target types must match incl. qualifiers.  */
-	if (TREE_TYPE (t1) != TREE_TYPE (t2)
-	    && (val = comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2),
-					  enum_and_int_p,
-					  different_types_p)) == 0)
-	  return 0;
-
-	if (different_types_p != NULL
-	    && (d1 == NULL_TREE) != (d2 == NULL_TREE))
-	  *different_types_p = true;
+	if (!comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data))
+	  return false;
+
+	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
+	  data->different_types_p = true;
 	/* Sizes must match unless one is missing or variable.  */
 	if (d1 == NULL_TREE || d2 == NULL_TREE || d1 == d2)
-	  break;
+	  return true;
 
 	d1_zero = !TYPE_MAX_VALUE (d1);
 	d2_zero = !TYPE_MAX_VALUE (d2);
@@ -1241,51 +1242,37 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
 	d1_variable = d1_variable || (d1_zero && C_TYPE_VARIABLE_SIZE (t1));
 	d2_variable = d2_variable || (d2_zero && C_TYPE_VARIABLE_SIZE (t2));
 
-	if (different_types_p != NULL
-	    && d1_variable != d2_variable)
-	  *different_types_p = true;
+	if (d1_variable != d2_variable)
+	  data->different_types_p = true;
 	if (d1_variable || d2_variable)
-	  break;
+	  return true;
 	if (d1_zero && d2_zero)
-	  break;
+	  return true;
 	if (d1_zero || d2_zero
 	    || !tree_int_cst_equal (TYPE_MIN_VALUE (d1), TYPE_MIN_VALUE (d2))
 	    || !tree_int_cst_equal (TYPE_MAX_VALUE (d1), TYPE_MAX_VALUE (d2)))
-	  val = 0;
+	  return false;
 
-	break;
+	return true;
       }
 
     case ENUMERAL_TYPE:
     case RECORD_TYPE:
     case UNION_TYPE:
-      if (val != 1 && false)
+      if (false)
 	{
-	  tree a1 = TYPE_ATTRIBUTES (t1);
-	  tree a2 = TYPE_ATTRIBUTES (t2);
-
-	  if (! attribute_list_contained (a1, a2)
-	      && ! attribute_list_contained (a2, a1))
-	    break;
-
-	  val = tagged_types_tu_compatible_p (t1, t2, enum_and_int_p,
-					      different_types_p);
-
-	  if (attrval != 2)
-	    return val;
+	  return tagged_types_tu_compatible_p (t1, t2, data);
 	}
-      break;
+      return false;
 
     case VECTOR_TYPE:
-      val = (known_eq (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2))
-	     && comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2),
-				    enum_and_int_p, different_types_p));
-      break;
+      return known_eq (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2))
+	     && comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data);
 
     default:
-      break;
+      return false;
     }
-  return attrval == 2 && val == 1 ? 2 : val;
+  gcc_unreachable ();
 }
 
 /* Return 1 if TTL and TTR are pointers to types that are equivalent, ignoring
@@ -1293,7 +1280,7 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
    different named addresses, then we must determine if one address space is a
    subset of the other.  */
 
-static int
+static bool
 comp_target_types (location_t location, tree ttl, tree ttr)
 {
   int val;
@@ -1395,17 +1382,13 @@ free_all_tagged_tu_seen_up_to (const struct tagged_tu_seen_cache *tu_til)
 
 /* Return 1 if two 'struct', 'union', or 'enum' types T1 and T2 are
    compatible.  If the two types are not the same (which has been
-   checked earlier), this can only happen when multiple translation
-   units are being compiled.  See C99 6.2.7 paragraph 1 for the exact
-   rules.  ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in
-   comptypes_internal.  */
+   checked earlier).  */
 
-static int
+static bool
 tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
-			      bool *enum_and_int_p, bool *different_types_p)
+			      struct comptypes_data* data)
 {
   tree s1, s2;
-  bool needs_warning = false;
 
   /* We have to verify that the tags of the types are the same.  This
      is harder than it looks because this may be a typedef, so we have
@@ -1513,8 +1496,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 
 	    if (DECL_NAME (s1) != DECL_NAME (s2))
 	      break;
-	    result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2),
-					 enum_and_int_p, different_types_p);
+	    result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2), data);
 
 	    if (result != 1 && !DECL_NAME (s1))
 	      break;
@@ -1523,8 +1505,6 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		tu->val = 0;
 		return 0;
 	      }
-	    if (result == 2)
-	      needs_warning = true;
 
 	    if (TREE_CODE (s1) == FIELD_DECL
 		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
@@ -1536,7 +1516,6 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	  }
 	if (!s1 && !s2)
 	  {
-	    tu->val = needs_warning ? 2 : 1;
 	    return tu->val;
 	  }
 
@@ -1550,8 +1529,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		  int result;
 
 		  result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2),
-					       enum_and_int_p,
-					       different_types_p);
+					       data);
 
 		  if (result != 1 && !DECL_NAME (s1))
 		    continue;
@@ -1560,8 +1538,6 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		      tu->val = 0;
 		      return 0;
 		    }
-		  if (result == 2)
-		    needs_warning = true;
 
 		  if (TREE_CODE (s1) == FIELD_DECL
 		      && simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
@@ -1577,7 +1553,6 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		return 0;
 	      }
 	  }
-	tu->val = needs_warning ? 2 : 1;
 	return tu->val;
       }
 
@@ -1599,12 +1574,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	    if (TREE_CODE (s1) != TREE_CODE (s2)
 		|| DECL_NAME (s1) != DECL_NAME (s2))
 	      break;
-	    result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2),
-					 enum_and_int_p, different_types_p);
+	    result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2), data);
 	    if (result == 0)
 	      break;
-	    if (result == 2)
-	      needs_warning = true;
 
 	    if (TREE_CODE (s1) == FIELD_DECL
 		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
@@ -1614,7 +1586,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	if (s1 && s2)
 	  tu->val = 0;
 	else
-	  tu->val = needs_warning ? 2 : 1;
+	  tu->val = 1;
 	return tu->val;
       }
 
@@ -1631,9 +1603,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
    Otherwise, the argument types must match.
    ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in comptypes_internal.  */
 
-static int
+static bool
 function_types_compatible_p (const_tree f1, const_tree f2,
-			     bool *enum_and_int_p, bool *different_types_p)
+			     struct comptypes_data *data)
 {
   tree args1, args2;
   /* 1 if no need for warning yet, 2 if warning cause has been seen.  */
@@ -1654,16 +1626,15 @@ function_types_compatible_p (const_tree f1, const_tree f2,
   if (TYPE_VOLATILE (ret2))
     ret2 = build_qualified_type (TYPE_MAIN_VARIANT (ret2),
 				 TYPE_QUALS (ret2) & ~TYPE_QUAL_VOLATILE);
-  val = comptypes_internal (ret1, ret2, enum_and_int_p, different_types_p);
+  val = comptypes_internal (ret1, ret2, data);
   if (val == 0)
     return 0;
 
   args1 = TYPE_ARG_TYPES (f1);
   args2 = TYPE_ARG_TYPES (f2);
 
-  if (different_types_p != NULL
-      && (args1 == NULL_TREE) != (args2 == NULL_TREE))
-    *different_types_p = true;
+  if ((args1 == NULL_TREE) != (args2 == NULL_TREE))
+    data->different_types_p = true;
 
   /* An unspecified parmlist matches any specified parmlist
      whose argument types don't need default promotions.  */
@@ -1679,8 +1650,11 @@ function_types_compatible_p (const_tree f1, const_tree f2,
 	 If they don't match, ask for a warning (but no error).  */
       if (TYPE_ACTUAL_ARG_TYPES (f1)
 	  && type_lists_compatible_p (args2, TYPE_ACTUAL_ARG_TYPES (f1),
-				      enum_and_int_p, different_types_p) != 1)
-	val = 2;
+				      data) != 1)
+	{
+	  val = 1;
+	  data->warning_needed = true;
+	}
       return val;
     }
   if (args2 == NULL_TREE)
@@ -1691,35 +1665,32 @@ function_types_compatible_p (const_tree f1, const_tree f2,
 	return 0;
       if (TYPE_ACTUAL_ARG_TYPES (f2)
 	  && type_lists_compatible_p (args1, TYPE_ACTUAL_ARG_TYPES (f2),
-				      enum_and_int_p, different_types_p) != 1)
-	val = 2;
+				      data) != 1)
+	{
+	  val = 1;
+	  data->warning_needed = true;
+	}
       return val;
     }
 
   /* Both types have argument lists: compare them and propagate results.  */
-  val1 = type_lists_compatible_p (args1, args2, enum_and_int_p,
-				  different_types_p);
-  return val1 != 1 ? val1 : val;
+  val1 = type_lists_compatible_p (args1, args2, data);
+  return val1;
 }
 
 /* Check two lists of types for compatibility, returning 0 for
-   incompatible, 1 for compatible, or 2 for compatible with
-   warning.  ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in
-   comptypes_internal.  */
+   incompatible, 1 for compatible.  ENUM_AND_INT_P and
+   DIFFERENT_TYPES_P are as in comptypes_internal.  */
 
-static int
+static bool
 type_lists_compatible_p (const_tree args1, const_tree args2,
-			 bool *enum_and_int_p, bool *different_types_p)
+			 struct comptypes_data *data)
 {
-  /* 1 if no need for warning yet, 2 if warning cause has been seen.  */
-  int val = 1;
-  int newval = 0;
-
   while (1)
     {
       tree a1, mv1, a2, mv2;
       if (args1 == NULL_TREE && args2 == NULL_TREE)
-	return val;
+	return true;
       /* If one list is shorter than the other,
 	 they fail to match.  */
       if (args1 == NULL_TREE || args2 == NULL_TREE)
@@ -1740,9 +1711,8 @@ type_lists_compatible_p (const_tree args1, const_tree args2,
 	 means there is supposed to be an argument
 	 but nothing is specified about what type it has.
 	 So match anything that self-promotes.  */
-      if (different_types_p != NULL
-	  && (a1 == NULL_TREE) != (a2 == NULL_TREE))
-	*different_types_p = true;
+      if ((a1 == NULL_TREE) != (a2 == NULL_TREE))
+	data->different_types_p = true;
       if (a1 == NULL_TREE)
 	{
 	  if (c_type_promotes_to (a2) != a2)
@@ -1757,11 +1727,9 @@ type_lists_compatible_p (const_tree args1, const_tree args2,
       else if (TREE_CODE (a1) == ERROR_MARK
 	       || TREE_CODE (a2) == ERROR_MARK)
 	;
-      else if (!(newval = comptypes_internal (mv1, mv2, enum_and_int_p,
-					      different_types_p)))
+      else if (!comptypes_internal (mv1, mv2, data))
 	{
-	  if (different_types_p != NULL)
-	    *different_types_p = true;
+	  data->different_types_p = true;
 	  /* Allow  wait (union {union wait *u; int *i} *)
 	     and  wait (union wait *)  to be compatible.  */
 	  if (TREE_CODE (a1) == UNION_TYPE
@@ -1782,8 +1750,7 @@ type_lists_compatible_p (const_tree args1, const_tree args2,
 			   ? c_build_qualified_type (TYPE_MAIN_VARIANT (mv3),
 						     TYPE_QUAL_ATOMIC)
 			   : TYPE_MAIN_VARIANT (mv3));
-		  if (comptypes_internal (mv3, mv2, enum_and_int_p,
-					  different_types_p))
+		  if (comptypes_internal (mv3, mv2, data))
 		    break;
 		}
 	      if (memb == NULL_TREE)
@@ -1807,8 +1774,7 @@ type_lists_compatible_p (const_tree args1, const_tree args2,
 			   ? c_build_qualified_type (TYPE_MAIN_VARIANT (mv3),
 						     TYPE_QUAL_ATOMIC)
 			   : TYPE_MAIN_VARIANT (mv3));
-		  if (comptypes_internal (mv3, mv1, enum_and_int_p,
-					  different_types_p))
+		  if (comptypes_internal (mv3, mv1, data))
 		    break;
 		}
 	      if (memb == NULL_TREE)
@@ -1818,10 +1784,6 @@ type_lists_compatible_p (const_tree args1, const_tree args2,
 	    return 0;
 	}
 
-      /* comptypes said ok, but record if it said to warn.  */
-      if (newval > val)
-	val = newval;
-
       args1 = TREE_CHAIN (args1);
       args2 = TREE_CHAIN (args2);
     }
@@ -7298,7 +7260,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       tree mvl = ttl;
       tree mvr = ttr;
       bool is_opaque_pointer;
-      int target_cmp = 0;   /* Cache comp_target_types () result.  */
+      bool target_cmp = false;   /* Cache comp_target_types () result.  */
       addr_space_t asl;
       addr_space_t asr;
 
-- 
2.30.2



^ permalink raw reply	[flat|nested] 15+ messages in thread

* [C PATCH 2/6] c23: recursive type checking of tagged type
  2023-08-26 16:19 c23 type compatibility rules, v2 Martin Uecker
  2023-08-26 16:20 ` [C PATCH 1/6] c: reorganize recursive type checking Martin Uecker
@ 2023-08-26 16:22 ` Martin Uecker
  2023-11-07 23:06   ` Joseph Myers
  2023-08-26 16:23 ` [C PATCH 3/6] c23: tag compatibility rules for struct and unions Martin Uecker
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 15+ messages in thread
From: Martin Uecker @ 2023-08-26 16:22 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers




Adapt the old and unused code for type checking for C23.

gcc/c/:
	* c-typeck.c (struct comptypes_data): Add anon_field flag.
	(comptypes, comptypes_check_unum_int,
	comptypes_check_different_types): Remove old cache.
	(tagged_tu_types_compatible_p): Rewrite.
---
 gcc/c/c-typeck.cc | 261 +++++++++++-----------------------------------
 1 file changed, 58 insertions(+), 203 deletions(-)

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index ed1520ed6ba..41ef05f005c 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -190,20 +190,14 @@ remove_c_maybe_const_expr (tree expr)
     return expr;
 }
 
-\f/* This is a cache to hold if two types are compatible or not.  */
+\f/* This is a cache to hold if two types are seen.  */
 
 struct tagged_tu_seen_cache {
   const struct tagged_tu_seen_cache * next;
   const_tree t1;
   const_tree t2;
-  /* The return value of tagged_types_tu_compatible_p if we had seen
-     these two types already.  */
-  int val;
 };
 
-static const struct tagged_tu_seen_cache * tagged_tu_seen_base;
-static void free_all_tagged_tu_seen_up_to (const struct tagged_tu_seen_cache *);
-
 /* Do `exp = require_complete_type (loc, exp);' to make sure exp
    does not have an incomplete type.  (That includes void types.)
    LOC is the location of the use.  */
@@ -1043,10 +1037,12 @@ common_type (tree t1, tree t2)
 }
 
 struct comptypes_data {
-
   bool enum_and_int_p;
   bool different_types_p;
   bool warning_needed;
+  bool anon_field;
+
+  const struct tagged_tu_seen_cache* cache;
 };
 
 /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
@@ -1056,13 +1052,9 @@ struct comptypes_data {
 int
 comptypes (tree type1, tree type2)
 {
-  const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;
-
   struct comptypes_data data = { };
   bool ret = comptypes_internal (type1, type2, &data);
 
-  free_all_tagged_tu_seen_up_to (tagged_tu_seen_base1);
-
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
@@ -1072,14 +1064,10 @@ comptypes (tree type1, tree type2)
 int
 comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
 {
-  const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;
-
   struct comptypes_data data = { };
   bool ret = comptypes_internal (type1, type2, &data);
   *enum_and_int_p = data.enum_and_int_p;
 
-  free_all_tagged_tu_seen_up_to (tagged_tu_seen_base1);
-
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
@@ -1090,14 +1078,10 @@ int
 comptypes_check_different_types (tree type1, tree type2,
 				 bool *different_types_p)
 {
-  const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;
-
   struct comptypes_data data = { };
   bool ret = comptypes_internal (type1, type2, &data);
   *different_types_p = data.different_types_p;
 
-  free_all_tagged_tu_seen_up_to (tagged_tu_seen_base1);
-
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 \f
@@ -1334,53 +1318,7 @@ comp_target_types (location_t location, tree ttl, tree ttr)
 \f
 /* Subroutines of `comptypes'.  */
 
-
-
-/* Allocate the seen two types, assuming that they are compatible. */
-
-static struct tagged_tu_seen_cache *
-alloc_tagged_tu_seen_cache (const_tree t1, const_tree t2)
-{
-  struct tagged_tu_seen_cache *tu = XNEW (struct tagged_tu_seen_cache);
-  tu->next = tagged_tu_seen_base;
-  tu->t1 = t1;
-  tu->t2 = t2;
-
-  tagged_tu_seen_base = tu;
-
-  /* The C standard says that two structures in different translation
-     units are compatible with each other only if the types of their
-     fields are compatible (among other things).  We assume that they
-     are compatible until proven otherwise when building the cache.
-     An example where this can occur is:
-     struct a
-     {
-       struct a *next;
-     };
-     If we are comparing this against a similar struct in another TU,
-     and did not assume they were compatible, we end up with an infinite
-     loop.  */
-  tu->val = 1;
-  return tu;
-}
-
-/* Free the seen types until we get to TU_TIL. */
-
-static void
-free_all_tagged_tu_seen_up_to (const struct tagged_tu_seen_cache *tu_til)
-{
-  const struct tagged_tu_seen_cache *tu = tagged_tu_seen_base;
-  while (tu != tu_til)
-    {
-      const struct tagged_tu_seen_cache *const tu1
-	= (const struct tagged_tu_seen_cache *) tu;
-      tu = tu1->next;
-      XDELETE (CONST_CAST (struct tagged_tu_seen_cache *, tu1));
-    }
-  tagged_tu_seen_base = tu_til;
-}
-
-/* Return 1 if two 'struct', 'union', or 'enum' types T1 and T2 are
+/* Return true if two 'struct', 'union', or 'enum' types T1 and T2 are
    compatible.  If the two types are not the same (which has been
    checked earlier).  */
 
@@ -1406,189 +1344,106 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	 && DECL_ORIGINAL_TYPE (TYPE_NAME (t2)))
     t2 = DECL_ORIGINAL_TYPE (TYPE_NAME (t2));
 
-  /* C90 didn't have the requirement that the two tags be the same.  */
-  if (flag_isoc99 && TYPE_NAME (t1) != TYPE_NAME (t2))
-    return 0;
+  if (TYPE_NAME (t1) != TYPE_NAME (t2))
+    return false;
 
-  /* C90 didn't say what happened if one or both of the types were
-     incomplete; we choose to follow C99 rules here, which is that they
-     are compatible.  */
-  if (TYPE_SIZE (t1) == NULL
-      || TYPE_SIZE (t2) == NULL)
-    return 1;
+  if (!data->anon_field && NULL_TREE == TYPE_NAME (t1))
+    return false;
 
-  {
-    const struct tagged_tu_seen_cache * tts_i;
-    for (tts_i = tagged_tu_seen_base; tts_i != NULL; tts_i = tts_i->next)
-      if (tts_i->t1 == t1 && tts_i->t2 == t2)
-	return tts_i->val;
-  }
+  if (!data->anon_field && TYPE_STUB_DECL (t1) != TYPE_STUB_DECL (t2))
+    data->different_types_p = true;
+
+  data->anon_field = false;
+
+  /* Incomplete types are incompatible inside a TU.  */
+  if (TYPE_SIZE (t1) == NULL || TYPE_SIZE (t2) == NULL)
+    return false;
+
+  if (ENUMERAL_TYPE != TREE_CODE (t1)
+      && (TYPE_REVERSE_STORAGE_ORDER (t1)
+	  != TYPE_REVERSE_STORAGE_ORDER (t2)))
+    return false;
+
+  /* For types already in being looked at in some active
+     invocation of this function, assume compatibility.
+     The cache is built as a linked list on the stack
+     with the head of the list past downwards.  */
+  for (const struct tagged_tu_seen_cache *t = data->cache;
+       t != NULL; t = t->next)
+    if (t->t1 == t1 && t->t2 == t2)
+      return true;
+
+  const struct tagged_tu_seen_cache entry = { data->cache, t1, t2 };
 
   switch (TREE_CODE (t1))
     {
     case ENUMERAL_TYPE:
       {
-	struct tagged_tu_seen_cache *tu = alloc_tagged_tu_seen_cache (t1, t2);
 	/* Speed up the case where the type values are in the same order.  */
 	tree tv1 = TYPE_VALUES (t1);
 	tree tv2 = TYPE_VALUES (t2);
 
 	if (tv1 == tv2)
-	  {
-	    return 1;
-	  }
+	  return true;
 
 	for (;tv1 && tv2; tv1 = TREE_CHAIN (tv1), tv2 = TREE_CHAIN (tv2))
 	  {
 	    if (TREE_PURPOSE (tv1) != TREE_PURPOSE (tv2))
 	      break;
-	    if (simple_cst_equal (TREE_VALUE (tv1), TREE_VALUE (tv2)) != 1)
-	      {
-		tu->val = 0;
-		return 0;
-	      }
+
+	    if (simple_cst_equal (DECL_INITIAL (TREE_VALUE (tv1)),
+				  DECL_INITIAL (TREE_VALUE (tv2))) != 1)
+	      break;
 	  }
 
 	if (tv1 == NULL_TREE && tv2 == NULL_TREE)
-	  {
-	    return 1;
-	  }
+	  return true;
+
 	if (tv1 == NULL_TREE || tv2 == NULL_TREE)
-	  {
-	    tu->val = 0;
-	    return 0;
-	  }
+	  return false;
 
 	if (list_length (TYPE_VALUES (t1)) != list_length (TYPE_VALUES (t2)))
-	  {
-	    tu->val = 0;
-	    return 0;
-	  }
+	  return false;
 
 	for (s1 = TYPE_VALUES (t1); s1; s1 = TREE_CHAIN (s1))
 	  {
 	    s2 = purpose_member (TREE_PURPOSE (s1), TYPE_VALUES (t2));
-	    if (s2 == NULL
-		|| simple_cst_equal (TREE_VALUE (s1), TREE_VALUE (s2)) != 1)
-	      {
-		tu->val = 0;
-		return 0;
-	      }
-	  }
-	return 1;
-      }
-
-    case UNION_TYPE:
-      {
-	struct tagged_tu_seen_cache *tu = alloc_tagged_tu_seen_cache (t1, t2);
-
-	if (list_length (TYPE_FIELDS (t1)) != list_length (TYPE_FIELDS (t2)))
-	  {
-	    tu->val = 0;
-	    return 0;
-	  }
-
-	/*  Speed up the common case where the fields are in the same order. */
-	for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2); s1 && s2;
-	     s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2))
-	  {
-	    int result;
-
-	    if (DECL_NAME (s1) != DECL_NAME (s2))
-	      break;
-	    result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2), data);
-
-	    if (result != 1 && !DECL_NAME (s1))
-	      break;
-	    if (result == 0)
-	      {
-		tu->val = 0;
-		return 0;
-	      }
 
-	    if (TREE_CODE (s1) == FIELD_DECL
-		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
-				     DECL_FIELD_BIT_OFFSET (s2)) != 1)
-	      {
-		tu->val = 0;
-		return 0;
-	      }
-	  }
-	if (!s1 && !s2)
-	  {
-	    return tu->val;
+	    if (s2 == NULL
+		|| simple_cst_equal (DECL_INITIAL (TREE_VALUE (s1)), DECL_INITIAL (TREE_VALUE (s2))) != 1)
+	      return false;
 	  }
 
-	for (s1 = TYPE_FIELDS (t1); s1; s1 = DECL_CHAIN (s1))
-	  {
-	    bool ok = false;
-
-	    for (s2 = TYPE_FIELDS (t2); s2; s2 = DECL_CHAIN (s2))
-	      if (DECL_NAME (s1) == DECL_NAME (s2))
-		{
-		  int result;
-
-		  result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2),
-					       data);
-
-		  if (result != 1 && !DECL_NAME (s1))
-		    continue;
-		  if (result == 0)
-		    {
-		      tu->val = 0;
-		      return 0;
-		    }
-
-		  if (TREE_CODE (s1) == FIELD_DECL
-		      && simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
-					   DECL_FIELD_BIT_OFFSET (s2)) != 1)
-		    break;
-
-		  ok = true;
-		  break;
-		}
-	    if (!ok)
-	      {
-		tu->val = 0;
-		return 0;
-	      }
-	  }
-	return tu->val;
+	return true;
       }
 
+    case UNION_TYPE:
     case RECORD_TYPE:
-      {
-	struct tagged_tu_seen_cache *tu = alloc_tagged_tu_seen_cache (t1, t2);
 
 	if (list_length (TYPE_FIELDS (t1)) != list_length (TYPE_FIELDS (t2)))
-	  {
-	    tu->val = 0;
-	    return 0;
-	  }
+	  return false;
 
 	for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2);
 	     s1 && s2;
 	     s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2))
 	  {
-	    int result;
 	    if (TREE_CODE (s1) != TREE_CODE (s2)
 		|| DECL_NAME (s1) != DECL_NAME (s2))
-	      break;
-	    result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2), data);
-	    if (result == 0)
-	      break;
+	      return false;
+
+	    if (!DECL_NAME (s1) && RECORD_OR_UNION_TYPE_P (TREE_TYPE (s1)))
+	      data->anon_field = true;
+
+	    data->cache = &entry;
+	    if (!comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2), data))
+	      return false;
 
 	    if (TREE_CODE (s1) == FIELD_DECL
 		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
 				     DECL_FIELD_BIT_OFFSET (s2)) != 1)
-	      break;
+	      return false;
 	  }
-	if (s1 && s2)
-	  tu->val = 0;
-	else
-	  tu->val = 1;
-	return tu->val;
-      }
+	return true;
 
     default:
       gcc_unreachable ();
-- 
2.30.2



^ permalink raw reply	[flat|nested] 15+ messages in thread

* [C PATCH 3/6] c23: tag compatibility rules for struct and unions
  2023-08-26 16:19 c23 type compatibility rules, v2 Martin Uecker
  2023-08-26 16:20 ` [C PATCH 1/6] c: reorganize recursive type checking Martin Uecker
  2023-08-26 16:22 ` [C PATCH 2/6] c23: recursive type checking of tagged type Martin Uecker
@ 2023-08-26 16:23 ` Martin Uecker
  2023-11-07 23:18   ` Joseph Myers
  2023-08-26 16:24 ` [C PATCH 4/6] c23: tag compatibility rules for enums Martin Uecker
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 15+ messages in thread
From: Martin Uecker @ 2023-08-26 16:23 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



Implement redeclaration and compatibility rules for
structures and unions in C23.

gcc/c/:
	* c-decl.cc (previous_tag): New function.
	(get_parm_info): Turn off warning for C2X.
	(start_struct): Allow redefinitons.
	(finish_struct): Diagnose conflicts.
	* c-tree.h (comptypes_same_p): Add prototype.
	* c-typeck.cc (comptypes_same_p): New function
	(comptypes_internal): Activate comparison of tagged
	types (convert_for_assignment): Ingore qualifiers.
	(digest_init): Add error.
	(initialized_elementwise_p): Allow compatible types.

gcc/testsuite/:
	* gcc.dg/c2x-enum-7.c: Remove warning.
	* gcc.dg/c2x-tag-1.c: New test.
	* gcc.dg/c2x-tag-2.c: New test.
	* gcc.dg/c2x-tag-3.c: New test.
	* gcc.dg/c2x-tag-4.c: New test.
	* gcc.dg/c2x-tag-5.c: New test.
	* gcc.dg/c2x-tag-6.c: New test.
	* gcc.dg/c2x-tag-7.c: New test.
	* gcc.dg/c2x-tag-8.c: New test.
	* gcc.dg/c2x-tag-9.c: New test.
	* gcc.dg/c2x-tag-10.c: New test.
---
 gcc/c/c-decl.cc                   | 56 ++++++++++++++++++++++---
 gcc/c/c-tree.h                    |  1 +
 gcc/c/c-typeck.cc                 | 38 +++++++++++++----
 gcc/testsuite/gcc.dg/c2x-enum-7.c |  6 +--
 gcc/testsuite/gcc.dg/c2x-tag-1.c  | 68 +++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-10.c | 31 ++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-2.c  | 43 +++++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-3.c  | 16 ++++++++
 gcc/testsuite/gcc.dg/c2x-tag-4.c  | 19 +++++++++
 gcc/testsuite/gcc.dg/c2x-tag-5.c  | 26 ++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-6.c  | 34 ++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-7.c  | 28 +++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-8.c  | 25 ++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-9.c  | 12 ++++++
 14 files changed, 387 insertions(+), 16 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-10.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-5.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-6.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-7.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-8.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-9.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 1f9eb44dbaa..c5c6a853fa9 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -1993,6 +1993,24 @@ locate_old_decl (tree decl)
 	    decl, TREE_TYPE (decl));
 }
 
+static tree
+previous_tag (tree type)
+{
+  struct c_binding *b = NULL;
+  tree name = TYPE_NAME (type);
+
+  if (name)
+    b = I_TAG_BINDING (name);
+
+  if (b)
+    b = b->shadowed;
+
+  if (b && B_IN_CURRENT_SCOPE (b))
+    return b->decl;
+
+  return NULL_TREE;
+}
+
 /* Subroutine of duplicate_decls.  Compare NEWDECL to OLDDECL.
    Returns true if the caller should proceed to merge the two, false
    if OLDDECL should simply be discarded.  As a side effect, issues
@@ -8442,11 +8460,14 @@ get_parm_info (bool ellipsis, tree expr)
 	  if (TREE_CODE (decl) != UNION_TYPE || b->id != NULL_TREE)
 	    {
 	      if (b->id)
-		/* The %s will be one of 'struct', 'union', or 'enum'.  */
-		warning_at (b->locus, 0,
-			    "%<%s %E%> declared inside parameter list"
-			    " will not be visible outside of this definition or"
-			    " declaration", keyword, b->id);
+		{
+		  /* The %s will be one of 'struct', 'union', or 'enum'.  */
+		  if (!flag_isoc2x)
+		    warning_at (b->locus, 0,
+				"%<%s %E%> declared inside parameter list"
+				" will not be visible outside of this definition or"
+				" declaration", keyword, b->id);
+		}
 	      else
 		/* The %s will be one of 'struct', 'union', or 'enum'.  */
 		warning_at (b->locus, 0,
@@ -8651,6 +8672,12 @@ start_struct (location_t loc, enum tree_code code, tree name,
 
   if (name != NULL_TREE)
     ref = lookup_tag (code, name, true, &refloc);
+
+  /* For C2X, even if we already have a completed definition,
+     we do not use it. We will check for consistency later.  */
+  if (flag_isoc2x && ref && TYPE_SIZE (ref))
+    ref = NULL_TREE;
+
   if (ref && TREE_CODE (ref) == code)
     {
       if (TYPE_STUB_DECL (ref))
@@ -9439,6 +9466,25 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
       warning_at (loc, 0, "union cannot be made transparent");
     }
 
+  /* Check for consistency with previous definition */
+  if (flag_isoc2x)
+    {
+      tree vistype = previous_tag (t);
+      if (vistype
+	  && TREE_CODE (vistype) == TREE_CODE (t)
+	  && !C_TYPE_BEING_DEFINED (vistype))
+	{
+	  TYPE_STUB_DECL (vistype) = TYPE_STUB_DECL (t);
+	  if (c_type_variably_modified_p (t))
+	    error ("redefinition of struct or union %qT with variably "
+		   "modified type", t);
+	  else if (!comptypes_same_p (t, vistype))
+	    error ("redefinition of struct or union %qT", t);
+	}
+    }
+
+  C_TYPE_BEING_DEFINED (t) = 0;
+
   tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
   for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
     {
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 7c5234e80fd..511fd9ee0e5 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -747,6 +747,7 @@ extern tree c_objc_common_truthvalue_conversion (location_t, tree);
 extern tree require_complete_type (location_t, tree);
 extern bool same_translation_unit_p (const_tree, const_tree);
 extern int comptypes (tree, tree);
+extern bool comptypes_same_p (tree, tree);
 extern int comptypes_check_different_types (tree, tree, bool *);
 extern int comptypes_check_enum_int (tree, tree, bool *);
 extern bool c_mark_addressable (tree, bool = false);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 41ef05f005c..802c727d9d3 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1058,6 +1058,23 @@ comptypes (tree type1, tree type2)
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
+
+/* Like comptypes, but it returns non-zero only for identical
+   types.  */
+
+bool
+comptypes_same_p (tree type1, tree type2)
+{
+  struct comptypes_data data = { };
+  bool ret = comptypes_internal (type1, type2, &data);
+
+  if (data.different_types_p)
+    return false;
+
+  return ret;
+}
+
+
 /* Like comptypes, but if it returns non-zero because enum and int are
    compatible, it sets *ENUM_AND_INT_P to true.  */
 
@@ -1243,11 +1260,11 @@ comptypes_internal (const_tree type1, const_tree type2,
     case ENUMERAL_TYPE:
     case RECORD_TYPE:
     case UNION_TYPE:
-      if (false)
-	{
-	  return tagged_types_tu_compatible_p (t1, t2, data);
-	}
-      return false;
+
+      if (!flag_isoc2x)
+	return false;
+
+      return tagged_types_tu_compatible_p (t1, t2, data);
 
     case VECTOR_TYPE:
       return known_eq (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2))
@@ -6978,7 +6995,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
   /* Aggregates in different TUs might need conversion.  */
   if ((codel == RECORD_TYPE || codel == UNION_TYPE)
       && codel == coder
-      && comptypes (type, rhstype))
+      && comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (rhstype)))
     return convert_and_check (expr_loc != UNKNOWN_LOCATION
 			      ? expr_loc : location, type, rhs);
 
@@ -8315,6 +8332,13 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype,
 	   conversion.  */
 	inside_init = convert (type, inside_init);
 
+      if ((code == RECORD_TYPE || code == UNION_TYPE)
+	  && !comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (TREE_TYPE (inside_init))))
+	{
+	  error_init (init_loc, "invalid initializer %qT %qT", type, TREE_TYPE (inside_init));
+	  return error_mark_node;
+	}
+
       if (require_constant
 	  && TREE_CODE (inside_init) == COMPOUND_LITERAL_EXPR)
 	{
@@ -10399,7 +10423,7 @@ initialize_elementwise_p (tree type, tree value)
     return !VECTOR_TYPE_P (value_type);
 
   if (AGGREGATE_TYPE_P (type))
-    return type != TYPE_MAIN_VARIANT (value_type);
+    return !comptypes (type, TYPE_MAIN_VARIANT (value_type));
 
   return false;
 }
diff --git a/gcc/testsuite/gcc.dg/c2x-enum-7.c b/gcc/testsuite/gcc.dg/c2x-enum-7.c
index 08bae31d82c..d4ddcc821dc 100644
--- a/gcc/testsuite/gcc.dg/c2x-enum-7.c
+++ b/gcc/testsuite/gcc.dg/c2x-enum-7.c
@@ -26,17 +26,15 @@ enum e13 : short x13; /* { dg-error "'enum' underlying type may not be specified
 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" } */
+int f22 (enum e22 : long long { E22 } p);
+int f23 (enum e23 : long long { E23 } p);
 struct s24 { enum e24 : int { E24 } x; };
 
 /* Incompatible kinds of tags in the same scope are errors.  */
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-1.c b/gcc/testsuite/gcc.dg/c2x-tag-1.c
new file mode 100644
index 00000000000..0cda7aa0c34
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-1.c
@@ -0,0 +1,68 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// allowed and forbidden redefinitions of the same struct/union in the same scope
+
+typedef struct p { int a; } pd_t;
+typedef struct p { int a; } pd_t;
+
+typedef struct bar { int x; } X;
+typedef struct bar { float x; } Y; /* { dg-error "redefinition of struct or union" } */
+
+void test(void)
+{
+	struct foo { int x; };
+	struct foo { float x; }; /* { dg-error "redefinition of struct or union" } */
+}
+
+struct aa { int a; };
+
+void f(void)
+{
+	typedef struct aa A;
+	struct bb { struct aa a; } x;
+	struct aa { int a; };
+	typedef struct aa A;		/* { dg-error "redefinition" } */
+	struct bb { struct aa a; } y; /* { dg-error "redefinition of struct or union" } */
+	(void)x; (void)y;
+}
+
+
+
+void h(void)
+{
+	struct a2 { int a; };
+	{
+		typedef struct a2 A;
+		struct b2 { struct a2 a; } x;
+		struct a2 { int a; };
+		typedef struct a2 A;		/* { dg-error "redefinition" } */
+		struct b2 { struct a2 a; } y; /* { dg-error "redefinition of struct or union" } */
+		(void)x; (void)y;
+	}
+}
+
+
+union cc { int x; float y; } z;
+union cc { int x; float y; } z1;
+union cc { float y; int x; } z2;	/* { dg-error "redefinition of struct or union" } */
+
+void g(void)
+{
+	struct s { int a; };
+	struct s { int a; } x0;
+	struct p { struct s c; } y1 = { x0 };
+	struct p { struct s { int a; } c; } y = { x0 };
+}
+
+struct q { struct { int a; }; }; 
+struct q { struct { int a; }; }; 
+struct q { int a; };  /* { dg-error "redefinition of struct or union" } */
+
+struct r { int a; char b[]; };
+struct r { int a; char b[]; };
+struct r { int a; char b[0]; };
+struct r { int a; char b[1]; }; /* { dg-error "redefinition of struct or union" } */
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-10.c b/gcc/testsuite/gcc.dg/c2x-tag-10.c
new file mode 100644
index 00000000000..39abcb2db60
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-10.c
@@ -0,0 +1,31 @@
+/* { dg-do compile }
+ * { dg-options "-std=c2x" } */
+
+// structs with variably modified types
+
+void bar(int n, int m)
+{
+	struct f { int b; int a[n]; } *x;
+	{ struct f { int b; int a[n]; } *x2 = x; }
+	{ struct f { int b; int a[m]; } *x2 = x; }
+	{ struct f { int b; int a[5]; } *x2 = x; }
+	{ struct f { int b; int a[0]; }  *x2 = x; }
+	{ struct f { int b; int a[]; }  *x2 = x; }
+
+	struct g { int a[n]; int b; } *y;
+	{ struct g { int a[n]; int b; } *y2 = y; }
+	{ struct g { int a[m]; int b; } *y2 = y; }
+	{ struct g { int a[4]; int b; } *y2 = y; }
+
+	struct h { int b; int a[5]; } *w;
+	{ struct h { int b; int a[5]; } *w2 = w; }
+	{ struct h { int b; int a[n]; } *w2 = w; }
+	{ struct h { int b; int a[m]; } *w2 = w; }
+
+	struct i { int b; int (*a)(int c[n]); } *u;
+	{ struct i { int b; int (*a)(int c[4]); } *u2 = u; }
+	{ struct i { int b; int (*a)(int c[]); } *u2 = u; }
+	{ struct i { int b; int (*a)(int c[*]); } *u2 = u; }
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-2.c b/gcc/testsuite/gcc.dg/c2x-tag-2.c
new file mode 100644
index 00000000000..a68392e1fab
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-2.c
@@ -0,0 +1,43 @@
+/* { dg-do { compile xfail { *-*-* } } }
+ * { dg-options "-std=c2x" }
+ */
+
+// compatibility of structs in assignment
+
+typedef struct p { int a; } pd_t;
+
+void test1(void)
+{
+  pd_t y0;
+  struct p { int a; } x;
+  y0 = x;
+}
+
+void test2(void)
+{
+  struct p { int a; } x;
+  struct p y0 = x;
+}
+
+void test3(void)
+{
+  struct p { int a; } x;
+  pd_t y0 = x;
+}
+
+typedef struct p { int a; } p2_t;
+
+void test4(void)
+{
+  p2_t x;
+  pd_t y0 = x;
+}
+
+void test5(void)
+{
+  struct q { int a; } a;
+  struct q { int a; } b;
+  a = b;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-3.c b/gcc/testsuite/gcc.dg/c2x-tag-3.c
new file mode 100644
index 00000000000..bafb08ca11d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-3.c
@@ -0,0 +1,16 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// conflicting types via linkage
+
+extern struct foo { int x; } x;
+extern struct bar { float x; } y;
+
+void test(void)
+{
+  extern struct foo { int x; } x;
+  extern struct bar { int x; } y;	/* { dg-error "conflicting types" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-4.c b/gcc/testsuite/gcc.dg/c2x-tag-4.c
new file mode 100644
index 00000000000..b7c793c2dce
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-4.c
@@ -0,0 +1,19 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// conflicting attributes
+
+extern struct __attribute__(( transaction_safe )) foo { int x; } x;
+extern struct __attribute__(( unused )) foo2 { int x; } x2;
+extern struct __attribute__(( may_alias )) foo3 { int x; } x3;
+
+void test(void)
+{
+  extern struct foo { int x; } x;		/* { dg-error "conflicting types" } */
+  extern struct  foo2 { int x; } x2;
+  extern struct  foo3 { int x; } x3;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-5.c b/gcc/testsuite/gcc.dg/c2x-tag-5.c
new file mode 100644
index 00000000000..b597d6403d2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-5.c
@@ -0,0 +1,26 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// conflicting types for anonymous structs / unions
+
+extern struct { int x; } a;
+extern struct { int x; } a;	/* { dg-error "conflicting types" } */
+
+extern union { int x; } b;
+extern union { int x; } b;	/* { dg-error "conflicting types" } */
+
+typedef struct { int x; } u;
+typedef struct { int x; } v;
+
+u c;
+v c;				/* { dg-error "conflicting types" } */
+
+typedef union { int x; } q;
+typedef union { int x; } r;
+
+q d;
+r d;				/* { dg-error "conflicting types" } */
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-6.c b/gcc/testsuite/gcc.dg/c2x-tag-6.c
new file mode 100644
index 00000000000..bf7cfb342d4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-6.c
@@ -0,0 +1,34 @@
+/*
+ * { dg-do { run xfail { "*-*-*" } } }
+ * { dg-options "-std=c2x" }
+ */
+
+// nesting and parameters
+
+#define product_type(T, A, B) \
+struct product_ ## T { A a ; B b ; }
+#define sum_type(T, A, B) \
+struct sum_ ## T { _Bool flag ; union { A a ; B b ; }; }
+
+float foo1(product_type(iSfd_, int, sum_type(fd, float, double)) x)
+{
+	return x.b.a;
+}
+
+static void test1(void)
+{
+	product_type(iSfd_, int, sum_type(fd, float, double)) y = { 3, { 1, { .a = 1. } } };
+	product_type(iSfd_, int, sum_type(fd, float, double)) z = y;
+	product_type(iSfd_, int, sum_type(fd, float, double)) *zp = &y;
+	float a = foo1(y);
+	product_type(iSid_, int, sum_type(id, int, double)) *wp = &y; /* { dg-warning "incompatible pointer type" } */
+	float b = foo1(y);
+	product_type(iSid_, int, sum_type(id, int, double)) w = *wp;
+	(void)a; (void)b; (void)z; (void)zp; (void)w; (void)wp;
+}
+
+int main()
+{
+	test1();
+}
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-7.c b/gcc/testsuite/gcc.dg/c2x-tag-7.c
new file mode 100644
index 00000000000..7ec121fe80e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-7.c
@@ -0,0 +1,28 @@
+/* 
+ * { dg-do compile } 
+ * { dg-options "-Wno-vla -std=gnu2x" } 
+ */
+
+// arrays in structs
+
+void foo(int n, int m)
+{
+	struct f { int b; int a[n]; };
+	struct f { int b; int a[n]; };	/* { dg-error "redefinition of struct or union" } */
+	struct f { int b; int a[m]; };	/* { dg-error "redefinition of struct or union" } */
+	struct f { int b; int a[5]; };	/* { dg-error "redefinition of struct or union" } */
+	struct f { int b; int a[]; };	/* { dg-error "redefinition of struct or union" } */
+
+	struct g { int a[n]; int b; };
+	struct g { int a[n]; int b; };	/* { dg-error "redefinition of struct or union" } */
+	struct g { int a[m]; int b; };	/* { dg-error "redefinition of struct or union" } */
+	struct g { int a[4]; int b; };	/* { dg-error "redefinition of struct or union" } */
+
+	struct h { int (*a)[n]; int b; };
+	struct h { int (*a)[n]; int b; };	/* { dg-error "redefinition of struct or union" } */
+	struct h { int (*a)[m]; int b; };	/* { dg-error "redefinition of struct or union" } */
+	struct h { int (*a)[4]; int b; };	/* { dg-error "redefinition of struct or union" } */
+	struct h { int (*a)[]; int b; };	/* { dg-error "redefinition of struct or union" } */
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-8.c b/gcc/testsuite/gcc.dg/c2x-tag-8.c
new file mode 100644
index 00000000000..d1f503f23ba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-8.c
@@ -0,0 +1,25 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// (in-)completeness
+
+struct foo {
+	char x[10];
+} x;
+
+struct foo {
+	_Static_assert(_Generic(&x, struct foo*: 0, default: 1));
+	char x[_Generic(&x, struct foo*: 1, default: 10)];
+};
+
+void f(void)
+{
+	struct foo { char x[_Generic(&x, struct foo*: 1, default: 10)]; };
+
+	struct foo z;
+	_Static_assert(10 == sizeof(z.x), "");
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-9.c b/gcc/testsuite/gcc.dg/c2x-tag-9.c
new file mode 100644
index 00000000000..fdbae7baf46
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-9.c
@@ -0,0 +1,12 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// recursive declarations
+
+extern struct bar { struct bar* p; int x; } b;
+extern struct bar { struct bar* p; int x; } b;
+
+struct foo { struct foo { struct foo* p; int x; }* p; int x; } a;	/* { dg-error "nested" } */
+
-- 
2.30.2



^ permalink raw reply	[flat|nested] 15+ messages in thread

* [C PATCH 4/6] c23: tag compatibility rules for enums
  2023-08-26 16:19 c23 type compatibility rules, v2 Martin Uecker
                   ` (2 preceding siblings ...)
  2023-08-26 16:23 ` [C PATCH 3/6] c23: tag compatibility rules for struct and unions Martin Uecker
@ 2023-08-26 16:24 ` Martin Uecker
  2023-11-07 23:20   ` Joseph Myers
  2023-08-26 16:25 ` [C PATCH 5/6] c23: aliasing of compatible tagged types Martin Uecker
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 15+ messages in thread
From: Martin Uecker @ 2023-08-26 16:24 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



Allow redefinition of enum types and enumerators.

gcc/c:
	* c-decl.cc (start_num): Allow redefinition.
	(finish_enum): Diagnose conflicts.
	(build_enumerator): Set context.
	(diagnose_mismatched_decls): Diagnose conflicting enumerators.
	(push_decl): Preserve context for enumerators.

gcc/testsuide/:
	* gcc.dg/c2x-tag-enum-1.c: New test.
	* gcc.dg/c2x-tag-enum-2.c: New test.
	* gcc.dg/c2x-tag-enum-3.c: New test.
	* gcc.dg/c2x-tag-enum-4.c: New test.
---
 gcc/c/c-decl.cc                       | 47 ++++++++++++++++++++--
 gcc/c/c-typeck.cc                     |  5 ++-
 gcc/testsuite/gcc.dg/c2x-tag-enum-1.c | 56 +++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-enum-2.c | 23 +++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-enum-3.c |  7 ++++
 gcc/testsuite/gcc.dg/c2x-tag-enum-4.c | 22 +++++++++++
 6 files changed, 155 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-enum-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-enum-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-enum-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-enum-4.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index c5c6a853fa9..b514e8a35ee 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2064,9 +2064,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_isoc2x
+	  && 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;
     }
 
@@ -3227,8 +3242,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;
@@ -9606,9 +9624,15 @@ 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 (flag_isoc2x && enumtype != NULL_TREE
+      && TREE_CODE (enumtype) == ENUMERAL_TYPE
+      && 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)
 	{
@@ -9868,6 +9892,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_isoc2x)
+    {
+      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;
@@ -10047,6 +10085,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);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 802c727d9d3..2b79cbba950 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1396,6 +1396,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);
@@ -6895,7 +6898,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/c2x-tag-enum-1.c b/gcc/testsuite/gcc.dg/c2x-tag-enum-1.c
new file mode 100644
index 00000000000..d66d30d356f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-enum-1.c
@@ -0,0 +1,56 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// 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/c2x-tag-enum-2.c b/gcc/testsuite/gcc.dg/c2x-tag-enum-2.c
new file mode 100644
index 00000000000..bbe872c1480
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-enum-2.c
@@ -0,0 +1,23 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// 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/c2x-tag-enum-3.c b/gcc/testsuite/gcc.dg/c2x-tag-enum-3.c
new file mode 100644
index 00000000000..6f65bb73ef6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-enum-3.c
@@ -0,0 +1,7 @@
+/* { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+enum A { N = 0 * sizeof(enum A { M = 1 }) }; 	/* { dg-error "nested" } */
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-enum-4.c b/gcc/testsuite/gcc.dg/c2x-tag-enum-4.c
new file mode 100644
index 00000000000..413b1fe8110
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-enum-4.c
@@ -0,0 +1,22 @@
+/* { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// 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;
+}
+
-- 
2.30.2



^ permalink raw reply	[flat|nested] 15+ messages in thread

* [C PATCH 5/6] c23: aliasing of compatible tagged types
  2023-08-26 16:19 c23 type compatibility rules, v2 Martin Uecker
                   ` (3 preceding siblings ...)
  2023-08-26 16:24 ` [C PATCH 4/6] c23: tag compatibility rules for enums Martin Uecker
@ 2023-08-26 16:25 ` Martin Uecker
  2023-08-26 16:26 ` [C PATCH 6/6] c23: construct composite type for " Martin Uecker
  2023-08-26 16:26 ` [C PATCH] c: flag for tag compatibility rules Martin Uecker
  6 siblings, 0 replies; 15+ messages in thread
From: Martin Uecker @ 2023-08-26 16:25 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



Tell the backend which types are equivalent by setting
TYPE_CANONICAL to one struct in the set of equivalent
structs. Structs are considered equivalent by ignoring
all sizes of arrays nested in types below field level.

gcc/c:
	* c-decl.cc (c_struct_hasher): Hash stable for struct
	types.
	(c_struct_hasher::hash, c_struct_hasher::equal): New functions.
	(finish_struct): Set TYPE_CANONICAL to first struct in
	equivalence class.
	* c-objc-common.cc (c_get_alias_set): Let structs or
	unions with variable size alias anything.
	* c-tree.h (comptypes_equiv): New prototype.
	* c-typeck.cc (comptypes_equiv): New function.
	(comptypes_internal): Implement equivalence mode.
	(tagged_types_tu_compatible): Implement equivalence mode.

gcc/testsuite:
	* gcc.dg/c2x-tag-2.c: Remove xfail.
	* gcc.dg/c2x-tag-6.c: Remove xfail.
	* gcc.dg/c2x-tag-alias-1.c: New test.
	* gcc.dg/c2x-tag-alias-2.c: New test.
	* gcc.dg/c2x-tag-alias-3.c: New test.
	* gcc.dg/c2x-tag-alias-4.c: New test.
	* gcc.dg/c2x-tag-alias-5.c: New test.
	* gcc.dg/c2x-tag-alias-6.c: New test.
	* gcc.dg/c2x-tag-alias-7.c: New test.
	* gcc.dg/c2x-tag-alias-8.c: New test.
---
 gcc/c/c-decl.cc                        | 48 +++++++++++++
 gcc/c/c-objc-common.cc                 |  5 ++
 gcc/c/c-tree.h                         |  1 +
 gcc/c/c-typeck.cc                      | 31 ++++++++
 gcc/testsuite/gcc.dg/c2x-tag-2.c       |  2 +-
 gcc/testsuite/gcc.dg/c2x-tag-6.c       |  2 +-
 gcc/testsuite/gcc.dg/c2x-tag-alias-1.c | 48 +++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-alias-2.c | 73 +++++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-alias-3.c | 48 +++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-alias-4.c | 73 +++++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-alias-5.c | 30 ++++++++
 gcc/testsuite/gcc.dg/c2x-tag-alias-6.c | 77 ++++++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-alias-7.c | 98 ++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-alias-8.c | 90 +++++++++++++++++++++++
 14 files changed, 624 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-5.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-6.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-7.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-8.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index b514e8a35ee..2137ba8b845 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -603,6 +603,36 @@ public:
   auto_vec<tree> typedefs_seen;
 };
 
+
+/* Hash table for structs and unions.  */
+struct c_struct_hasher : ggc_ptr_hash<tree_node>
+{
+  static hashval_t hash (tree t);
+  static bool equal (tree, tree);
+};
+
+/* Hash an RECORD OR UNION.  */
+hashval_t
+c_struct_hasher::hash (tree type)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (TREE_CODE (type));
+  hstate.add_object (TYPE_NAME (type));
+
+  return hstate.end ();
+}
+
+/* Compare two RECORD or UNION types.  */
+bool
+c_struct_hasher::equal (tree t1,  tree t2)
+{
+  return comptypes_equiv_p (t1, t2);
+}
+
+/* All tagged typed so that TYPE_CANONICAL can be set correctly.  */
+static GTY (()) hash_table<c_struct_hasher> *c_struct_htab;
+
 /* Information for the struct or union currently being parsed, or
    NULL if not parsing a struct or union.  */
 static class c_struct_parse_info *struct_parse_info;
@@ -9503,6 +9533,24 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
 
   C_TYPE_BEING_DEFINED (t) = 0;
 
+  /* Set type canonical based on equivalence class.  */
+  if (flag_isoc2x)
+    {
+      if (NULL == c_struct_htab)
+	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
+
+      hashval_t hash = c_struct_hasher::hash (t);
+
+      tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
+      if (*e)
+	TYPE_CANONICAL (t) = *e;
+      else
+	{
+	  TYPE_CANONICAL (t) = t;
+	  *e = t;
+	}
+    }
+
   tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
   for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
     {
diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
index e4aed61ed00..992225bbb29 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -389,6 +389,11 @@ c_get_alias_set (tree t)
   if (TREE_CODE (t) == ENUMERAL_TYPE)
     return get_alias_set (ENUM_UNDERLYING_TYPE (t));
 
+  /* Structs with variable size can alias different incompatible
+     structs.  Let them alias anything.   */
+  if (RECORD_OR_UNION_TYPE_P (t) && C_TYPE_VARIABLE_SIZE (t))
+    return 0;
+
   return c_common_get_alias_set (t);
 }
 
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 511fd9ee0e5..1a8e8f072bd 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -748,6 +748,7 @@ extern tree require_complete_type (location_t, tree);
 extern bool same_translation_unit_p (const_tree, const_tree);
 extern int comptypes (tree, tree);
 extern bool comptypes_same_p (tree, tree);
+extern int comptypes_equiv_p (tree, tree);
 extern int comptypes_check_different_types (tree, tree, bool *);
 extern int comptypes_check_enum_int (tree, tree, bool *);
 extern bool c_mark_addressable (tree, bool = false);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 2b79cbba950..2489fa1e3d1 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1041,6 +1041,7 @@ struct comptypes_data {
   bool different_types_p;
   bool warning_needed;
   bool anon_field;
+  bool equiv;
 
   const struct tagged_tu_seen_cache* cache;
 };
@@ -1101,6 +1102,21 @@ comptypes_check_different_types (tree type1, tree type2,
 
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
+
+
+/* Like comptypes, but if it returns nonzero for struct and union
+   types considered equivalent for aliasing purposes.  */
+
+int
+comptypes_equiv_p (tree type1, tree type2)
+{
+  struct comptypes_data data = { };
+  data.equiv = true;
+  bool ret = comptypes_internal (type1, type2, &data);
+
+  return ret;
+}
+
 \f
 /* Return true if TYPE1 and TYPE2 are compatible types for assignment
    or various other operations.  If they are compatible but a warning may
@@ -1227,6 +1243,9 @@ comptypes_internal (const_tree type1, const_tree type2,
 
 	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
 	  data->different_types_p = true;
+	/* ignore size mismatches */
+	if (data->equiv)
+	  return 1;
 	/* Sizes must match unless one is missing or variable.  */
 	if (d1 == NULL_TREE || d2 == NULL_TREE || d1 == d2)
 	  return true;
@@ -1443,6 +1462,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	if (list_length (TYPE_FIELDS (t1)) != list_length (TYPE_FIELDS (t2)))
 	  return false;
 
+	if (data->equiv && (C_TYPE_VARIABLE_SIZE (t1) || C_TYPE_VARIABLE_SIZE (t2)))
+	  return 0;
+
 	for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2);
 	     s1 && s2;
 	     s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2))
@@ -1462,6 +1484,15 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
 				     DECL_FIELD_BIT_OFFSET (s2)) != 1)
 	      return false;
+
+	    tree st1 = TYPE_SIZE (TREE_TYPE (s1));
+	    tree st2 = TYPE_SIZE (TREE_TYPE (s2));
+
+	    if (data->equiv
+		&& st1 && TREE_CODE (st1) == INTEGER_CST
+		&& st2 && TREE_CODE (st2) == INTEGER_CST
+		&& !tree_int_cst_equal (st1, st2))
+	     return 0;
 	  }
 	return true;
 
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-2.c b/gcc/testsuite/gcc.dg/c2x-tag-2.c
index a68392e1fab..e28c2b5eea2 100644
--- a/gcc/testsuite/gcc.dg/c2x-tag-2.c
+++ b/gcc/testsuite/gcc.dg/c2x-tag-2.c
@@ -1,4 +1,4 @@
-/* { dg-do { compile xfail { *-*-* } } }
+/* { dg-do compile }
  * { dg-options "-std=c2x" }
  */
 
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-6.c b/gcc/testsuite/gcc.dg/c2x-tag-6.c
index bf7cfb342d4..95a04bf9b0e 100644
--- a/gcc/testsuite/gcc.dg/c2x-tag-6.c
+++ b/gcc/testsuite/gcc.dg/c2x-tag-6.c
@@ -1,5 +1,5 @@
 /*
- * { dg-do { run xfail { "*-*-*" } } }
+ * { dg-do run }
  * { dg-options "-std=c2x" }
  */
 
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-1.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-1.c
new file mode 100644
index 00000000000..dfa1f084743
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-1.c
@@ -0,0 +1,48 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c2x -O2" }
+ */
+
+
+struct foo { int x; };
+
+int test_foo(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+enum bar { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (A == test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-2.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-2.c
new file mode 100644
index 00000000000..1354adb3483
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-2.c
@@ -0,0 +1,73 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c2x -O2" }
+ */
+
+
+struct foo { int x; };
+
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+int test_foo2(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct fox { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+enum bar { A = 1, B = 3, C = 5, D = 9 };
+
+int test_bar1(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+int test_bar2(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (1 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	if (1 != test_foo2(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (A == test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (A == test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-3.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-3.c
new file mode 100644
index 00000000000..ec086dd52ab
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-3.c
@@ -0,0 +1,48 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c2x -flto -O2" }
+ */
+
+
+struct foo { int x; };
+
+int test_foo(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+enum bar { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (A == test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-4.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-4.c
new file mode 100644
index 00000000000..537a1874454
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-4.c
@@ -0,0 +1,73 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c2x -flto -O2" }
+ */
+
+
+struct foo { int x; };
+
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+int test_foo2(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct fox { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+enum bar { A = 1, B = 3, C = 5, D = 9 };
+
+int test_bar1(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+int test_bar2(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (1 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	if (1 != test_foo2(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (A == test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (A == test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-5.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-5.c
new file mode 100644
index 00000000000..061676c2672
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-5.c
@@ -0,0 +1,30 @@
+/* { dg-do run }
+ * { dg-options "-std=c2x -O2" }
+ */
+
+// not sure this is wise, but this was already like thi sbefore
+
+typedef struct { int x; } foo_t;
+
+int test_foo(foo_t* a, void* b)
+{
+	a->x = 1;
+
+	struct { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int main()
+{
+	foo_t y;
+
+	if (1 != test_foo(&y, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-6.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-6.c
new file mode 100644
index 00000000000..3617a159d7c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-6.c
@@ -0,0 +1,77 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c2x -O2" }
+ */
+
+
+/* We check that we tolerate differences for
+ * optimization.
+ */
+
+
+struct bar0 { int x; int f[3]; int y; };
+
+int test_bar0(struct bar0* a, void* b)
+{
+	a->x = 1;
+
+	struct bar0 { int x; int f[4]; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+struct bar1 { int x; int (*f)[3]; };
+
+int test_bar1(struct bar1* a, void* b)
+{
+	a->x = 1;
+
+	struct bar1 { int x; int (*f)[3]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar2 { int x; int (*f)[3]; };
+
+int test_bar2(struct bar2* a, void* b)
+{
+	a->x = 1;
+
+	struct bar2 { int x; int (*f)[4]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	// control
+
+	struct bar0 z0;
+
+	if (1 != test_bar0(&z0, &z0))
+		__builtin_abort();
+
+	// this could be different
+	struct bar1 z1;
+
+	if (2 != test_bar1(&z1, &z1))
+		__builtin_abort();
+
+	struct bar2 z2;
+
+	if (2 != test_bar2(&z2, &z2))
+		__builtin_abort();
+
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-7.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-7.c
new file mode 100644
index 00000000000..0c1114e2b70
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-7.c
@@ -0,0 +1,98 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c2x -O2" }
+ */
+
+
+
+struct bar { int x; int f[]; };
+
+int test_bar1(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+int test_bar2(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[0]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+int test_bar3(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[1]; }* p = b;
+	struct bar* q = a;			/* { dg-warning "incompatible" } */
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int test_bar4(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	int n = 3;
+	struct bar { int x; int f[n]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct foo { int x; int f[3]; };
+
+
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	int n = 3;
+	struct foo { int x; int f[n]; }* p = b;
+	struct foo* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (2 != test_bar2(&z, &z))
+		__builtin_abort();
+#if 0
+	if (1 != test_bar3(&z, &z))
+		__builtin_abort();
+#endif
+	if (2 != test_bar4(&z, &z))
+		__builtin_abort();
+
+	struct foo y;
+
+	if (2 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-8.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-8.c
new file mode 100644
index 00000000000..e3f6173005f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-8.c
@@ -0,0 +1,90 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c2x -O2" }
+ */
+
+
+/* We check that we tolerate differences for
+ * optimization.
+ */
+
+struct bar1 { int x; enum A1 { X1 = 1 } f; };
+
+int test_bar1(struct bar1* a, void* b)
+{
+	a->x = 1;
+
+	struct bar1 { int x; enum A1 { X1 = 2 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar2 { int x; enum A2 { X2 = 1 } f; };
+
+int test_bar2(struct bar2* a, void* b)
+{
+	a->x = 1;
+
+	struct bar2 { int x; enum B2 { X2 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+struct bar3 { int x; enum A3 { X3 = 1 } f; };
+
+int test_bar3(struct bar3* a, void* b)
+{
+	a->x = 1;
+
+	struct bar3 { int x; enum A3 { Y3 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar4 { int x; enum { Z4 = 1 } f; };
+
+int test_bar4(struct bar4* a, void* b)
+{
+	a->x = 1;
+
+	struct bar4 { int x; enum { Z4 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar1 z1;
+
+	if (1 != test_bar1(&z1, &z1))
+		__builtin_abort();
+
+	struct bar2 z2;
+
+	if (1 != test_bar2(&z2, &z2))
+		__builtin_abort();
+
+	struct bar3 z3;
+
+	if (1 != test_bar3(&z3, &z3))
+		__builtin_abort();
+
+	struct bar4 z4;
+
+	if (1 != test_bar4(&z4, &z4))
+		__builtin_abort();
+
+	return 0;
+}
+
+
-- 
2.30.2



^ permalink raw reply	[flat|nested] 15+ messages in thread

* [C PATCH 6/6] c23: construct composite type for tagged types
  2023-08-26 16:19 c23 type compatibility rules, v2 Martin Uecker
                   ` (4 preceding siblings ...)
  2023-08-26 16:25 ` [C PATCH 5/6] c23: aliasing of compatible tagged types Martin Uecker
@ 2023-08-26 16:26 ` Martin Uecker
  2023-11-07 23:45   ` Joseph Myers
  2023-08-26 16:26 ` [C PATCH] c: flag for tag compatibility rules Martin Uecker
  6 siblings, 1 reply; 15+ messages in thread
From: Martin Uecker @ 2023-08-26 16:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



Support for constructing composite type for structs and unions
in C23.

gcc/c:
	* c-typeck.cc (composite_type_internal): Adapted from
	composite_type to support structs and unions.
	(composite_type): New wrapper function.
	(build_conditional_operator): Return composite type.

gcc/testsuite:
	* gcc.dg/c2x-tag-composite-1.c: New test.
	* gcc.dg/c2x-tag-composite-2.c: New test.
	* gcc.dg/c2x-tag-composite-3.c: New test.
	* gcc.dg/c2x-tag-composite-4.c: New test.
---
 gcc/c/c-typeck.cc                          | 114 +++++++++++++++++----
 gcc/testsuite/gcc.dg/c2x-tag-composite-1.c |  26 +++++
 gcc/testsuite/gcc.dg/c2x-tag-composite-2.c |  16 +++
 gcc/testsuite/gcc.dg/c2x-tag-composite-3.c |  17 +++
 gcc/testsuite/gcc.dg/c2x-tag-composite-4.c |  21 ++++
 5 files changed, 176 insertions(+), 18 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-composite-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-composite-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-composite-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-composite-4.c

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 2489fa1e3d1..357367eab09 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -381,8 +381,15 @@ build_functype_attribute_variant (tree ntype, tree otype, tree attrs)
    nonzero; if that isn't so, this may crash.  In particular, we
    assume that qualifiers match.  */
 
+struct composite_cache {
+  tree t1;
+  tree t2;
+  tree composite;
+  struct composite_cache* next;
+};
+
 tree
-composite_type (tree t1, tree t2)
+composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
 {
   enum tree_code code1;
   enum tree_code code2;
@@ -425,7 +432,8 @@ composite_type (tree t1, tree t2)
       {
 	tree pointed_to_1 = TREE_TYPE (t1);
 	tree pointed_to_2 = TREE_TYPE (t2);
-	tree target = composite_type (pointed_to_1, pointed_to_2);
+	tree target = composite_type_internal (pointed_to_1,
+					       pointed_to_2, cache);
         t1 = build_pointer_type_for_mode (target, TYPE_MODE (t1), false);
 	t1 = build_type_attribute_variant (t1, attributes);
 	return qualify_type (t1, t2);
@@ -433,7 +441,8 @@ composite_type (tree t1, tree t2)
 
     case ARRAY_TYPE:
       {
-	tree elt = composite_type (TREE_TYPE (t1), TREE_TYPE (t2));
+	tree elt = composite_type_internal (TREE_TYPE (t1), TREE_TYPE (t2),
+					    cache);
 	int quals;
 	tree unqual_elt;
 	tree d1 = TYPE_DOMAIN (t1);
@@ -501,9 +510,61 @@ composite_type (tree t1, tree t2)
 	return build_type_attribute_variant (t1, attributes);
       }
 
-    case ENUMERAL_TYPE:
     case RECORD_TYPE:
     case UNION_TYPE:
+      if (flag_isoc2x && !comptypes_same_p (t1, t2))
+	{
+	  gcc_checking_assert (COMPLETE_TYPE_P (t1) && COMPLETE_TYPE_P (t2));
+	  gcc_checking_assert (comptypes (t1, t2));
+
+	  /* If a composite type for these two types is already under
+	     construction, return it.  */
+
+	  for (struct composite_cache *c = cache; c != NULL; c = c->next)
+	    if (c->t1 == t1 && c->t2 == t2)
+	       return c->composite;
+
+	  /* Otherwise, create a new type node and link it into the cache.  */
+
+	  tree n = make_node (code1);
+	  struct composite_cache cache2 = { t1, t2, n, cache };
+	  cache = &cache2;
+
+	  tree f1 = TYPE_FIELDS (t1);
+	  tree f2 = TYPE_FIELDS (t2);
+	  tree fields = NULL_TREE;
+
+	  for (tree a = f1, b = f2; a && b;
+	       a = DECL_CHAIN (a), b = DECL_CHAIN (b))
+	    {
+	      tree ta = TREE_TYPE (a);
+	      tree tb = TREE_TYPE (b);
+
+	      gcc_assert (DECL_NAME (a) == DECL_NAME (b));
+	      gcc_assert (comptypes (ta, tb));
+
+	      tree f = build_decl (input_location, FIELD_DECL, DECL_NAME (a),
+				   composite_type_internal (ta, tb, cache));
+
+	      DECL_FIELD_CONTEXT (f) = n;
+	      DECL_CHAIN (f) = fields;
+	      fields = f;
+	    }
+
+	  TYPE_NAME (n) = TYPE_NAME (t1);
+	  TYPE_FIELDS (n) = nreverse (fields);
+	  TYPE_ATTRIBUTES (n) = attributes;
+	  layout_type (n);
+	  n = build_type_attribute_variant (n, attributes);
+	  n = qualify_type (n, t1);
+
+	  gcc_checking_assert (comptypes (n, t1));
+	  gcc_checking_assert (comptypes (n, t2));
+
+	  return n;
+	}
+      /* FALLTHRU */
+    case ENUMERAL_TYPE:
       if (attributes != NULL)
 	{
 	  /* Try harder not to create a new aggregate type.  */
@@ -518,7 +579,8 @@ composite_type (tree t1, tree t2)
       /* Function types: prefer the one that specified arg types.
 	 If both do, merge the arg types.  Also merge the return types.  */
       {
-	tree valtype = composite_type (TREE_TYPE (t1), TREE_TYPE (t2));
+	tree valtype = composite_type_internal (TREE_TYPE (t1),
+						TREE_TYPE (t2), cache);
 	tree p1 = TYPE_ARG_TYPES (t1);
 	tree p2 = TYPE_ARG_TYPES (t2);
 	int len;
@@ -563,6 +625,16 @@ composite_type (tree t1, tree t2)
 	for (; p1 && p1 != void_list_node;
 	     p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2), n = TREE_CHAIN (n))
 	  {
+	     tree mv1 = TREE_VALUE (p1);
+	     if (mv1 && mv1 != error_mark_node
+		 && TREE_CODE (mv1) != ARRAY_TYPE)
+	       mv1 = TYPE_MAIN_VARIANT (mv1);
+
+	     tree mv2 = TREE_VALUE (p2);
+	     if (mv2 && mv2 != error_mark_node
+		 && TREE_CODE (mv2) != ARRAY_TYPE)
+	       mv2 = TYPE_MAIN_VARIANT (mv2);
+
 	    /* A null type means arg type is not specified.
 	       Take whatever the other function type has.  */
 	    if (TREE_VALUE (p1) == NULL_TREE)
@@ -583,10 +655,6 @@ composite_type (tree t1, tree t2)
 		&& TREE_VALUE (p1) != TREE_VALUE (p2))
 	      {
 		tree memb;
-		tree mv2 = TREE_VALUE (p2);
-		if (mv2 && mv2 != error_mark_node
-		    && TREE_CODE (mv2) != ARRAY_TYPE)
-		  mv2 = TYPE_MAIN_VARIANT (mv2);
 		for (memb = TYPE_FIELDS (TREE_VALUE (p1));
 		     memb; memb = DECL_CHAIN (memb))
 		  {
@@ -596,8 +664,9 @@ composite_type (tree t1, tree t2)
 		      mv3 = TYPE_MAIN_VARIANT (mv3);
 		    if (comptypes (mv3, mv2))
 		      {
-			TREE_VALUE (n) = composite_type (TREE_TYPE (memb),
-							 TREE_VALUE (p2));
+			TREE_VALUE (n) = composite_type_internal (TREE_TYPE (memb),
+								  TREE_VALUE (p2),
+								  cache);
 			pedwarn (input_location, OPT_Wpedantic,
 				 "function types not truly compatible in ISO C");
 			goto parm_done;
@@ -608,10 +677,6 @@ composite_type (tree t1, tree t2)
 		&& TREE_VALUE (p2) != TREE_VALUE (p1))
 	      {
 		tree memb;
-		tree mv1 = TREE_VALUE (p1);
-		if (mv1 && mv1 != error_mark_node
-		    && TREE_CODE (mv1) != ARRAY_TYPE)
-		  mv1 = TYPE_MAIN_VARIANT (mv1);
 		for (memb = TYPE_FIELDS (TREE_VALUE (p2));
 		     memb; memb = DECL_CHAIN (memb))
 		  {
@@ -621,15 +686,17 @@ composite_type (tree t1, tree t2)
 		      mv3 = TYPE_MAIN_VARIANT (mv3);
 		    if (comptypes (mv3, mv1))
 		      {
-			TREE_VALUE (n) = composite_type (TREE_TYPE (memb),
-							 TREE_VALUE (p1));
+			TREE_VALUE (n)
+				= composite_type_internal (TREE_TYPE (memb),
+							   TREE_VALUE (p1),
+							   cache);
 			pedwarn (input_location, OPT_Wpedantic,
 				 "function types not truly compatible in ISO C");
 			goto parm_done;
 		      }
 		  }
 	      }
-	    TREE_VALUE (n) = composite_type (TREE_VALUE (p1), TREE_VALUE (p2));
+	    TREE_VALUE (n) = composite_type_internal (mv1, mv2, cache);
 	  parm_done: ;
 	  }
 
@@ -641,7 +708,13 @@ composite_type (tree t1, tree t2)
     default:
       return build_type_attribute_variant (t1, attributes);
     }
+}
 
+tree
+composite_type (tree t1, tree t2)
+{
+  struct composite_cache cache = { };
+  return composite_type_internal (t1, t2, &cache);
 }
 
 /* Return the type of a conditional expression between pointers to
@@ -5480,6 +5553,11 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
     result_type = type2;
   else if (code1 == POINTER_TYPE && code2 == NULLPTR_TYPE)
     result_type = type1;
+  else if (RECORD_OR_UNION_TYPE_P (type1) && RECORD_OR_UNION_TYPE_P (type2)
+	   && comptypes (TYPE_MAIN_VARIANT (type1),
+			 TYPE_MAIN_VARIANT (type2)))
+    result_type = composite_type (TYPE_MAIN_VARIANT (type1),
+				  TYPE_MAIN_VARIANT (type2));
 
   if (!result_type)
     {
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-composite-1.c b/gcc/testsuite/gcc.dg/c2x-tag-composite-1.c
new file mode 100644
index 00000000000..aae11f6cee2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-composite-1.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c2x" } */
+
+void b(void)
+{
+	int n = 3;
+
+	  extern struct f { char (*x)[3]; char (*y)[]; } q;
+	{ extern struct f { char (*x)[]; char (*y)[4]; } q; 
+	  _Static_assert(3 == sizeof(*q.x), "");
+	  _Static_assert(4 == sizeof(*q.y), "");
+	}
+	{ extern struct f { char (*x)[2]; char (*y)[]; } q; (void)q; }	/* { dg-error "conflicting" } */
+
+	{ struct f { char (*x)[n]; char (*y)[3]; }* qp = &q; (void)*qp; }
+	(void)q;
+
+	  static struct g { int a; char buf[n]; } *p; (void)p;
+	{ static struct g { int a; char buf[3]; } *p; (void)p; }
+
+	  static struct h { int a; void (*s)(char buf[n]); } *t; (void)t;
+	{ static struct h { int a; void (*s)(char buf[3]); } *t; (void)t; }
+}
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-composite-2.c b/gcc/testsuite/gcc.dg/c2x-tag-composite-2.c
new file mode 100644
index 00000000000..dfc4336751a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-composite-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c2x" } */
+
+
+struct foo { int (*(*i)(void))[]; } x;
+
+
+void f(void)
+{ 
+	const struct foo { int (*(*i)())[3]; } y;
+	_Static_assert(3 * sizeof(int) == sizeof(*((1 ? &x : &y)->i())), "");
+}
+
+void g(struct foo { int x; } a);
+void g(const struct foo { int x; } a);
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-composite-3.c b/gcc/testsuite/gcc.dg/c2x-tag-composite-3.c
new file mode 100644
index 00000000000..02159f3c44d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-composite-3.c
@@ -0,0 +1,17 @@
+/* { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// bit-fields
+
+extern struct foo { int x:3; } x;
+struct foo { int x:3; } y;
+
+void f(void)
+{
+	extern typeof(*(1 ? &x : &y)) x;
+	&x.x;					/* { dg-error "bit-field" } */
+}
+
+struct foo { int x:2; };			/* { dg-error "redefinition" } */
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-composite-4.c b/gcc/testsuite/gcc.dg/c2x-tag-composite-4.c
new file mode 100644
index 00000000000..1bd98cc1a44
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-composite-4.c
@@ -0,0 +1,21 @@
+/* { dg-do compile }
+ * { dg-options "-std=c2x" } 
+ */
+
+// conditional operator
+
+void f(void)
+{
+	struct foo { int x; } a;
+	struct foo { int x; } b;
+	1 ? a : b;
+}
+
+struct bar { int x; } a;
+
+void g(void)
+{
+	struct bar { int x; } b;
+	1 ? a : b;
+}
+
-- 
2.30.2



^ permalink raw reply	[flat|nested] 15+ messages in thread

* [C PATCH] c: flag for tag compatibility rules
  2023-08-26 16:19 c23 type compatibility rules, v2 Martin Uecker
                   ` (5 preceding siblings ...)
  2023-08-26 16:26 ` [C PATCH 6/6] c23: construct composite type for " Martin Uecker
@ 2023-08-26 16:26 ` Martin Uecker
  6 siblings, 0 replies; 15+ messages in thread
From: Martin Uecker @ 2023-08-26 16:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


Add a flag to turn tag compatibility rules on or off
independent from the language version.

gcc/c-family:
	* c.opt (flag_tag_compat): New flag.

gcc/c:
	* c-decl.cc (diagnose_mismatched_decls, start_struct,
	finish_struct, start_enum, finish_enum): Support flag.
	* c-typeck.cc (composite_type_internal): Support flag.

gcc/doc:
	* invoke.texi: Document flag.

gcc/testsuite:
	* gcc.dg/asan/pr81470.c: Turn off tag compatibility.
	* gcc.dg/c99-tag-1.c: Turn off tag compatibility.
	* gcc.dg/c99-tag-2.c: Turn off tag compatibility.
	* gcc.dg/decl-3.c: Turn off tag compatibility.
	* gcc.dg/enum-redef-1.c: Turn off tag compatibility.
	* gcc.dg/pr17188-1.c: Turn off tag compatibility.
	* gcc.dg/pr18809-1.c: Turn off tag compatibility.
	* gcc.dg/pr39084.c: Turn off tag compatibility.
	* gcc.dg/pr79983.c: Turn off tag compatibility.
---
 gcc/c-family/c.opt                  |  3 +++
 gcc/c/c-decl.cc                     | 12 ++++++------
 gcc/c/c-typeck.cc                   |  2 +-
 gcc/doc/invoke.texi                 |  5 +++++
 gcc/testsuite/gcc.dg/asan/pr81460.c |  1 +
 gcc/testsuite/gcc.dg/c99-tag-1.c    |  2 +-
 gcc/testsuite/gcc.dg/c99-tag-2.c    |  2 +-
 gcc/testsuite/gcc.dg/decl-3.c       |  1 +
 gcc/testsuite/gcc.dg/enum-redef-1.c |  2 ++
 gcc/testsuite/gcc.dg/pr17188-1.c    |  2 +-
 gcc/testsuite/gcc.dg/pr18809-1.c    |  1 +
 gcc/testsuite/gcc.dg/pr39084.c      |  2 +-
 gcc/testsuite/gcc.dg/pr79983.c      |  2 +-
 13 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 2242524cd3e..f95f12ba249 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -2214,6 +2214,9 @@ Enum(strong_eval_order) String(some) Value(1)
 EnumValue
 Enum(strong_eval_order) String(all) Value(2)
 
+ftag-compat
+C Var(flag_tag_compat) Init(1)
+
 ftemplate-backtrace-limit=
 C++ ObjC++ Joined RejectNegative UInteger Var(template_backtrace_limit) Init(10)
 Set the maximum number of template instantiation notes for a single warning or error.
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 2137ba8b845..6d1e0d5c382 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2094,7 +2094,7 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
      given scope.  */
   if (TREE_CODE (olddecl) == CONST_DECL)
     {
-      if (flag_isoc2x
+      if ((flag_isoc2x || flag_tag_compat)
 	  && TYPE_NAME (DECL_CONTEXT (newdecl))
 	  && DECL_CONTEXT (newdecl) != DECL_CONTEXT (olddecl)
 	  && TYPE_NAME (DECL_CONTEXT (newdecl)) == TYPE_NAME (DECL_CONTEXT (olddecl)))
@@ -8723,7 +8723,7 @@ start_struct (location_t loc, enum tree_code code, tree name,
 
   /* For C2X, even if we already have a completed definition,
      we do not use it. We will check for consistency later.  */
-  if (flag_isoc2x && ref && TYPE_SIZE (ref))
+  if ((flag_isoc2x || flag_tag_compat) && ref && TYPE_SIZE (ref))
     ref = NULL_TREE;
 
   if (ref && TREE_CODE (ref) == code)
@@ -9515,7 +9515,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
     }
 
   /* Check for consistency with previous definition */
-  if (flag_isoc2x)
+  if (flag_isoc2x || flag_tag_compat)
     {
       tree vistype = previous_tag (t);
       if (vistype
@@ -9534,7 +9534,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
   C_TYPE_BEING_DEFINED (t) = 0;
 
   /* Set type canonical based on equivalence class.  */
-  if (flag_isoc2x)
+  if (flag_isoc2x || flag_tag_compat)
     {
       if (NULL == c_struct_htab)
 	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
@@ -9672,7 +9672,7 @@ 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 (flag_isoc2x && enumtype != NULL_TREE
+  if ((flag_isoc2x || flag_tag_compat) && enumtype != NULL_TREE
       && TREE_CODE (enumtype) == ENUMERAL_TYPE
       && TYPE_VALUES (enumtype) != NULL_TREE)
     enumtype = NULL_TREE;
@@ -9941,7 +9941,7 @@ finish_enum (tree enumtype, tree values, tree attributes)
     struct_parse_info->struct_types.safe_push (enumtype);
 
   /* Check for consistency with previous definition */
-  if (flag_isoc2x)
+  if (flag_isoc2x || flag_tag_compat)
     {
       tree vistype = previous_tag (enumtype);
       if (vistype
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 357367eab09..b99f0c3e2fd 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -512,7 +512,7 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
 
     case RECORD_TYPE:
     case UNION_TYPE:
-      if (flag_isoc2x && !comptypes_same_p (t1, t2))
+      if ((flag_isoc2x || flag_tag_compat) && !comptypes_same_p (t1, t2))
 	{
 	  gcc_checking_assert (COMPLETE_TYPE_P (t1) && COMPLETE_TYPE_P (t2));
 	  gcc_checking_assert (comptypes (t1, t2));
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a32dabf0405..d7a21ca8678 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -2911,6 +2911,11 @@ the target (the default).  This option is not supported for C++.
 @strong{Warning:} the @option{-fsso-struct} switch causes GCC to generate
 code that is not binary compatible with code generated without it if the
 specified endianness is not the native endianness of the target.
+
+@item -ftag-compat
+@opindex ftag-compat
+This option makes tagged types that are structurally equivalent compatible
+and allows identical redeclarations of tagged types in the same scope.
 @end table
 
 @node C++ Dialect Options
diff --git a/gcc/testsuite/gcc.dg/asan/pr81460.c b/gcc/testsuite/gcc.dg/asan/pr81460.c
index 00c1bb7c9f2..98ea40edb56 100644
--- a/gcc/testsuite/gcc.dg/asan/pr81460.c
+++ b/gcc/testsuite/gcc.dg/asan/pr81460.c
@@ -1,5 +1,6 @@
 /* PR sanitizer/80460 */
 /* { dg-do compile } */
+/* { dg-options "-fno-tag-compat" } */
 
 int
 f (int a, struct { int b[a]; } c) /* { dg-warning "anonymous struct declared inside parameter list will not be visible outside of this definition or declaration" } */
diff --git a/gcc/testsuite/gcc.dg/c99-tag-1.c b/gcc/testsuite/gcc.dg/c99-tag-1.c
index d7011d2cbec..1b52234ee3f 100644
--- a/gcc/testsuite/gcc.dg/c99-tag-1.c
+++ b/gcc/testsuite/gcc.dg/c99-tag-1.c
@@ -1,7 +1,7 @@
 /* Test for handling of tags (6.7.2.3).  */
 /* Origin: Joseph Myers <jsm28@cam.ac.uk> */
 /* { dg-do compile } */
-/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */
+/* { dg-options "-std=iso9899:1999 -pedantic-errors -fno-tag-compat" } */
 
 void
 foo (void)
diff --git a/gcc/testsuite/gcc.dg/c99-tag-2.c b/gcc/testsuite/gcc.dg/c99-tag-2.c
index 22cf90e27d3..b12c7bcd964 100644
--- a/gcc/testsuite/gcc.dg/c99-tag-2.c
+++ b/gcc/testsuite/gcc.dg/c99-tag-2.c
@@ -2,7 +2,7 @@
    not match one declared in an outer scope.  */
 /* Origin: Joseph Myers <jsm@polyomino.org.uk> */
 /* { dg-do compile } */
-/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */
+/* { dg-options "-std=iso9899:1999 -pedantic-errors -fno-tag-compat" } */
 
 struct s;
 struct t { struct s *p; } x;
diff --git a/gcc/testsuite/gcc.dg/decl-3.c b/gcc/testsuite/gcc.dg/decl-3.c
index cba0b906db3..66204270337 100644
--- a/gcc/testsuite/gcc.dg/decl-3.c
+++ b/gcc/testsuite/gcc.dg/decl-3.c
@@ -1,5 +1,6 @@
 /* PR c/9928 */
 /* { dg-do compile } */
+/* { dg-options "-fno-tag-compat" } */
 
 enum { CODES }; /* { dg-message "note: previous definition" } */
 enum { CODES }; /* { dg-error "conflicting types|redeclaration of enumerator" } */
diff --git a/gcc/testsuite/gcc.dg/enum-redef-1.c b/gcc/testsuite/gcc.dg/enum-redef-1.c
index b3fa6cbf8f1..837992f7441 100644
--- a/gcc/testsuite/gcc.dg/enum-redef-1.c
+++ b/gcc/testsuite/gcc.dg/enum-redef-1.c
@@ -1,3 +1,5 @@
+/* { dg-options "-fno-tag-compat" } */
+
 enum a { A };
 enum a { B }; /* { dg-bogus "nested redefinition" } */
 /* { dg-error "redeclaration of 'enum a'" "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/gcc.dg/pr17188-1.c b/gcc/testsuite/gcc.dg/pr17188-1.c
index 522a14f7d75..bb31ba30b5d 100644
--- a/gcc/testsuite/gcc.dg/pr17188-1.c
+++ b/gcc/testsuite/gcc.dg/pr17188-1.c
@@ -3,7 +3,7 @@
    diagnosed.  Bug 17188.  */
 /* Origin: Joseph Myers <jsm@polyomino.org.uk> */
 /* { dg-do compile } */
-/* { dg-options "" } */
+/* { dg-options "-fno-tag-compat" } */
 
 struct s0 { }; /* { dg-message "note: originally defined here" } */
 struct s0;
diff --git a/gcc/testsuite/gcc.dg/pr18809-1.c b/gcc/testsuite/gcc.dg/pr18809-1.c
index 5be41809da6..d7f55feae61 100644
--- a/gcc/testsuite/gcc.dg/pr18809-1.c
+++ b/gcc/testsuite/gcc.dg/pr18809-1.c
@@ -1,6 +1,7 @@
 /* PR c/18809 */
 /* Origin: Andrew Pinski <pinskia@gcc.gnu.org> */
 
+/* { dg-options "-pedantic-errors -fno-tag-compat" } */
 /* { dg-do compile } */
 
 void foo(enum E e) {}   /* { dg-error "forward ref" "forward" } */
diff --git a/gcc/testsuite/gcc.dg/pr39084.c b/gcc/testsuite/gcc.dg/pr39084.c
index ff731492154..776fbea6750 100644
--- a/gcc/testsuite/gcc.dg/pr39084.c
+++ b/gcc/testsuite/gcc.dg/pr39084.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2" } */
+/* { dg-options "-O2 -fno-tag-compat" } */
 
 struct color { int i; }; /* { dg-message "note: originally defined here" } */
 static const struct color col;
diff --git a/gcc/testsuite/gcc.dg/pr79983.c b/gcc/testsuite/gcc.dg/pr79983.c
index 1e292d42108..c9fe72f9169 100644
--- a/gcc/testsuite/gcc.dg/pr79983.c
+++ b/gcc/testsuite/gcc.dg/pr79983.c
@@ -1,6 +1,6 @@
 /* PR c/79983 */
 /* { dg-do compile } */
-/* { dg-options "" } */
+/* { dg-options "-fno-tag-compat" } */
 
 struct S;
 struct S { int i; }; /* { dg-message "originally defined here" } */
-- 
2.30.2



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [C PATCH 1/6]  c: reorganize recursive type checking
  2023-08-26 16:20 ` [C PATCH 1/6] c: reorganize recursive type checking Martin Uecker
@ 2023-09-06 20:59   ` Joseph Myers
  2023-09-10  8:17     ` [C PATCH 1/6 v2] " Martin Uecker
  0 siblings, 1 reply; 15+ messages in thread
From: Joseph Myers @ 2023-09-06 20:59 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Sat, 26 Aug 2023, Martin Uecker via Gcc-patches wrote:

> -static int
> +static bool
>  comp_target_types (location_t location, tree ttl, tree ttr)

The comment above this function should be updated to refer to returning 
true, not to returning 1.  And other comments on common_pointer_type and 
inside that function should be updated to refer to comp_target_types 
returning true, not nonzero.

> @@ -1395,17 +1382,13 @@ free_all_tagged_tu_seen_up_to (const struct tagged_tu_seen_cache *tu_til)
>  
>  /* Return 1 if two 'struct', 'union', or 'enum' types T1 and T2 are
>     compatible.  If the two types are not the same (which has been
> -   checked earlier), this can only happen when multiple translation
> -   units are being compiled.  See C99 6.2.7 paragraph 1 for the exact
> -   rules.  ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in
> -   comptypes_internal.  */
> +   checked earlier).  */
>  
> -static int
> +static bool
>  tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
> -			      bool *enum_and_int_p, bool *different_types_p)
> +			      struct comptypes_data* data)

Similarly, this comment should be updated for the new return type.  Also 
the GNU style is "struct comptypes_data *data" with space before not after 
'*'.

> @@ -1631,9 +1603,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
>     Otherwise, the argument types must match.
>     ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in comptypes_internal.  */
>  
> -static int
> +static bool
>  function_types_compatible_p (const_tree f1, const_tree f2,
> -			     bool *enum_and_int_p, bool *different_types_p)
> +			     struct comptypes_data *data)

Another comment to update for a changed return type.

>  /* Check two lists of types for compatibility, returning 0 for
> -   incompatible, 1 for compatible, or 2 for compatible with
> -   warning.  ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in
> -   comptypes_internal.  */
> +   incompatible, 1 for compatible.  ENUM_AND_INT_P and
> +   DIFFERENT_TYPES_P are as in comptypes_internal.  */
>  
> -static int
> +static bool
>  type_lists_compatible_p (const_tree args1, const_tree args2,
> -			 bool *enum_and_int_p, bool *different_types_p)
> +			 struct comptypes_data *data)

This one also needs updating to remove references to parameters that no 
longer exist.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [C PATCH 1/6 v2]  c: reorganize recursive type checking
  2023-09-06 20:59   ` Joseph Myers
@ 2023-09-10  8:17     ` Martin Uecker
  2023-09-11 20:28       ` Joseph Myers
  0 siblings, 1 reply; 15+ messages in thread
From: Martin Uecker @ 2023-09-10  8:17 UTC (permalink / raw)
  To: Joseph Myers; +Cc: gcc-patches


Thanks Joseph, below is a a revised version of this patch
with slight additional changes to the comment of
tagged_types_tu_compatible_p.

ok for trunk? 

Martin

Am Mittwoch, dem 06.09.2023 um 20:59 +0000 schrieb Joseph Myers:
> On Sat, 26 Aug 2023, Martin Uecker via Gcc-patches wrote:
> 
> > -static int
> > +static bool
> >  comp_target_types (location_t location, tree ttl, tree ttr)
> 
> The comment above this function should be updated to refer to returning 
> true, not to returning 1.  And other comments on common_pointer_type and 
> inside that function should be updated to refer to comp_target_types 
> returning true, not nonzero.
> 
> > @@ -1395,17 +1382,13 @@ free_all_tagged_tu_seen_up_to (const struct tagged_tu_seen_cache *tu_til)
> >  
> >  /* Return 1 if two 'struct', 'union', or 'enum' types T1 and T2 are
> >     compatible.  If the two types are not the same (which has been
> > -   checked earlier), this can only happen when multiple translation
> > -   units are being compiled.  See C99 6.2.7 paragraph 1 for the exact
> > -   rules.  ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in
> > -   comptypes_internal.  */
> > +   checked earlier).  */
> >  
> > -static int
> > +static bool
> >  tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
> > -			      bool *enum_and_int_p, bool *different_types_p)
> > +			      struct comptypes_data* data)
> 
> Similarly, this comment should be updated for the new return type.  Also 
> the GNU style is "struct comptypes_data *data" with space before not after 
> '*'.
> 
> > @@ -1631,9 +1603,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
> >     Otherwise, the argument types must match.
> >     ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in comptypes_internal.  */
> >  
> > -static int
> > +static bool
> >  function_types_compatible_p (const_tree f1, const_tree f2,
> > -			     bool *enum_and_int_p, bool *different_types_p)
> > +			     struct comptypes_data *data)
> 
> Another comment to update for a changed return type.
> 
> >  /* Check two lists of types for compatibility, returning 0 for
> > -   incompatible, 1 for compatible, or 2 for compatible with
> > -   warning.  ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in
> > -   comptypes_internal.  */
> > +   incompatible, 1 for compatible.  ENUM_AND_INT_P and
> > +   DIFFERENT_TYPES_P are as in comptypes_internal.  */
> >  
> > -static int
> > +static bool
> >  type_lists_compatible_p (const_tree args1, const_tree args2,
> > -			 bool *enum_and_int_p, bool *different_types_p)
> > +			 struct comptypes_data *data)
> 
> This one also needs updating to remove references to parameters that no 
> longer exist.
> 

c: reorganize recursive type checking

Reorganize recursive type checking to use a structure to
store information collected during the recursion and
returned to the caller (warning_needed, enum_and_init_p,
different_types_p).

gcc/c:
	* c-typeck.cc (struct comptypes_data): Add structure.
	(tagged_types_tu_compatible_p,
	function_types_compatible_p, type_lists_compatible_p,
	comptypes_internal): Add structure to interface, change
	return type to bool, and adapt calls.
	(comptarget_types): Change return type too bool.
	(comptypes, comptypes_check_enum_int,
	comptypes_check_different_types): Adapt calls.
---
 gcc/c/c-typeck.cc | 282 ++++++++++++++++++++--------------------------
 1 file changed, 121 insertions(+), 161 deletions(-)

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index e2bfd2caf85..e55e887da14 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -90,12 +90,14 @@ static bool require_constant_elements;
 static bool require_constexpr_value;
 
 static tree qualify_type (tree, tree);
-static int tagged_types_tu_compatible_p (const_tree, const_tree, bool *,
-					 bool *);
-static int comp_target_types (location_t, tree, tree);
-static int function_types_compatible_p (const_tree, const_tree, bool *,
-					bool *);
-static int type_lists_compatible_p (const_tree, const_tree, bool *, bool *);
+struct comptypes_data;
+static bool tagged_types_tu_compatible_p (const_tree, const_tree,
+					  struct comptypes_data *);
+static bool comp_target_types (location_t, tree, tree);
+static bool function_types_compatible_p (const_tree, const_tree,
+					 struct comptypes_data *);
+static bool type_lists_compatible_p (const_tree, const_tree,
+				     struct comptypes_data *);
 static tree lookup_field (tree, tree);
 static int convert_arguments (location_t, vec<location_t>, tree,
 			      vec<tree, va_gc> *, vec<tree, va_gc> *, tree,
@@ -125,7 +127,8 @@ static tree find_init_member (tree, struct obstack *);
 static void readonly_warning (tree, enum lvalue_use);
 static int lvalue_or_else (location_t, const_tree, enum lvalue_use);
 static void record_maybe_used_decl (tree);
-static int comptypes_internal (const_tree, const_tree, bool *, bool *);
+static bool comptypes_internal (const_tree, const_tree,
+				struct comptypes_data *data);
 \f
 /* Return true if EXP is a null pointer constant, false otherwise.  */
 
@@ -653,7 +656,7 @@ composite_type (tree t1, tree t2)
    possibly differently qualified versions of compatible types.
 
    We assume that comp_target_types has already been done and returned
-   nonzero; if that isn't so, this may crash.  */
+   true; if that isn't so, this may crash.  */
 
 static tree
 common_pointer_type (tree t1, tree t2)
@@ -707,7 +710,7 @@ common_pointer_type (tree t1, tree t2)
 
   /* If the two named address spaces are different, determine the common
      superset address space.  This is guaranteed to exist due to the
-     assumption that comp_target_type returned non-zero.  */
+     assumption that comp_target_type returned true.  */
   as1 = TYPE_ADDR_SPACE (pointed_to_1);
   as2 = TYPE_ADDR_SPACE (pointed_to_2);
   if (!addr_space_superset (as1, as2, &as_common))
@@ -1061,6 +1064,13 @@ common_type (tree t1, tree t2)
   return c_common_type (t1, t2);
 }
 
+struct comptypes_data {
+
+  bool enum_and_int_p;
+  bool different_types_p;
+  bool warning_needed;
+};
+
 /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
    or various other operations.  Return 2 if they are compatible
    but a warning may be needed if you use them together.  */
@@ -1069,12 +1079,13 @@ int
 comptypes (tree type1, tree type2)
 {
   const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;
-  int val;
 
-  val = comptypes_internal (type1, type2, NULL, NULL);
+  struct comptypes_data data = { };
+  bool ret = comptypes_internal (type1, type2, &data);
+
   free_all_tagged_tu_seen_up_to (tagged_tu_seen_base1);
 
-  return val;
+  return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
 /* Like comptypes, but if it returns non-zero because enum and int are
@@ -1084,12 +1095,14 @@ int
 comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
 {
   const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;
-  int val;
 
-  val = comptypes_internal (type1, type2, enum_and_int_p, NULL);
+  struct comptypes_data data = { };
+  bool ret = comptypes_internal (type1, type2, &data);
+  *enum_and_int_p = data.enum_and_int_p;
+
   free_all_tagged_tu_seen_up_to (tagged_tu_seen_base1);
 
-  return val;
+  return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
 /* Like comptypes, but if it returns nonzero for different types, it
@@ -1100,40 +1113,40 @@ comptypes_check_different_types (tree type1, tree type2,
 				 bool *different_types_p)
 {
   const struct tagged_tu_seen_cache * tagged_tu_seen_base1 = tagged_tu_seen_base;
-  int val;
 
-  val = comptypes_internal (type1, type2, NULL, different_types_p);
+  struct comptypes_data data = { };
+  bool ret = comptypes_internal (type1, type2, &data);
+  *different_types_p = data.different_types_p;
+
   free_all_tagged_tu_seen_up_to (tagged_tu_seen_base1);
 
-  return val;
+  return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 \f
-/* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
-   or various other operations.  Return 2 if they are compatible
-   but a warning may be needed if you use them together.  If
-   ENUM_AND_INT_P is not NULL, and one type is an enum and the other a
-   compatible integer type, then this sets *ENUM_AND_INT_P to true;
-   *ENUM_AND_INT_P is never set to false.  If DIFFERENT_TYPES_P is not
-   NULL, and the types are compatible but different enough not to be
+/* Return true if TYPE1 and TYPE2 are compatible types for assignment
+   or various other operations.  If they are compatible but a warning may
+   be needed if you use them together, 'warning_needed' in DATA is set.
+   If one type is an enum and the other a compatible integer type, then
+   this sets 'enum_and_int_p' in DATA to true (it is never set to
+   false).  If the types are compatible but different enough not to be
    permitted in C11 typedef redeclarations, then this sets
-   *DIFFERENT_TYPES_P to true; *DIFFERENT_TYPES_P is never set to
+   'different_types_p' in DATA to true; it is never set to
    false, but may or may not be set if the types are incompatible.
    This differs from comptypes, in that we don't free the seen
    types.  */
 
-static int
-comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
-		    bool *different_types_p)
+static bool
+comptypes_internal (const_tree type1, const_tree type2,
+		    struct comptypes_data *data)
 {
   const_tree t1 = type1;
   const_tree t2 = type2;
-  int attrval, val;
 
   /* Suppress errors caused by previously reported errors.  */
 
   if (t1 == t2 || !t1 || !t2
       || TREE_CODE (t1) == ERROR_MARK || TREE_CODE (t2) == ERROR_MARK)
-    return 1;
+    return true;
 
   /* Enumerated types are compatible with integer types, but this is
      not transitive: two enumerated types in the same translation unit
@@ -1146,10 +1159,8 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
       t1 = ENUM_UNDERLYING_TYPE (t1);
       if (TREE_CODE (t2) != VOID_TYPE)
 	{
-	  if (enum_and_int_p != NULL)
-	    *enum_and_int_p = true;
-	  if (different_types_p != NULL)
-	    *different_types_p = true;
+	  data->enum_and_int_p = true;
+	  data->different_types_p = true;
 	}
     }
   else if (TREE_CODE (t2) == ENUMERAL_TYPE
@@ -1159,25 +1170,23 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
       t2 = ENUM_UNDERLYING_TYPE (t2);
       if (TREE_CODE (t1) != VOID_TYPE)
 	{
-	  if (enum_and_int_p != NULL)
-	    *enum_and_int_p = true;
-	  if (different_types_p != NULL)
-	    *different_types_p = true;
+	  data->enum_and_int_p = true;
+	  data->different_types_p = true;
 	}
     }
 
   if (t1 == t2)
-    return 1;
+    return true;
 
   /* Different classes of types can't be compatible.  */
 
   if (TREE_CODE (t1) != TREE_CODE (t2))
-    return 0;
+    return false;
 
   /* Qualifiers must match. C99 6.7.3p9 */
 
   if (TYPE_QUALS (t1) != TYPE_QUALS (t2))
-    return 0;
+    return false;
 
   /* Allow for two different type nodes which have essentially the same
      definition.  Note that we already checked for equality of the type
@@ -1185,14 +1194,16 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
 
   if (TREE_CODE (t1) != ARRAY_TYPE
       && TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2))
-    return 1;
+    return true;
+
+  int attrval;
 
   /* 1 if no need for warning yet, 2 if warning cause has been seen.  */
   if (!(attrval = comp_type_attributes (t1, t2)))
-     return 0;
+     return false;
 
-  /* 1 if no need for warning yet, 2 if warning cause has been seen.  */
-  val = 0;
+  if (2 == attrval)
+    data->warning_needed = true;
 
   switch (TREE_CODE (t1))
     {
@@ -1219,16 +1230,11 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
     case POINTER_TYPE:
       /* Do not remove mode information.  */
       if (TYPE_MODE (t1) != TYPE_MODE (t2))
-	break;
-      val = (TREE_TYPE (t1) == TREE_TYPE (t2)
-	     ? 1 : comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2),
-				       enum_and_int_p, different_types_p));
-      break;
+	return false;
+      return comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data);
 
     case FUNCTION_TYPE:
-      val = function_types_compatible_p (t1, t2, enum_and_int_p,
-					 different_types_p);
-      break;
+      return function_types_compatible_p (t1, t2, data);
 
     case ARRAY_TYPE:
       {
@@ -1236,21 +1242,16 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
 	tree d2 = TYPE_DOMAIN (t2);
 	bool d1_variable, d2_variable;
 	bool d1_zero, d2_zero;
-	val = 1;
 
 	/* Target types must match incl. qualifiers.  */
-	if (TREE_TYPE (t1) != TREE_TYPE (t2)
-	    && (val = comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2),
-					  enum_and_int_p,
-					  different_types_p)) == 0)
-	  return 0;
-
-	if (different_types_p != NULL
-	    && (d1 == NULL_TREE) != (d2 == NULL_TREE))
-	  *different_types_p = true;
+	if (!comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data))
+	  return false;
+
+	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
+	  data->different_types_p = true;
 	/* Sizes must match unless one is missing or variable.  */
 	if (d1 == NULL_TREE || d2 == NULL_TREE || d1 == d2)
-	  break;
+	  return true;
 
 	d1_zero = !TYPE_MAX_VALUE (d1);
 	d2_zero = !TYPE_MAX_VALUE (d2);
@@ -1264,59 +1265,45 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p,
 	d1_variable = d1_variable || (d1_zero && C_TYPE_VARIABLE_SIZE (t1));
 	d2_variable = d2_variable || (d2_zero && C_TYPE_VARIABLE_SIZE (t2));
 
-	if (different_types_p != NULL
-	    && d1_variable != d2_variable)
-	  *different_types_p = true;
+	if (d1_variable != d2_variable)
+	  data->different_types_p = true;
 	if (d1_variable || d2_variable)
-	  break;
+	  return true;
 	if (d1_zero && d2_zero)
-	  break;
+	  return true;
 	if (d1_zero || d2_zero
 	    || !tree_int_cst_equal (TYPE_MIN_VALUE (d1), TYPE_MIN_VALUE (d2))
 	    || !tree_int_cst_equal (TYPE_MAX_VALUE (d1), TYPE_MAX_VALUE (d2)))
-	  val = 0;
+	  return false;
 
-	break;
+	return true;
       }
 
     case ENUMERAL_TYPE:
     case RECORD_TYPE:
     case UNION_TYPE:
-      if (val != 1 && false)
+      if (false)
 	{
-	  tree a1 = TYPE_ATTRIBUTES (t1);
-	  tree a2 = TYPE_ATTRIBUTES (t2);
-
-	  if (! attribute_list_contained (a1, a2)
-	      && ! attribute_list_contained (a2, a1))
-	    break;
-
-	  val = tagged_types_tu_compatible_p (t1, t2, enum_and_int_p,
-					      different_types_p);
-
-	  if (attrval != 2)
-	    return val;
+	  return tagged_types_tu_compatible_p (t1, t2, data);
 	}
-      break;
+      return false;
 
     case VECTOR_TYPE:
-      val = (known_eq (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2))
-	     && comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2),
-				    enum_and_int_p, different_types_p));
-      break;
+      return known_eq (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2))
+	     && comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data);
 
     default:
-      break;
+      return false;
     }
-  return attrval == 2 && val == 1 ? 2 : val;
+  gcc_unreachable ();
 }
 
-/* Return 1 if TTL and TTR are pointers to types that are equivalent, ignoring
+/* Return true if TTL and TTR are pointers to types that are equivalent, ignoring
    their qualifiers, except for named address spaces.  If the pointers point to
    different named addresses, then we must determine if one address space is a
    subset of the other.  */
 
-static int
+static bool
 comp_target_types (location_t location, tree ttl, tree ttr)
 {
   int val;
@@ -1416,19 +1403,15 @@ free_all_tagged_tu_seen_up_to (const struct tagged_tu_seen_cache *tu_til)
   tagged_tu_seen_base = tu_til;
 }
 
-/* Return 1 if two 'struct', 'union', or 'enum' types T1 and T2 are
-   compatible.  If the two types are not the same (which has been
-   checked earlier), this can only happen when multiple translation
-   units are being compiled.  See C99 6.2.7 paragraph 1 for the exact
-   rules.  ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in
-   comptypes_internal.  */
+/* Return true if two 'struct', 'union', or 'enum' types T1 and T2 are
+   compatible.  The two types are not the same (which has been
+   checked earlier in comptypes_internal).  */
 
-static int
+static bool
 tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
-			      bool *enum_and_int_p, bool *different_types_p)
+			      struct comptypes_data *data)
 {
   tree s1, s2;
-  bool needs_warning = false;
 
   /* We have to verify that the tags of the types are the same.  This
      is harder than it looks because this may be a typedef, so we have
@@ -1536,8 +1519,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 
 	    if (DECL_NAME (s1) != DECL_NAME (s2))
 	      break;
-	    result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2),
-					 enum_and_int_p, different_types_p);
+	    result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2), data);
 
 	    if (result != 1 && !DECL_NAME (s1))
 	      break;
@@ -1546,8 +1528,6 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		tu->val = 0;
 		return 0;
 	      }
-	    if (result == 2)
-	      needs_warning = true;
 
 	    if (TREE_CODE (s1) == FIELD_DECL
 		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
@@ -1559,7 +1539,6 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	  }
 	if (!s1 && !s2)
 	  {
-	    tu->val = needs_warning ? 2 : 1;
 	    return tu->val;
 	  }
 
@@ -1573,8 +1552,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		  int result;
 
 		  result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2),
-					       enum_and_int_p,
-					       different_types_p);
+					       data);
 
 		  if (result != 1 && !DECL_NAME (s1))
 		    continue;
@@ -1583,8 +1561,6 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		      tu->val = 0;
 		      return 0;
 		    }
-		  if (result == 2)
-		    needs_warning = true;
 
 		  if (TREE_CODE (s1) == FIELD_DECL
 		      && simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
@@ -1600,7 +1576,6 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		return 0;
 	      }
 	  }
-	tu->val = needs_warning ? 2 : 1;
 	return tu->val;
       }
 
@@ -1622,12 +1597,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	    if (TREE_CODE (s1) != TREE_CODE (s2)
 		|| DECL_NAME (s1) != DECL_NAME (s2))
 	      break;
-	    result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2),
-					 enum_and_int_p, different_types_p);
+	    result = comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2), data);
 	    if (result == 0)
 	      break;
-	    if (result == 2)
-	      needs_warning = true;
 
 	    if (TREE_CODE (s1) == FIELD_DECL
 		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
@@ -1637,7 +1609,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	if (s1 && s2)
 	  tu->val = 0;
 	else
-	  tu->val = needs_warning ? 2 : 1;
+	  tu->val = 1;
 	return tu->val;
       }
 
@@ -1646,17 +1618,16 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
     }
 }
 
-/* Return 1 if two function types F1 and F2 are compatible.
+/* Return true if two function types F1 and F2 are compatible.
    If either type specifies no argument types,
    the other must specify a fixed number of self-promoting arg types.
    Otherwise, if one type specifies only the number of arguments,
    the other must specify that number of self-promoting arg types.
-   Otherwise, the argument types must match.
-   ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in comptypes_internal.  */
+   Otherwise, the argument types must match.  */
 
-static int
+static bool
 function_types_compatible_p (const_tree f1, const_tree f2,
-			     bool *enum_and_int_p, bool *different_types_p)
+			     struct comptypes_data *data)
 {
   tree args1, args2;
   /* 1 if no need for warning yet, 2 if warning cause has been seen.  */
@@ -1677,16 +1648,15 @@ function_types_compatible_p (const_tree f1, const_tree f2,
   if (TYPE_VOLATILE (ret2))
     ret2 = build_qualified_type (TYPE_MAIN_VARIANT (ret2),
 				 TYPE_QUALS (ret2) & ~TYPE_QUAL_VOLATILE);
-  val = comptypes_internal (ret1, ret2, enum_and_int_p, different_types_p);
+  val = comptypes_internal (ret1, ret2, data);
   if (val == 0)
     return 0;
 
   args1 = TYPE_ARG_TYPES (f1);
   args2 = TYPE_ARG_TYPES (f2);
 
-  if (different_types_p != NULL
-      && (args1 == NULL_TREE) != (args2 == NULL_TREE))
-    *different_types_p = true;
+  if ((args1 == NULL_TREE) != (args2 == NULL_TREE))
+    data->different_types_p = true;
 
   /* An unspecified parmlist matches any specified parmlist
      whose argument types don't need default promotions.  */
@@ -1702,8 +1672,11 @@ function_types_compatible_p (const_tree f1, const_tree f2,
 	 If they don't match, ask for a warning (but no error).  */
       if (TYPE_ACTUAL_ARG_TYPES (f1)
 	  && type_lists_compatible_p (args2, TYPE_ACTUAL_ARG_TYPES (f1),
-				      enum_and_int_p, different_types_p) != 1)
-	val = 2;
+				      data) != 1)
+	{
+	  val = 1;
+	  data->warning_needed = true;
+	}
       return val;
     }
   if (args2 == NULL_TREE)
@@ -1714,35 +1687,31 @@ function_types_compatible_p (const_tree f1, const_tree f2,
 	return 0;
       if (TYPE_ACTUAL_ARG_TYPES (f2)
 	  && type_lists_compatible_p (args1, TYPE_ACTUAL_ARG_TYPES (f2),
-				      enum_and_int_p, different_types_p) != 1)
-	val = 2;
+				      data) != 1)
+	{
+	  val = 1;
+	  data->warning_needed = true;
+	}
       return val;
     }
 
   /* Both types have argument lists: compare them and propagate results.  */
-  val1 = type_lists_compatible_p (args1, args2, enum_and_int_p,
-				  different_types_p);
-  return val1 != 1 ? val1 : val;
+  val1 = type_lists_compatible_p (args1, args2, data);
+  return val1;
 }
 
-/* Check two lists of types for compatibility, returning 0 for
-   incompatible, 1 for compatible, or 2 for compatible with
-   warning.  ENUM_AND_INT_P and DIFFERENT_TYPES_P are as in
-   comptypes_internal.  */
+/* Check two lists of types for compatibility, returning false for
+   incompatible, true for compatible.  */
 
-static int
+static bool
 type_lists_compatible_p (const_tree args1, const_tree args2,
-			 bool *enum_and_int_p, bool *different_types_p)
+			 struct comptypes_data *data)
 {
-  /* 1 if no need for warning yet, 2 if warning cause has been seen.  */
-  int val = 1;
-  int newval = 0;
-
   while (1)
     {
       tree a1, mv1, a2, mv2;
       if (args1 == NULL_TREE && args2 == NULL_TREE)
-	return val;
+	return true;
       /* If one list is shorter than the other,
 	 they fail to match.  */
       if (args1 == NULL_TREE || args2 == NULL_TREE)
@@ -1763,9 +1732,8 @@ type_lists_compatible_p (const_tree args1, const_tree args2,
 	 means there is supposed to be an argument
 	 but nothing is specified about what type it has.
 	 So match anything that self-promotes.  */
-      if (different_types_p != NULL
-	  && (a1 == NULL_TREE) != (a2 == NULL_TREE))
-	*different_types_p = true;
+      if ((a1 == NULL_TREE) != (a2 == NULL_TREE))
+	data->different_types_p = true;
       if (a1 == NULL_TREE)
 	{
 	  if (c_type_promotes_to (a2) != a2)
@@ -1780,11 +1748,9 @@ type_lists_compatible_p (const_tree args1, const_tree args2,
       else if (TREE_CODE (a1) == ERROR_MARK
 	       || TREE_CODE (a2) == ERROR_MARK)
 	;
-      else if (!(newval = comptypes_internal (mv1, mv2, enum_and_int_p,
-					      different_types_p)))
+      else if (!comptypes_internal (mv1, mv2, data))
 	{
-	  if (different_types_p != NULL)
-	    *different_types_p = true;
+	  data->different_types_p = true;
 	  /* Allow  wait (union {union wait *u; int *i} *)
 	     and  wait (union wait *)  to be compatible.  */
 	  if (TREE_CODE (a1) == UNION_TYPE
@@ -1805,8 +1771,7 @@ type_lists_compatible_p (const_tree args1, const_tree args2,
 			   ? c_build_qualified_type (TYPE_MAIN_VARIANT (mv3),
 						     TYPE_QUAL_ATOMIC)
 			   : TYPE_MAIN_VARIANT (mv3));
-		  if (comptypes_internal (mv3, mv2, enum_and_int_p,
-					  different_types_p))
+		  if (comptypes_internal (mv3, mv2, data))
 		    break;
 		}
 	      if (memb == NULL_TREE)
@@ -1830,8 +1795,7 @@ type_lists_compatible_p (const_tree args1, const_tree args2,
 			   ? c_build_qualified_type (TYPE_MAIN_VARIANT (mv3),
 						     TYPE_QUAL_ATOMIC)
 			   : TYPE_MAIN_VARIANT (mv3));
-		  if (comptypes_internal (mv3, mv1, enum_and_int_p,
-					  different_types_p))
+		  if (comptypes_internal (mv3, mv1, data))
 		    break;
 		}
 	      if (memb == NULL_TREE)
@@ -1841,10 +1805,6 @@ type_lists_compatible_p (const_tree args1, const_tree args2,
 	    return 0;
 	}
 
-      /* comptypes said ok, but record if it said to warn.  */
-      if (newval > val)
-	val = newval;
-
       args1 = TREE_CHAIN (args1);
       args2 = TREE_CHAIN (args2);
     }
@@ -7335,7 +7295,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       tree mvl = ttl;
       tree mvr = ttr;
       bool is_opaque_pointer;
-      int target_cmp = 0;   /* Cache comp_target_types () result.  */
+      bool target_cmp = false;   /* Cache comp_target_types () result.  */
       addr_space_t asl;
       addr_space_t asr;
 
-- 
2.30.2






^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [C PATCH 1/6 v2]  c: reorganize recursive type checking
  2023-09-10  8:17     ` [C PATCH 1/6 v2] " Martin Uecker
@ 2023-09-11 20:28       ` Joseph Myers
  0 siblings, 0 replies; 15+ messages in thread
From: Joseph Myers @ 2023-09-11 20:28 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Sun, 10 Sep 2023, Martin Uecker via Gcc-patches wrote:

> Thanks Joseph, below is a a revised version of this patch
> with slight additional changes to the comment of
> tagged_types_tu_compatible_p.
> 
> ok for trunk? 

The revised version of this patch is OK.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [C PATCH 2/6] c23: recursive type checking of tagged type
  2023-08-26 16:22 ` [C PATCH 2/6] c23: recursive type checking of tagged type Martin Uecker
@ 2023-11-07 23:06   ` Joseph Myers
  0 siblings, 0 replies; 15+ messages in thread
From: Joseph Myers @ 2023-11-07 23:06 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Sat, 26 Aug 2023, Martin Uecker via Gcc-patches wrote:

> Adapt the old and unused code for type checking for C23.
> 
> gcc/c/:
> 	* c-typeck.c (struct comptypes_data): Add anon_field flag.
> 	(comptypes, comptypes_check_unum_int,
> 	comptypes_check_different_types): Remove old cache.
> 	(tagged_tu_types_compatible_p): Rewrite.

OK.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [C PATCH 3/6] c23: tag compatibility rules for struct and unions
  2023-08-26 16:23 ` [C PATCH 3/6] c23: tag compatibility rules for struct and unions Martin Uecker
@ 2023-11-07 23:18   ` Joseph Myers
  0 siblings, 0 replies; 15+ messages in thread
From: Joseph Myers @ 2023-11-07 23:18 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Sat, 26 Aug 2023, Martin Uecker via Gcc-patches wrote:

> 	types (convert_for_assignment): Ingore qualifiers.

"Ignore".

> @@ -1993,6 +1993,24 @@ locate_old_decl (tree decl)
>  	    decl, TREE_TYPE (decl));
>  }
>  
> +static tree
> +previous_tag (tree type)

This function needs a comment documenting its semantics.

> @@ -8651,6 +8672,12 @@ start_struct (location_t loc, enum tree_code code, tree name,
>  
>    if (name != NULL_TREE)
>      ref = lookup_tag (code, name, true, &refloc);
> +
> +  /* For C2X, even if we already have a completed definition,
> +     we do not use it. We will check for consistency later.  */
> +  if (flag_isoc2x && ref && TYPE_SIZE (ref))
> +    ref = NULL_TREE;
> +
>    if (ref && TREE_CODE (ref) == code)
>      {
>        if (TYPE_STUB_DECL (ref))

This comes before the check for nested redefinitions (which are still 
invalid) - so meaning that, if ref is set to NULL_TREE here, the check 
for nested redefinitions won't apply.

You have a testcase for nested redefinitions in a slightly different case 
(where the struct's first definition hasn't finished when the nested 
definition is encountered).  But what about the case where: first, the 
struct gets defined; then, in the same scope, it gets redefined, with the 
redefinition containing a nested redefinition?  I don't see anything here 
to detect that case of nested redefinitions

For enums, note that nested redefinitions include cases where the nesting 
is inside an enum type specifier (currently diagnosed by GCC following an 
ordinary redefinition path, not one for nested definitions).

typedef __SIZE_TYPE__ size_t;
enum e : typeof (sizeof (enum e : size_t { A })) { A };

is invalid because the definitions of enum e are nested, so should be 
diagnosed, and there should be a test that it is.

> @@ -8315,6 +8332,13 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype,
>  	   conversion.  */
>  	inside_init = convert (type, inside_init);
>  
> +      if ((code == RECORD_TYPE || code == UNION_TYPE)
> +	  && !comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (TREE_TYPE (inside_init))))
> +	{
> +	  error_init (init_loc, "invalid initializer %qT %qT", type, TREE_TYPE (inside_init));
> +	  return error_mark_node;
> +	}

I'd expect some words between the two type names, or explaining how they 
relate to the initialization, rather than just two type names in 
succession with no explanation of what's the type of the initializer and 
what's the type of the object being initialized.

> diff --git a/gcc/testsuite/gcc.dg/c2x-tag-1.c b/gcc/testsuite/gcc.dg/c2x-tag-1.c

> +struct r { int a; char b[0]; };

I tend to think tests such as this, involving GNU extensions ([0] arrays), 
should go in gnu23-* tests not c23-* ones.

(I'm currently testing the final C2X -> C23 patch, that renames existing 
tests.  The next revision of this patch series will need updating for the 
renaming in both file names and file contents.)

> +++ b/gcc/testsuite/gcc.dg/c2x-tag-10.c

This is definitely a GNU extensions test (VLAs in structures).

> +++ b/gcc/testsuite/gcc.dg/c2x-tag-4.c

Another GNU extensions test (GNU attributes).

> diff --git a/gcc/testsuite/gcc.dg/c2x-tag-7.c b/gcc/testsuite/gcc.dg/c2x-tag-7.c

Another GNU extensions test (VLAs in structures).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [C PATCH 4/6] c23: tag compatibility rules for enums
  2023-08-26 16:24 ` [C PATCH 4/6] c23: tag compatibility rules for enums Martin Uecker
@ 2023-11-07 23:20   ` Joseph Myers
  0 siblings, 0 replies; 15+ messages in thread
From: Joseph Myers @ 2023-11-07 23:20 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Sat, 26 Aug 2023, Martin Uecker via Gcc-patches wrote:

> Allow redefinition of enum types and enumerators.
> 
> gcc/c:
> 	* c-decl.cc (start_num): Allow redefinition.

start_enum not start_num.

> @@ -9606,9 +9624,15 @@ 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 (flag_isoc2x && enumtype != NULL_TREE
> +      && TREE_CODE (enumtype) == ENUMERAL_TYPE
> +      && TYPE_VALUES (enumtype) != NULL_TREE)
> +    enumtype = NULL_TREE;

Much the same comment applies as on the struct/union patch regarding 
ensuring nested redefinitions are detected when there's a previous 
definition outside the two nested definitions, in addition to the point 
there about making sure that a definition nested inside an enum type 
specifier for another definition of the same enum gets detected.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [C PATCH 6/6] c23: construct composite type for tagged types
  2023-08-26 16:26 ` [C PATCH 6/6] c23: construct composite type for " Martin Uecker
@ 2023-11-07 23:45   ` Joseph Myers
  0 siblings, 0 replies; 15+ messages in thread
From: Joseph Myers @ 2023-11-07 23:45 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Sat, 26 Aug 2023, Martin Uecker via Gcc-patches wrote:

> @@ -501,9 +510,61 @@ composite_type (tree t1, tree t2)
>  	return build_type_attribute_variant (t1, attributes);
>        }
>  
> -    case ENUMERAL_TYPE:
>      case RECORD_TYPE:
>      case UNION_TYPE:
> +      if (flag_isoc2x && !comptypes_same_p (t1, t2))
> +	{
> +	  gcc_checking_assert (COMPLETE_TYPE_P (t1) && COMPLETE_TYPE_P (t2));
> +	  gcc_checking_assert (comptypes (t1, t2));
> +
> +	  /* If a composite type for these two types is already under
> +	     construction, return it.  */
> +
> +	  for (struct composite_cache *c = cache; c != NULL; c = c->next)
> +	    if (c->t1 == t1 && c->t2 == t2)
> +	       return c->composite;
> +
> +	  /* Otherwise, create a new type node and link it into the cache.  */
> +
> +	  tree n = make_node (code1);
> +	  struct composite_cache cache2 = { t1, t2, n, cache };
> +	  cache = &cache2;
> +
> +	  tree f1 = TYPE_FIELDS (t1);
> +	  tree f2 = TYPE_FIELDS (t2);
> +	  tree fields = NULL_TREE;
> +
> +	  for (tree a = f1, b = f2; a && b;
> +	       a = DECL_CHAIN (a), b = DECL_CHAIN (b))
> +	    {
> +	      tree ta = TREE_TYPE (a);
> +	      tree tb = TREE_TYPE (b);
> +
> +	      gcc_assert (DECL_NAME (a) == DECL_NAME (b));
> +	      gcc_assert (comptypes (ta, tb));
> +
> +	      tree f = build_decl (input_location, FIELD_DECL, DECL_NAME (a),
> +				   composite_type_internal (ta, tb, cache));
> +
> +	      DECL_FIELD_CONTEXT (f) = n;
> +	      DECL_CHAIN (f) = fields;

There is a lot more per-field setup done in grokdeclarator, grokfield and 
finish_struct when a struct or union is defined.  I'm concerned that just 
calling build_decl here and then missing most of the per-field setup done 
elsewhere will not get the composite type set up correctly, especially in 
cases such as bit-fields and packed structures.

Note that the test you have of bit-fields (c2x-tag-composite-3.c) probably 
doesn't exercise this code, because the two types are the same (defined in 
the same scope, so it would be an error if they weren't the same) and so 
the comptypes_same_p check should short-circuit this code.  You need to 
test such issues in cases where the types are genuinely not the same - and 
for bit-fields, that includes ensuring you cover code paths that depend on 
each of DECL_BIT_FIELD, DECL_C_BIT_FIELD, DECL_BIT_FIELD_TYPE, to make 
sure that all of those are correct.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2023-11-07 23:45 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-26 16:19 c23 type compatibility rules, v2 Martin Uecker
2023-08-26 16:20 ` [C PATCH 1/6] c: reorganize recursive type checking Martin Uecker
2023-09-06 20:59   ` Joseph Myers
2023-09-10  8:17     ` [C PATCH 1/6 v2] " Martin Uecker
2023-09-11 20:28       ` Joseph Myers
2023-08-26 16:22 ` [C PATCH 2/6] c23: recursive type checking of tagged type Martin Uecker
2023-11-07 23:06   ` Joseph Myers
2023-08-26 16:23 ` [C PATCH 3/6] c23: tag compatibility rules for struct and unions Martin Uecker
2023-11-07 23:18   ` Joseph Myers
2023-08-26 16:24 ` [C PATCH 4/6] c23: tag compatibility rules for enums Martin Uecker
2023-11-07 23:20   ` Joseph Myers
2023-08-26 16:25 ` [C PATCH 5/6] c23: aliasing of compatible tagged types Martin Uecker
2023-08-26 16:26 ` [C PATCH 6/6] c23: construct composite type for " Martin Uecker
2023-11-07 23:45   ` Joseph Myers
2023-08-26 16:26 ` [C PATCH] c: flag for tag compatibility rules Martin Uecker

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).