Attribute copy can be invoked with an expression argument to copy from the expression's type. However, it must avoid passing the expression as the last (optional) argument to decl_attributes because the function is only prepared to deal with DECLs and types.
The attached patch passes null as the last argument in these situations instead. In addition, it makes more robust the handling of expressions of pointers to function type (a subtle bug here was exposed by testing of additional cases beyond the one in the report). Tested on x86_64-linux. I plan to commit the patch in the next day or so unless there are concerns/suggestions. Martin
PR c++/94346 - [9/10 Regression] ICE due to handle_copy_attribute since r9-3982 gcc/c-family/ChangeLog: PR c++/94346 * c-attribs.c (handle_copy_attribute): Avoid passing expressions to decl_attributes. Make handling of different kinds of entities more robust. gcc/c-c++-common/ChangeLog: PR c++/94346 * c-c++-common/attr-copy.c: New test. diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index f30158a258b..93e740853a9 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -2526,17 +2526,21 @@ handle_copy_attribute (tree *node, tree name, tree args, && !FUNCTION_POINTER_TYPE_P (TREE_TYPE (ref))) ref = TREE_TYPE (ref); + tree reftype = DECL_P (ref) || EXPR_P (ref) ? TREE_TYPE (ref) : ref; + if (DECL_P (decl)) { if ((VAR_P (decl) && (TREE_CODE (ref) == FUNCTION_DECL || (EXPR_P (ref) - && POINTER_TYPE_P (TREE_TYPE (ref)) - && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (ref)))))) + && POINTER_TYPE_P (reftype) + && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (reftype))))) || (TREE_CODE (decl) == FUNCTION_DECL && (VAR_P (ref) || (EXPR_P (ref) - && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (ref)))))) + && !FUNC_OR_METHOD_TYPE_P (reftype) + && (!POINTER_TYPE_P (reftype) + || !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (reftype))))))) { /* It makes no sense to try to copy function attributes to a variable, or variable attributes to a function. */ @@ -2606,15 +2610,11 @@ handle_copy_attribute (tree *node, tree name, tree args, /* Similarly, a function declared with attribute noreturn has it attached on to it, but a C11 _Noreturn function does not. */ - tree reftype = ref; if (DECL_P (ref) && TREE_THIS_VOLATILE (ref) - && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (reftype))) + && FUNC_OR_METHOD_TYPE_P (reftype)) TREE_THIS_VOLATILE (decl) = true; - if (DECL_P (ref) || EXPR_P (ref)) - reftype = TREE_TYPE (ref); - if (POINTER_TYPE_P (reftype)) reftype = TREE_TYPE (reftype); @@ -2623,9 +2623,10 @@ handle_copy_attribute (tree *node, tree name, tree args, tree attrs = TYPE_ATTRIBUTES (reftype); - /* Copy type attributes from REF to DECL. */ + /* Copy type attributes from REF to DECL. Pass in REF if it's a DECL + or a type but not if it's an expression. */ for (tree at = attrs; at; at = TREE_CHAIN (at)) - decl_attributes (node, at, flags, ref); + decl_attributes (node, at, flags, EXPR_P (ref) ? NULL_TREE : ref); return NULL_TREE; } diff --git a/gcc/testsuite/c-c++-common/attr-copy.c b/gcc/testsuite/c-c++-common/attr-copy.c new file mode 100644 index 00000000000..284088a8b97 --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-copy.c @@ -0,0 +1,43 @@ +/* PR c++/94346 - ICE due to handle_copy_attribute + { dg-do compile } + { dg-options "-Wall" } */ + +#define ATTR(...) __attribute__ ((__VA_ARGS__)) + +#if __cplusplus > 199711L +# define SA(expr) static_assert (expr, #expr) +#elif __cplusplus +# define SA(expr) \ + typedef __attribute__ ((unused)) char Assert[!(expr) ? -1 : 1] +#else +# define SA(expr) _Static_assert (expr, #expr) +#endif + +typedef struct ATTR (packed) A { ATTR (packed) unsigned bf: 1; } A; + +int bar (void); + +struct C +{ + char c; + ATTR (copy ((bar (), ((struct A *)(0))[0]))) int i; +}; + +/* Verify the attribute has been copied. */ +SA (__builtin_offsetof (struct C, i) == 1); + + + +/* Verify attribute copy can copy from the type a comma expression. */ +ATTR (alloc_size (1)) void* alloc1 (int); + +ATTR (copy ((bar (), alloc1))) void* alloc2 (int, int); + +ATTR (copy ((bar (), alloc1))) void alloc3 (int); /* { dg-warning "'alloc_size' attribute ignored on a function returning 'void'" } */ + + +typedef ATTR (alloc_size (1)) void* F (int); + +ATTR (copy ((bar (), (F*)0))) void* alloc4 (int, int); + +ATTR (copy ((bar (), (F*)0))) void alloc5 (int, int); /* { dg-warning "'alloc_size' attribute ignored on a function returning 'void'" } */