Hi,

This is the 3rd version of the patch.

Compared to the 2nd version, the major difference are:
https://gcc.gnu.org/pipermail/gcc-patches/2025-November/701902.html

1. Support counted-by in untagged structures as top-level type.
2. Verify parameters of counted-by in untagged structures as top-level type. 
3. Additional testing cases for the above 2 as the following (in 
gcc/testsuite/gcc.dg/counted-by-anonymous.c):

/* Support an untagged type as its own top-level type.  */

/* A. Variable declaration.  */
struct { int a; char b[] __attribute__ ((counted_by (a))); } *x;
struct { char b1[] __attribute__ ((counted_by (a1))); } *x1; /* { dg-error 
"attribute is not a field declaration in the same structure as" } */

struct { char *e __attribute__ ((counted_by (f))); int f; } *x2;
struct { char *e1 __attribute__ ((counted_by (f1))); } *x3; /* { dg-error 
"attribute is not a field declaration in the same structure as" } */

/* B. Function declaration and definitions.  */
struct { int c; char d[] __attribute__ ((counted_by (c))); } *func (int a, int 
b);
struct { char d1[] __attribute__ ((counted_by (c1))); } *func1 (int a, int b); 
/* { dg-error "attribute is not a field declaration in the same structure as" } 
*/

struct { int c2; char *d2 __attribute__ ((counted_by (c2))); } *func2 () { 
return 0; }
struct { char *d3 __attribute__ ((counted_by (c3))); } *func3 () { return 0; } 
/* { dg-error "attribute is not a field declaration in the same structure as" } 
*/

/* C. Parameter declaration.  */
void func4 (struct { float *f __attribute__ ((counted_by (g))); int g; } *pa, 
int count); /* { dg-warning "anonymous struct declared inside parameter list 
will not be visible outside" } */
void func5 (struct { float *f1 __attribute__ ((counted_by (g1)));} *pa, int 
count); /* { dg-error "attribute is not a field declaration in the same 
structure as" } */
                                                                                
   /* { dg-warning "anonymous struct declared inside parameter list will not be 
visible outside" "" { target *-*-* } .-1 } */

/* D. Typename.  */
int foo ()
{
  int res = sizeof (struct {int count; char *p __attribute__ ((counted_by 
(count))); });

  res += alignof (struct {char *p1 __attribute__ ((counted_by (count1))); }); 
/* { dg-error "attribute is not a field declaration in the same structure as" } 
*/
  return res;
}

typedef struct {
 int mc;
 char *d __attribute__ ((counted_by (mc)));
} mys;

typedef struct {
 char *md1 __attribute__ ((counted_by (mc1))); /* { dg-error "attribute is not 
a field declaration in the same structure as" } */
} mys1;


/* Support an unnamed field with a named struct/union.  */
struct s { struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; } 
*y;
struct s1 { struct { char c[] __attribute__ ((counted_by (d))); } *x; } *yy; /* 
{ dg-error "attribute is not a field declaration in the same structure as" } */

struct s2 { struct { char *b __attribute__ ((counted_by (a))); int a; } *x; } 
*y2;
struct s3 { struct { char *c __attribute__ ((counted_by (d))); } *x; } *y3; /* 
{ dg-error "attribute is not a field declaration in the same structure as" } */

Bootstrapped and regression tested on both X86 and aarch64, Okay for committing?

Thanks a lot.

Qing

================================
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.

When supporting the above in general, we also need to handle the following
several special cases correctly:

A. Support an untagged type as its own top-level type.  */
struct { int a; char b[] __attribute__ ((counted_by (a))); } *x;

B. Support an unnamed field with a named struct/union.  */
struct s { struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; } 
*y;

C. When -fms-extensions is enabled:

   C.1 Do not support the inward-to-outward counted-by field reference
       since checking the validity of such reference depends on unknown
       situation at the end of the structure definition.

struct bar {
  char *buf __counted_by (n); /* { dg-error "attribute is not a field 
declaration in the same structure as" } */
};

   C.2 support the outward-to-inward counted-by field reference.

        PR C/122495
        PR C/122496

