The Plan 9 compilers defer duplicate declaration checks until field
resolution time.  Further, there is a priority order of resolution such
that field lookups first match the name, then typedef'd names before
recursing into substructures.

This enables large portions of the Plan 9 userspace and kernel to be
compiled with GCC without modification.

gcc/c/ChangeLog:

        * c-decl.cc (is_duplicate_field): Remove unused Plan 9 logic.
        (detect_field_duplicates_hash): Likewise.
        (detect_field_duplicates): If -fplan9-extensions, disable
        duplicate field detection.
        * c-typeck.cc (lookup_field_plan9): Implement the Plan 9 look up
          scheme.
        (lookup_field): Likewise.

gcc/testsuite/ChangeLog:

        * gcc.dg/anon-struct-13.c: Add duplicate fields.

Signed-off-by: Keegan Saunders <kee...@undefinedbehaviour.org>
---
 gcc/c/c-decl.cc                       |  63 ++--------------
 gcc/c/c-typeck.cc                     | 101 ++++++++++++++++++++------
 gcc/testsuite/gcc.dg/anon-struct-13.c |   9 +++
 3 files changed, 96 insertions(+), 77 deletions(-)

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 193e268f04e..539ad407f49 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -8538,41 +8538,7 @@ grokfield (location_t loc,
 static bool
 is_duplicate_field (tree x, tree y)
 {
-  if (DECL_NAME (x) != NULL_TREE && DECL_NAME (x) == DECL_NAME (y))
-    return true;
-
-  /* When using -fplan9-extensions, an anonymous field whose name is a
-     typedef can duplicate a field name.  */
-  if (flag_plan9_extensions
-      && (DECL_NAME (x) == NULL_TREE || DECL_NAME (y) == NULL_TREE))
-    {
-      tree xt, xn, yt, yn;
-
-      xt = TREE_TYPE (x);
-      if (DECL_NAME (x) != NULL_TREE)
-       xn = DECL_NAME (x);
-      else if (RECORD_OR_UNION_TYPE_P (xt)
-              && TYPE_NAME (xt) != NULL_TREE
-              && TREE_CODE (TYPE_NAME (xt)) == TYPE_DECL)
-       xn = DECL_NAME (TYPE_NAME (xt));
-      else
-       xn = NULL_TREE;
-
-      yt = TREE_TYPE (y);
-      if (DECL_NAME (y) != NULL_TREE)
-       yn = DECL_NAME (y);
-      else if (RECORD_OR_UNION_TYPE_P (yt)
-              && TYPE_NAME (yt) != NULL_TREE
-              && TREE_CODE (TYPE_NAME (yt)) == TYPE_DECL)
-       yn = DECL_NAME (TYPE_NAME (yt));
-      else
-       yn = NULL_TREE;
-
-      if (xn != NULL_TREE && xn == yn)
-       return true;
-    }
-
-  return false;
+  return DECL_NAME (x) != NULL_TREE && DECL_NAME (x) == DECL_NAME (y);
 }
 
 /* Subroutine of detect_field_duplicates: add the fields of FIELDLIST
@@ -8599,19 +8565,6 @@ detect_field_duplicates_hash (tree fieldlist,
     else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (x)))
       {
        detect_field_duplicates_hash (TYPE_FIELDS (TREE_TYPE (x)), htab);
-
-       /* When using -fplan9-extensions, an anonymous field whose
-          name is a typedef can duplicate a field name.  */
-       if (flag_plan9_extensions
-           && TYPE_NAME (TREE_TYPE (x)) != NULL_TREE
-           && TREE_CODE (TYPE_NAME (TREE_TYPE (x))) == TYPE_DECL)
-         {
-           tree xn = DECL_NAME (TYPE_NAME (TREE_TYPE (x)));
-           slot = htab->find_slot (xn, INSERT);
-           if (*slot)
-             error ("duplicate member %q+D", TYPE_NAME (TREE_TYPE (x)));
-           *slot = xn;
-         }
       }
 }
 
@@ -8639,6 +8592,11 @@ detect_field_duplicates (tree fieldlist)
     if (objc_detect_field_duplicates (false))
       return;
 
+  /* When using -fplan9-extensions, do not perform duplicate field
+     checks until a field look up is performed.  */
+  if (flag_plan9_extensions)
+   return;
+
   /* First, see if there are more than "a few" fields.
      This is trivially true if there are zero or one fields.  */
   if (!fieldlist || !DECL_CHAIN (fieldlist))
@@ -8658,14 +8616,7 @@ detect_field_duplicates (tree fieldlist)
   if (timeout > 0)
     {
       for (x = DECL_CHAIN (fieldlist); x; x = DECL_CHAIN (x))
-       /* When using -fplan9-extensions, we can have duplicates
-          between typedef names and fields.  */
-       if (DECL_NAME (x)
-           || (flag_plan9_extensions
-               && DECL_NAME (x) == NULL_TREE
-               && RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))
-               && TYPE_NAME (TREE_TYPE (x)) != NULL_TREE
-               && TREE_CODE (TYPE_NAME (TREE_TYPE (x))) == TYPE_DECL))
+       if (DECL_NAME (x))
          {
            for (y = fieldlist; y != x; y = TREE_CHAIN (y))
              if (is_duplicate_field (y, x))
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index f9190680a3c..97ffa329ba7 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -95,6 +95,7 @@ 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 *);
+static tree lookup_field_plan9 (tree, tree);
 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,
