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