https://gcc.gnu.org/g:3d2a91a3767982dde5a37abf45c12c08d4fdbf41

commit r16-7102-g3d2a91a3767982dde5a37abf45c12c08d4fdbf41
Author: Jakub Jelinek <[email protected]>
Date:   Wed Jan 28 09:50:26 2026 +0100

    c, c++: Use c*_build_qualified_type instead of build_qualified_type from 
within build_type_attribute_qual_variant [PR101312]
    
    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 stops using build_type_attribute_qual_variant
    for that and instead uses copy_node on the type and adjusts the quals and
    adds the attribute to the copy and then ggc_frees it.  Also it renames the
    attribute from "array" to "array " to make it clear it is internal
    attribute users can't specify even in vendor attributes.
    
    2026-01-28  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".  If attribute has been created or intended quals differ
            from quals of build_array_type, use copy_node and adjust quals and
            attributes on the copy, print and then ggc_free.
    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.

Diff:
---
 gcc/attribs.cc                          | 55 +++++++++++++++++++++++++--------
 gcc/c-family/c-pretty-print.cc          |  2 +-
 gcc/c/c-objc-common.cc                  | 11 +++++++
 gcc/c/c-objc-common.h                   |  2 ++
 gcc/c/c-tree.h                          |  1 +
 gcc/cp/cp-objcp-common.h                |  4 +--
 gcc/cp/cp-tree.h                        |  1 +
 gcc/cp/tree.cc                          | 21 +++++++++++++
 gcc/langhooks-def.h                     |  5 +--
 gcc/langhooks.cc                        | 10 ++++++
 gcc/langhooks.h                         |  7 +++--
 gcc/testsuite/c-c++-common/pr101312-1.c |  4 +++
 gcc/testsuite/c-c++-common/pr101312-2.c |  5 +++
 13 files changed, 107 insertions(+), 21 deletions(-)

diff --git a/gcc/attribs.cc b/gcc/attribs.cc
index db9a5c125ac8..942273757010 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -1322,14 +1322,15 @@ build_type_attribute_qual_variant (tree otype, tree 
attribute, int quals)
          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 otype, tree 
attribute, int quals)
       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;
 }
@@ -2652,6 +2655,7 @@ std::string
 attr_access::array_as_string (tree type) const
 {
   std::string typstr;
+  bool free_type = false;
 
   if (type == error_mark_node)
     return std::string ();
@@ -2692,12 +2696,35 @@ attr_access::array_as_string (tree type) const
             [*] 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);
-      type = build_type_attribute_qual_variant (type, arat, quals);
+      if (arat || TYPE_QUALS (type) != quals)
+       {
+         /* We create a new array type which is only used during
+            printing.  Can't use build_type_attribute_qual_variant
+            because it might with some lang hooks e.g. try to push
+            qualifiers to the element type, while for the printing
+            this wants the element type qualifiers to be unmodified
+            and ARRAY_TYPE qualifiers to be a copy of the pointer
+            type qualifiers.  Furthermore, a type with somtimes
+            weird qualifiers or artificial attribute should be
+            freed right after the use.  */
+         type = copy_node (type);
+         if (arat)
+           {
+             TREE_CHAIN (arat) = TYPE_ATTRIBUTES (type);
+             TYPE_ATTRIBUTES (type) = arat;
+           }
+         TYPE_READONLY (type) = (quals & TYPE_QUAL_CONST) != 0;
+         TYPE_VOLATILE (type) = (quals & TYPE_QUAL_VOLATILE) != 0;
+         TYPE_ATOMIC (type) = (quals & TYPE_QUAL_ATOMIC) != 0;
+         TYPE_RESTRICT (type) = (quals & TYPE_QUAL_RESTRICT) != 0;
+         TYPE_ADDR_SPACE (type) = DECODE_QUAL_ADDR_SPACE (quals);
+         free_type = true;
+       }
     }
 
   /* Format the type using the current pretty printer.  The generic tree
@@ -2705,6 +2732,8 @@ attr_access::array_as_string (tree type) const
   std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
   pp_printf (pp.get (), "%qT", type);
   typstr = pp_formatted_text (pp.get ());
+  if (free_type)
+    ggc_free (type);
 
   return typstr;
 }
diff --git a/gcc/c-family/c-pretty-print.cc b/gcc/c-family/c-pretty-print.cc
index d3d6c8bf8758..b084163e6a35 100644
--- a/gcc/c-family/c-pretty-print.cc
+++ b/gcc/c-family/c-pretty-print.cc
@@ -634,7 +634,7 @@ c_pretty_printer::direct_abstract_declarator (tree t)
          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))
            {
diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
index 38e88733b509..49782d4eec8e 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -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)
 {
diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h
index e103646b67e8..8a67f6d99aa7 100644
--- a/gcc/c/c-objc-common.h
+++ b/gcc/c/c-objc-common.h
@@ -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
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 167f50e900c7..a63d1b674d64 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -780,6 +780,7 @@ extern tree c_finish_bc_name (location_t, tree, bool);
 
 /* 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 *);
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index 2999b36a5ddc..441a250e8d3f 100644
--- a/gcc/cp/cp-objcp-common.h
+++ b/gcc/cp/cp-objcp-common.h
@@ -112,8 +112,8 @@ extern tree cxx_simulate_record_decl (location_t, const 
char *,
   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
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index c98c8e3eb746..19a426bfc2c0 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7540,6 +7540,7 @@ extern tmpl_spec_kind current_tmpl_spec_kind      (int);
 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);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 55afa3efb273..2f386e16b9cc 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -1470,6 +1470,27 @@ c_build_qualified_type (tree type, int type_quals, tree 
/* orig_qual_type */,
   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)
+{
+  /* Only handle ARRAY_TYPEs using cp_build_qualified_type, so that
+     quals are pushed to the element type.  That is needed for PR101312.
+     For other types just keep using build_qualified_type with
+     cxx_copy_lang_qualifiers if needed, otherwise we can run into
+     issues with FUNCTION_TYPEs with cv-qualifier-seq vs. type-specifier,
+     or REFERENCE_TYPE which will error on certain 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
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index fc409ec08b96..1603bd8e63e0 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -63,6 +63,7 @@ extern tree lhd_type_for_size (unsigned precision, int 
unsignedp);
 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_reusable_padding (tree);
 #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_reusable_padding (tree);
   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, \
diff --git a/gcc/langhooks.cc b/gcc/langhooks.cc
index e2c7735596e0..d17d60737ea7 100644
--- a/gcc/langhooks.cc
+++ b/gcc/langhooks.cc
@@ -202,6 +202,16 @@ lhd_register_builtin_type (tree ARG_UNUSED (type),
 {
 }
 
+/* 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),
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index 6eb5c1602f83..47996ff19137 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -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.  */
diff --git a/gcc/testsuite/c-c++-common/pr101312-1.c 
b/gcc/testsuite/c-c++-common/pr101312-1.c
new file mode 100644
index 000000000000..8d312db140ae
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/pr101312-1.c
@@ -0,0 +1,4 @@
+/* PR c/101312 */
+/* { dg-do compile } */
+
+volatile int a[1] __attribute__((may_alias));
diff --git a/gcc/testsuite/c-c++-common/pr101312-2.c 
b/gcc/testsuite/c-c++-common/pr101312-2.c
new file mode 100644
index 000000000000..16d77941eafe
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/pr101312-2.c
@@ -0,0 +1,5 @@
+/* PR c/101312 */
+/* { dg-do compile } */
+/* { dg-options "-g" } */
+
+volatile int a[1] __attribute__((may_alias));

Reply via email to