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. 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. 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-2.c: New test. * gcc.dg/pointer-counted-by-3.c: New test. * gcc.dg/pointer-counted-by.c: New test. --- gcc/c-family/c-attribs.cc | 15 ++- gcc/c/c-decl.cc | 91 +++++++------ gcc/doc/extend.texi | 35 ++++- gcc/testsuite/gcc.dg/flex-array-counted-by.c | 2 +- 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.c | 70 ++++++++++ 7 files changed, 299 insertions(+), 51 deletions(-) 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.c diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index 5a0e3d328ba..51d42999578 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -2906,16 +2906,18 @@ 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 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" @@ -2930,7 +2932,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 8c420f22976..53e7b726ee6 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -9448,56 +9448,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)); + } + } } } @@ -9572,7 +9578,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. @@ -9653,9 +9659,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, @@ -9945,8 +9958,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/doc/extend.texi b/gcc/doc/extend.texi index 0978c4c41b2..1f73a9430a7 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -7116,9 +7116,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. @@ -7139,8 +7141,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 @@ -7164,6 +7180,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 @@ -7185,6 +7208,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-2.c b/gcc/testsuite/gcc.dg/pointer-counted-by-2.c new file mode 100644 index 00000000000..33007803d50 --- /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.c b/gcc/testsuite/gcc.dg/pointer-counted-by.c new file mode 100644 index 00000000000..45a83612c73 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pointer-counted-by.c @@ -0,0 +1,70 @@ +/* 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 "-O2" } */ + +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 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