gcc/c/ChangeLog:

        * c-decl.cc (grokfield): Call verify_counted_by_for_top_anonymous_type
        for named field.
        (verify_counted_by_attribute): Change the prototype to a recursive
        routine.
        (verify_counted_by_for_top_anonymous_type): New routine.
        (finish_struct): Set C_TYPE_FIELDS_HAS_COUNTED_BY and call the routine
        verify_counted_by_attribute only for named structure.
        * c-parser.cc (c_parser_declaration_or_fndef): Call
        verify_counted_by_for_top_anonymous_type for the decl.
        (c_parser_parameter_declaration): Call
        verify_counted_by_for_top_anonymous_type for the parameter.
        (c_parser_type_name): Call verify_counted_by_for_top_anonymous_type
        for the type.
        * c-tree.h (C_TYPE_FIELDS_HAS_COUNTED_BY): New flag.
        (verify_counted_by_for_top_anonymous_type): New routine.
        * 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-3.c: New test.
        * gcc.dg/counted-by-anonymous.c: New test.
        * gcc.dg/ubsan/counted-by-anonymous-bounds-1.c: New test.
        * gcc.dg/ubsan/counted-by-anonymous-bounds-2.c: New test.
        * gcc.dg/ubsan/counted-by-anonymous-bounds.c: New test.
---
 gcc/c/c-decl.cc                               | 142 +++++++++++-------
 gcc/c/c-parser.cc                             |  14 ++
 gcc/c/c-tree.h                                |   6 +
 gcc/c/c-typeck.cc                             |  49 ++++--
 .../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-3.c |  44 ++++++
 gcc/testsuite/gcc.dg/counted-by-anonymous.c   | 141 +++++++++++++++++
 .../ubsan/counted-by-anonymous-bounds-1.c     |  60 ++++++++
 .../ubsan/counted-by-anonymous-bounds-2.c     |  66 ++++++++
 .../ubsan/counted-by-anonymous-bounds.c       |  23 +++
 14 files changed, 591 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-3.c
 create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-1.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-2.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 0a368e410e5..35a70414cff 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -9083,6 +9083,11 @@ grokfield (location_t loc,
                          width ? &width : NULL, decl_attrs, expr, NULL,
                          DEPRECATED_NORMAL);
 
+  /* When this field has name, its type is a top level type, we should
+     call verify_counted_by_for_top_anonymous_type.  */
+  if (DECL_NAME (value) != NULL_TREE)
+    verify_counted_by_for_top_anonymous_type (TREE_TYPE (value));
+
   finish_decl (value, loc, NULL_TREE, NULL_TREE, NULL_TREE);
   DECL_INITIAL (value) = width;
   if (width)
@@ -9482,65 +9487,94 @@ 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)
+  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));
     }
 }
 
+/* Caller should make sure the TYPE is a top-level type (i.e. not being
+   nested in other structure/uniona). For such type, verify its counted_by
+   if it (or its pointee type) is an anonymous structure/union.  */
+
+void
+verify_counted_by_for_top_anonymous_type (tree type)
+{
+  tree checked_type = NULL_TREE;
+  if (POINTER_TYPE_P (type) && RECORD_OR_UNION_TYPE_P (TREE_TYPE (type)))
+    checked_type = TREE_TYPE (type);
+  else if (RECORD_OR_UNION_TYPE_P (type))
+    checked_type = type;
+
+  if (checked_type
+      && C_TYPE_FIELDS_HAS_COUNTED_BY (checked_type)
+      && c_type_tag (checked_type) == NULL_TREE)
+    verify_counted_by_attribute (checked_type, checked_type);
+}
+
 /* TYPE is a struct or union that we're applying may_alias to after the body is
    parsed.  Fixup any POINTER_TO types.  */
 
@@ -9612,7 +9646,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 +9721,22 @@ 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 an 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))
+         && C_TYPE_FIELDS_HAS_COUNTED_BY (TREE_TYPE (x))
+         && DECL_NAME (x) == NULL_TREE
+         && c_type_tag (TREE_TYPE (x)) == NULL_TREE)
+       C_TYPE_FIELDS_HAS_COUNTED_BY (t) = 1;
 
       if (pedantic && TREE_CODE (t) == RECORD_TYPE
          && flexible_array_type_p (TREE_TYPE (x)))
