Convert a pointer reference with counted_by attribute to .ACCESS_WITH_SIZE.
Fix PR120929.

For example:

struct PP {
  size_t count2;
  char other1;
  char *array2 __attribute__ ((counted_by (count2)));
  int other2;
} *pp;

specifies that the "array2" is an array that is pointed by the
pointer field, and its number of elements is given by the field
"count2" in the same structure.

In order to fix PR120929, we agreed on the following solution:

for a pointer field with counted_by attribute:

struct S {
  int n;
  int *p __attribute__((counted_by(n)));
} *f;

when generating call to .ACCESS_WITH_SIZE for f->p, instead of generating
 *.ACCESS_WITH_SIZE (&f->p, &f->n,...)

We should generate
 .ACCESS_WITH_SIZE (f->p, &f->n,...)

i.e.,
the return type and the type of the first argument of the call is the
   original pointer type in this version.

However, this code generation might bring undefined behavior into the
applicaiton if the call to .ACCESS_WITH_SIZE is generated for a pointer
field reference when this refernece is written to.

For example:

f->p = malloc (size);

***** the IL for the above is:

  tmp1 = f->p;
  tmp2 = &f->n;
  tmp3 = .ACCESS_WITH_SIZE (tmp1, tmp2, ...);
  tmp4 = malloc (size);
  tmp3 = tmp4;

In the above, in order to generate a call to .ACCESS_WITH_SIZE for the pointer
reference f->p,  the new GIMPLE tmp1 = f->p is necessary to pass the value of
the pointer f->p to the call to .ACCESS_WITH_SIZE. However, this new GIMPLE is
the one that brings UB into the application since the value of f->p is not
initialized yet when it is assigned to "tmp1".

the above IL will be expanded to the following when .ACCESS_WITH_SIZE is
expanded to its first argument:

  tmp1 = f->p;
  tmp2 = &f->n;
  tmp3 = tmp1;
  tmp4 = malloc (size);
  tmp3 = tmp4;

the final optimized IL will be:

  tmp3 = f->p;
  tmp3 = malloc (size);;

As a result, the f->p will NOT be set correctly to the pointer
returned by malloc (size).

Due to this potential issue, We will need to selectively generate the call to
.ACCESS_WITH_SIZE for f->p according to whether it's a read or a write.

We will only generate call to .ACCESS_WITH_SIZE for f->p when it's a read in
C FE.

gcc/c-family/ChangeLog:

        * c-attribs.cc (handle_counted_by_attribute): Accept counted_by
        attribute for pointer fields.

gcc/c/ChangeLog:

        * c-decl.cc (verify_counted_by_attribute): Change the 2nd argument
        to a vector of fields with counted_by attribute. Verify all fields
        in this vector.
        (finish_struct): Collect all the fields with counted_by attribute
        to a vector and pass this vector to verify_counted_by_attribute.
        * c-tree.h (handle_counted_by_for_component_ref): New prototype of
        handle_counted_by_form_component_ref.
        * c-parser.cc (c_parser_postfix_expression): Call the new prototype
        of handle_counted_by_for_component_ref.
        * c-typeck.cc (default_function_array_read_conversion): Only generate
        call to .ACCESS_WITH_SIZE for a pointer field when it's a read.
        (convert_lvalue_to_rvalue): Likewise.
        (default_conversion): Likewise.
        (handle_counted_by_p): New routine.
        (check_counted_by_attribute): New routine.
        (build_counted_by_ref): Handle pointers with counted_by.
        (build_access_with_size_for_counted_by): Handle pointers with 
counted_by.
        (handle_counted_by_for_component_ref): Add one more argument.
        (build_component_ref): Call the new prototype of
        handle_counted_by_for_component_ref.

gcc/ChangeLog:

        * doc/extend.texi: Extend counted_by attribute to pointer fields in
        structures. Add one more requirement to pointers with counted_by
        attribute.

gcc/testsuite/ChangeLog:

        * gcc.dg/flex-array-counted-by.c: Update test.
        * gcc.dg/pointer-counted-by-1.c: New test.
        * gcc.dg/pointer-counted-by-2.c: New test.
        * gcc.dg/pointer-counted-by-3.c: New test.
        * gcc.dg/pointer-counted-by-8.c: New test.
        * gcc.dg/pointer-counted-by-9.c: New test.
        * gcc.dg/pointer-counted-by.c: New test.
---
 gcc/c-family/c-attribs.cc                    |  44 +++++-
 gcc/c/c-decl.cc                              |  91 ++++++-----
 gcc/c/c-parser.cc                            |   2 +-
 gcc/c/c-tree.h                               |   2 +-
 gcc/c/c-typeck.cc                            | 156 ++++++++++++++++---
 gcc/doc/extend.texi                          |  41 ++++-
 gcc/testsuite/gcc.dg/flex-array-counted-by.c |   2 +-
 gcc/testsuite/gcc.dg/pointer-counted-by-1.c  |  34 ++++
 gcc/testsuite/gcc.dg/pointer-counted-by-2.c  |  10 ++
 gcc/testsuite/gcc.dg/pointer-counted-by-3.c  | 127 +++++++++++++++
 gcc/testsuite/gcc.dg/pointer-counted-by-8.c  |  29 ++++
 gcc/testsuite/gcc.dg/pointer-counted-by-9.c  |  85 ++++++++++
 gcc/testsuite/gcc.dg/pointer-counted-by.c    | 111 +++++++++++++
 13 files changed, 659 insertions(+), 75 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-1.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-2.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-3.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-8.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-9.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by.c

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 5d7a31fd99b..ea04ed7f0d4 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -2906,22 +2906,53 @@ handle_counted_by_attribute (tree *node, tree name,
                " declaration %q+D", name, decl);
       *no_add_attrs = true;
     }
