https://gcc.gnu.org/g:9eae9268e41463927c9383004e58708048ec379f

commit r15-4812-g9eae9268e41463927c9383004e58708048ec379f
Author: Martin Uecker <uec...@tugraz.at>
Date:   Tue Oct 22 23:25:00 2024 +0200

    c: detect variably-modified types [PR117145,PR117245,PR100420]
    
    This fixes two cases where variably-modified types were not recognized as
    such.  The first is when building composite types and the other when a type
    is reconstructed for the 'vector' attribute.  Construction of types in
    the C FE is reorganized to use c_build_* functions which are responsible for
    setting C_TYPE_VARIABLE_SIZE, C_TYPE_VARIABLY_MODIFIED and 
TYPE_TYPELESS_STORAGE
    based on the properties of the type itself and these replace all other logic
    elsewhere (e.g. in grokdeclarator).  A new 'c_reconstruct_complex_type' 
based
    on these functions is introduced which is called via a language hook when 
the
    'vector' attribute is processed (as for C++).
    
    One problem is are arrays of unspecified size 'T[*]' which were represented
    identically to zero-sized arrays but with C_TYPE_VARIABLE_SIZE set.  To 
avoid
    having to create distinct type copies for this, the representation was 
changed
    to make it a natural VLA by giving it an upper bound of '(0, 0)'.  This also
    then allows fixing of PR100420 where such arrays were printed as 'T[0]'.
    
    Finally, a new function 'c_verify_type' checks consistency of properties
    specific to C FE and is called when checking is on.
    
            PR c/117145
            PR c/117245
            PR c/100420
    
    gcc/c/ChangeLog:
            * c-decl.cc (c_build_pointer_type): Move to c-typeck.cc
            (grokdeclarator): Simplify logic.
            (match_builtin_function_types): Adapt.
            (push_decl): Adapt.
            (implicitly_declare): Adapt.
            (c_update_type_canonical): Adapt.
            (c_make_fname_decl): Adapt.
            (start_function): Adapt.
            * c-objc-common.h: Add LANG_HOOKS_RECONSTRUCT_COMPLEX_TYPE.
            * c-tree.h: Add prototypes.
            * c-typeck.cc (c_verify_type): New function.
            (c_set_type_bits). New function.
            (c_build_pointer_type): Moved from c-decl.cc.
            (c_build_pointer_type_for_mode): New function.
            (c_build_function_type): New function.
            (c_build_array_type): New function.
            (c_build_type_attribute_variant): New function.
            (c_reconstruct_complex_type): New function.
            (c_build_functype_attribute_variant): Renamed.
            (array_to_pointer_conversion): Simplify logic.
            (composite_type_internal): Simplify logic..
            (build_unary_op): Simplify logic..
            (comptypes_verify): Add checking assertions.
            (c_build_qualified_type): Add checking assertions.
            (c_build_function_call_vec): Adapt.
            (qualify_type): Adapt.
            (build_functype_attribute_variant): Adapt.
            (common_pointer_type): Adapt.
            (c_common_type): Adapt.
            (convert_for_assignment): Adapt.
            (type_or_builtin_type): Adapt.
            (build_access_with_size_for_counted_by): Adapt.
            (build_conditional_expr): Adapt.
            (build_modify_expr): Adapt.
            (build_binary_op): Adapt.
            (build_omp_array_section): Adapt.
            (handle_omp_array_sections): Adapt.
            (c_finish_omp_clauses): Adapt.
            * c-parser.cc (c_parser_typeof_specifier): Adapt.
            (c_parser_generic_selection): Adapt.
    
    gcc/c-family/ChangeLog:
            * c-pretty-print.cc (c_pretty_printer::direct_abstract_declarator):
            Detect arrays of unspecified size.
    
    gcc/testsuite/ChangeLog:
            * gcc.dg/c23-tag-composite-11.c: New test.
            * gcc.dg/Warray-parameter-4.c: Resolve xfails.
            * gcc.dg/Wvla-parameter-2.c: Resolve xfails.
            * gcc.dg/Wvla-parameter-3.c: Resolve xfails.
            * gcc.dg/pr117145-1.c: New test.
            * gcc.dg/pr117145-2.c: New test.
            * gcc.dg/pr117245.c: New test.

Diff:
---
 gcc/c-family/c-pretty-print.cc              |   6 +-
 gcc/c/c-decl.cc                             | 130 ++++-------
 gcc/c/c-objc-common.h                       |   2 +
 gcc/c/c-parser.cc                           |   4 +-
 gcc/c/c-tree.h                              |   7 +
 gcc/c/c-typeck.cc                           | 331 ++++++++++++++++++++++------
 gcc/testsuite/gcc.dg/Warray-parameter-4.c   |   6 +-
 gcc/testsuite/gcc.dg/Wvla-parameter-2.c     |  15 +-
 gcc/testsuite/gcc.dg/Wvla-parameter-3.c     |  11 +-
 gcc/testsuite/gcc.dg/c23-tag-composite-11.c |  27 +++
 gcc/testsuite/gcc.dg/pr117145-1.c           |  14 ++
 gcc/testsuite/gcc.dg/pr117145-2.c           |  10 +
 gcc/testsuite/gcc.dg/pr117245.c             |  17 ++
 13 files changed, 393 insertions(+), 187 deletions(-)

diff --git a/gcc/c-family/c-pretty-print.cc b/gcc/c-family/c-pretty-print.cc
index fadb9e3ecfef..810e48780492 100644
--- a/gcc/c-family/c-pretty-print.cc
+++ b/gcc/c-family/c-pretty-print.cc
@@ -690,7 +690,11 @@ c_pretty_printer::direct_abstract_declarator (tree t)
                        maxval = TREE_OPERAND (maxval, 0);
                    }
 
-                 expression (maxval);
+                 /* This covers unspecified bounds.  */
+                 if (TREE_CODE (maxval) == COMPOUND_EXPR)
+                   pp_string (this, "*");
+                 else
+                   expression (maxval);
                }
            }
          else if (TYPE_SIZE (t))
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 9cb33e0e9d36..42d329e4fd52 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -716,22 +716,9 @@ add_stmt (tree t)
 
   return t;
 }
-
-/* Build a pointer type using the default pointer mode.  */
 
-static tree
-c_build_pointer_type (tree to_type)
-{
-  addr_space_t as = to_type == error_mark_node? ADDR_SPACE_GENERIC
-                                             : TYPE_ADDR_SPACE (to_type);
-  machine_mode pointer_mode;
 
-  if (as != ADDR_SPACE_GENERIC || c_default_pointer_mode == VOIDmode)
-    pointer_mode = targetm.addr_space.pointer_mode (as);
-  else
-    pointer_mode = c_default_pointer_mode;
-  return build_pointer_type_for_mode (to_type, pointer_mode, false);
-}
+
 
 
 /* Return true if we will want to say something if a goto statement
@@ -1923,7 +1910,7 @@ match_builtin_function_types (tree newtype, tree oldtype,
       newargs = TREE_CHAIN (newargs);
     }
 
-  tree trytype = build_function_type (newrettype, tryargs);
+  tree trytype = c_build_function_type (newrettype, tryargs);
 
   /* Allow declaration to change transaction_safe attribute.  */
   tree oldattrs = TYPE_ATTRIBUTES (oldtype);