@@ -9961,6 +9996,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 +10037,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 (C_TYPE_FIELDS_HAS_COUNTED_BY (t) && c_type_tag (t) != NULL_TREE)
+    verify_counted_by_attribute (t, t);
 
   return t;
 }
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index d8b7beea923..f86a99def9d 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -2495,6 +2495,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool 
fndef_ok,
     }
 
   finish_declspecs (specs);
+  /* When the decl is declared, its type is a top level type, we should
+     call verify_counted_by_for_top_anonymous_type.  */
+  if (specs->typespec_kind == ctsk_tagdef)
+    verify_counted_by_for_top_anonymous_type (specs->type);
+
   bool gnu_auto_type_p = specs->typespec_word == cts_auto_type;
   bool std_auto_type_p = specs->c23_auto_p;
   bool any_auto_type_p = gnu_auto_type_p || std_auto_type_p;
@@ -5396,6 +5401,11 @@ c_parser_parameter_declaration (c_parser *parser, tree 
attrs,
   c_parser_declspecs (parser, specs, true, true, true, true, false,
                      !have_gnu_attrs, true, cla_nonabstract_decl);
   finish_declspecs (specs);
+  /* When the param is declared, its type is a top level type, we should
+     call verify_counted_by_for_top_anonymous_type.  */
+  if (specs->typespec_kind == ctsk_tagdef)
+    verify_counted_by_for_top_anonymous_type (specs->type);
+
   pending_xref_error ();
   prefix_attrs = specs->attrs;
   specs->attrs = NULL_TREE;
@@ -6398,6 +6408,10 @@ c_parser_type_name (c_parser *parser, bool alignas_ok)
     {
       pending_xref_error ();
       finish_declspecs (specs);
+      /* When the typename is declared, its type is a top level type, we should
+        call verify_counted_by_for_top_anonymous_type.  */
+      if (specs->typespec_kind == ctsk_tagdef)
+       verify_counted_by_for_top_anonymous_type (specs->type);
     }
   declarator = c_parser_declarator (parser,
                                    specs->typespec_kind != ctsk_none,
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index ff63d69e85d..2ce6af6edb9 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)
@@ -984,6 +988,8 @@ extern void c_pushtag (location_t, tree, tree);
 extern void c_bind (location_t, tree, bool);
 extern bool tag_exists_p (enum tree_code, tree);
 
+extern void verify_counted_by_for_top_anonymous_type (tree);
+
 /* In c-errors.cc */
 extern bool pedwarn_c90 (location_t, diagnostics::option_id, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 2cef4636bd7..f0495bd93e7 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,46 @@ 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,
+     and this anonymous struct/union is not a root type, get the first
+     outer named structure/union type.  */
+  while (TREE_CODE (datum) == COMPONENT_REF
+        && c_type_tag (type) == NULL_TREE
+        && DECL_NAME (TREE_OPERAND (datum, 1)) == 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);
+      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-3.c 
b/gcc/testsuite/gcc.dg/counted-by-anonymous-3.c
new file mode 100644
index 00000000000..0867539e3d2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-3.c
@@ -0,0 +1,44 @@
+/* Testing the correct usage of attribute counted_by for anonymous
+   structures with -fms-extensions.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fms-extensions" } */
+
+#define __counted_by(member) \
+    __attribute__((__counted_by__(member)))
+
+/* Do not support the inward-to-outward counted-by field reference for 
+   ms-extensions since checking the validity of such reference depends
+   on unknown situation at the end of the structure definition.  */
+struct bar {
+  char *buf __counted_by (n); /* { dg-error "attribute is not a field 
declaration in the same structure as" } */
+};
+
+struct foo {
+  int n;
+  struct bar;
+};
+
+/* However, at the same time, support the outward-to-inward counted-by
+   field reference for ms-extensions.  */
+struct ids
+{
+  int length_ad;
+  int length_na;
+};
+
+typedef union
+{
+  int length_hb;
+  float other;
+} ids_2;
+
+struct person
+{
+  int age;
+  int weight;
+  struct ids;    // Anonymous structure, no name needed
+  ids_2; // Anonymous union, no name needed
+  char *address __attribute__ ((counted_by (length_ad)));
+  char *hobby __attribute__ ((counted_by (length_hb)));
+  char name[]  __attribute__ ((counted_by (length_na)));
+};
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..44a90447662
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/counted-by-anonymous.c
@@ -0,0 +1,141 @@
+/* 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;
+
+/* Support an untagged type as its own top-level type.  */
+
+/* A. Variable declaration.  */
+struct { int a; char b[] __attribute__ ((counted_by (a))); } *x;
+struct { char b1[] __attribute__ ((counted_by (a1))); } *x1; /* { dg-error 
"attribute is not a field declaration in the same structure as" } */
+
+struct { char *e __attribute__ ((counted_by (f))); int f; } *x2;
+struct { char *e1 __attribute__ ((counted_by (f1))); } *x3; /* { dg-error 
"attribute is not a field declaration in the same structure as" } */
+
+/* B. Function declaration and definitions.  */
+struct { int c; char d[] __attribute__ ((counted_by (c))); } *func (int a, int 
b);
+struct { char d1[] __attribute__ ((counted_by (c1))); } *func1 (int a, int b); 
/* { dg-error "attribute is not a field declaration in the same structure as" } 
*/
+
+struct { int c2; char *d2 __attribute__ ((counted_by (c2))); } *func2 () { 
return 0; }
+struct { char *d3 __attribute__ ((counted_by (c3))); } *func3 () { return 0; } 
/* { dg-error "attribute is not a field declaration in the same structure as" } 
*/
+
+/* C. Parameter declaration.  */
+void func4 (struct { float *f __attribute__ ((counted_by (g))); int g; } *pa, 
int count); /* { dg-warning "anonymous struct declared inside parameter list 
will not be visible outside" } */
+void func5 (struct { float *f1 __attribute__ ((counted_by (g1)));} *pa, int 
count); /* { dg-error "attribute is not a field declaration in the same 
structure as" } */
+                                                                               
    /* { dg-warning "anonymous struct declared inside parameter list will not 
be visible outside" "" { target *-*-* } .-1 } */
+
+/* D. Typename.  */
+int foo ()
+{
+  int res = sizeof (struct {int count; char *p __attribute__ ((counted_by 
(count))); });
+
+  res += alignof (struct {char *p1 __attribute__ ((counted_by (count1))); }); 
/* { dg-error "attribute is not a field declaration in the same structure as" } 
*/
+  return res;
+}
+
+typedef struct {
+ int mc;
+ char *d __attribute__ ((counted_by (mc)));
+} mys;
+
+typedef struct {
+ char *md1 __attribute__ ((counted_by (mc1))); /* { dg-error "attribute is not 
a field declaration in the same structure as" } */
+} mys1;
+
+
+/* Support an unnamed field with a named struct/union.  */
+struct s { struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; } 
*y;
+struct s1 { struct { char c[] __attribute__ ((counted_by (d))); } *x; } *yy; 
/* { dg-error "attribute is not a field declaration in the same structure as" } 
*/
+
+struct s2 { struct { char *b __attribute__ ((counted_by (a))); int a; } *x; } 
*y2;
+struct s3 { struct { char *c __attribute__ ((counted_by (d))); } *x; } *y3; /* 
{ dg-error "attribute is not a field declaration in the same structure as" } */
+
diff --git a/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-1.c 
b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-1.c
new file mode 100644
index 00000000000..837d0261c49
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-1.c
@@ -0,0 +1,60 @@
+/* Testing the attribute counted_by for anonymous structures as ms-extensions. 
+   used in bounds sanitizer.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -fms-extensions -fsanitize=bounds" } */
+/* { dg-output "index 12 out of bounds for type 'char 
\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 22 out of bounds for type 'char 
\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 31 out of bounds for type 'char 
\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+
+struct ids 
+{
+  int length_ad;
+  int length_na;
+};
+
+typedef union 
+{
+  int length_hb;
+  float other;
+} ids_2;
+
+struct person
+{
+  int age;
+  int weight;
+  struct ids;    // Anonymous structure, no name needed
+  ids_2; // Anonymous union, no name needed
+  char *address __attribute__ ((counted_by (length_ad))); 
+  char *hobby __attribute__ ((counted_by (length_hb)));
+  char name[]  __attribute__ ((counted_by (length_na)));
+} *Jim;
+
+static void
+setup (int address_l, int name_l, int hb_l)
+{
+  Jim = (struct person *) __builtin_malloc (sizeof (struct person)
+                                           + name_l * sizeof (char));
+  Jim->length_na = name_l;
+  Jim->address = (char *) __builtin_malloc (sizeof (char) * address_l);
+  Jim->length_ad = address_l; 
+  Jim->hobby = (char *) __builtin_malloc (sizeof (char) * hb_l);
+  Jim->length_hb = hb_l;
+}
+
+static void
+cleanup ()
+{
+  __builtin_free (Jim->address);
+  __builtin_free (Jim->hobby);
+  __builtin_free (Jim);
+}
+
+int main()
+{
+  setup (20, 10, 30);
+  Jim->name[12] = 'a';
+  Jim->address[22] = 'k';
+  Jim->hobby[31] = 'h';
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-2.c 
b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-2.c
new file mode 100644
index 00000000000..a837951f74c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-2.c
@@ -0,0 +1,66 @@
+/* Testing the attribute counted_by for anonymous structures as 
plan9-extensions.
+   used in bounds sanitizer.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -fplan9-extensions -fsanitize=bounds" } */
+/* { dg-output "index 12 out of bounds for type 'char 
\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 22 out of bounds for type 'char 
\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 31 out of bounds for type 'char 
\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+struct ids 
+{
+  int length_ad;
+  int length_na;
+};
+
+typedef union 
+{
+  int length_hb;
+  float other;
+} ids_2;
+
+struct person
+{
+  int age;
+  int weight;
+  struct ids;    // Anonymous structure, no name needed
+  ids_2; // Anonymous union, no name needed
+  char *address __attribute__ ((counted_by (length_ad))); 
+  char *hobby __attribute__ ((counted_by (length_hb)));
+  char name[]  __attribute__ ((counted_by (length_na)));
+} *Jim;
+
+static void
+set_counted_by (struct ids *p, ids_2 *p2,
+                int address_l, int name_l, int hb_l)
+{
+  p->length_ad = address_l;
+  p->length_na = name_l;
+  p2->length_hb = hb_l;
+}
+
+static void
+setup (int address_l, int name_l, int hb_l)
+{
+  Jim = (struct person *) __builtin_malloc (sizeof (struct person)
+                                           + name_l * sizeof (char));
+  Jim->address = (char *) __builtin_malloc (sizeof (char) * address_l);
+  Jim->hobby = (char *) __builtin_malloc (sizeof (char) * hb_l);
+  set_counted_by (Jim, Jim, address_l, name_l, hb_l);
+}
+
+static void
+cleanup ()
+{
+  __builtin_free (Jim->address);
+  __builtin_free (Jim->hobby);
+  __builtin_free (Jim);
+}
+
+int main()
+{
+  setup (20, 10, 30);
+  Jim->name[12] = 'a';
+  Jim->address[22] = 'k';
+  Jim->hobby[31] = 'h';
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds.c 
b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds.c
new file mode 100644
index 00000000000..8c348560d07
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds.c
@@ -0,0 +1,23 @@
+/* Testing the attribute counted_by for anonymous structures as the top-level
+   type and as the type for an unnamed field.  */ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fsanitize=bounds" } */
+/* { dg-output "index 11 out of bounds for type 'char 
\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 22 out of bounds for type 'char 
\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+
+
+struct { int a; char b[] __attribute__ ((counted_by (a))); } *x;
+struct s { struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; } 
*y;
+
+int main ()
+{
+  x = (typeof (x)) __builtin_malloc (sizeof (*x) + sizeof (char) * 10);
+  x->a = 10;
+  x->b[11] = 0;
+
+  y = (struct s *) __builtin_malloc (sizeof (struct s));
+  y->x = (typeof (y->x)) __builtin_malloc (sizeof (y->x) + sizeof (char) * 20);
+  y->x->a = 20;
+  y->x->b[22] = 0;
+  return 0;
+}
-- 
2.31.1

Reply via email to