Currently, GCC does not fully support the cases when a FAM or pointer field and
its corresponding counted_by field are in different anonymous structure/unions
of a common named structure.

For example:

struct nested_mixed {
  struct {
    union {
      int b;
      float f;
    };
    int n;
  };
  struct {
    PTR_TYPE *pointer __attribute__((__counted_by__(n)));
    FAM_TYPE c[] __attribute__((__counted_by__(b)));
  };
} *nested_mixed_annotated;

In order to support such cases, we always need to locate the first outer
named structure as the root, and then lookup_field inside this named
structure. When building the component_ref for the counted_by field,
we need to build a chain of component_ref starting from the root structure.

bootstrapped and regression tested on both x86 and aarch64.
Okay for committing?

thanks.

Qing.

=============================

        PR C/122495
        PR C/122496

gcc/c/ChangeLog:

        * c-decl.cc (verify_counted_by_attribute): Change the prototype to
        a recursive routine.
        (finish_struct): Set C_TYPE_FIELDS_HAS_COUNTED_BY and call the routine
        verify_counted_by_attribute only for named structure.
        * c-tree.h (C_TYPE_FIELDS_HAS_COUNTED_BY): New flag.
        * c-typeck.cc (build_counted_by_ref): Locate the root named structure,
        build a chain of component_ref starting from the root structure.

gcc/testsuite/ChangeLog:

        * gcc.dg/counted-by-anonymous-2-char.c: New test.
        * gcc.dg/counted-by-anonymous-2-float.c: New test.
        * gcc.dg/counted-by-anonymous-2-struct.c: New test.
        * gcc.dg/counted-by-anonymous-2-union.c: New test.
        * gcc.dg/counted-by-anonymous-2.c: New test.
        * gcc.dg/counted-by-anonymous.c: New test.
---
 gcc/c/c-decl.cc                               | 118 ++++++++++--------
 gcc/c/c-tree.h                                |   4 +
 gcc/c/c-typeck.cc                             |  47 ++++---
 .../gcc.dg/counted-by-anonymous-2-char.c      |   8 ++
 .../gcc.dg/counted-by-anonymous-2-float.c     |   8 ++
 .../gcc.dg/counted-by-anonymous-2-struct.c    |  16 +++
 .../gcc.dg/counted-by-anonymous-2-union.c     |  16 +++
 gcc/testsuite/gcc.dg/counted-by-anonymous-2.c |  66 ++++++++++
 gcc/testsuite/gcc.dg/counted-by-anonymous.c   |  93 ++++++++++++++
 9 files changed, 308 insertions(+), 68 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-2-char.c
 create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-2-float.c
 create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-2-struct.c
 create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-2-union.c
 create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-2.c
 create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 0a368e410e5..a8a812dc523 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -9482,62 +9482,73 @@ c_update_type_canonical (tree t)
     }
 }
 
-/* Verify the argument of the counted_by attribute of each of the
-   FIELDS_WITH_COUNTED_BY is a valid field of the containing structure,
-   STRUCT_TYPE, Report error and remove the corresponding attribute
-   when it's not.  */
+/* Verify the argument of the counted_by attribute of each field of
+   the containing structure, OUTMOST_STRUCT_TYPE, including its inner
+   anonymous struct/union, Report error and remove the corresponding
+   attribute when it's not.  */
 
 static void