@@ -1936,7 +1923,7 @@ match_builtin_function_types (tree newtype, tree oldtype,
     oldattrs = tree_cons (get_identifier ("transaction_safe"),
                          NULL_TREE, oldattrs);
 
-  return build_type_attribute_variant (trytype, oldattrs);
+  return c_build_type_attribute_variant (trytype, oldattrs);
 }
 
 /* Subroutine of diagnose_mismatched_decls.  Check for function type
@@ -3397,9 +3384,9 @@ pushdecl (tree x)
              if (TREE_CODE (b_use->decl) == FUNCTION_DECL
                  && fndecl_built_in_p (b_use->decl))
                thistype
-                 = build_type_attribute_variant (thistype,
-                                                 TYPE_ATTRIBUTES
-                                                 (b_use->u.type));
+                 = c_build_type_attribute_variant (thistype,
+                                                   TYPE_ATTRIBUTES
+                                                   (b_use->u.type));
              TREE_TYPE (b_use->decl) = thistype;
            }
          return b_use->decl;
@@ -3497,8 +3484,8 @@ pushdecl (tree x)
          b->u.type = TREE_TYPE (b->decl);
          /* Propagate the type attributes to the decl.  */
          thistype
-           = build_type_attribute_variant (thistype,
-                                           TYPE_ATTRIBUTES (b->u.type));
+           = c_build_type_attribute_variant (thistype,
+                                             TYPE_ATTRIBUTES (b->u.type));
          TREE_TYPE (b->decl) = thistype;
          bind (name, b->decl, scope, /*invisible=*/false, /*nested=*/true,
                locus);
@@ -3896,9 +3883,9 @@ implicitly_declare (location_t loc, tree functionid)
            }
          if (fndecl_built_in_p (decl))
            {
-             newtype = build_type_attribute_variant (newtype,
-                                                     TYPE_ATTRIBUTES
-                                                     (TREE_TYPE (decl)));
+             newtype = c_build_type_attribute_variant (newtype,
+                                                       TYPE_ATTRIBUTES
+                                                       (TREE_TYPE (decl)));
              if (!comptypes (newtype, TREE_TYPE (decl)))
                {
                  auto_diagnostic_group d;
@@ -4838,8 +4825,8 @@ c_make_fname_decl (location_t loc, tree id, int type_dep)
   tree decl, type, init;
   size_t length = strlen (name);
 
-  type = build_array_type (char_type_node,
-                          build_index_type (size_int (length)));
+  type = c_build_array_type (char_type_node,
+                            build_index_type (size_int (length)));
   type = c_build_qualified_type (type, TYPE_QUAL_CONST);
 
   decl = build_decl (loc, VAR_DECL, id, type);
@@ -7224,8 +7211,6 @@ grokdeclarator (const struct c_declarator *declarator,
          array_parm_static = false;
        }
 
-      bool varmod = C_TYPE_VARIABLY_MODIFIED (type);
-
       switch (declarator->kind)
        {
        case cdk_attrs:
@@ -7507,31 +7492,20 @@ grokdeclarator (const struct c_declarator *declarator,
 
                /* ISO C99 Flexible array members are effectively
                   identical to GCC's zero-length array extension.  */
-               if (flexible_array_member || array_parm_vla_unspec_p)
-                 itype = build_range_type (sizetype, size_zero_node,
-                                           NULL_TREE);
+               if (flexible_array_member)
+                 itype = build_index_type (NULL_TREE);
              }
-           else if (decl_context == PARM)
+
+           if (array_parm_vla_unspec_p)
              {
-               if (array_parm_vla_unspec_p)
-                 {
-                   itype = build_range_type (sizetype, size_zero_node, 
NULL_TREE);
-                   size_varies = true;
-                 }
-             }
-           else if (decl_context == TYPENAME)
-             {
-               if (array_parm_vla_unspec_p)
-                 {
-                   /* C99 6.7.5.2p4 */
-                   warning (0, "%<[*]%> not in a declaration");
-                   /* We use this to avoid messing up with incomplete
-                      array types of the same type, that would
-                      otherwise be modified below.  */
-                   itype = build_range_type (sizetype, size_zero_node,
-                                             NULL_TREE);
-                   size_varies = true;
-                 }
+               /* C99 6.7.5.2p4 */
+               if (decl_context == TYPENAME)
+                 warning (0, "%<[*]%> not in a declaration");
+               /* Array of unspecified size.  */
+               tree upper = build2 (COMPOUND_EXPR, TREE_TYPE (size_zero_node),
+                                    integer_zero_node, size_zero_node);
+               itype = build_index_type (upper);
+               size_varies = true;
              }
 
            /* Complain about arrays of incomplete types.  */
@@ -7562,34 +7536,15 @@ grokdeclarator (const struct c_declarator *declarator,
               modify the shared type, so we gcc_assert (itype)
               below.  */
              {
-               /* Identify typeless storage as introduced in C2Y
-                  and supported also in earlier language modes.  */
-               bool typeless = (char_type_p (type)
-                                && !(type_quals & TYPE_QUAL_ATOMIC))
-                               || (AGGREGATE_TYPE_P (type)
-                                   && TYPE_TYPELESS_STORAGE (type));
-
                addr_space_t as = DECODE_QUAL_ADDR_SPACE (type_quals);
                if (!ADDR_SPACE_GENERIC_P (as) && as != TYPE_ADDR_SPACE (type))
-                 type = build_qualified_type (type,
-                                              ENCODE_QUAL_ADDR_SPACE (as));
-               type = build_array_type (type, itype, typeless);
+                 type = c_build_qualified_type (type,
+                                                ENCODE_QUAL_ADDR_SPACE (as));
+               type = c_build_array_type (type, itype);
              }
 
            if (type != error_mark_node)
              {
-               if (size_varies)
-                 {
-                   /* It is ok to modify type here even if itype is
-                      NULL: if size_varies, we're in a
-                      multi-dimensional array and the inner type has
-                      variable size, so the enclosing shared array type
-                      must too.  */
-                   if (size && TREE_CODE (size) == INTEGER_CST)
-                     type = build_distinct_type_copy (TYPE_MAIN_VARIANT 
(type));
-                   C_TYPE_VARIABLE_SIZE (type) = 1;
-                 }
-
                /* The GCC extension for zero-length arrays differs from
                   ISO flexible array members in that sizeof yields
                   zero.  */
@@ -7601,15 +7556,6 @@ grokdeclarator (const struct c_declarator *declarator,
                    TYPE_SIZE_UNIT (type) = size_zero_node;
                    SET_TYPE_STRUCTURAL_EQUALITY (type);
                  }
-               if (array_parm_vla_unspec_p)
-                 {
-                   gcc_assert (itype);
-                   /* The type is complete.  C99 6.7.5.2p4  */
-                   type = build_distinct_type_copy (TYPE_MAIN_VARIANT (type));
-                   TYPE_SIZE (type) = bitsize_zero_node;
-                   TYPE_SIZE_UNIT (type) = size_zero_node;
-                   SET_TYPE_STRUCTURAL_EQUALITY (type);
-                 }
 
                if (!valid_array_size_p (loc, type, name))
                  type = error_mark_node;
@@ -7732,8 +7678,8 @@ grokdeclarator (const struct c_declarator *declarator,
              }
            type_quals = TYPE_UNQUALIFIED;
 
-           type = build_function_type (type, arg_types,
-                                       arg_info->no_named_args_stdarg_p);
+           type = c_build_function_type (type, arg_types,
+                                         arg_info->no_named_args_stdarg_p);
            declarator = declarator->declarator;
 
            /* Set the TYPE_CONTEXTs for each tagged type which is local to
@@ -7798,8 +7744,6 @@ grokdeclarator (const struct c_declarator *declarator,
        default:
          gcc_unreachable ();
        }
-      if (type != error_mark_node)
-       C_TYPE_VARIABLY_MODIFIED (type) = varmod || size_varies;
     }
   *decl_attrs = chainon (returned_attrs, *decl_attrs);
   *decl_attrs = chainon (decl_id_attrs, *decl_attrs);
@@ -8122,7 +8066,7 @@ grokdeclarator (const struct c_declarator *declarator,
        if (TREE_CODE (type) == FUNCTION_TYPE)
          {
            error_at (loc, "field %qE declared as a function", name);
-           type = build_pointer_type (type);
+           type = c_build_pointer_type (type);
          }
        else if (TREE_CODE (type) != ERROR_MARK
                 && !COMPLETE_OR_UNBOUND_ARRAY_TYPE_P (type))
@@ -9451,7 +9395,7 @@ c_update_type_canonical (tree t)
          else
            {
              tree
-               c = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x));
+               c = c_build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x));
              if (TYPE_STRUCTURAL_EQUALITY_P (c))
                {
                  gcc_checking_assert (TYPE_CANONICAL (t) == t);
@@ -9486,8 +9430,8 @@ c_update_type_canonical (tree t)
            continue;
          if (TYPE_CANONICAL (x) != x || TYPE_REF_CAN_ALIAS_ALL (p))
            TYPE_CANONICAL (p)
-             = build_pointer_type_for_mode (TYPE_CANONICAL (x), TYPE_MODE (p),
-                                            false);
+             = c_build_pointer_type_for_mode (TYPE_CANONICAL (x), TYPE_MODE 
(p),
+                                              false);
          else
            TYPE_CANONICAL (p) = p;
          c_update_type_canonical (p);
@@ -10724,9 +10668,9 @@ start_function (struct c_declspecs *declspecs, struct 
c_declarator *declarator,
       error_at (loc, "return type is an incomplete type");
       /* Make it return void instead.  */
       TREE_TYPE (decl1)
-       = build_function_type (void_type_node,
-                              TYPE_ARG_TYPES (TREE_TYPE (decl1)),
-                              TYPE_NO_NAMED_ARGS_STDARG_P (TREE_TYPE (decl1)));
+       = c_build_function_type (void_type_node,
+                                TYPE_ARG_TYPES (TREE_TYPE (decl1)),
+                                TYPE_NO_NAMED_ARGS_STDARG_P (TREE_TYPE 
(decl1)));
     }
 
   if (warn_about_return_type)
diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h
index 365b59388038..80b8ec9e8fcb 100644
--- a/gcc/c/c-objc-common.h
+++ b/gcc/c/c-objc-common.h
@@ -74,6 +74,8 @@ extern void c_register_features ();
 #define LANG_HOOKS_EMITS_BEGIN_STMT true
 #undef LANG_HOOKS_FINALIZE_EARLY_DEBUG
 #define LANG_HOOKS_FINALIZE_EARLY_DEBUG c_common_finalize_early_debug
+#undef LANG_HOOKS_RECONSTRUCT_COMPLEX_TYPE
+#define LANG_HOOKS_RECONSTRUCT_COMPLEX_TYPE c_reconstruct_complex_type
 
 static const scoped_attribute_specs *const c_objc_attribute_table[] =
 {
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 90c33970fda3..179c772fb76f 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -4417,7 +4417,7 @@ c_parser_typeof_specifier (c_parser *parser)
          else if (FUNCTION_POINTER_TYPE_P (ret.spec)
                   && TYPE_QUALS (TREE_TYPE (ret.spec)) != TYPE_UNQUALIFIED)
            ret.spec
-             = build_pointer_type (TYPE_MAIN_VARIANT (TREE_TYPE (ret.spec)));
+             = c_build_pointer_type (TYPE_MAIN_VARIANT (TREE_TYPE (ret.spec)));
        }
     }
   return ret;
@@ -10700,7 +10700,7 @@ c_parser_generic_selection (c_parser *parser)
       if (FUNCTION_POINTER_TYPE_P (selector_type)
          && TYPE_QUALS (TREE_TYPE (selector_type)) != TYPE_UNQUALIFIED)
        selector_type
-         = build_pointer_type (TYPE_MAIN_VARIANT (TREE_TYPE (selector_type)));
+         = c_build_pointer_type (TYPE_MAIN_VARIANT (TREE_TYPE 
(selector_type)));
     }
 
   if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index a1435e7cb0ca..c8e9731bfc42 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -879,6 +879,13 @@ extern tree c_build_function_call_vec (location_t, const 
vec<location_t>&,
                                       tree, vec<tree, va_gc> *,
                                       vec<tree, va_gc> *);
 extern tree c_omp_clause_copy_ctor (tree, tree, tree);
+extern tree c_reconstruct_complex_type (tree, tree);
+extern tree c_build_type_attribute_variant (tree ntype, tree attrs);
+extern tree c_build_pointer_type (tree type);
+extern tree c_build_array_type (tree type, tree domain);
+extern tree c_build_function_type (tree type, tree args, bool no = false);
+extern tree c_build_pointer_type_for_mode (tree type, machine_mode mode, bool 
m);
+
 
 /* Set to 0 at beginning of a function definition, set to 1 if
    a return statement that specifies a return value is seen.  */
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index dbc9f885f4a4..6673cbf72947 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -357,6 +357,191 @@ qualify_type (tree type, tree like)
                                 | ENCODE_QUAL_ADDR_SPACE (as_common));
 }
 