-  /* This attribute only applies to field with array type.  */
-  else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
+  /* This attribute only applies to a field with array type or pointer type.  
*/
+  else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE
+          && TREE_CODE (TREE_TYPE (decl)) != POINTER_TYPE)
     {
       error_at (DECL_SOURCE_LOCATION (decl),
-               "%qE attribute is not allowed for a non-array field",
-               name);
+               "%qE attribute is not allowed for a non-array"
+               " or non-pointer field", name);
       *no_add_attrs = true;
     }
   /* This attribute only applies to a C99 flexible array member type.  */
-  else if (! c_flexible_array_member_type_p (TREE_TYPE (decl)))
+  else if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
+          && !c_flexible_array_member_type_p (TREE_TYPE (decl)))
     {
       error_at (DECL_SOURCE_LOCATION (decl),
                "%qE attribute is not allowed for a non-flexible"
                " array member field", name);
       *no_add_attrs = true;
     }
+  /* This attribute cannot be applied to a pointer to void type.  */
+  else if (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE
+          && TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) == VOID_TYPE)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+               "%qE attribute is not allowed for a pointer to void",
+               name);
+      *no_add_attrs = true;
+    }
+  /* This attribute cannot be applied to a pointer to function type.  */
+  else if (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE
+          && TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) == FUNCTION_TYPE)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+               "%qE attribute is not allowed for a pointer to"
+               " function", name);
+      *no_add_attrs = true;
+    }
+  /* This attribute cannot be applied to a pointer to structure or union
+     with flexible array member.  */
+  else if (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE
+          && RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (decl)))
+          && TYPE_INCLUDES_FLEXARRAY (TREE_TYPE (TREE_TYPE (decl))))
+    {
+       error_at (DECL_SOURCE_LOCATION (decl),
+                 "%qE attribute is not allowed for a pointer to"
+                 " structure or union with flexible array member", name);
+       *no_add_attrs = true;
+    }
   /* The argument should be an identifier.  */
   else if (TREE_CODE (argval) != IDENTIFIER_NODE)
     {
@@ -2930,7 +2961,8 @@ handle_counted_by_attribute (tree *node, tree name,
       *no_add_attrs = true;
     }
   /* Issue error when there is a counted_by attribute with a different
-     field as the argument for the same flexible array member field.  */
+     field as the argument for the same flexible array member or
+     pointer field.  */
   else if (old_counted_by != NULL_TREE)
     {
       tree old_fieldname = TREE_VALUE (TREE_VALUE (old_counted_by));
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 8bbd6ebc66a..7e1c197a7ed 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -9432,56 +9432,62 @@ c_update_type_canonical (tree t)
     }
 }
 
-/* Verify the argument of the counted_by attribute of the flexible array
-   member FIELD_DECL is a valid field of the containing structure,
-   STRUCT_TYPE, Report error and remove this attribute when it's not.  */
+/* 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.  */
 
 static void