-verify_counted_by_attribute (tree struct_type,
-                            auto_vec<tree> *fields_with_counted_by)
+verify_counted_by_attribute (tree outmost_struct_type,
+                            tree cur_struct_type)
 {
-  for (tree field_decl : *fields_with_counted_by)
+  gcc_assert (TYPE_NAME (outmost_struct_type) != NULL);
+  for (tree field = TYPE_FIELDS (cur_struct_type); field;
+       field = TREE_CHAIN (field))
     {
-      tree attr_counted_by = lookup_attribute ("counted_by",
-                                               DECL_ATTRIBUTES (field_decl));
+      if (c_flexible_array_member_type_p (TREE_TYPE (field))
+          || TREE_CODE (TREE_TYPE (field)) == POINTER_TYPE)
+       {
+         tree attr_counted_by = lookup_attribute ("counted_by",
+                                                  DECL_ATTRIBUTES (field));
 
-      if (!attr_counted_by)
-       continue;
+         if (!attr_counted_by)
+           continue;
 
-      /* If there is an counted_by attribute attached to the field,
-        verify it.  */
+         /* If there is an counted_by attribute attached to the field,
+            verify it.  */
 
-      tree fieldname = TREE_VALUE (TREE_VALUE (attr_counted_by));
+         tree fieldname = TREE_VALUE (TREE_VALUE (attr_counted_by));
 
-      /* Verify the argument of the attrbute is a valid field of the
-        containing structure.  */
+         /* Verify the argument of the attrbute is a valid field of the
+            containing structure.  */
 
-      tree counted_by_field = lookup_field (struct_type, fieldname);
+         tree counted_by_field = lookup_field (outmost_struct_type,
+                                               fieldname);
 
-      /* Error when the field is not found in the containing structure and
-        remove the corresponding counted_by attribute from the field_decl.  */
-      if (!counted_by_field)
-       {
-         error_at (DECL_SOURCE_LOCATION (field_decl),
+         /* Error when the field is not found in the containing structure
+            and remove the corresponding counted_by attribute from the
+            field_decl.  */
+         if (!counted_by_field)
+           {
+             error_at (DECL_SOURCE_LOCATION (field),
                    "argument %qE to the %<counted_by%> attribute"
                    " is not a field declaration in the same structure"
-                   " as %qD", fieldname, field_decl);
-         DECL_ATTRIBUTES (field_decl)
-           = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl));
-       }
-      else
-      /* Error when the field is not with an integer type.  */
-       {
-         while (TREE_CHAIN (counted_by_field))
-           counted_by_field = TREE_CHAIN (counted_by_field);
-         tree real_field = TREE_VALUE (counted_by_field);
-
-         if (!INTEGRAL_TYPE_P (TREE_TYPE (real_field)))
+                   " as %qD", fieldname, field);
+             DECL_ATTRIBUTES (field)
+               = remove_attribute ("counted_by", DECL_ATTRIBUTES (field));
+           }
+         else
+         /* Error when the field is not with an integer type.  */
            {
-             error_at (DECL_SOURCE_LOCATION (field_decl),
+             while (TREE_CHAIN (counted_by_field))
+               counted_by_field = TREE_CHAIN (counted_by_field);
+             tree real_field = TREE_VALUE (counted_by_field);
+
+             if (!INTEGRAL_TYPE_P (TREE_TYPE (real_field)))
+               {
+                 error_at (DECL_SOURCE_LOCATION (field),
                        "argument %qE to the %<counted_by%> attribute"
                        " is not a field declaration with an integer type",
                        fieldname);
-             DECL_ATTRIBUTES (field_decl)
-               = remove_attribute ("counted_by",
-                                   DECL_ATTRIBUTES (field_decl));
+                 DECL_ATTRIBUTES (field)
+                   = remove_attribute ("counted_by",
+                                   DECL_ATTRIBUTES (field));
+               }
            }
        }
+      else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))
+              && (DECL_NAME (field) == NULL_TREE))
+       verify_counted_by_attribute (outmost_struct_type, TREE_TYPE (field));
     }
 }
 
@@ -9612,7 +9623,6 @@ finish_struct (location_t loc, tree t, tree fieldlist, 
tree attributes,
      until now.)  */
 
   bool saw_named_field = false;
-  auto_vec<tree> fields_with_counted_by;
   for (x = fieldlist; x; x = DECL_CHAIN (x))
     {
       /* Whether this field is the last field of the structure or union.
@@ -9688,20 +9698,21 @@ finish_struct (location_t loc, tree t, tree fieldlist, 
tree attributes,
            pedwarn (DECL_SOURCE_LOCATION (x), OPT_Wpedantic,
                     "flexible array member in a struct with no named "
                     "members is a GCC extension");
-
-         /* If there is a counted_by attribute attached to this field,
-            record it here and do more verification later after the
-            whole structure is complete.  */
          if (lookup_attribute ("counted_by", DECL_ATTRIBUTES (x)))
-           fields_with_counted_by.safe_push (x);
+           C_TYPE_FIELDS_HAS_COUNTED_BY (t) = 1;
        }
 