+
+/* Check consistency of type TYP.E  For derived types, we test that
+   C_TYPE_VARIABLE_SIZE and C_TYPE_VARIABLY_MODIFIED are consistent with
+   the requirements of the base type.  We also check that arrays with a
+   non-constant length are marked with C_TYPE_VARIABLE_SIZE. If any
+   inconsistency is detected false is returned and true otherwise.  */
+
+static bool
+c_verify_type (tree type)
+{
+  switch (TREE_CODE (type))
+    {
+    case POINTER_TYPE:
+    case FUNCTION_TYPE:
+      /* Pointer and funcions can not have variable size.  */
+      if (C_TYPE_VARIABLE_SIZE (type))
+       return false;
+      /* Pointer and funcions are variably modified if and only if the
+        return / target type is variably modified.  */
+      if (C_TYPE_VARIABLY_MODIFIED (type)
+         != C_TYPE_VARIABLY_MODIFIED (TREE_TYPE (type)))
+       return false;
+      break;
+    case ARRAY_TYPE:
+      /* An array has variable size if and only if it has a non-constant
+        dimensions or its element type has variable size.  */
+      if ((C_TYPE_VARIABLE_SIZE (TREE_TYPE (type))
+          || (TYPE_DOMAIN (type) && TYPE_MAX_VALUE (TYPE_DOMAIN (type))
+              && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (type)))
+                 != INTEGER_CST))
+         != C_TYPE_VARIABLE_SIZE (type))
+       return false;
+      /* If the element type or the array has variable size, then the
+        array has variable size and is variably modified.  */
+      if (C_TYPE_VARIABLE_SIZE (TREE_TYPE (type))
+         || C_TYPE_VARIABLE_SIZE (type))
+       {
+         if (!C_TYPE_VARIABLE_SIZE (type))
+           return false;
+         if (!C_TYPE_VARIABLY_MODIFIED (type))
+           return false;
+       }
+      /* If the element type is variably modified, then also the array.  */
+      if (C_TYPE_VARIABLY_MODIFIED (TREE_TYPE (type))
+         && !C_TYPE_VARIABLY_MODIFIED (type))
+       return false;
+      break;
+    default:
+      break;
+    }
+  return true;
+}
+
+/* Propagate C_TYPE_VARIABLY_MODIFIED and C_TYPE_VARIABLE_SIZE
+   from a base type to a newly built derived or qualified type.  */
+
+static tree
+c_set_type_bits (tree new_type, tree old_type)
+{
+  gcc_checking_assert (c_verify_type (old_type));
+
+  if (C_TYPE_VARIABLY_MODIFIED (old_type))
+    C_TYPE_VARIABLY_MODIFIED (new_type) = true;
+
+  if (TREE_CODE (new_type) == ARRAY_TYPE && C_TYPE_VARIABLE_SIZE (old_type))
+    {
+      C_TYPE_VARIABLY_MODIFIED (new_type) = true;
+      C_TYPE_VARIABLE_SIZE (new_type) = true;
+    }
+  return new_type;
+}
+
+/* Build a pointer type using the default pointer mode.  */
+
+tree
+c_build_pointer_type (tree to_type)
+{
+  addr_space_t as = to_type == error_mark_node ? ADDR_SPACE_GENERIC
+                                              : TYPE_ADDR_SPACE (to_type);
+  machine_mode pointer_mode;
+
+  if (as != ADDR_SPACE_GENERIC || c_default_pointer_mode == VOIDmode)
+    pointer_mode = targetm.addr_space.pointer_mode (as);
+  else
+    pointer_mode = c_default_pointer_mode;
+
+  return c_build_pointer_type_for_mode (to_type, pointer_mode, false);
+}
+
+/* Build a pointer type using the given mode.  */
+
+tree
+c_build_pointer_type_for_mode (tree type, machine_mode mode, bool m)
+{
+  tree ret = build_pointer_type_for_mode (type, mode, m);
+  return c_set_type_bits (ret, type);
+}
+
+/* Build a function type.  */
+
+tree
+c_build_function_type (tree type, tree args, bool no)
+{
+  tree ret = build_function_type (type, args, no);
+  return c_set_type_bits (ret, type);
+}
+
+/* Build an array type.  This sets typeless storage as required
+   by C2Y and C_TYPE_VARIABLY_MODIFIED and C_TYPE_VARIABLE_SIZE
+   based on the element type and domain.  */
+
+tree
+c_build_array_type (tree type, tree domain)
+{
+  int type_quals = TYPE_QUALS (type);
+
+  /* Identify typeless storage as introduced in C2Y
+     and supported also in earlier language modes.  */
+  bool typeless = (char_type_p (type) && !(type_quals & TYPE_QUAL_ATOMIC))
+                 || (AGGREGATE_TYPE_P (type) && TYPE_TYPELESS_STORAGE (type));
+
+  tree ret = build_array_type (type, domain, typeless);
+
+  if (domain && TYPE_MAX_VALUE (domain)
+      && TREE_CODE (TYPE_MAX_VALUE (domain)) != INTEGER_CST)
+    {
+      C_TYPE_VARIABLE_SIZE (ret) = 1;
+      C_TYPE_VARIABLY_MODIFIED (ret) = 1;
+    }
+
+  return c_set_type_bits (ret, type);
+}
+
+tree
+c_build_type_attribute_qual_variant (tree type, tree attrs, int quals)
+{
+  tree ret = build_type_attribute_qual_variant (type, attrs, quals);
+  return c_set_type_bits (ret, type);
+}
+
+tree
+c_build_type_attribute_variant (tree type, tree attrs)
+{
+  return c_build_type_attribute_qual_variant (type, attrs, TYPE_QUALS (type));
+}
+
+/* Reconstruct a complex derived type.  This is used to re-construct types
+   with the vector attribute.  It is called via a langhook.  */
+
+tree
+c_reconstruct_complex_type (tree type, tree bottom)
+{
+  tree inner, outer;
+
+  if (TREE_CODE (type) == POINTER_TYPE)
+    {
+      inner = c_reconstruct_complex_type (TREE_TYPE (type), bottom);
+      outer = c_build_pointer_type_for_mode (inner, TYPE_MODE (type),
+                                            TYPE_REF_CAN_ALIAS_ALL (type));
+    }
+  else if (TREE_CODE (type) == ARRAY_TYPE)
+    {
+      inner = c_reconstruct_complex_type (TREE_TYPE (type), bottom);
+      outer = c_build_array_type (inner, TYPE_DOMAIN (type));
+
+      gcc_checking_assert (C_TYPE_VARIABLE_SIZE (type)
+                          == C_TYPE_VARIABLE_SIZE (outer));
+      gcc_checking_assert (C_TYPE_VARIABLY_MODIFIED (outer)
+                          == C_TYPE_VARIABLY_MODIFIED (type));
+    }
+  else if (TREE_CODE (type) == FUNCTION_TYPE)
+    {
+      inner = c_reconstruct_complex_type (TREE_TYPE (type), bottom);
+      outer = c_build_function_type (inner, TYPE_ARG_TYPES (type),
+                                    TYPE_NO_NAMED_ARGS_STDARG_P (type));
+    }
+  else if (TREE_CODE (type) == REFERENCE_TYPE
+          || TREE_CODE (type) == OFFSET_TYPE)
+    gcc_unreachable ();
+  else
+    return bottom;
+
+  return c_build_type_attribute_qual_variant (outer, TYPE_ATTRIBUTES (type),
+                                             TYPE_QUALS (type));
+}
 
 /* If NTYPE is a type of a non-variadic function with a prototype
    and OTYPE is a type of a function without a prototype and ATTRS
@@ -365,7 +550,7 @@ qualify_type (tree type, tree like)
    the (possibly) modified ATTRS.  */
 
 static tree
