Hi! The following testcases ICE in various ways because of the interaction between attributes and C/C++ c*_build_qualified_type behavior on array types and how they affect TYPE_CANONICAL. For array types, C/C++ moves qualifiers to the element type, but when a cv qualified array build that way has an attribute applied to it, we call build_type_attribute_qual_variant and that doesn't have that handling and builds non-qualified version of the array type with qualified element type and puts it as TYPE_CANONICAL of the type with attribute which is a distinct type copy.
The following patch adds a langhook, so that even build_type_attribute_qual_variant uses for C/C++ for array types c*_build_qualified_type. There has been already a related langhook lang_hooks.types.copy_lang_qualifiers used solely for C++, so instead of adding another langhook this adds a combined langhook for those two, where C can handle array types specially and otherwise build_qualified_type, while C++ ditto + do the function/method type modifiers propagation as well. Unfortunately there is a terrible array_as_string hack used by some of the middle-end warnings which creates some array type with sometimes an artificial attribute and then has hacks in the c-family type printing to tweak the printed form, and this hack relies on the previous behavior of build_type_attribute_qual_variant where it even for C/C++ kept element type quals unmodified and added normally invalid quals on the array type itself. The patch right now temporarily overrides the langhook to the generic version, so it keeps working (and renames the attribute from "array" to "array " to make it clear it is internal attribute users can't specify even in vendor attributes). Though, I wonder if maybe it wouldn't be better to simply build the array type type = build_array_type (eltype, index_type); as now, and instead of the build_type_attribute_qual_variant call copy_node, prepend TYPE_ATTRIBUTES, change quals on the copy, print it and then ggc_free it, I think such types don't deserve to be cached in variant type lists etc. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? Or shall I go the copy_node way for array_as_string instead? 2025-12-18 Jakub Jelinek <[email protected]> PR c/101312 gcc/ * langhooks.h (struct lang_hooks_for_types): Remove copy_lang_qualifiers. Add build_lang_qualified_type. * langhooks.cc (lhd_build_lang_qualified_type): New function. * langhooks-def.h (lhd_build_lang_qualified_type): Declare. (LANG_HOOKS_COPY_LANG_QUALIFIERS): Remove. (LANG_HOOKS_BUILD_LANG_QUALIFIED_TYPE): Add. (LANG_HOOKS_FOR_TYPES_INITIALIZER): Use LANG_HOOKS_BUILD_LANG_QUALIFIED_TYPE instead of LANG_HOOKS_COPY_LANG_QUALIFIERS. * attribs.cc (build_type_attribute_qual_variant): Use lang_hooks.types.build_lang_qualified_type instead of build_qualified_type and/or build_qualified_type with optional lang_hooks.types.copy_lang_qualifiers call. (attr_access::array_as_string): Use "array " attribute instead of "array". Temporarily override lang_hooks.types.build_lang_qualified_type to lhd_build_lang_qualified_type as the hack relies on that behavior. gcc/c-family/ * c-pretty-print.cc (c_pretty_printer::direct_abstract_declarator): Look up "array " attribute instead of "array". gcc/c/ * c-tree.h (c_build_lang_qualified_type): Declare. * c-objc-common.h (LANG_HOOKS_BUILD_LANG_QUALIFIED_TYPE): Define. * c-objc-common.cc (c_build_lang_qualified_type): New function. gcc/cp/ * cp-tree.h (cxx_build_lang_qualified_type): Declare. * cp-objcp-common.h (LANG_HOOKS_COPY_LANG_QUALIFIERS): Remove. (LANG_HOOKS_BUILD_LANG_QUALIFIED_TYPE): Define. * tree.cc (cxx_build_lang_qualified_type): New function. gcc/testsuite/ * c-c++-common/pr101312-1.c: New test. * c-c++-common/pr101312-2.c: New test. --- gcc/langhooks.h.jj 2025-07-27 17:19:23.607127969 +0200 +++ gcc/langhooks.h 2025-12-18 12:57:10.780239979 +0100 @@ -134,9 +134,10 @@ struct lang_hooks_for_types FUNCTION_TYPE or METHOD_TYPE. */ bool (*type_hash_eq) (const_tree, const_tree); - /* If non-NULL, return TYPE1 with any language-specific modifiers copied from - TYPE2. */ - tree (*copy_lang_qualifiers) (const_tree, const_tree); + /* Return a version of the TYPE, qualified as indicated by the + TYPE_QUALS in a language-specific way, if one exists, otherwise create it. + If OTYPE is non-NULL, copy extra language modifiers from it too. */ + tree (*build_lang_qualified_type) (tree, tree, int); /* Return TRUE if TYPE uses a hidden descriptor and fills in information for the debugger about the array bounds, strides, etc. */ --- gcc/langhooks.cc.jj 2025-10-11 22:30:51.527481745 +0200 +++ gcc/langhooks.cc 2025-12-18 12:19:41.234383962 +0100 @@ -202,6 +202,16 @@ lhd_register_builtin_type (tree ARG_UNUS { } +/* Return a version of the TYPE, qualified as indicated by the + TYPE_QUALS, if one exists. If no qualified version exists yet, + creates it and returns it. */ +tree +lhd_build_lang_qualified_type (tree type, tree ARG_UNUSED (otype), + int type_quals) +{ + return build_qualified_type (type, type_quals); +} + /* Invalid use of an incomplete type. */ void lhd_incomplete_type_error (location_t ARG_UNUSED (loc), --- gcc/langhooks-def.h.jj 2025-07-27 17:19:23.607127969 +0200 +++ gcc/langhooks-def.h 2025-12-18 12:17:28.864684315 +0100 @@ -63,6 +63,7 @@ extern tree lhd_type_for_size (unsigned extern void lhd_incomplete_type_error (location_t, const_tree, const_tree); extern tree lhd_type_promotes_to (tree); extern void lhd_register_builtin_type (tree, const char *); +extern tree lhd_build_lang_qualified_type (tree, tree, int); extern bool lhd_decl_ok_for_sibcall (const_tree); extern size_t lhd_tree_size (enum tree_code); extern HOST_WIDE_INT lhd_to_target_charset (HOST_WIDE_INT); @@ -212,7 +213,7 @@ extern tree lhd_unit_size_without_reusab #define LANG_HOOKS_OMP_FIRSTPRIVATIZE_TYPE_SIZES \ lhd_omp_firstprivatize_type_sizes #define LANG_HOOKS_TYPE_HASH_EQ NULL -#define LANG_HOOKS_COPY_LANG_QUALIFIERS NULL +#define LANG_HOOKS_BUILD_LANG_QUALIFIED_TYPE lhd_build_lang_qualified_type #define LANG_HOOKS_GET_ARRAY_DESCR_INFO NULL #define LANG_HOOKS_GET_SUBRANGE_BOUNDS NULL #define LANG_HOOKS_GET_TYPE_BIAS NULL @@ -240,7 +241,7 @@ extern tree lhd_unit_size_without_reusab LANG_HOOKS_TYPE_MAX_SIZE, \ LANG_HOOKS_OMP_FIRSTPRIVATIZE_TYPE_SIZES, \ LANG_HOOKS_TYPE_HASH_EQ, \ - LANG_HOOKS_COPY_LANG_QUALIFIERS, \ + LANG_HOOKS_BUILD_LANG_QUALIFIED_TYPE, \ LANG_HOOKS_GET_ARRAY_DESCR_INFO, \ LANG_HOOKS_GET_SUBRANGE_BOUNDS, \ LANG_HOOKS_GET_TYPE_BIAS, \ --- gcc/attribs.cc.jj 2025-12-08 23:28:55.081950885 +0100 +++ gcc/attribs.cc 2025-12-18 13:16:24.200295574 +0100 @@ -1322,14 +1322,15 @@ build_type_attribute_qual_variant (tree warning (OPT_Wattributes, "ignoring attributes applied to %qT after definition", TYPE_MAIN_VARIANT (ttype)); - return build_qualified_type (ttype, quals); + return lang_hooks.types.build_lang_qualified_type (ttype, NULL_TREE, + quals); } - ttype = build_qualified_type (ttype, TYPE_UNQUALIFIED); - if (lang_hooks.types.copy_lang_qualifiers - && otype != TYPE_MAIN_VARIANT (otype)) - ttype = (lang_hooks.types.copy_lang_qualifiers - (ttype, TYPE_MAIN_VARIANT (otype))); + tree mtype = NULL_TREE; + if (otype != TYPE_MAIN_VARIANT (otype)) + mtype = TYPE_MAIN_VARIANT (otype); + ttype = lang_hooks.types.build_lang_qualified_type (ttype, mtype, + TYPE_UNQUALIFIED); tree dtype = ntype = build_distinct_type_copy (ttype); @@ -1354,13 +1355,15 @@ build_type_attribute_qual_variant (tree else if (TYPE_CANONICAL (ntype) == ntype) TYPE_CANONICAL (ntype) = TYPE_CANONICAL (ttype); - ttype = build_qualified_type (ntype, quals); - if (lang_hooks.types.copy_lang_qualifiers - && otype != TYPE_MAIN_VARIANT (otype)) - ttype = lang_hooks.types.copy_lang_qualifiers (ttype, otype); + if (otype != TYPE_MAIN_VARIANT (otype)) + mtype = otype; + else + mtype = NULL_TREE; + ttype = lang_hooks.types.build_lang_qualified_type (ntype, mtype, quals); } else if (TYPE_QUALS (ttype) != quals) - ttype = build_qualified_type (ttype, quals); + ttype = lang_hooks.types.build_lang_qualified_type (ttype, NULL_TREE, + quals); return ttype; } @@ -2660,12 +2663,24 @@ attr_access::array_as_string (tree type) [*] is represented the same as [0] this hack only works for the most significant bound like static and the others are rendered as [0]. */ - arat = build_tree_list (get_identifier ("array"), flag); + arat = build_tree_list (get_identifier ("array "), flag); } const int quals = TYPE_QUALS (type); type = build_array_type (eltype, index_type); + /* This terrible hack relies on + lang_hooks.types.build_lang_qualified_type not honoring the + C/C++ FE array handling, so that quals from the pointed type + stay unmodified on element type and quals from the pointer + type are installed without errors on the array type. */ + auto save_build_lang_qualified_type + = lang_hooks.types.build_lang_qualified_type; + extern tree lhd_build_lang_qualified_type (tree, tree, int); + lang_hooks.types.build_lang_qualified_type + = lhd_build_lang_qualified_type; type = build_type_attribute_qual_variant (type, arat, quals); + lang_hooks.types.build_lang_qualified_type + = save_build_lang_qualified_type; } /* Format the type using the current pretty printer. The generic tree --- gcc/c-family/c-pretty-print.cc.jj 2025-09-23 15:22:09.984552905 +0200 +++ gcc/c-family/c-pretty-print.cc 2025-12-18 13:05:30.823630822 +0100 @@ -634,7 +634,7 @@ c_pretty_printer::direct_abstract_declar add_space = true; } - if (tree arr = lookup_attribute ("array", TYPE_ATTRIBUTES (t))) + if (tree arr = lookup_attribute ("array ", TYPE_ATTRIBUTES (t))) { if (TREE_VALUE (arr)) { --- gcc/c/c-tree.h.jj 2025-11-27 21:34:24.276458859 +0100 +++ gcc/c/c-tree.h 2025-12-18 12:14:13.951071552 +0100 @@ -776,6 +776,7 @@ extern tree c_finish_bc_name (location_t /* in c-objc-common.cc */ extern bool c_objc_common_init (void); +extern tree c_build_lang_qualified_type (tree, tree, int); extern bool c_missing_noreturn_ok_p (tree); extern bool c_warn_unused_global_decl (const_tree); extern void c_initialize_diagnostics (diagnostics::context *); --- gcc/c/c-objc-common.h.jj 2025-05-30 14:37:31.489079636 +0200 +++ gcc/c/c-objc-common.h 2025-12-16 22:26:49.120983402 +0100 @@ -59,6 +59,8 @@ extern void c_register_features (); #define LANG_HOOKS_PRINT_IDENTIFIER c_print_identifier #undef LANG_HOOKS_TYPES_COMPATIBLE_P #define LANG_HOOKS_TYPES_COMPATIBLE_P c_types_compatible_p +#undef LANG_HOOKS_BUILD_LANG_QUALIFIED_TYPE +#define LANG_HOOKS_BUILD_LANG_QUALIFIED_TYPE c_build_lang_qualified_type #undef LANG_HOOKS_MISSING_NORETURN_OK_P #define LANG_HOOKS_MISSING_NORETURN_OK_P c_missing_noreturn_ok_p #undef LANG_HOOKS_BLOCK_MAY_FALLTHRU --- gcc/c/c-objc-common.cc.jj 2025-09-23 15:22:09.986552878 +0200 +++ gcc/c/c-objc-common.cc 2025-12-18 12:14:00.347307960 +0100 @@ -70,6 +70,17 @@ c_register_features () } } +/* Langhook for building qualified types. */ + +tree +c_build_lang_qualified_type (tree type, tree, int type_quals) +{ + if (TREE_CODE (type) == ARRAY_TYPE) + return c_build_qualified_type (type, type_quals); + else + return build_qualified_type (type, type_quals); +} + bool c_missing_noreturn_ok_p (tree decl) { --- gcc/cp/cp-tree.h.jj 2025-12-16 22:26:28.436420328 +0100 +++ gcc/cp/cp-tree.h 2025-12-18 12:12:07.605267206 +0100 @@ -7370,6 +7370,7 @@ extern tmpl_spec_kind current_tmpl_spec_ extern tree cxx_builtin_function (tree decl); extern tree cxx_builtin_function_ext_scope (tree decl); extern tree cxx_simulate_builtin_function_decl (tree); +extern tree cxx_build_lang_qualified_type (tree, tree, int); extern tree check_elaborated_type_specifier (enum tag_types, tree, bool); extern void warn_extern_redeclared_static (tree, tree); extern tree cxx_comdat_group (tree); --- gcc/cp/cp-objcp-common.h.jj 2025-05-30 14:32:05.168473440 +0200 +++ gcc/cp/cp-objcp-common.h 2025-12-16 22:26:49.122248232 +0100 @@ -112,8 +112,8 @@ extern tree cxx_simulate_record_decl (lo cxx_simulate_builtin_function_decl #undef LANG_HOOKS_TYPE_HASH_EQ #define LANG_HOOKS_TYPE_HASH_EQ cxx_type_hash_eq -#undef LANG_HOOKS_COPY_LANG_QUALIFIERS -#define LANG_HOOKS_COPY_LANG_QUALIFIERS cxx_copy_lang_qualifiers +#undef LANG_HOOKS_BUILD_LANG_QUALIFIED_TYPE +#define LANG_HOOKS_BUILD_LANG_QUALIFIED_TYPE cxx_build_lang_qualified_type #undef LANG_HOOKS_MISSING_NORETURN_OK_P #define LANG_HOOKS_MISSING_NORETURN_OK_P cp_missing_noreturn_ok_p #undef LANG_HOOKS_BLOCK_MAY_FALLTHRU --- gcc/cp/tree.cc.jj 2025-11-24 18:30:41.669332263 +0100 +++ gcc/cp/tree.cc 2025-12-18 12:57:31.851426620 +0100 @@ -1390,6 +1390,21 @@ c_build_qualified_type (tree type, int t return cp_build_qualified_type (type, type_quals); } +/* Implementation of the build_lang_qualified_type langhook. */ +tree +cxx_build_lang_qualified_type (tree type, tree otype, int type_quals) +{ + if (TREE_CODE (type) == ARRAY_TYPE) + return cp_build_qualified_type (type, type_quals); + else + { + tree ret = build_qualified_type (type, type_quals); + if (otype) + ret = cxx_copy_lang_qualifiers (ret, otype); + return ret; + } +} + /* Make a variant of TYPE, qualified with the TYPE_QUALS. Handles arrays correctly. In particular, if TYPE is an array of T's, and --- gcc/testsuite/c-c++-common/pr101312-1.c.jj 2025-12-16 22:26:49.123005327 +0100 +++ gcc/testsuite/c-c++-common/pr101312-1.c 2025-12-16 22:26:49.123005327 +0100 @@ -0,0 +1,4 @@ +/* PR c/101312 */ +/* { dg-do compile } */ + +volatile int a[1] __attribute__((may_alias)); --- gcc/testsuite/c-c++-common/pr101312-2.c.jj 2025-12-16 22:26:49.123089596 +0100 +++ gcc/testsuite/c-c++-common/pr101312-2.c 2025-12-16 22:26:49.123089596 +0100 @@ -0,0 +1,5 @@ +/* PR c/101312 */ +/* { dg-do compile } */ +/* { dg-options "-g" } */ + +volatile int a[1] __attribute__((may_alias)); Jakub