-      if (TREE_CODE (TREE_TYPE (x)) == POINTER_TYPE)
-       /* If there is a counted_by attribute attached to this field,
-          record it here and do more verification later after the
-          whole structure is complete.  */
-       if (lookup_attribute ("counted_by", DECL_ATTRIBUTES (x)))
-         fields_with_counted_by.safe_push (x);
+      if (TREE_CODE (TREE_TYPE (x)) == POINTER_TYPE
+         && lookup_attribute ("counted_by", DECL_ATTRIBUTES (x)))
+       C_TYPE_FIELDS_HAS_COUNTED_BY (t) = 1;
+
+      /* If the field is a anonymous structure that includes a field
+        with counted_by attribute, this structure should also be marked
+        too.  */
+      if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))
+         && TYPE_NAME (TREE_TYPE (x)) == NULL_TREE
+         && C_TYPE_FIELDS_HAS_COUNTED_BY (TREE_TYPE (x)))
+       C_TYPE_FIELDS_HAS_COUNTED_BY (t) = 1;
 
       if (pedantic && TREE_CODE (t) == RECORD_TYPE
          && flexible_array_type_p (TREE_TYPE (x)))
@@ -9961,6 +9972,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, 
tree attributes,
       C_TYPE_FIELDS_READONLY (x) = C_TYPE_FIELDS_READONLY (t);
       C_TYPE_FIELDS_VOLATILE (x) = C_TYPE_FIELDS_VOLATILE (t);
       C_TYPE_FIELDS_NON_CONSTEXPR (x) = C_TYPE_FIELDS_NON_CONSTEXPR (t);
+      C_TYPE_FIELDS_HAS_COUNTED_BY (x) = C_TYPE_FIELDS_HAS_COUNTED_BY (t);
       C_TYPE_VARIABLE_SIZE (x) = C_TYPE_VARIABLE_SIZE (t);
       C_TYPE_VARIABLY_MODIFIED (x) = C_TYPE_VARIABLY_MODIFIED (t);
       C_TYPE_INCOMPLETE_VARS (x) = NULL_TREE;
@@ -10001,8 +10013,10 @@ finish_struct (location_t loc, tree t, tree fieldlist, 
tree attributes,
        struct_parse_info->struct_types.safe_push (t);
      }
 
-  if (fields_with_counted_by.length () > 0)
-    verify_counted_by_attribute (t, &fields_with_counted_by);
+  /* Only when the enclosing struct/union type is not anonymous, do more
+     verification on the fields with counted_by attributes.  */
+  if (TYPE_NAME (t) != NULL_TREE && C_TYPE_FIELDS_HAS_COUNTED_BY (t))
+    verify_counted_by_attribute (t, t);
 
   return t;
 }
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index ff63d69e85d..e758f672065 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -39,6 +39,10 @@ along with GCC; see the file COPYING3.  If not see
    permitted for a constexpr object.  */
 #define C_TYPE_FIELDS_NON_CONSTEXPR(TYPE) TREE_LANG_FLAG_4 (TYPE)
 
