diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/c-family/c-attribs.cc | 68 | ||||
-rw-r--r-- | gcc/c-family/c-common.cc | 13 | ||||
-rw-r--r-- | gcc/c-family/c-common.h | 1 | ||||
-rw-r--r-- | gcc/c/c-decl.cc | 80 | ||||
-rw-r--r-- | gcc/c/c-tree.h | 1 | ||||
-rw-r--r-- | gcc/c/c-typeck.cc | 37 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 68 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/flex-array-counted-by-7.c | 8 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/flex-array-counted-by-8.c | 127 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/flex-array-counted-by.c | 62 |
10 files changed, 444 insertions, 21 deletions
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index e3833ed..f9b229a 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -105,6 +105,8 @@ static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree, int, bool *); static tree handle_strict_flex_array_attribute (tree *, tree, tree, int, bool *); +static tree handle_counted_by_attribute (tree *, tree, tree, + int, bool *); static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ; static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *); @@ -412,6 +414,8 @@ const struct attribute_spec c_common_gnu_attributes[] = handle_warn_if_not_aligned_attribute, NULL }, { "strict_flex_array", 1, 1, true, false, false, false, handle_strict_flex_array_attribute, NULL }, + { "counted_by", 1, 1, true, false, false, false, + handle_counted_by_attribute, NULL }, { "weak", 0, 0, true, false, false, false, handle_weak_attribute, NULL }, { "noplt", 0, 0, true, false, false, false, @@ -659,7 +663,8 @@ attribute_takes_identifier_p (const_tree attr_id) else if (!strcmp ("mode", spec->name) || !strcmp ("format", spec->name) || !strcmp ("cleanup", spec->name) - || !strcmp ("access", spec->name)) + || !strcmp ("access", spec->name) + || !strcmp ("counted_by", spec->name)) return true; else return targetm.attribute_takes_identifier_p (attr_id); @@ -2807,6 +2812,67 @@ handle_strict_flex_array_attribute (tree *node, tree name, return NULL_TREE; } +/* Handle a "counted_by" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_counted_by_attribute (tree *node, tree name, + tree args, int ARG_UNUSED (flags), + bool *no_add_attrs) +{ + tree decl = *node; + tree argval = TREE_VALUE (args); + tree old_counted_by = lookup_attribute ("counted_by", DECL_ATTRIBUTES (decl)); + + /* This attribute only applies to field decls of a structure. */ + if (TREE_CODE (decl) != FIELD_DECL) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE attribute is not allowed for a non-field" + " 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) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE attribute is not allowed for a non-array 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))) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE attribute is not allowed for a non-flexible" + " array member field", name); + *no_add_attrs = true; + } + /* The argument should be an identifier. */ + else if (TREE_CODE (argval) != IDENTIFIER_NODE) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%<counted_by%> argument is not an identifier"); + *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. */ + else if (old_counted_by != NULL_TREE) + { + tree old_fieldname = TREE_VALUE (TREE_VALUE (old_counted_by)); + if (strcmp (IDENTIFIER_POINTER (old_fieldname), + IDENTIFIER_POINTER (argval)) != 0) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%<counted_by%> argument %qE conflicts with" + " previous declaration %qE", argval, old_fieldname); + *no_add_attrs = true; + } + } + + return NULL_TREE; +} + /* Handle a "weak" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index aae998d..0341c44 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -9942,6 +9942,19 @@ c_common_finalize_early_debug (void) (*debug_hooks->early_global_decl) (cnode->decl); } +/* Determine whether TYPE is an ISO C99 flexible array member type "[]". */ +bool +c_flexible_array_member_type_p (const_tree type) +{ + if (TREE_CODE (type) == ARRAY_TYPE + && TYPE_SIZE (type) == NULL_TREE + && TYPE_DOMAIN (type) != NULL_TREE + && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) + return true; + + return false; +} + /* Get the LEVEL of the strict_flex_array for the ARRAY_FIELD based on the values of attribute strict_flex_array and the flag_strict_flex_arrays. */ unsigned int diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 2d5f539..3e0eed0 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -904,6 +904,7 @@ extern tree fold_for_warn (tree); extern tree c_common_get_narrower (tree, int *); extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *); extern void c_common_finalize_early_debug (void); +extern bool c_flexible_array_member_type_p (const_tree); extern unsigned int c_strict_flex_array_level_of (tree); extern bool c_option_is_from_cpp_diagnostics (int); extern tree c_hardbool_type_attr_1 (tree, tree *, tree *); diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 9f7d55c..64924b8 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -5303,19 +5303,6 @@ set_array_declarator_inner (struct c_declarator *decl, return decl; } -/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ -static bool -flexible_array_member_type_p (const_tree type) -{ - if (TREE_CODE (type) == ARRAY_TYPE - && TYPE_SIZE (type) == NULL_TREE - && TYPE_DOMAIN (type) != NULL_TREE - && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) - return true; - - return false; -} - /* Determine whether TYPE is a one-element array type "[1]". */ static bool one_element_array_type_p (const_tree type) @@ -5353,7 +5340,7 @@ add_flexible_array_elts_to_size (tree decl, tree init) elt = CONSTRUCTOR_ELTS (init)->last ().value; type = TREE_TYPE (elt); - if (flexible_array_member_type_p (type)) + if (c_flexible_array_member_type_p (type)) { complete_array_type (&type, elt, false); /* For a structure, add the size of the initializer to the DECL's @@ -9340,7 +9327,7 @@ is_flexible_array_member_p (bool is_last_field, bool is_zero_length_array = zero_length_array_type_p (TREE_TYPE (x)); bool is_one_element_array = one_element_array_type_p (TREE_TYPE (x)); - bool is_flexible_array = flexible_array_member_type_p (TREE_TYPE (x)); + bool is_flexible_array = c_flexible_array_member_type_p (TREE_TYPE (x)); unsigned int strict_flex_array_level = c_strict_flex_array_level_of (x); @@ -9410,6 +9397,55 @@ 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. */ + +static void +verify_counted_by_attribute (tree struct_type, tree field_decl) +{ + tree attr_counted_by = lookup_attribute ("counted_by", + DECL_ATTRIBUTES (field_decl)); + + if (!attr_counted_by) + return; + + /* If there is an counted_by attribute attached to the field, + verify it. */ + + tree fieldname = TREE_VALUE (TREE_VALUE (attr_counted_by)); + + /* Verify the argument of the attrbute is a valid field of the + containing structure. */ + + tree counted_by_field = lookup_field (struct_type, fieldname); + + /* Error when the field is not found in the containing structure. */ + if (!counted_by_field) + error_at (DECL_SOURCE_LOCATION (field_decl), + "argument %qE to the %qE attribute is not a field declaration" + " in the same structure as %qD", fieldname, + (get_attribute_name (attr_counted_by)), + 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 %qE attribute is not a field declaration" + " with an integer type", fieldname, + (get_attribute_name (attr_counted_by))); + + } + + return; +} + /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T. LOC is the location of the RECORD_TYPE or UNION_TYPE's definition. FIELDLIST is a chain of FIELD_DECL nodes for the fields. @@ -9470,6 +9506,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; for (x = fieldlist; x; x = DECL_CHAIN (x)) { /* Whether this field is the last field of the structure or union. @@ -9530,7 +9567,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, DECL_PACKED (x) = 1; /* Detect flexible array member in an invalid context. */ - if (flexible_array_member_type_p (TREE_TYPE (x))) + if (c_flexible_array_member_type_p (TREE_TYPE (x))) { if (TREE_CODE (t) == UNION_TYPE) pedwarn (DECL_SOURCE_LOCATION (x), OPT_Wpedantic, @@ -9545,6 +9582,12 @@ 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))) + counted_by_fam_field = x; } if (pedantic && TREE_CODE (t) == RECORD_TYPE @@ -9559,7 +9602,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, when x is an array and is the last field. */ if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE) TYPE_INCLUDES_FLEXARRAY (t) - = is_last_field && flexible_array_member_type_p (TREE_TYPE (x)); + = is_last_field && c_flexible_array_member_type_p (TREE_TYPE (x)); /* Recursively set TYPE_INCLUDES_FLEXARRAY for the context of x, t when x is an union or record and is the last field. */ else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))) @@ -9816,6 +9859,9 @@ 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); + return t; } diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 22b0009..531a7e8 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -777,6 +777,7 @@ extern struct c_expr convert_lvalue_to_rvalue (location_t, struct c_expr, extern tree decl_constant_value_1 (tree, bool); extern void mark_exp_read (tree); 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); extern tree build_array_ref (location_t, tree, tree); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 4893480..80ed599 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -101,7 +101,6 @@ static bool function_types_compatible_p (const_tree, const_tree, struct comptypes_data *); static bool type_lists_compatible_p (const_tree, const_tree, struct comptypes_data *); -static tree lookup_field (tree, tree); static int convert_arguments (location_t, vec<location_t>, tree, vec<tree, va_gc> *, vec<tree, va_gc> *, tree, tree); @@ -1691,6 +1690,38 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2, && st2 && TREE_CODE (st2) == INTEGER_CST && !tree_int_cst_equal (st1, st2)) return false; + + tree counted_by1 = lookup_attribute ("counted_by", + DECL_ATTRIBUTES (s1)); + tree counted_by2 = lookup_attribute ("counted_by", + DECL_ATTRIBUTES (s2)); + /* If there is no counted_by attribute for both fields. */ + if (!counted_by1 && !counted_by2) + continue; + + /* If only one field has counted_by attribute. */ + if ((counted_by1 && !counted_by2) + || (!counted_by1 && counted_by2)) + return false; + + /* Now both s1 and s2 have counted_by attributes, check + whether they are the same. */ + + tree counted_by_field1 + = lookup_field (t1, TREE_VALUE (TREE_VALUE (counted_by1))); + tree counted_by_field2 + = lookup_field (t2, TREE_VALUE (TREE_VALUE (counted_by2))); + + gcc_assert (counted_by_field1 && counted_by_field2); + + while (TREE_CHAIN (counted_by_field1)) + counted_by_field1 = TREE_CHAIN (counted_by_field1); + while (TREE_CHAIN (counted_by_field2)) + counted_by_field2 = TREE_CHAIN (counted_by_field2); + + if (DECL_NAME (TREE_VALUE (counted_by_field1)) + != DECL_NAME (TREE_VALUE (counted_by_field2))) + return false; } return true; @@ -2449,8 +2480,8 @@ default_conversion (tree exp) the component is embedded within (nested) anonymous structures or unions, the list steps down the chain to the component. */ -static tree -lookup_field (tree type, tree component) +tree +lookup_field (const_tree type, tree component) { tree field; diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 00449bd..799a365 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -7788,6 +7788,74 @@ align them on any target. The @code{aligned} attribute can also be used for functions (@pxref{Common Function Attributes}.) +@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. +GCC may use this information to improve detection of object size information +for such structures and provide better results in compile-time diagnostics +and runtime features like the array bound sanitizer and +the @code{__builtin_dynamic_object_size}. + +For instance, the following code: + +@smallexample +struct P @{ + size_t count; + char other; + char array[] __attribute__ ((counted_by (count))); +@} *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. + +The field that represents the number of the elements should have an +integer type. Otherwise, the compiler reports an error and ignores +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. + +An explicit @code{counted_by} annotation defines a relationship between +two objects, @code{p->array} and @code{p->count}, and there are the +following requirementthat on the relationship between this pair: + +@itemize @bullet +@item +@code{p->count} must be initialized before the first reference to +@code{p->array}; + +@item +@code{p->array} has @emph{at least} @code{p->count} number of elements +available all the time. This relationship must hold even after any of +these related objects are updated during the program. +@end itemize + +It's the user's responsibility to make sure the above requirements to +be kept all the time. Otherwise the compiler reports warnings, +at the same time, the results of the array bound sanitizer and the +@code{__builtin_dynamic_object_size} is undefined. + +One important feature of the attribute is, a reference to the flexible +array member field uses the latest value assigned to the field that +represents the number of the elements before that reference. For example, + +@smallexample + p->count = val1; + p->array[20] = 0; // ref1 to p->array + p->count = val2; + p->array[30] = 0; // ref2 to p->array +@end smallexample + +@noindent +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}. + @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-7.c b/gcc/testsuite/gcc.dg/flex-array-counted-by-7.c new file mode 100644 index 0000000..fcb6f1b --- /dev/null +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-7.c @@ -0,0 +1,8 @@ +/* Testing the correct usage of attribute counted_by: _BitInt */ +/* { dg-do compile { target bitint } } */ +/* { dg-options "-O2 -std=c23" } */ + +struct trailing_array { + _BitInt(24) count; + int array[] __attribute ((counted_by (count))); +}; diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by-8.c b/gcc/testsuite/gcc.dg/flex-array-counted-by-8.c new file mode 100644 index 0000000..058d58f --- /dev/null +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-8.c @@ -0,0 +1,127 @@ + /* Testing the correct usage of attribute counted_by 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/flex-array-counted-by.c b/gcc/testsuite/gcc.dg/flex-array-counted-by.c new file mode 100644 index 0000000..e8b54c2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by.c @@ -0,0 +1,62 @@ +/* Testing the correct usage of attribute counted_by. */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +#include <wchar.h> + +int size; +int x __attribute ((counted_by (size))); /* { dg-error "attribute is not allowed for a non-field declaration" } */ + +struct trailing { + int count; + int field __attribute ((counted_by (count))); /* { dg-error "attribute is not allowed for a non-array field" } */ +}; + +struct trailing_1 { + int count; + int array_1[0] __attribute ((counted_by (count))); /* { dg-error "attribute is not allowed for a non-flexible array member field" } */ +}; + +int count; +struct trailing_array_2 { + int count; + int array_2[] __attribute ((counted_by ("count"))); /* { dg-error "argument is not an identifier" } */ +}; + +struct trailing_array_3 { + int other; + int array_3[] __attribute ((counted_by (L"count"))); /* { dg-error "argument is not an identifier" } */ +}; + +struct trailing_array_4 { + int other; + int array_4[] __attribute ((counted_by (count))); /* { dg-error "attribute is not a field declaration in the same structure as" } */ +}; + +int count; +struct trailing_array_5 { + float count; + int array_5[] __attribute ((counted_by (count))); /* { dg-error "attribute is not a field declaration with an integer type" } */ +}; + +struct trailing_array_6 { + int count; + int array_6[] __attribute ((counted_by (count))) __attribute ((counted_by (count))); +}; + +struct trailing_array_7 { + int count1; + int count2; + int array_7[] __attribute ((counted_by (count1))) __attribute ((counted_by (count2))); /* { dg-error "conflicts with previous declaration" } */ +}; + +struct trailing_array_8 { + _Bool count; + int array_8[] __attribute ((counted_by (count))); +}; + +enum week {Mon, Tue, Wed}; +struct trailing_array_9 { + enum week days; + int array_9[] __attribute ((counted_by (days))); +}; |