-build_functype_attribute_variant (tree ntype, tree otype, tree attrs)
+c_build_functype_attribute_variant (tree ntype, tree otype, tree attrs)
 {
   if (!prototype_p (otype)
       && prototype_p (ntype)
@@ -376,9 +561,11 @@ build_functype_attribute_variant (tree ntype, tree otype, 
tree attrs)
                  "does not take variable arguments", "format");
       attrs = remove_attribute ("format", attrs);
     }
-  return build_type_attribute_variant (ntype, attrs);
+  return c_build_type_attribute_variant (ntype, attrs);
 
 }
+
+
 /* Return the composite type of two compatible types.
 
    We assume that comptypes has already been done and returned
@@ -440,8 +627,8 @@ composite_type_internal (tree t1, tree t2, struct 
composite_cache* cache)
        tree pointed_to_2 = TREE_TYPE (t2);
        tree target = composite_type_internal (pointed_to_1,
                                               pointed_to_2, cache);
-        t1 = build_pointer_type_for_mode (target, TYPE_MODE (t1), false);
-       t1 = build_type_attribute_variant (t1, attributes);
+       t1 = c_build_pointer_type_for_mode (target, TYPE_MODE (t1), false);
+       t1 = c_build_type_attribute_variant (t1, attributes);
        return qualify_type (t1, t2);
       }
 
@@ -479,15 +666,15 @@ composite_type_internal (tree t1, tree t2, struct 
composite_cache* cache)
        /* Save space: see if the result is identical to one of the args.  */
        if (elt == TREE_TYPE (t1) && TYPE_DOMAIN (t1)
            && (d2_variable || d2_zero || !d1_variable))