-verify_counted_by_attribute (tree struct_type, tree field_decl)
+verify_counted_by_attribute (tree struct_type,
+                            auto_vec<tree> *fields_with_counted_by)
 {
-  tree attr_counted_by = lookup_attribute ("counted_by",
-                                          DECL_ATTRIBUTES (field_decl));
-
-  if (!attr_counted_by)
-    return;
+  for (tree field_decl : *fields_with_counted_by)
+    {
+      tree attr_counted_by = lookup_attribute ("counted_by",
+                                               DECL_ATTRIBUTES (field_decl));
 
-  /* If there is an counted_by attribute attached to the field,
-     verify it.  */
+      if (!attr_counted_by)
+       continue;
 
-  tree fieldname = TREE_VALUE (TREE_VALUE (attr_counted_by));
+      /* If there is an counted_by attribute attached to the field,
+        verify it.  */
 
-  /* Verify the argument of the attrbute is a valid field of the
-     containing structure.  */
+      tree fieldname = TREE_VALUE (TREE_VALUE (attr_counted_by));
 
-  tree counted_by_field = lookup_field (struct_type, fieldname);
+      /* Verify the argument of the attrbute is a valid field of the
+        containing structure.  */
 
-  /* 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),
-               "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);
+      tree counted_by_field = lookup_field (struct_type, fieldname);
 
-      if (!INTEGRAL_TYPE_P (TREE_TYPE (real_field)))
+      /* 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),
                    "argument %qE to the %<counted_by%> attribute"
-                   " is not a field declaration with an integer type",
-                   fieldname);
+                   " 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)))
+           {
+             error_at (DECL_SOURCE_LOCATION (field_decl),
+                       "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));
+           }
+       }
     }
 }
 
@@ -9556,7 +9562,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, 
tree attributes,
      until now.)  */
 
   bool saw_named_field = false;
-  tree counted_by_fam_field = NULL_TREE;
+  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.
@@ -9637,9 +9643,16 @@ finish_struct (location_t loc, tree t, tree fieldlist, 
tree attributes,
             record it here and do more verification later after the
             whole structure is complete.  */
          if (lookup_attribute ("counted_by", DECL_ATTRIBUTES (x)))
-           counted_by_fam_field = x;
+           fields_with_counted_by.safe_push (x);
        }
 
+      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 (pedantic && TREE_CODE (t) == RECORD_TYPE
          && flexible_array_type_p (TREE_TYPE (x)))
        pedwarn (DECL_SOURCE_LOCATION (x), OPT_Wpedantic,
@@ -9938,8 +9951,8 @@ finish_struct (location_t loc, tree t, tree fieldlist, 
tree attributes,
        struct_parse_info->struct_types.safe_push (t);
      }
 
-  if (counted_by_fam_field)
-    verify_counted_by_attribute (t, counted_by_fam_field);
+  if (fields_with_counted_by.length () > 0)
+    verify_counted_by_attribute (t, &fields_with_counted_by);
 
   return t;
 }
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 0c3e3e2889c..4fe6c8ca890 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -12686,7 +12686,7 @@ c_parser_postfix_expression (c_parser *parser)
               .ACCESS_WITH_SIZE was not generated by the routine
               build_component_ref by default, we should generate it here.  */
            if ((in_typeof || in_alignof) && TREE_CODE (ref) == COMPONENT_REF)
-             ref = handle_counted_by_for_component_ref (loc, ref);
+             ref = handle_counted_by_for_component_ref (loc, ref, false);
 
            if (has_counted_by_object (ref))
              expr.value = get_counted_by_ref (ref);
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 364f51df58c..7253903dbe8 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -821,7 +821,7 @@ extern tree composite_type (tree, tree);
 extern tree lookup_field (const_tree, tree);
 extern tree build_component_ref (location_t, tree, tree, location_t,
                                 location_t, bool = true);
-extern tree handle_counted_by_for_component_ref (location_t, tree);
+extern tree handle_counted_by_for_component_ref (location_t, tree, bool);
 extern tree build_array_ref (location_t, tree, tree);
 extern tree build_omp_array_section (location_t, tree, tree, tree);
 extern tree build_external_ref (location_t, tree, bool, tree *);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 9a5eb0da3a1..f58ff91fb7f 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -137,6 +137,7 @@ static void record_maybe_used_decl (tree);
 static bool comptypes_internal (const_tree, const_tree,
                                struct comptypes_data *data);
 static bool comptypes_check_for_composite (tree t1, tree t2);
+static bool handle_counted_by_p (tree);
 
 /* Return true if EXP is a null pointer constant, false otherwise.  */
 
@@ -2438,6 +2439,11 @@ struct c_expr
 default_function_array_read_conversion (location_t loc, struct c_expr exp)
 {
   mark_exp_read (exp.value);
+  /* We only generate a call to .ACCESS_WITH_SIZE for a pointer field when
+     it is a read.  */
+  if (TREE_CODE (exp.value) == COMPONENT_REF
+      && handle_counted_by_p (exp.value))
+    exp.value = handle_counted_by_for_component_ref (loc, exp.value, true);
   return default_function_array_conversion (loc, exp);
 }
 
@@ -2539,6 +2545,12 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr 
exp,
   bool force_non_npc = false;
   if (read_p)
     mark_exp_read (exp.value);
+  /* We only generate a call to .ACCESS_WITH_SIZE for a pointer field when
+     it is a read.  */
+  if (read_p && TREE_CODE (exp.value) == COMPONENT_REF
+      && handle_counted_by_p (exp.value))
+    exp.value = handle_counted_by_for_component_ref (loc, exp.value, true);
+
   if (convert_p)
     exp = default_function_array_conversion (loc, exp);
   if (!VOID_TYPE_P (TREE_TYPE (exp.value)))
@@ -2702,6 +2714,11 @@ default_conversion (tree exp)
   tree promoted_type;
 
   mark_exp_read (exp);
+  /* We only generate a call to .ACCESS_WITH_SIZE for a pointer field when
+     it is a read.  */
+  if (TREE_CODE (exp) == COMPONENT_REF
+      && handle_counted_by_p (exp))
+    exp = handle_counted_by_for_component_ref (EXPR_LOCATION (exp), exp, true);
 
   /* Functions and arrays have been converted during parsing.  */
   gcc_assert (code != FUNCTION_TYPE);
@@ -2920,10 +2937,63 @@ should_suggest_deref_p (tree datum_type)
     return false;
 }
 
-/* For a SUBDATUM field of a structure or union DATUM, generate a REF to
-   the object that represents its counted_by per the attribute counted_by
-   attached to this field if it's a flexible array member field, otherwise
-   return NULL_TREE.
+/* Give a component ref REF, decide whether we should handle its counted_by
+   attribute based on its context:
+   Do not handle counted_by when in offsetof, typeof and alignof operator.  */
+
+static bool
+handle_counted_by_p (tree ref)
+{
+  gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
+  tree datum = TREE_OPERAND (ref, 0);
+  /* If the component_ref is build for a offsetof, i.e., the datum
+     of the component_ref is a indirect_ref of null_pointer_node,
+     we should not generate call to .ACCESS_WITH_SIZE.  */
+  if (TREE_CODE (datum) == INDIRECT_REF
+      && TREE_OPERAND (datum, 0) == null_pointer_node)
+    return false;
+  if (in_typeof || in_alignof)
+    return false;
+  return true;
+}
+
+/* Given a component ref REF, if there is a counted_by attribute attached,
+   issue error when the element_type is a structure or union including a
+   flexible array member.  */
+
+static void
+check_counted_by_attribute (location_t loc, tree ref)
+{
+  tree subdatum = TREE_OPERAND (ref, 1);
+  tree sub_type = TREE_TYPE (subdatum);
+
+  if (!c_flexible_array_member_type_p (sub_type)
+      && TREE_CODE (sub_type) != POINTER_TYPE)
+    return;
+
+  tree element_type = TREE_TYPE (sub_type);
+
+  tree attr_counted_by = lookup_attribute ("counted_by",
+                                          DECL_ATTRIBUTES (subdatum));
+  if (attr_counted_by)
+    {
+      /* Issue error when the element_type is a structure or
+       union including a flexible array member.  */
+      if (RECORD_OR_UNION_TYPE_P (element_type)
+         && TYPE_INCLUDES_FLEXARRAY (element_type))
+       {
+         error_at (loc,
+                   "%<counted_by%> attribute is not allowed for a pointer to"
+                   " structure or union with flexible array member");
+         return;
+       }
+    }
+}
+
+/* For a SUBDATUM field of a structure or union DATUM, generate a REF
+   to the object that represents its counted_by per the attribute
+   counted_by attached to this field if it's a flexible array member
+   or a pointer field, otherwise return NULL_TREE.
    Set COUNTED_BY_TYPE to the TYPE of the counted_by field.
    For example, if:
 
@@ -2938,13 +3008,16 @@ should_suggest_deref_p (tree datum_type)
     the ref to the object that represents its element count will be:
 
     &(p->k)
-
 */
+
 static tree
-build_counted_by_ref (tree datum, tree subdatum, tree *counted_by_type)
+build_counted_by_ref (tree datum, tree subdatum,
+                     tree *counted_by_type)
 {
   tree type = TREE_TYPE (datum);
-  if (!c_flexible_array_member_type_p (TREE_TYPE (subdatum)))
+  tree sub_type = TREE_TYPE (subdatum);
+  if (!c_flexible_array_member_type_p (sub_type)
+      && TREE_CODE (sub_type) != POINTER_TYPE)
     return NULL_TREE;
 
   tree attr_counted_by = lookup_attribute ("counted_by",
@@ -2975,8 +3048,11 @@ build_counted_by_ref (tree datum, tree subdatum, tree 
*counted_by_type)
 }
 
 /* Given a COMPONENT_REF REF with the location LOC, the corresponding
-   COUNTED_BY_REF, and the COUNTED_BY_TYPE, generate an INDIRECT_REF
-   to a call to the internal function .ACCESS_WITH_SIZE.
+   COUNTED_BY_REF, and the COUNTED_BY_TYPE, generate the corresponding
+   call to the internal function .ACCESS_WITH_SIZE.
+
+   A: For the Flexible Array Member, Generate an INDIRECT_REF to a call to
+   the internal function .ACCESS_WITH_SIZE.
 
    REF
 
@@ -2986,12 +3062,25 @@ build_counted_by_ref (tree datum, tree subdatum, tree 
*counted_by_type)
                        TYPE_SIZE_UNIT for element)
 
    NOTE: The return type of this function is the POINTER type pointing
-   to the original flexible array type.
-   Then the type of the INDIRECT_REF is the original flexible array type.
-
+   to the original flexible array type.  Then the type of the INDIRECT_REF
+   is the original flexible array type.
    The type of the first argument of this function is a POINTER type
    to the original flexible array type.
 
+   B: For pointers with counted_by, generate a call to the internal function
+   .ACCESS_WITH_SIZE.
+
+    REF
+
+    to:
+
+    .ACCESS_WITH_SIZE (REF, COUNTED_BY_REF, (* TYPE_OF_SIZE)0,
+                      TYPE_SIZE_UNIT for element)
+
+   NOTE: The return type of this function is the original pointer type.
+   The type of the first argument of this function is the original
+   pointer type.
+
    The 3rd argument of the call is a constant 0 with the pointer TYPE whose
    pointee type is the TYPE of the object pointed by COUNTED_BY_REF.
 
@@ -3004,16 +3093,25 @@ build_access_with_size_for_counted_by (location_t loc, 
tree ref,
                                       tree counted_by_ref,
                                       tree counted_by_type)
 {
-  gcc_assert (c_flexible_array_member_type_p (TREE_TYPE (ref)));
-  /* The result type of the call is a pointer to the flexible array type.  */
-  tree result_type = c_build_pointer_type (TREE_TYPE (ref));
+  gcc_assert (c_flexible_array_member_type_p (TREE_TYPE (ref))
+             || TREE_CODE (TREE_TYPE (ref)) == POINTER_TYPE);
+
+  bool is_fam = c_flexible_array_member_type_p (TREE_TYPE (ref));
+
+  /* The result type of the call is a pointer to the flexible array type;
+     or is the original ponter type to the pointer field with counted_by.  */
+  tree result_type = is_fam ? c_build_pointer_type (TREE_TYPE (ref))
+                    : TREE_TYPE (ref);
+
   tree element_size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ref)));
 
