diff mbox series

c: detect variably-modified types [PR117145,PR117245,PR100420]

Message ID b500ffaab630b1b77a4b051fb5530d149fd437e6.camel@tugraz.at
State New
Headers show
Series c: detect variably-modified types [PR117145,PR117245,PR100420] | expand

Commit Message

Martin Uecker Oct. 26, 2024, 9:15 p.m. UTC
Here is a patch that hopefully fixes the last cases where we do not
tag variably-modified types correctly.  It is probably best to look
at the c-typeck.cc changes first.

Martin


    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-obj-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.

Comments

Joseph Myers Oct. 29, 2024, 6:50 p.m. UTC | #1
On Sat, 26 Oct 2024, Martin Uecker wrote:

> +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);

This is badly formatted, missing space before '?'.

> +/* Build an array type.  This sets typeless storage as required
> +   by C23 and C_TYPE_VARIABLY_MODIFIED and C_TYPE_VARIABLE_SIZE
> +   based on the element type and domain.  */

As required by C2Y, not C23.

> +  else if (TREE_CODE (type) == REFERENCE_TYPE
> +	   || TREE_CODE (type) == OFFSET_TYPE)
> +    {
> +      gcc_assert (0);
> +    }

Should be gcc_unreachable (), and no braces around a single statement.

> diff --git a/gcc/testsuite/gcc.dg/Wvla-parameter-2.c b/gcc/testsuite/gcc.dg/Wvla-parameter-2.c
> index daa71d897c9..ebd61522563 100644
> --- a/gcc/testsuite/gcc.dg/Wvla-parameter-2.c
> +++ b/gcc/testsuite/gcc.dg/Wvla-parameter-2.c
> @@ -37,14 +37,10 @@ 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].  */

This "Due to a limitation" comment should be updated / removed to reflect 
the changes to representation of [*].

OK with those fixes.
diff mbox series

Patch

diff --git a/gcc/c-family/c-pretty-print.cc b/gcc/c-family/c-pretty-print.cc
index 13806714446..f843b33eb79 100644
--- a/gcc/c-family/c-pretty-print.cc
+++ b/gcc/c-family/c-pretty-print.cc
@@ -688,7 +688,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 1827bbf0646..e66a6e6b131 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -714,22 +714,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
@@ -1921,7 +1908,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);
@@ -1934,7 +1921,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
@@ -3395,9 +3382,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;
@@ -3495,8 +3482,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);
@@ -3894,9 +3881,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;
@@ -4836,8 +4823,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);
@@ -7222,8 +7209,6 @@  grokdeclarator (const struct c_declarator *declarator,
 	  array_parm_static = false;
 	}
 
-      bool varmod = C_TYPE_VARIABLY_MODIFIED (type);
-
       switch (declarator->kind)
 	{
 	case cdk_attrs:
@@ -7505,31 +7490,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.  */
@@ -7560,34 +7534,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.  */
@@ -7599,15 +7554,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;
@@ -7730,8 +7676,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
@@ -7796,8 +7742,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);
@@ -8120,7 +8064,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))
@@ -9444,7 +9388,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);
@@ -9479,8 +9423,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);
@@ -10709,9 +10653,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 365b5938803..80b8ec9e8fc 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 090ab1cbc08..d6143ff84a6 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -4415,7 +4415,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;
@@ -10708,7 +10708,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 bfdcb78bbcc..f6823f7778e 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -875,6 +875,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 108ea5ca3e8..00e959e2009 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -356,6 +356,193 @@  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 C23 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_assert (0);
+    }
+  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
@@ -364,7 +551,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)
@@ -375,9 +562,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
@@ -439,8 +628,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);
       }
 
@@ -478,15 +667,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
@@ -495,13 +684,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
@@ -512,7 +709,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:
@@ -611,7 +808,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.
@@ -627,23 +824,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);
 	  }
 
@@ -738,13 +935,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);
     }
 }
 
@@ -821,8 +1018,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
@@ -854,13 +1051,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.  */
@@ -1180,6 +1377,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))
@@ -2001,11 +2201,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));
@@ -2749,7 +2945,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,
@@ -3208,7 +3404,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);
@@ -4886,7 +5082,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)
@@ -5375,11 +5570,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.  */
@@ -5658,7 +5849,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;
 }
@@ -5943,7 +6134,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))
@@ -5966,8 +6157,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
@@ -6921,8 +7112,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
@@ -7572,11 +7763,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);
@@ -13646,8 +13837,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
@@ -13679,10 +13870,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))
@@ -13780,8 +13971,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");
 	    }
@@ -15046,8 +15237,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);
@@ -15517,10 +15708,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));
@@ -17176,17 +17367,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;
@@ -17208,6 +17399,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 b702d730a13..2c91ccdec06 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 daa71d897c9..ebd61522563 100644
--- a/gcc/testsuite/gcc.dg/Wvla-parameter-2.c
+++ b/gcc/testsuite/gcc.dg/Wvla-parameter-2.c
@@ -37,14 +37,10 @@  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 f1cf139192d..33abe3e3aeb 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 00000000000..960c7a0ba81
--- /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 00000000000..20482d3b3b7
--- /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 00000000000..088a0cf571d
--- /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 00000000000..ebcc60b898f
--- /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());
+}
+