-         return build_type_attribute_variant (t1, attributes);
+         return c_build_type_attribute_variant (t1, attributes);
        if (elt == TREE_TYPE (t2) && TYPE_DOMAIN (t2)
            && (d1_variable || d1_zero || !d2_variable))
-         return build_type_attribute_variant (t2, attributes);
+         return c_build_type_attribute_variant (t2, attributes);
 
        if (elt == TREE_TYPE (t1) && !TYPE_DOMAIN (t2) && !TYPE_DOMAIN (t1))
-         return build_type_attribute_variant (t1, attributes);
+         return c_build_type_attribute_variant (t1, attributes);
        if (elt == TREE_TYPE (t2) && !TYPE_DOMAIN (t2) && !TYPE_DOMAIN (t1))
-         return build_type_attribute_variant (t2, attributes);
+         return c_build_type_attribute_variant (t2, attributes);
 
        /* Merge the element types, and have a size if either arg has
           one.  We may have qualifiers on the element types.  To set
@@ -496,13 +683,21 @@ composite_type_internal (tree t1, tree t2, struct 
composite_cache* cache)
           back at the end.  */
        quals = TYPE_QUALS (strip_array_types (elt));
        unqual_elt = c_build_qualified_type (elt, TYPE_UNQUALIFIED);
-       t1 = build_array_type (unqual_elt,
-                              TYPE_DOMAIN ((TYPE_DOMAIN (t1)
-                                            && (d2_variable
-                                                || d2_zero
-                                                || !d1_variable))
-                                           ? t1
-                                           : t2));
+       t1 = c_build_array_type (unqual_elt,
+                                TYPE_DOMAIN ((TYPE_DOMAIN (t1)
+                                             && (d2_variable
+                                                 || d2_zero
+                                                 || !d1_variable))
+                                             ? t1
+                                             : t2));
+
+       /* Check that a type which has a varying outermost dimension
+          got marked has having a variable size.  */
+       bool varsize = (d1_variable && d2_variable)
+                      || (d1_variable && !t2_complete)
+                      || (d2_variable && !t1_complete);
+       gcc_checking_assert (!varsize || C_TYPE_VARIABLE_SIZE (t1));
+
        /* Ensure a composite type involving a zero-length array type
           is a zero-length type not an incomplete type.  */
        if (d1_zero && d2_zero
@@ -513,7 +708,7 @@ composite_type_internal (tree t1, tree t2, struct 
composite_cache* cache)
            TYPE_SIZE_UNIT (t1) = size_zero_node;
          }
        t1 = c_build_qualified_type (t1, quals);
-       return build_type_attribute_variant (t1, attributes);
+       return c_build_type_attribute_variant (t1, attributes);
       }
 
     case RECORD_TYPE:
@@ -612,7 +807,7 @@ composite_type_internal (tree t1, tree t2, struct 
composite_cache* cache)
          if (attribute_list_equal (TYPE_ATTRIBUTES (t2), attributes))
            return t2;
        }
-      return build_type_attribute_variant (t1, attributes);
+      return c_build_type_attribute_variant (t1, attributes);
 
     case FUNCTION_TYPE:
       /* Function types: prefer the one that specified arg types.
@@ -628,23 +823,23 @@ composite_type_internal (tree t1, tree t2, struct 
composite_cache* cache)
 
        /* Save space: see if the result is identical to one of the args.  */
        if (valtype == TREE_TYPE (t1) && !TYPE_ARG_TYPES (t2))
-         return build_functype_attribute_variant (t1, t2, attributes);
+         return c_build_functype_attribute_variant (t1, t2, attributes);
        if (valtype == TREE_TYPE (t2) && !TYPE_ARG_TYPES (t1))
-         return build_functype_attribute_variant (t2, t1, attributes);
+         return c_build_functype_attribute_variant (t2, t1, attributes);
 
        /* Simple way if one arg fails to specify argument types.  */
        if (TYPE_ARG_TYPES (t1) == NULL_TREE)
          {
-           t1 = build_function_type (valtype, TYPE_ARG_TYPES (t2),
-                                     TYPE_NO_NAMED_ARGS_STDARG_P (t2));
-           t1 = build_type_attribute_variant (t1, attributes);
+           t1 = c_build_function_type (valtype, TYPE_ARG_TYPES (t2),
+                                       TYPE_NO_NAMED_ARGS_STDARG_P (t2));
+           t1 = c_build_type_attribute_variant (t1, attributes);
            return qualify_type (t1, t2);
         }
        if (TYPE_ARG_TYPES (t2) == NULL_TREE)
          {
-           t1 = build_function_type (valtype, TYPE_ARG_TYPES (t1),
-                                     TYPE_NO_NAMED_ARGS_STDARG_P (t1));
-           t1 = build_type_attribute_variant (t1, attributes);
+           t1 = c_build_function_type (valtype, TYPE_ARG_TYPES (t1),
+                                       TYPE_NO_NAMED_ARGS_STDARG_P (t1));
+           t1 = c_build_type_attribute_variant (t1, attributes);
            return qualify_type (t1, t2);
          }
 
@@ -739,13 +934,13 @@ composite_type_internal (tree t1, tree t2, struct 
composite_cache* cache)
          parm_done: ;
          }
 
-       t1 = build_function_type (valtype, newargs);
+       t1 = c_build_function_type (valtype, newargs);
        t1 = qualify_type (t1, t2);
       }
       /* FALLTHRU */
 
     default:
-      return build_type_attribute_variant (t1, attributes);
+      return c_build_type_attribute_variant (t1, attributes);
     }
 }
 
@@ -822,8 +1017,8 @@ common_pointer_type (tree t1, tree t2)
 
   target_quals |= ENCODE_QUAL_ADDR_SPACE (as_common);
 
-  t1 = build_pointer_type (c_build_qualified_type (target, target_quals));
-  return build_type_attribute_variant (t1, attributes);
+  t1 = c_build_pointer_type (c_build_qualified_type (target, target_quals));
+  return c_build_type_attribute_variant (t1, attributes);
 }
 
 /* Return the common type for two arithmetic types under the usual
@@ -855,13 +1050,13 @@ c_common_type (tree t1, tree t2)
   if (TYPE_ATTRIBUTES (t1) != NULL_TREE)
     {
       tree attrs = affects_type_identity_attributes (TYPE_ATTRIBUTES (t1));
-      t1 = build_type_attribute_variant (t1, attrs);
+      t1 = c_build_type_attribute_variant (t1, attrs);
     }
 
   if (TYPE_ATTRIBUTES (t2) != NULL_TREE)
     {
       tree attrs = affects_type_identity_attributes (TYPE_ATTRIBUTES (t2));
-      t2 = build_type_attribute_variant (t2, attrs);
+      t2 = c_build_type_attribute_variant (t2, attrs);
     }
 
   /* Save time if the two types are the same.  */
@@ -1181,6 +1376,9 @@ comptypes_verify (tree type1, tree type2)
       || TREE_CODE (type1) == ERROR_MARK || TREE_CODE (type2) == ERROR_MARK)
     return true;
 