+/* In a RECORD_TYPE or UNION_TYPE, nonzero if any component has a
+   counted_by attribute.  */
+#define C_TYPE_FIELDS_HAS_COUNTED_BY(TYPE) TYPE_LANG_FLAG_3 (TYPE)
+
 /* In a RECORD_TYPE or UNION_TYPE or ENUMERAL_TYPE
    nonzero if the definition of the type has already started.  */
 #define C_TYPE_BEING_DEFINED(TYPE) TYPE_LANG_FLAG_0 (TYPE)
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 2cef4636bd7..23a6f0a01ee 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -3090,7 +3090,6 @@ static tree
 build_counted_by_ref (tree datum, tree subdatum,
                      tree *counted_by_type)
 {
-  tree type = TREE_TYPE (datum);
   tree sub_type = TREE_TYPE (subdatum);
   if (!c_flexible_array_member_type_p (sub_type)
       && TREE_CODE (sub_type) != POINTER_TYPE)
@@ -3098,28 +3097,44 @@ build_counted_by_ref (tree datum, tree subdatum,
 
   tree attr_counted_by = lookup_attribute ("counted_by",
                                           DECL_ATTRIBUTES (subdatum));
+  if (!attr_counted_by)
+    return NULL_TREE;
+
   tree counted_by_ref = NULL_TREE;
   *counted_by_type = NULL_TREE;
-  if (attr_counted_by)
+
+  tree type = TREE_TYPE (datum);
+
+  /* If the type of the containing structure is an anonymous struct/union,
+     get the first outer named structure/union type.  */
+  while (TYPE_NAME (type) == NULL_TREE)
     {
-      tree field_id = TREE_VALUE (TREE_VALUE (attr_counted_by));
-      counted_by_ref
-       = build_component_ref (UNKNOWN_LOCATION,
-                              datum, field_id,
-                              UNKNOWN_LOCATION, UNKNOWN_LOCATION);
-      counted_by_ref = build_fold_addr_expr (counted_by_ref);
+      gcc_assert (TREE_CODE (datum) == COMPONENT_REF);
+      datum = TREE_OPERAND (datum, 0);
+      type = TREE_TYPE (datum);
+    }
 
+  tree field_id = TREE_VALUE (TREE_VALUE (attr_counted_by));
+  tree counted_by_field = lookup_field (type, field_id);
+  gcc_assert (counted_by_field);
+
+  tree counted_by_subdatum;
+  do
+    {
+      counted_by_subdatum = TREE_VALUE (counted_by_field);
       /* Get the TYPE of the counted_by field.  */
-      tree counted_by_field = lookup_field (type, field_id);
-      gcc_assert (counted_by_field);
+      *counted_by_type = TREE_TYPE (counted_by_subdatum);
 
-      do
-       {
-         *counted_by_type = TREE_TYPE (TREE_VALUE (counted_by_field));
-         counted_by_field = TREE_CHAIN (counted_by_field);
-       }
-      while (counted_by_field);
+      counted_by_ref
+       = build3 (COMPONENT_REF, TREE_TYPE (counted_by_subdatum),
+                 datum, counted_by_subdatum, NULL_TREE);
+
+      datum = counted_by_ref;
+      counted_by_field = TREE_CHAIN (counted_by_field);
     }
+  while (counted_by_field);
+
+  counted_by_ref = build_fold_addr_expr (counted_by_ref);
   return counted_by_ref;
 }
 
diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-2-char.c 
b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-char.c
new file mode 100644
index 00000000000..20067a29816
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-char.c
@@ -0,0 +1,8 @@
+/* Test the attribute counted_by for pointer field in anonymous struct/union
+   and its usage in __builtin_dynamic_object_size.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define PTR_TYPE char 
+#define FAM_TYPE char 
+#include "counted-by-anonymous-2.c"
diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-2-float.c 
b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-float.c
new file mode 100644
index 00000000000..bb3560141f1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-float.c
@@ -0,0 +1,8 @@
+/* Test the attribute counted_by for pointer field in anonymous struct/union
+   and its usage in __builtin_dynamic_object_size.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define PTR_TYPE float 
+#define FAM_TYPE float 
+#include "counted-by-anonymous-2.c"
diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-2-struct.c 
b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-struct.c
new file mode 100644
index 00000000000..37e011decd2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-struct.c
@@ -0,0 +1,16 @@
+/* Test the attribute counted_by for pointer field in anonymous struct/union
+   and its usage in __builtin_dynamic_object_size.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+struct A {
+  int a;
+  char *b;
+};
+struct B {
+  float a;
+  double b;
+};
+#define PTR_TYPE struct A
+#define FAM_TYPE struct B 
+#include "counted-by-anonymous-2.c"
diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-2-union.c 
b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-union.c
new file mode 100644
index 00000000000..eebb4d63e3a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-union.c
@@ -0,0 +1,16 @@
+/* Test the attribute counted_by for pointer field in anonymous struct/union
+   and its usage in __builtin_dynamic_object_size.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+union A {
+  int a;
+  char *b;
+};
+union B {
+  float a;
+  double b;
+};
+#define PTR_TYPE union A
+#define FAM_TYPE union B 
+#include "counted-by-anonymous-2.c"
diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-2.c 
b/gcc/testsuite/gcc.dg/counted-by-anonymous-2.c
new file mode 100644
index 00000000000..000524aa6d4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-2.c
@@ -0,0 +1,66 @@
+/* Test the attribute counted_by for pointer field in anonymous struct/union
+   and its usage in __builtin_dynamic_object_size.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#include "builtin-object-size-common.h"
+#ifndef PTR_TYPE
+#define PTR_TYPE int
+#endif
+#ifndef FAM_TYPE
+#define FAM_TYPE int
+#endif
+
+#define __counted_by(member) \
+    __attribute__((__counted_by__(member)))
+
+struct nested_mixed {
+  struct {
+    union {
+      int b;
+      float f;
+    };
+    int n;
+  };
+  struct {
+    PTR_TYPE *pointer __counted_by(n);
+    FAM_TYPE c[] __counted_by(b);
+  };
+} *nested_mixed_annotated;
+
+
+void __attribute__((__noinline__)) setup (int pointer_array_count,
+                                         int fam_count)
+{
+  nested_mixed_annotated
+    = (struct nested_mixed *) malloc (sizeof (struct nested_mixed)
+                                     + fam_count * sizeof (FAM_TYPE));
+  nested_mixed_annotated->pointer 
+    = (PTR_TYPE *) malloc (sizeof (PTR_TYPE) * pointer_array_count);
+  nested_mixed_annotated->b = fam_count;
+  nested_mixed_annotated->n = pointer_array_count;
+  return;
+}
+
+void __attribute__((__noinline__)) test ()
+{
+  EXPECT(__builtin_dynamic_object_size(nested_mixed_annotated->c, 1),
+        nested_mixed_annotated->b * sizeof (FAM_TYPE));
+  EXPECT(__builtin_dynamic_object_size(nested_mixed_annotated->pointer, 1),
+        nested_mixed_annotated->n * sizeof (PTR_TYPE));
+}
+
+void cleanup ()
+{
+  free (nested_mixed_annotated->pointer);
+  free (nested_mixed_annotated);
+}
+
+int main(int argc, char *argv[])
+{
+  setup (10,20);   
+  test ();
+  DONE ();
+  cleanup ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous.c 
b/gcc/testsuite/gcc.dg/counted-by-anonymous.c
new file mode 100644
index 00000000000..aeafcf833d1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/counted-by-anonymous.c
@@ -0,0 +1,93 @@
+/* Testing the correct usage of attribute counted_by for anonymous
+   structures.  */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#define __counted_by(member) \
+    __attribute__((__counted_by__(member)))
+
+struct fam_in_union {
+  int count;
+  union {
+    char a;
+    char fam[] __counted_by(count);
+  };
+};
+
+struct fam_in_union_2 {
+  int count;
+  union inside {
+    char a;
+    char fam[] __counted_by(count); /* { dg-error "attribute is not a field 
declaration in the same structure as" } */
+  } inside_u;
+};
+
+struct fam_in_struct {
+  int count;
+  struct {
+    char a;
+    char fam[] __counted_by(count);
+  };
+};
+
+struct fam_in_struct_2 {
+  int count;
+  struct insidestruct {
+    char a;
+    char fam[] __counted_by(count); /* { dg-error "attribute is not a field 
declaration in the same structure as" } */
+  } inside_s;
+};
+
+struct pointer_in_union {
+  union {
+    char a;
+    char* p __counted_by(count);
+  };
+  int count;
+};
+
+struct pointer_in_union_2 {
+  union insideunion {
+    char a;
+    char* p __counted_by(count); /* { dg-error "attribute is not a field 
declaration in the same structure as" } */
+  } inside_u;
+  int count;
+};
+
+struct pointer_in_union_3 {
+  union {
+    char b;
+    char* p __counted_by(countp); /* { dg-error "attribute is not a field 
declaration with an integer type" } */
+
+  };
+  float countp;
+};
+
+struct pointer_in_struct {
+  struct {
+    int count_q;
+    char *p __counted_by(count_p);
+    float *q __counted_by(count_q);
+    int count_fam;
+    struct {
+      int count_p;
+      char a;
+      char fam[] __counted_by(count_fam);
+    };
+  };
+};
+
+struct nested_mixed {
+  struct {
+    union {
+      int b;
+      float f;
+    };
+    int n;
+  };
+  struct {
+    int *pointer __counted_by(n);
+    float *pointer_2 __counted_by(f); /* { dg-error "attribute is not a field 
declaration with an integer type" } */
+    char c[] __counted_by(b);
+  };
+} *array_nested_annotated;
-- 
2.43.5

Reply via email to