@@ -2280,6 +2281,81 @@ default_conversion (tree exp)
   return exp;
 }
 
+/* Look up COMPONENT in a structure or union TYPE like a Plan 9 C compiler.
+
+   Look up is performed in 3 passes:
+   1.  Look for field names that match the look up name.
+   2.  Look for anonymous records who's typedef name matches the look up name.
+   3.  Look for fields in embedded anonymous records.
+
+   If the component name is not found, returns NULL_TREE. If the component
+   name references multiple fields, issue an error.  */
+
+static tree
+lookup_field_plan9 (tree type, tree component)
+{
+  tree field;
+  tree found = NULL_TREE;
+
+  /* Passes 1 & 2 are fused: conflicts are an error.  */
+  for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      /* Pass 1.  */
+      if (DECL_NAME (field) == component)
+       {
+         if (found != NULL_TREE)
+           {
+             error ("duplicate member %q+D", found);
+             return NULL_TREE;
+           }
+         found = field;
+       }
+
+      /* Pass 2.  */
+      if (DECL_NAME (field) == NULL_TREE
+         && RECORD_OR_UNION_TYPE_P (TREE_TYPE (field)))
+       {
+         if (TYPE_NAME (TREE_TYPE (field)) != NULL_TREE
+             && TREE_CODE (TYPE_NAME (TREE_TYPE (field))) == TYPE_DECL
+             && (DECL_NAME (TYPE_NAME (TREE_TYPE (field)))
+                 == component))
+           {
+             if (found != NULL_TREE)
+               {
+                 error ("duplicate member %q+D", found);
+                 return NULL_TREE;
+               }
+             found = field;
+           }
+       }
+    }
+
+  if (found != NULL_TREE)
+    return tree_cons (NULL_TREE, found, NULL_TREE);
+
+  /* Pass 3.  */
+  for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (DECL_NAME (field) == NULL_TREE
+         && RECORD_OR_UNION_TYPE_P (TREE_TYPE (field)))
+       {
+         tree anon = lookup_field_plan9 (TREE_TYPE (field), component);
+
+         if (anon != NULL_TREE)
+           {
+             if (found != NULL_TREE)
+               {
+                 error ("duplicate member %q+D", TREE_VALUE (nreverse (anon)));
+                 return NULL_TREE;
+               }
+             found = tree_cons (NULL_TREE, field, anon);
+           }
+       }
+    }
+
+  return found;
+}
+
 /* Look up COMPONENT in a structure or union TYPE.
 
    If the component name is not found, returns NULL_TREE.  Otherwise,
@@ -2294,6 +2370,10 @@ lookup_field (tree type, tree component)
 {
   tree field;
 
+  /* The Plan 9 compiler has a different field resolution scheme.  */
+  if (flag_plan9_extensions)
+    return lookup_field_plan9 (type, component);
+
   /* If TYPE_LANG_SPECIFIC is set, then it is a sorted array of pointers
      to the field elements.  Use a binary search on this array to quickly
      find the element.  Otherwise, do a linear search.  TYPE_LANG_SPECIFIC
@@ -2329,17 +2409,6 @@ lookup_field (tree type, tree component)
 
                      if (anon)
                        return tree_cons (NULL_TREE, field, anon);
-
-                     /* The Plan 9 compiler permits referring
-                        directly to an anonymous struct/union field
-                        using a typedef name.  */
-                     if (flag_plan9_extensions
-                         && TYPE_NAME (TREE_TYPE (field)) != NULL_TREE
-                         && (TREE_CODE (TYPE_NAME (TREE_TYPE (field)))
-                             == TYPE_DECL)
-                         && (DECL_NAME (TYPE_NAME (TREE_TYPE (field)))
-                             == component))
-                       break;
                    }
                }
 
@@ -2375,16 +2444,6 @@ lookup_field (tree type, tree component)
 
              if (anon)
                return tree_cons (NULL_TREE, field, anon);
-
-             /* The Plan 9 compiler permits referring directly to an
-                anonymous struct/union field using a typedef
-                name.  */
-             if (flag_plan9_extensions
-                 && TYPE_NAME (TREE_TYPE (field)) != NULL_TREE
-                 && TREE_CODE (TYPE_NAME (TREE_TYPE (field))) == TYPE_DECL
-                 && (DECL_NAME (TYPE_NAME (TREE_TYPE (field)))
-                     == component))
-               break;
            }
 
          if (DECL_NAME (field) == component)
diff --git a/gcc/testsuite/gcc.dg/anon-struct-13.c 
b/gcc/testsuite/gcc.dg/anon-struct-13.c
index 6a508141bac..c7b7d8cb101 100644
--- a/gcc/testsuite/gcc.dg/anon-struct-13.c
+++ b/gcc/testsuite/gcc.dg/anon-struct-13.c
@@ -5,6 +5,8 @@
 
 struct A {
   char a;              /* { dg-error "duplicate member" } */
+  char b;
+  char b;
 };
 
 struct B
@@ -46,6 +48,13 @@ struct E
   struct D;
 };
 
+struct F
+{
+  char a;
+  char a;
+  char a;
+};
+
 char
 f4 (struct E *p)
 {
-- 
2.37.1

Reply via email to