+  gcc_checking_assert (c_verify_type (type1));
+  gcc_checking_assert (c_verify_type (type2));
+
   if (TYPE_CANONICAL (type1) != TYPE_CANONICAL (type2)
       && !TYPE_STRUCTURAL_EQUALITY_P (type1)
       && !TYPE_STRUCTURAL_EQUALITY_P (type2))
@@ -2002,11 +2200,7 @@ array_to_pointer_conversion (location_t loc, tree exp)
 
   copy_warning (exp, orig_exp);
 
-  bool varmod = C_TYPE_VARIABLY_MODIFIED (restype);
-
-  ptrtype = build_pointer_type (restype);
-
-  C_TYPE_VARIABLY_MODIFIED (ptrtype) = varmod;
+  ptrtype = c_build_pointer_type (restype);
 
   if (INDIRECT_REF_P (exp))
     return convert (ptrtype, TREE_OPERAND (exp, 0));
@@ -2750,7 +2944,7 @@ build_access_with_size_for_counted_by (location_t loc, 
tree ref,
 {
   gcc_assert (c_flexible_array_member_type_p (TREE_TYPE (ref)));
   /* The result type of the call is a pointer to the flexible array type.  */
-  tree result_type = build_pointer_type (TREE_TYPE (ref));
+  tree result_type = c_build_pointer_type (TREE_TYPE (ref));
 
   tree call
     = build_call_expr_internal_loc (loc, IFN_ACCESS_WITH_SIZE,
@@ -3209,7 +3403,7 @@ build_omp_array_section (location_t loc, tree array, tree 
index, tree length)
 
       gcc_assert (!error_operand_p (idxtype));
 
-      sectype = build_array_type (eltype, idxtype);
+      sectype = c_build_array_type (eltype, idxtype);
     }
 
   return build3_loc (loc, OMP_ARRAY_SECTION, sectype, array, index, length);
@@ -4887,7 +5081,6 @@ build_unary_op (location_t location, enum tree_code code, 
tree xarg,
   tree eptype = NULL_TREE;
   const char *invalid_op_diag;
   bool int_operands;
-  bool varmod;
 
   int_operands = EXPR_INT_CONST_OPERANDS (xarg);
   if (int_operands)
@@ -5376,11 +5569,7 @@ build_unary_op (location_t location, enum tree_code 
code, tree xarg,
       gcc_assert (TREE_CODE (arg) != COMPONENT_REF
                  || !DECL_C_BIT_FIELD (TREE_OPERAND (arg, 1)));
 
-      varmod = C_TYPE_VARIABLY_MODIFIED (argtype);
-
-      argtype = build_pointer_type (argtype);
-
-      C_TYPE_VARIABLY_MODIFIED (argtype) = varmod;
+      argtype = c_build_pointer_type (argtype);
 
       /* ??? Cope with user tricks that amount to offsetof.  Delete this
         when we have proper support for integer constant expressions.  */
@@ -5659,7 +5848,7 @@ type_or_builtin_type (tree expr, tree *bltin = NULL)
     return type;
 
   if ((*bltin = builtin_decl_implicit (code)))
-    type = build_pointer_type (TREE_TYPE (*bltin));
+    type = c_build_pointer_type (TREE_TYPE (*bltin));
 
   return type;
 }
@@ -5944,7 +6133,7 @@ build_conditional_expr (location_t colon_loc, tree ifexp, 
bool ifexp_bcp,
          /* for array, use qualifiers of element type */
          if (flag_isoc23)
            t2 = t2_stripped;
-         result_type = build_pointer_type (qualify_type (t1, t2));
+         result_type = c_build_pointer_type (qualify_type (t1, t2));
        }
       /* Objective-C pointer comparisons are a bit more lenient.  */
       else if (objc_have_common_type (type1, type2, -3, NULL_TREE))
@@ -5967,8 +6156,8 @@ build_conditional_expr (location_t colon_loc, tree ifexp, 
bool ifexp_bcp,
              inform (op1_loc, "first expression has type %qT", type1);
              inform (op2_loc, "second expression has type %qT", type2);
            }
-         result_type = build_pointer_type
-                         (build_qualified_type (void_type_node, qual));
+         result_type = c_build_pointer_type
+                         (c_build_qualified_type (void_type_node, qual));
        }
     }
   else if (code1 == POINTER_TYPE
@@ -6922,8 +7111,8 @@ build_modify_expr (location_t location, tree lhs, tree 
lhs_origtype,
     }
 
   /* Remove qualifiers.  */
-  lhstype = build_qualified_type (lhstype, TYPE_UNQUALIFIED);
-  olhstype = build_qualified_type (olhstype, TYPE_UNQUALIFIED);
+  lhstype = c_build_qualified_type (lhstype, TYPE_UNQUALIFIED);
+  olhstype = c_build_qualified_type (olhstype, TYPE_UNQUALIFIED);
 
   /* Convert new value to destination type.  Fold it first, then
      restore any excess precision information, for the sake of
@@ -7573,11 +7762,11 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
        }
       if (!c_mark_addressable (rhs))
        return error_mark_node;
-      rhs = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (rhs)), rhs);
+      rhs = build1 (ADDR_EXPR, c_build_pointer_type (TREE_TYPE (rhs)), rhs);
       SET_EXPR_LOCATION (rhs, location);
 
       rhs = convert_for_assignment (location, expr_loc,
-                                   build_pointer_type (TREE_TYPE (type)),
+                                   c_build_pointer_type (TREE_TYPE (type)),
                                    rhs, origtype, errtype,
                                    null_pointer_constant, fundecl, function,
                                    parmnum, warnopt);
@@ -13647,8 +13836,8 @@ build_binary_op (location_t location, enum tree_code 
code,
          if (result_type == NULL_TREE)
            {
              int qual = ENCODE_QUAL_ADDR_SPACE (as_common);
-             result_type = build_pointer_type
-                             (build_qualified_type (void_type_node, qual));
+             result_type = c_build_pointer_type
+                             (c_build_qualified_type (void_type_node, qual));
            }
        }
       else if (code0 == POINTER_TYPE
@@ -13680,10 +13869,10 @@ build_binary_op (location_t location, enum tree_code 
code,
        create a pointer type from its type.  */
       else if (code0 == NULLPTR_TYPE && null_pointer_constant_p (orig_op1))
        result_type = (INTEGRAL_TYPE_P (type1)
-                      ? build_pointer_type (type1) : type1);
+                      ? c_build_pointer_type (type1) : type1);
       else if (code1 == NULLPTR_TYPE && null_pointer_constant_p (orig_op0))
        result_type = (INTEGRAL_TYPE_P (type0)
-                      ? build_pointer_type (type0) : type0);
+                      ? c_build_pointer_type (type0) : type0);
       if ((C_BOOLEAN_TYPE_P (TREE_TYPE (orig_op0))
           || truth_value_p (TREE_CODE (orig_op0)))
          ^ (C_BOOLEAN_TYPE_P (TREE_TYPE (orig_op1))
@@ -13781,8 +13970,8 @@ build_binary_op (location_t location, enum tree_code 
code,
          else
            {
              int qual = ENCODE_QUAL_ADDR_SPACE (as_common);
-             result_type = build_pointer_type
-                             (build_qualified_type (void_type_node, qual));
+             result_type = c_build_pointer_type
+                             (c_build_qualified_type (void_type_node, qual));
               pedwarn (location, OPT_Wcompare_distinct_pointer_types,
                        "comparison of distinct pointer types lacks a cast");
            }
@@ -15047,8 +15236,8 @@ handle_omp_array_sections (tree &c, enum 
c_omp_region_type ort)
          tree eltype = TREE_TYPE (first);
          while (TREE_CODE (eltype) == ARRAY_TYPE)
            eltype = TREE_TYPE (eltype);
-         tree type = build_array_type (eltype, index_type);
-         tree ptype = build_pointer_type (eltype);
+         tree type = c_build_array_type (eltype, index_type);
+         tree ptype = c_build_pointer_type (eltype);
          if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
            t = build_fold_addr_expr (t);
          tree t2 = build_fold_addr_expr (first);
@@ -15518,10 +15707,10 @@ c_finish_omp_clauses (tree clauses, enum 
c_omp_region_type ort)
              size = size_binop (MINUS_EXPR, size, size_one_node);
              size = save_expr (size);
              tree index_type = build_index_type (size);
-             tree atype = build_array_type (TYPE_MAIN_VARIANT (type),
-                                            index_type);
+             tree atype = c_build_array_type (TYPE_MAIN_VARIANT (type),
+                                              index_type);
              atype = c_build_qualified_type (atype, TYPE_QUALS (type));
-             tree ptype = build_pointer_type (type);
+             tree ptype = c_build_pointer_type (type);
              if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
                t = build_fold_addr_expr (t);
              t = build2 (MEM_REF, atype, t, build_int_cst (ptype, 0));
@@ -17177,17 +17366,17 @@ c_build_qualified_type (tree type, int type_quals, 
tree orig_qual_type,
                    || (domain && TYPE_CANONICAL (domain) != domain))
             {
               tree unqualified_canon
-                = build_array_type (TYPE_CANONICAL (element_type),
-                                    domain? TYPE_CANONICAL (domain)
-                                          : NULL_TREE);
+               = c_build_array_type (TYPE_CANONICAL (element_type),
+                                     domain ? TYPE_CANONICAL (domain)
+                                            : NULL_TREE);
               if (TYPE_REVERSE_STORAGE_ORDER (type))
                 {
                   unqualified_canon
-                    = build_distinct_type_copy (unqualified_canon);
+                   = build_distinct_type_copy (unqualified_canon);
                   TYPE_REVERSE_STORAGE_ORDER (unqualified_canon) = 1;
                 }
               TYPE_CANONICAL (t)
-                = c_build_qualified_type (unqualified_canon, type_quals);
+               = c_build_qualified_type (unqualified_canon, type_quals);
             }
           else
             TYPE_CANONICAL (t) = t;
@@ -17209,6 +17398,12 @@ c_build_qualified_type (tree type, int type_quals, 
tree orig_qual_type,
   tree var_type = (orig_qual_type && orig_qual_indirect == 0
                   ? orig_qual_type
                   : build_qualified_type (type, type_quals));
+
+  gcc_checking_assert (C_TYPE_VARIABLE_SIZE (var_type)
+                      == C_TYPE_VARIABLE_SIZE (type));
+  gcc_checking_assert (C_TYPE_VARIABLY_MODIFIED (var_type)
+                      == C_TYPE_VARIABLY_MODIFIED (type));
+
   /* A variant type does not inherit the list of incomplete vars from the
      type main variant.  */
   if ((RECORD_OR_UNION_TYPE_P (var_type)
diff --git a/gcc/testsuite/gcc.dg/Warray-parameter-4.c 
b/gcc/testsuite/gcc.dg/Warray-parameter-4.c
index b702d730a13e..2c91ccdec06c 100644
--- a/gcc/testsuite/gcc.dg/Warray-parameter-4.c
+++ b/gcc/testsuite/gcc.dg/Warray-parameter-4.c
@@ -104,11 +104,9 @@ void ffpa7_n1 (void (* (* (* [8])[n1])(void))(void));
 // { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* 
?\\\(\\\*\\\[8]\\\)\\\[n1]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched 
bound" "" { target *-*-* } .-1 }
 
 void ffpa9_x (void (* (* (* [9])[*])(void))(void));
-// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* 
?\\\(\\\*\\\[9]\\\)\\\[\\\*]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "pr?????" { 
xfail *-*-* } .-1 }
-// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* 
?\\\(\\\*\\\[9]\\\)\\\[0]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "" { target *-*-* } 
.-2 }
+// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* 
?\\\(\\\*\\\[9]\\\)\\\[\\\*]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "pr?????" { 
target *-*-* } .-1 }
 void ffpa9_x (void (* (* (* [8])[*])(void))(void));
-// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* 
?\\\(\\\*\\\[8]\\\)\\\[\\\*]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched 
bound" "pr?????" { xfail *-*-* } .-1 }
-// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* 
?\\\(\\\*\\\[8]\\\)\\\[0]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched 
bound"  "" { target *-*-* } .-2 }
+// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* 
?\\\(\\\*\\\[8]\\\)\\\[\\\*]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched 
bound" "pr?????" { target *-*-* } .-1 }
 
 /* Verify a three-dimensional array of pointers to two-dimensional arrays
    of pointers to function pointers.  */
diff --git a/gcc/testsuite/gcc.dg/Wvla-parameter-2.c 
b/gcc/testsuite/gcc.dg/Wvla-parameter-2.c
index daa71d897c97..08d4a1069b68 100644
--- a/gcc/testsuite/gcc.dg/Wvla-parameter-2.c
+++ b/gcc/testsuite/gcc.dg/Wvla-parameter-2.c
@@ -34,17 +34,10 @@ void f (int[n1][2][n3][4][n5][6][n7][8][n9]);   // { 
dg-message "previously decl
                                                 // { dg-message "with 5 
variable bounds" "note" { target *-*-* } .-1  }
 void f (int[n1][2][n3][4][n5][6][n7][8][n9]);
 
-/* Due to a limitation and because [*] is represented the same as [0]
-   only the most significant array bound is rendered as [*]; the others
-   are rendered as [0].  */
-void f (int[n1][2][n3][4][n5][6][n7][8][*]);    // { dg-warning "argument 1 of 
type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[\\\*]' 
declared with 1 unspecified variable bound" "pr100420 (expected)" { xfail *-*-* 
} }
-// { dg-warning "argument 1 of type 
'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[0]' declared with 1 
unspecified variable bound" "pr100420" { target *-*-* } .-1 }
-void f (int[n1][2][n3][4][n5][6][*][8][n9]);   // { dg-warning "argument 1 of 
type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[\\\*]\\\[8]\\\[n9]' 
declared with 1 unspecified variable bound" "pr100420 (expected)" { xfail *-*-* 
} }
-// { dg-warning "argument 1 of type 
'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[0]\\\[8]\\\[n9]' declared with 1 
unspecified variable bound" "pr100420" { target *-*-* } .-1 }
-void f (int[n1][2][n3][4][*][6][n7][8][n9]);   // { dg-warning "argument 1 of 
type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[\\\*]\\\[6]\\\[n7]\\\[8]\\\[n9]' 
declared with 1 unspecified variable bound" "pr100420 (expected)" { xfail 
*-*-*} }
-// { dg-warning "argument 1 of type 
'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[0]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 
unspecified variable bound" "pr100420" { target *-*-* } .-1 }
-void f (int[n1][2][*][4][n5][6][n7][8][n9]);   // { dg-warning "argument 1 of 
type 'int\\\[n1]\\\[2]\\\[\\\*]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' 
declared with 1 unspecified variable bound" "pr100420 (expected)" { xfail *-*-* 
} }
-// { dg-warning "argument 1 of type 
'int\\\[n1]\\\[2]\\\[0]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 
unspecified variable bound" "pr100420" { target *-*-* } .-1 }
+void f (int[n1][2][n3][4][n5][6][n7][8][*]);    // { dg-warning "argument 1 of 
type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[\\\*]' 
declared with 1 unspecified variable bound" "pr100420 (expected)" { target 
*-*-* } }
+void f (int[n1][2][n3][4][n5][6][*][8][n9]);   // { dg-warning "argument 1 of 
type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[\\\*]\\\[8]\\\[n9]' 
declared with 1 unspecified variable bound" "pr100420 (expected)" { target 
*-*-* } }
+void f (int[n1][2][n3][4][*][6][n7][8][n9]);   // { dg-warning "argument 1 of 
type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[\\\*]\\\[6]\\\[n7]\\\[8]\\\[n9]' 
declared with 1 unspecified variable bound" "pr100420 (expected)" { target 
*-*-*} }
+void f (int[n1][2][*][4][n5][6][n7][8][n9]);   // { dg-warning "argument 1 of 
type 'int\\\[n1]\\\[2]\\\[\\\*]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' 
declared with 1 unspecified variable bound" "pr100420 (expected)" { target 
*-*-* } }
 void f (int[*][2][n3][4][n5][6][n7][8][n9]);   // { dg-warning "argument 1 of 