-  tree first_param
-    = c_fully_fold (array_to_pointer_conversion (loc, ref), false, NULL);
+  tree first_param = is_fam
+                    ? c_fully_fold (array_to_pointer_conversion (loc, ref),
+                                    false, NULL)
+                    : ref;
   tree second_param
     = c_fully_fold (counted_by_ref, false, NULL);
-  tree third_param = build_int_cst (build_pointer_type (counted_by_type), 0);
+  tree third_param = build_int_cst (c_build_pointer_type (counted_by_type), 0);
 
   tree call
     = build_call_expr_internal_loc (loc, IFN_ACCESS_WITH_SIZE,
@@ -3024,7 +3122,8 @@ build_access_with_size_for_counted_by (location_t loc, 
tree ref,
                                    element_size);
 
   /* Wrap the call with an INDIRECT_REF with the flexible array type.  */
-  call = build1 (INDIRECT_REF, TREE_TYPE (ref), call);
+  if (is_fam)
+    call = build1 (INDIRECT_REF, TREE_TYPE (ref), call);
   SET_EXPR_LOCATION (call, loc);
   return call;
 }
@@ -3032,15 +3131,27 @@ build_access_with_size_for_counted_by (location_t loc, 
tree ref,
 /* For the COMPONENT_REF ref, check whether it has a counted_by attribute,
    if so, wrap this COMPONENT_REF with the corresponding CALL to the
    function .ACCESS_WITH_SIZE.
-   Otherwise, return the ref itself.  */
+   Otherwise, return the ref itself.
+   FOR_POINTER is true when this is for pointer field.  */
 
 tree
-handle_counted_by_for_component_ref (location_t loc, tree ref)
+handle_counted_by_for_component_ref (location_t loc, tree ref,
+                                    bool for_pointer)
 {
   gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
   tree datum = TREE_OPERAND (ref, 0);
   tree subdatum = TREE_OPERAND (ref, 1);
   tree counted_by_type = NULL_TREE;
+
+  if (!(c_flexible_array_member_type_p (TREE_TYPE (ref))
+       || TREE_CODE (TREE_TYPE (ref)) == POINTER_TYPE))
+    return ref;
+
+  bool is_fam = c_flexible_array_member_type_p (TREE_TYPE (ref));
+
+  if (!(is_fam ^ for_pointer))
+    return ref;
+
   tree counted_by_ref = build_counted_by_ref (datum, subdatum,
                                              &counted_by_type);
   if (counted_by_ref)
@@ -3155,8 +3266,9 @@ build_component_ref (location_t loc, tree datum, tree 
component,
                        NULL_TREE);
          SET_EXPR_LOCATION (ref, loc);
 
+         check_counted_by_attribute (loc, ref);
          if (handle_counted_by)
-           ref = handle_counted_by_for_component_ref (loc, ref);
+           ref = handle_counted_by_for_component_ref (loc, ref, false);
 
          if (TREE_READONLY (subdatum)
              || (use_datum_quals && TREE_READONLY (datum)))
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8c29e24c267..a119ad31ea2 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -7138,9 +7138,11 @@ The @code{aligned} attribute can also be used for 
functions
 @cindex @code{counted_by} variable attribute
 @item counted_by (@var{count})
 The @code{counted_by} attribute may be attached to the C99 flexible array
-member of a structure.  It indicates that the number of the elements of the
-array is given by the field "@var{count}" in the same structure as the
-flexible array member.
+member, or a pointer field of a structure.  It indicates that the number
+of the elements of the array that is held by the flexible array member
+field, or is pointed to by the pointer field, is given by the field
+"@var{count}" in the same structure as the flexible array member or the
+pointer field.
 
 This attribute is available only in C for now.
 In C++ this attribute is ignored.
@@ -7161,8 +7163,22 @@ struct P @{
 @end smallexample
 
 @noindent
-specifies that the @code{array} is a flexible array member whose number of
-elements is given by the field @code{count} in the same structure.
+specifies that the @code{array} is a flexible array member whose number
+of elements is given by the field @code{count} in the same structure.
+
+@smallexample
+struct PP @{
+  size_t count2;
+  char other1;
+  char *array2 __attribute__ ((counted_by (count2)));
+  int other2;
+@} *pp;
+@end smallexample
+
+@noindent
+specifies that the @code{array2} is an array that is pointed by the
+pointer field, and its number of elements is given by the field
+@code{count2} in the same structure.
 
 The field that represents the number of the elements should have an
 integer type.  Otherwise, the compiler reports an error and ignores
@@ -7171,6 +7187,12 @@ the attribute.
 When the field that represents the number of the elements is assigned a
 negative integer value, the compiler treats the value as zero.
 
+The @code{counted_by} attribute is not allowed for a pointer to @code{void},
+a pointer to function, or a pointer to a structure or union that includes
+a flexible array member.  However, it is allowed for a pointer to
+non-void incomplete structure or union types, as long as the type could
+be completed before the first reference to the pointer.
+
 An explicit @code{counted_by} annotation defines a relationship between
 two objects, @code{p->array} and @code{p->count}, and there are the
 following requirements on the relationship between this pair:
@@ -7186,6 +7208,13 @@ available all the time.  This relationship must hold 
even after any of
 these related objects are updated during the program.
 @end itemize
 
+In addition to the above requirements, there is one more requirement
+between this pair if and only if @code{p->array} is an array that is
+pointed by the pointer field:
+
+@code{p->array} and @code{p->count} can only be changed by changing the
+whole structure at the same time.
+
 It's the programmer's responsibility to make sure the above requirements to
 be kept all the time.  Otherwise the compiler reports warnings and
 the results of the array bound sanitizer and the
@@ -7207,6 +7236,8 @@ In the above, @code{ref1} uses @code{val1} as the number 
of the elements in
 @code{p->array}, and @code{ref2} uses @code{val2} as the number of elements
 in @code{p->array}.
 
+Note, however, the above feature is not valid for the pointer field.
+
 @cindex @code{alloc_size} variable attribute
 @item alloc_size (@var{position})
 @itemx alloc_size (@var{position-1}, @var{position-2})
diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by.c 
b/gcc/testsuite/gcc.dg/flex-array-counted-by.c
index 16eb2c63010..4fa91ff0bdb 100644
--- a/gcc/testsuite/gcc.dg/flex-array-counted-by.c
+++ b/gcc/testsuite/gcc.dg/flex-array-counted-by.c
@@ -10,7 +10,7 @@ int x __attribute ((counted_by (size))); /* { dg-error 
"attribute is not allowed
 
 struct trailing {
   int count;
-  int field __attribute ((counted_by (count))); /* { dg-error "attribute is 
not allowed for a non-array field" } */
+  int field __attribute ((counted_by (count))); /* { dg-error "attribute is 
not allowed for a non-array or non-pointer field" } */
 };
 
 struct trailing_1 {
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-1.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-1.c
new file mode 100644
index 00000000000..395af3481b2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-1.c
@@ -0,0 +1,34 @@
+/* More testing the correct usage of attribute counted_by for pointer field.  
*/
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+typedef struct item1 Item1;
+typedef union item2 Item2;
+
+struct pointer_array {
+  int count1;
+  Item1 *array_1 __attribute__ ((counted_by (count1))); 
+  Item2 *array_2 __attribute__ ((counted_by (count2))); 
+  int count2;
+} *pointer_data; 
+
+struct item1 {
+  int a;
+  float b[];
+};
+
+union item2 {
+  int c;
+  float d[];
+};
+
+void foo ()
+{
+  pointer_data 
+    = (struct pointer_array *) __builtin_malloc (sizeof (struct 
pointer_array));
+  pointer_data->array_1 /* { dg-error "attribute is not allowed for a pointer 
to structure or union with flexible array member" } */ 
+    = (Item1 *) __builtin_malloc (sizeof (Item1) + 3 * sizeof (float));
+  pointer_data->array_2 /* { dg-error "attribute is not allowed for a pointer 
to structure or union with flexible array member" } */
+    = (Item2 *) __builtin_malloc (sizeof (Item2) + 3 * sizeof (float));
+  return;
+}
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-2.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-2.c
new file mode 100644
index 00000000000..1f4a278052c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-2.c
@@ -0,0 +1,10 @@
+/* Testing the correct usage of attribute counted_by for pointer: _BitInt  */
+/* { dg-do compile { target bitint } } */
+/* { dg-options "-O2 -std=c23" } */
+
+struct pointer_array {
+  _BitInt(24) count; 
+  int *array __attribute__ ((counted_by (count)));
+  int *array1 __attribute__ ((counted_by (count1)));
+  _BitInt(24) count1; 
+};
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-3.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-3.c
new file mode 100644
index 00000000000..70056098364
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-3.c
@@ -0,0 +1,127 @@
+ /* Testing the correct usage of attribute counted_by for pointer in c23,
+    multiple definitions of the same tag in same or different scopes.
+    { dg-do compile }
+    { dg-options "-std=c23" }
+ */
+
+/* Allowed redefinitions of the same struct in the same scope, with the
+   same counted_by attribute.  */
+struct f {
+  int b;
+  int c;
+  int *a __attribute__ ((counted_by (b))); };
+struct f {
+  int b;
+  int c;
+  int *a __attribute__ ((counted_by (b))); };
+struct f {
+  int b;
+  int c;
+  int *a; }; /* { dg-error "redefinition of struct or union" } */
+
+/* Error when the counted_by attribute is defined differently.  */
+struct f {
+  int b;
+  int c;
+  int *a __attribute__ ((counted_by (c))); }; /* { dg-error "redefinition of 
struct or union" } */
+
+struct h {
+  int b;
+  int c;
+  int *a __attribute__ ((counted_by (b))); } p;  
+
+void test (void)
+{
+  struct h {
+  int b;
+  int c;
+  int *a __attribute__ ((counted_by (b))); } x;
+
+  p = x;
+}
+
+void test1 (void)
+{
+  struct h {
+  int b;
+  int c;
+  int *a __attribute__ ((counted_by (c))); } y;
+
+  p = y;   /* { dg-error "incompatible types when assigning to type" } */
+}
+
+struct nested_f {
+  struct {
+    union {
+      int b;
+      float f;
+    };
+    int n;
+  };
+  char *c __attribute__ ((counted_by (b)));
+}; 
+
+struct nested_f {
+  struct {
+    union {
+      int b;
+      float f;
+    };
+    int n;
+  };
+  char *c __attribute__ ((counted_by (b)));
+}; 
+
+struct nested_f {
+  struct {
+    union {
+      int b;
+      float f;
+    };
+    int n;
+  };
+  char *c __attribute__ ((counted_by (n)));
+};  /* { dg-error "redefinition of struct or union" } */
+
+struct nested_h {
+  struct {
+    union {
+      int b;
+      float f;
+    };
+    int n;
+  };
+  char *c __attribute__ ((counted_by (b)));
+} nested_p; 
+
+void test_2 (void)
+{
+struct nested_h {
+  struct {
+    union {
+      int b;
+      float f;
+    };
+    int n;
+  };
+  char *c __attribute__ ((counted_by (b)));
+} nested_x; 
+
+ nested_p = nested_x;
+}
+
+void test_3 (void)
+{
+struct nested_h {
+  struct {
+    union {
+      int b;
+      float f;
+    };
+    int n;
+  };
+  char *c __attribute__ ((counted_by (n)));
+} nested_y; 
+
+ nested_p = nested_y; /* { dg-error "incompatible types when assigning to 
type" } */
+}
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-8.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-8.c
new file mode 100644
index 00000000000..96bb83f0bc2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-8.c
@@ -0,0 +1,29 @@
+/* Additional test of the attribute counted_by for pointer field and its usage
+   in __builtin_dynamic_object_size.  */ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#include "builtin-object-size-common.h"
+
+struct annotated {
+  int b;
+  int *c __attribute__ ((counted_by (b)));
+};
+
+
+void __attribute__((__noinline__)) setup (int **ptr, int attr_count)
+{
+  *ptr = (int *) malloc (sizeof (int) * attr_count);
+}
+
+int main(int argc, char *argv[])
+{
+  struct annotated *f 
+    = (struct annotated *) __builtin_malloc (sizeof (struct annotated));
+  setup (&f->c, 10);
+  f->b = 10;
+  EXPECT(__builtin_dynamic_object_size (f->c, 1), 10 * sizeof (int));
+  free (f->c);
+  free (f);
+  DONE ();
+}
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-9.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-9.c
new file mode 100644
index 00000000000..a0ca0a21d6f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-9.c
@@ -0,0 +1,85 @@
+/* Test the code generation for the new attribute counted_by.
+   And also the offsetof operator on such array.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-original" } */
+
+#include <stdlib.h>
+
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+struct annotated {
+  int b;
+  char *c __attribute__ ((counted_by (b)));
+} *p_annotated;
+
+struct flex {
+  int b;
+  char *c; 
+}; 
+
+struct nested_annotated {
+  struct {
+    union {
+      int b;
+      float f; 
+    };
+    int n;
+  };
+  char *c __attribute__ ((counted_by (b)));
+} *p_nested_annotated;
+
+struct nested_flex {
+  struct {
+    union {
+      int b;
+      float f; 
+    };
+    int n;
+  };
+  char *c;
+};
+
+void __attribute__((__noinline__)) setup (int normal_count, int attr_count)
+{
+  p_annotated
+    = (struct annotated *)malloc (sizeof (struct annotated));
+ 
+  p_annotated->c = (char *) malloc (sizeof (char) * attr_count); 
+  p_annotated->b = attr_count;
+
+  p_nested_annotated
+    = (struct nested_annotated *)malloc (sizeof (struct nested_annotated));
+  p_nested_annotated->c = (char *) malloc (attr_count *  sizeof (char));
+  p_nested_annotated->b = attr_count;
+
+  return;
+}
+
+void __attribute__((__noinline__)) test (char a, char b)
+{
+  if (__builtin_offsetof (struct annotated, c)
+      != __builtin_offsetof (struct flex, c))
+    abort ();
+  if (__builtin_offsetof (struct nested_annotated, c) 
+      != __builtin_offsetof (struct nested_flex, c)) 
+    abort ();
+
+  if (__alignof (*p_annotated->c) != __alignof (char))
+    abort ();
+  if (__alignof (*p_nested_annotated->c) != __alignof (char))
+    abort ();
+
+  p_annotated->c[2] = a;
+  p_nested_annotated->c[3] = b;
+}
+
+int main(int argc, char *argv[])
+{
+  setup (10,10);   
+  test ('A', 'B');
+  if (p_annotated->c[2] != 'A') abort ();
+  if (p_nested_annotated->c[3] != 'B') abort ();
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "ACCESS_WITH_SIZE" 4 "original" } } */
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by.c
new file mode 100644
index 00000000000..0f18828ac11
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by.c
@@ -0,0 +1,111 @@
+/* Testing the correct usage of attribute counted_by for pointer field.
+   and also mixed pointer field and FMA field in the same structure.  */
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+int size;
+int *x __attribute__ ((counted_by (size))); /* { dg-error "attribute is not 
allowed for a non-field declaration" } */
+
+struct pointer_array_0 {
+  int count;
+  int array __attribute__ ((counted_by (count))); /* { dg-error "attribute is 
not allowed for a non-array or non-pointer field" } */
+  int other;
+};
+
+int count;
+struct pointer_array_1 {
+  int other;
+  int *array_1 __attribute__ ((counted_by (count))); /* { dg-error "attribute 
is not a field declaration in the same structure as" } */
+  int array_fam[] __attribute__ ((counted_by (count))); /* { dg-error 
"attribute is not a field declaration in the same structure as" } */
+};
+
+struct pointer_array_2 {
+  float count1;
+  float count2;
+  int *array_2 __attribute__ ((counted_by (count1))); /* { dg-error "attribute 
is not a field declaration with an integer type" } */
+  int array_fam[] __attribute__ ((counted_by (count2))); /* { dg-error 
"attribute is not a field declaration with an integer type" } */
+}; 
+
+struct pointer_array_3 {
+  int count;
+  int *array_3 __attribute__ ((counted_by (count))) __attribute__ ((counted_by 
(count)));
+}; 
+
+struct pointer_array_4 {
+  int count1;
+  int count2;
+  int *array_4 __attribute__ ((counted_by (count1))) __attribute__ 
((counted_by (count2))); /* { dg-error "conflicts with previous declaration" } 
*/
+  float array_fam[] __attribute__ ((counted_by (count2))) __attribute__ 
((counted_by (count1))); /* { dg-error "conflicts with previous declaration" } 
*/
+}; 
+
+struct pointer_array_5 {
+  _Bool count;
+  int *array_5 __attribute__ ((counted_by (count)));
+}; 
+
+enum week {Mon, Tue, Wed};
+struct pointer_array_6 {
+  enum week days;
+  int *array_6 __attribute__ ((counted_by (days)));
+}; 
+
+struct pointer_array_7 {
+  int count;
+  void *array_7 __attribute__ ((counted_by (count))); /* { dg-error "attribute 
is not allowed for a pointer to void" } */
+}; 
+
+struct pointer_array_8 {
+  int count;
+  int (*fpr)(int,int) __attribute__ ((counted_by (count))); /* { dg-error 
"attribute is not allowed for a pointer to function" } */
+}; 
+
+struct item1 {
+  int a;
+  float b;
+};
+
+union item2 {
+  char *a;
+  int *b; 
+};
+
+typedef struct item3 Item3;
+typedef union item4 Item4;
+
+struct item5 {
+  int a;
+  float b[];
+};
+
+/* Incomplete structure and union are allowed.  */
+struct pointer_array_9 {
+  int count1;
+  int count2;
+  int count3;
+  struct item1 *array_1 __attribute__ ((counted_by (count1)));
+  union item2 *array_2 __attribute__ ((counted_by (count2)));
+  Item3 *array_3 __attribute__ ((counted_by (count3))); 
+  Item4 *array_4 __attribute__ ((counted_by (count4))); 
+  int count4;
+  int count5;
+  /* structure with flexible array member is not allowed.  */
+  struct item5 *array_5 __attribute__ ((counted_by (count5))); /* { dg-error 
"attribute is not allowed for a pointer to structure or union with flexible 
array member" } */
+}; 
+
+struct mixed_array {
+  int count1;
+  float *array_1 __attribute__ ((counted_by (count1)));
+  float *array_2 __attribute__ ((counted_by (count1)));
+  int count2;
+  long *array_3 __attribute__ ((counted_by (count2)));
+  long array_4[] __attribute__ ((counted_by (count2)));
+};
+
+struct mixed_array_2 {
+  float *array_1 __attribute__ ((counted_by (count1)));
+  int count1;
+  float *array_2 __attribute__ ((counted_by (count1)));
+  long *array_3 __attribute__ ((counted_by (count2)));
+  int count2;
+  long array_4[] __attribute__ ((counted_by (count2)));
+};
-- 
2.31.1

Reply via email to