type 'int\\\[\\\*]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' 
declared with 1 unspecified variable bound" }
 
 void f (int[n1][n2][n3][n4][n5][n6][n7][n8][n9]);   // { dg-warning "argument 
1 of type 'int\\\[n1]\\\[n2]\\\[n3]\\\[n4]\\\[n5]\\\[n6]\\\[n7]\\\[n8]\\\[n9]' 
declared with 9 variable bounds" }
diff --git a/gcc/testsuite/gcc.dg/Wvla-parameter-3.c 
b/gcc/testsuite/gcc.dg/Wvla-parameter-3.c
index f1cf139192d3..33abe3e3aebc 100644
--- a/gcc/testsuite/gcc.dg/Wvla-parameter-3.c
+++ b/gcc/testsuite/gcc.dg/Wvla-parameter-3.c
@@ -23,21 +23,16 @@ void pa1 (int (*)[n + 1]);              // { dg-warning 
"mismatch in bound 1 of
 
 void ppax (int (**)[*]);                // { dg-message "previously declared 
as 'int \\\(\\\*\\\*\\\)\\\[.]'" "note" }
 void ppax (int (**)[n]);                // { dg-warning "\\\[-Wvla-parameter" }
-/* A VLA with an unspecified bound is represented the same as [0] so
-   so the pretty printer can't differentiate between the two forms.  */
-void ppax (int (**)[1]);                // { dg-bogus "\\\[-Warray-parameter" 
"pr100420 (expected)" { xfail *-*-* } }
-                                        // { dg-warning "\\\[-Wvla-parameter" 
"pr100420 (expected)" { xfail *-*-* } .-1 }
+void ppax (int (**)[1]);                // { dg-warning "\\\[-Wvla-parameter" 
"pr100420 (expected)" }
 void ppax (int (**)[n + 1]);            // { dg-warning "mismatch in bound 1 
of argument 1 declared as 'int *\\\(\\\*\\\*\\\)\\\[n \\\+ 1\\\]'" }
 
 
 void pa1_n (int (*)[1][n]);
 void pa1_n (int (*)[1][n]);
-void pa1_n (int (*)[*][n]);             // { dg-warning "mismatch in bound 1 
of argument 1 declared as 'int \\\(\\\*\\\)\\\[\\\*]\\\[n]'" "pr100420 
(expected)" { xfail *-*-*} }
-                                        // { dg-warning "mismatch in bound 1 
of argument 1 declared as 'int \\\(\\\*\\\)\\\[0]\\\[n]'" "pr100420" { target 
*-*-* } .-1 }
+void pa1_n (int (*)[*][n]);             // { dg-warning "mismatch in bound 1 
of argument 1 declared as 'int \\\(\\\*\\\)\\\[\\\*]\\\[n]'" "pr100420 
(expected)" { target *-*-*} }
 
 void pa1_n_2 (int (*)[1][n][2]);
-void pa1_n_2 (int (*)[1][n][*]);        // { dg-warning "mismatch in bound 3 
of argument 1 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[\\\*]'" "pr100420 
(expected)" { xfail *-*-* } }
-                                        // { dg-warning "mismatch in bound 3 
of argument 1 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[0]'" "pr100420" { 
target *-*-* } .-1 }
+void pa1_n_2 (int (*)[1][n][*]);        // { dg-warning "mismatch in bound 3 
of argument 1 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[\\\*]'" "pr100420 
(expected)" { target *-*-* } }
 
 
 void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[1][n][2]);
diff --git a/gcc/testsuite/gcc.dg/c23-tag-composite-11.c 
b/gcc/testsuite/gcc.dg/c23-tag-composite-11.c
new file mode 100644
index 000000000000..960c7a0ba81d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-11.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+void f(int n, int m)
+{
+       typedef struct fo { int a; } aaa[n];
+       {
+               typedef struct fo { int a; } bbb[m];
+
+       goto foo;                       /* { dg-error "jump" } */
+               typeof((1 ? (aaa*)0 : (bbb*)0)) x;
+       foo:
+       }
+}
+
+void g(int n, int m)
+{
+       typedef struct fo { int a; } aaa[n];
+       {
+               typedef struct fo { int a; } bbb[];
+
+       goto foo;                       /* { dg-error "jump" } */
+               typeof((1 ? (aaa*)0 : (bbb*)0)) x;
+       foo:
+       }
+}
+
diff --git a/gcc/testsuite/gcc.dg/pr117145-1.c 
b/gcc/testsuite/gcc.dg/pr117145-1.c
new file mode 100644
index 000000000000..20482d3b3b79
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr117145-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+void b();
+int e(int c, struct d { [[gnu::vector_size(4)]] char an[c]; } *)
+{
+   (void)sizeof(struct d);
+   return 0;
+}
+void f() {
+  if (e(0, 0))
+    b();
+}
+
diff --git a/gcc/testsuite/gcc.dg/pr117145-2.c 
b/gcc/testsuite/gcc.dg/pr117145-2.c
new file mode 100644
index 000000000000..088a0cf571d0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr117145-2.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void e(int c)
+{
+    goto foo;                                                                  
/* { dg-error "jump into scope" } */
+    [[maybe_unused]] struct d { [[gnu::vector_size(4)]] char an[c]; } q;
+foo:
+}
+
diff --git a/gcc/testsuite/gcc.dg/pr117245.c b/gcc/testsuite/gcc.dg/pr117245.c
new file mode 100644
index 000000000000..ebcc60b898fc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr117245.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void a() {
+  int b;
+  struct {
+    char c[b];
+  } bar() {
+  }
+  struct bar {
+    __attribute__((vector_size(4))) char c[b];
+  } (*d)();
+  struct bar e() { struct bar f; }
+  d = e;
+  sizeof(d());
+}
+

Reply via email to