When checking compatibility of types during assignment, collect
all pairs of types where the outermost bound needs to match at
run-time. This list is then processed to add runtime checks for
each bound.

gcc/c-family:
 * c-opt (fvla-bounds): New flag.

gcc/c:
 * c-typeck.cc (struct instrument_data): New structure.
 (comp_target_types_instr convert_for_assignment_instrument): New
 interfaces for existing functions.
 (struct comptypes_data): Add instrumentation.
 (comptypes_check_enum_int_intr): New interface.
 (comptypes_check_enum_int): Old interface (calls new).
 (comptypes_internal): Collect VLA types needed for UBSan.
 (comp_target_types_instr): New interface.
 (comp_target_types): Old interface (calls new).
 (function_types_compatible_p): No instrumentation for function
 arguments.
 (process_vm_constraints): New function.
 (convert_argument): Adapt.
 (convert_for_assignment_instrument): New interface.
 (convert_for_assignment): Instrument assignments.
 (c_instrument_vm_assign): Helper function.
 (process_vm_constraints): Helper function.

gcc/doc/:
 * invoke.texi (fvla-bounds): Document new flag.

gcc/testsuite:
 * gcc.dg/vla-bounds-1.c: New test.
 * gcc.dg/vla-bounds-assign-1.c: New test.
 * gcc.dg/vla-bounds-assign-2.c: New test.
 * gcc.dg/vla-bounds-assign-3.c: New test.
 * gcc.dg/vla-bounds-assign-4.c: New test.
 * gcc.dg/vla-bounds-func-1.c: New test.
 * gcc.dg/vla-bounds-init-1.c: New test.
 * gcc.dg/vla-bounds-init-2.c: New test.
 * gcc.dg/vla-bounds-init-3.c: New test.
 * gcc.dg/vla-bounds-init-4.c: New test.
 * gcc.dg/vla-bounds-nest-1.c: New test.
 * gcc.dg/vla-bounds-nest-2.c: New test.
 * gcc.dg/vla-bounds-ret-1.c: New test.
 * gcc.dg/vla-bounds-ret-2.c: New test.
---
 gcc/c-family/c.opt | 4 +
 gcc/c/c-typeck.cc | 171 ++++++++++++++++++---
 gcc/doc/invoke.texi | 9 ++
 gcc/testsuite/gcc.dg/vla-bounds-1.c | 74 +++++++++
 gcc/testsuite/gcc.dg/vla-bounds-assign-1.c | 32 ++++
 gcc/testsuite/gcc.dg/vla-bounds-assign-2.c | 30 ++++
 gcc/testsuite/gcc.dg/vla-bounds-assign-3.c | 43 ++++++
 gcc/testsuite/gcc.dg/vla-bounds-assign-4.c | 26 ++++
 gcc/testsuite/gcc.dg/vla-bounds-func-1.c | 54 +++++++
 gcc/testsuite/gcc.dg/vla-bounds-init-1.c | 26 ++++
 gcc/testsuite/gcc.dg/vla-bounds-init-2.c | 25 +++
 gcc/testsuite/gcc.dg/vla-bounds-init-3.c | 25 +++
 gcc/testsuite/gcc.dg/vla-bounds-init-4.c | 27 ++++
 gcc/testsuite/gcc.dg/vla-bounds-nest-1.c | 31 ++++
 gcc/testsuite/gcc.dg/vla-bounds-nest-2.c | 26 ++++
 gcc/testsuite/gcc.dg/vla-bounds-ret-1.c | 27 ++++
 gcc/testsuite/gcc.dg/vla-bounds-ret-2.c | 28 ++++
 17 files changed, 639 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-func-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-2.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-3.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-4.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-ret-2.c

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index a52682d835c..786cd4ce52a 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -2324,6 +2324,10 @@ fvisibility-ms-compat
 C++ ObjC++ Var(flag_visibility_ms_compat)
 Changes visibility to match Microsoft Visual Studio by default.
+fvla-bounds
+C Var(flag_vla_bounds)
+Emit run-time consistency checks for variably-modified types.
+
 fvtable-gc
 C++ ObjC++ WarnRemoved
 No longer supported.
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 7e0f01ed22b..59940a29b53 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -97,11 +97,12 @@ static tree qualify_type (tree, tree);
 struct comptypes_data;
 static bool tagged_types_tu_compatible_p (const_tree, const_tree,
 struct comptypes_data *);
-static bool comp_target_types (location_t, tree, tree);
 static bool function_types_compatible_p (const_tree, const_tree,
 struct comptypes_data *);
 static bool type_lists_compatible_p (const_tree, const_tree,
 struct comptypes_data *);
+static bool comp_target_types_instr (location_t, tree, tree,
+ struct instrument_data **);
 static int convert_arguments (location_t, vec<location_t>, tree,
 vec<tree, va_gc> *, vec<tree, va_gc> *, tree,
 tree);
@@ -109,6 +110,9 @@ static tree pointer_diff (location_t, tree, tree, tree *);
 static tree convert_for_assignment (location_t, location_t, tree, tree, tree,
 enum impl_conv, bool, tree, tree, int,
 int = 0);
+static tree convert_for_assignment_instrument (location_t, location_t, tree, 
tree, tree,
+ enum impl_conv, bool, tree, tree, int, int,
+ struct instrument_data **);
 static tree valid_compound_expr_initializer (tree, tree);
 static void push_string (const char *);
 static void push_member_name (tree);
@@ -1193,6 +1197,39 @@ comptypes_verify (tree type1, tree type2)
 return true;
 }
+
+/* Instrument assignment of variably modified types. */
+
+static tree
+c_instrument_vm_assign (location_t loc, tree a, tree b)
+{
+ gcc_assert (flag_vla_bounds);
+
+ gcc_assert (TREE_CODE (a) == ARRAY_TYPE);
+ gcc_assert (TREE_CODE (b) == ARRAY_TYPE);
+
+ tree as = TYPE_MAX_VALUE (TYPE_DOMAIN (a));
+ tree bs = TYPE_MAX_VALUE (TYPE_DOMAIN (b));
+
+ as = fold_build2 (PLUS_EXPR, sizetype, as, size_one_node);
+ bs = fold_build2 (PLUS_EXPR, sizetype, bs, size_one_node);
+
+ tree t = build2 (NE_EXPR, boolean_type_node, as, bs);
+ tree tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+
+ return build3 (COND_EXPR, void_type_node, t, tt, void_node);
+}
+
+
+
+struct instrument_data {
+
+ tree t1;
+ tree t2;
+ struct instrument_data *next;
+};
+
+
 struct comptypes_data {
 bool enum_and_int_p;
 bool different_types_p;
@@ -1202,6 +1239,8 @@ struct comptypes_data {
 bool equiv;
 const struct tagged_tu_seen_cache* cache;
+
+ struct instrument_data **instr_vec;
 };
 /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
@@ -1241,11 +1280,14 @@ comptypes_same_p (tree type1, tree type2)
 /* Like comptypes, but if it returns non-zero because enum and int are
 compatible, it sets *ENUM_AND_INT_P to true. */
-int
-comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
+static int
+comptypes_check_enum_int_instr (tree type1, tree type2, bool *enum_and_int_p,
+ struct instrument_data **instr_vec)
 {
 struct comptypes_data data = { };
+ data.instr_vec = instr_vec;
 bool ret = comptypes_internal (type1, type2, &data);
+
 *enum_and_int_p = data.enum_and_int_p;
 gcc_checking_assert (!ret || comptypes_verify (type1, type2));
@@ -1253,6 +1295,12 @@ comptypes_check_enum_int (tree type1, tree type2, bool 
*enum_and_int_p)
 return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
+int
+comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
+{
+ return comptypes_check_enum_int_instr (type1, type2, enum_and_int_p, NULL);
+}
+
 /* Like comptypes, but if it returns nonzero for different types, it
 sets *DIFFERENT_TYPES_P to true. */
@@ -1465,7 +1513,18 @@ comptypes_internal (const_tree type1, const_tree type2,
 if (d1_variable != d2_variable)
 data->different_types_p = true;
 if (d1_variable || d2_variable)
- return true;
+ {
+ if (NULL != data->instr_vec)
+ {
+ struct instrument_data *id
+ = (struct instrument_data *)xmalloc (sizeof *id);;
+ id->t1 = TYPE_MAIN_VARIANT (t2);
+ id->t2 = TYPE_MAIN_VARIANT (t1);
+ id->next = *data->instr_vec;
+ *data->instr_vec = id;
+ }
+ return true;
+ }
 if (d1_zero && d2_zero)
 return true;
 if (d1_zero || d2_zero
@@ -1501,7 +1560,8 @@ comptypes_internal (const_tree type1, const_tree type2,
 subset of the other. */
 static bool
-comp_target_types (location_t location, tree ttl, tree ttr)
+comp_target_types_instr (location_t location, tree ttl, tree ttr,
+ struct instrument_data **instr_vec)
 {
 int val;
 int val_ped;
@@ -1535,8 +1595,7 @@ comp_target_types (location_t location, tree ttl, tree 
ttr)
 ? c_build_qualified_type (TYPE_MAIN_VARIANT (mvr), TYPE_QUAL_ATOMIC)
 : TYPE_MAIN_VARIANT (mvr));
- enum_and_int_p = false;
- val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p);
+ val = comptypes_check_enum_int_instr (mvl, mvr, &enum_and_int_p, instr_vec);
 if (val == 1 && val_ped != 1)
 pedwarn_c11 (location, OPT_Wpedantic, "invalid use of pointers to arrays with 
different qualifiers "
@@ -1551,6 +1610,13 @@ comp_target_types (location_t location, tree ttl, tree 
ttr)
 return val;
 }
+
+static int
+comp_target_types (location_t location, tree ttl, tree ttr)
+{
+ return comp_target_types_instr (location, ttl, ttr, NULL);
+}
+
 
 /* Subroutines of `comptypes'. */
@@ -1815,8 +1881,13 @@ function_types_compatible_p (const_tree f1, const_tree 
f2,
 return val;
 }
- /* Both types have argument lists: compare them and propagate results. */
+ /* Both types have argument lists: compare them and propagate results.
+ Turn off instrumentation for bounds as these are all arrays of
+ unspecified size. */
+ auto instr_vec_tmp = data->instr_vec;
+ data->instr_vec = NULL;
 val1 = type_lists_compatible_p (args1, args2, data);
+ data->instr_vec = instr_vec_tmp;
 return val1;
 }
@@ -3857,10 +3928,11 @@ convert_argument (location_t ploc, tree function, tree 
fundecl,
 if (excess_precision)
 val = build1 (EXCESS_PRECISION_EXPR, valtype, val);
- tree parmval = convert_for_assignment (ploc, ploc, type,
- val, origtype, ic_argpass,
- npc, fundecl, function,
- parmnum + 1, warnopt);
+ tree parmval = convert_for_assignment_instrument (ploc, ploc, type,
+ val, origtype, ic_argpass,
+ npc, fundecl, function,
+ parmnum + 1, warnopt,
+ NULL);
 if (targetm.calls.promote_prototypes (fundecl ? TREE_TYPE (fundecl) : 0)
 && INTEGRAL_TYPE_P (type)
@@ -3870,6 +3942,24 @@ convert_argument (location_t ploc, tree function, tree 
fundecl,
 return parmval;
 }
+
+/* Process all constraints for variably-modified types. */
+
+static tree
+process_vm_constraints (location_t location,
+ struct instrument_data **instr_vec)
+{
+ tree instr_expr = void_node;
+
+ for (struct instrument_data* d = *instr_vec; d; d = d->next)
+ {
+ tree in = c_instrument_vm_assign (location, d->t1, d->t2);
+ instr_expr = fold_build2 (COMPOUND_EXPR, void_type_node, in, instr_expr);
+ }
+ return instr_expr;
+}
+
+
 /* Convert the argument expressions in the vector VALUES
 to the types in the list TYPELIST.
@@ -7313,7 +7403,50 @@ static tree
 convert_for_assignment (location_t location, location_t expr_loc, tree type,
 tree rhs, tree origtype, enum impl_conv errtype,
 bool null_pointer_constant, tree fundecl,
- tree function, int parmnum, int warnopt /* = 0 */)
+ tree function, int parmnum, int warnopt)
+{
+ struct instrument_data *instr_first = NULL;
+ struct instrument_data **instr_vec = NULL;
+
+ if (flag_vla_bounds && (ic_init_const != errtype))
+ instr_vec = &instr_first;
+
+ tree ret = convert_for_assignment_instrument (location, expr_loc, type,
+ rhs, origtype, errtype,
+ null_pointer_constant, fundecl,
+ function, parmnum, warnopt,
+ instr_vec);
+ if (instr_vec)
+ {
+ if (ret && ret != error_mark_node && instr_first != NULL)
+ {
+ /* We have to make sure that the rhs is evaluated first,
+ because we may use size expressions in it to check bounds. */
+ tree instr_expr = process_vm_constraints (location, instr_vec);
+ if (instr_expr != void_node)
+ {
+ ret = save_expr (ret);
+ instr_expr = fold_build2 (COMPOUND_EXPR, void_type_node, ret, instr_expr);
+ ret = fold_build2 (COMPOUND_EXPR, TREE_TYPE (ret), instr_expr, ret);
+ }
+ }
+ while (instr_first)
+ {
+ struct instrument_data *next = instr_first->next;
+ free (instr_first);
+ instr_first = next;
+ }
+ instr_vec = NULL;
+ }
+ return ret;
+}
+
+static tree
+convert_for_assignment_instrument (location_t location, location_t expr_loc, 
tree type,
+ tree rhs, tree origtype, enum impl_conv errtype,
+ bool null_pointer_constant, tree fundecl,
+ tree function, int parmnum, int warnopt,
+ struct instrument_data **instr_vec)
 {
 enum tree_code codel = TREE_CODE (type);
 tree orig_rhs = rhs;
@@ -7557,11 +7690,11 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
 rhs = build1 (ADDR_EXPR, 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)),
- rhs, origtype, errtype,
- null_pointer_constant, fundecl, function,
- parmnum, warnopt);
+ rhs = convert_for_assignment_instrument (location, expr_loc,
+ build_pointer_type (TREE_TYPE (type)),
+ rhs, origtype, errtype,
+ null_pointer_constant, fundecl, function,
+ parmnum, warnopt, instr_vec);
 if (rhs == error_mark_node)
 return error_mark_node;
@@ -7955,7 +8088,7 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
 Meanwhile, the lhs target must have all the qualifiers of the rhs. */
 if ((VOID_TYPE_P (ttl) && !TYPE_ATOMIC (ttl))
 || (VOID_TYPE_P (ttr) && !TYPE_ATOMIC (ttr))
- || (target_cmp = comp_target_types (location, type, rhstype))
+ || (target_cmp = comp_target_types_instr (location, type, rhstype, instr_vec))
 || is_opaque_pointer
 || ((c_common_unsigned_type (mvl)
 == c_common_unsigned_type (mvr))
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 403ea9da1ab..8d6d68e4f27 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10698,6 +10698,7 @@ void g (int n)
 @option{-Warray-parameter} option triggers warnings for similar problems
 involving ordinary array arguments.
+
 @opindex Wvolatile-register-var
 @opindex Wno-volatile-register-var
 @item -Wvolatile-register-var
@@ -20831,6 +20832,14 @@ computing CRC32).
 The @var{string} should be different for every file you compile.
+@opindex fvla-bounds
+@item -fvla-bounds
+This option is only available when compiling C code. If activated,
+additional code is emitted that verifies at run time for assignments
+involving variably-modified types that corresponding size expressions
+evaluate to the same value.
+
+
 @opindex save-temps
 @item -save-temps
 Store the usual ``temporary'' intermediate files permanently; name them
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-1.c 
b/gcc/testsuite/gcc.dg/vla-bounds-1.c
new file mode 100644
index 00000000000..093f5e7ba25
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-1.c
@@ -0,0 +1,74 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+// test various valid initializations and assignments
+
+int main()
+{
+ int m = 4, n = 3;
+
+ int u = 4;
+ int v = 3;
+
+ /* initialization */
+
+ char a[4];
+ char (*pa)[u] = &a;
+ char (*qa)[v];
+
+ char b[u];
+ const char (*pb)[u] = &b;
+ char (*qb)[v];
+
+ char c[4][3];
+ char (*pc0)[u][v] = &c;
+ char (*qc0)[v][u];
+
+ char (*pc1)[u][3] = &c;
+ char (*qc1)[v][3];
+
+ char (*pc2)[4][v] = &c;
+ char (*qc2)[4][u];
+
+ char (*pc3)[][v] = &c; 
+
+ char d[u][v];
+ char (*pd0)[4][3] = &d;
+ char (*qd0)[3][4];
+
+ char (*pd1)[u][3] = &d; 
+ char (*qd1)[v][4];
+
+ char (*pd2)[4][v] = &d;
+ char (*qd2)[3][u];
+
+ char (*pd3)[u][v] = &d;
+ char (*qd3)[v][u];
+
+ char e[4][v];
+ char (*pe0)[4][3] = &e;
+ char (*qe0)[4][4];
+
+ char f[u][3];
+ char (*pf)[4][3] = &f;
+ char (*qf)[3][3];
+
+ char (*g[u])[v];
+ char (*(*pg)[u])[v] = &g;
+
+ /* assignment */
+
+ pa = &a;
+ pb = &b;
+ pc0 = &c;
+ pc1 = &c;
+ pc2 = &c;
+ pd0 = &d;
+ pd1 = &d;
+ pd2 = &d;
+ pd3 = &d;
+ pe0 = &e;
+ pf = &f;
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c 
b/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
new file mode 100644
index 00000000000..ffef7e502dd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid assignments involving pointers to arrays of
+ variable length. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ char a[4];
+ char (*pa)[u];
+ char (*qa)[v];
+
+ char b[u];
+ const char (*pb)[u];
+
+ pa = &a;
+ pb = &b;
+ qa = &a; // 3 != 4
+ abort();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c 
b/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
new file mode 100644
index 00000000000..cc445dea459
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
@@ -0,0 +1,30 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid assignment of a pointer to a 2D array to pointers
+ to 2D arrays of variable length. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ char c[4][3];
+ char (*pc0)[u][v];
+ char (*qc0)[v][u];
+ char (*pc1)[u][3];
+ char (*pc2)[4][v];
+
+ pc0 = &c;
+ pc1 = &c;
+ pc2 = &c;
+ qc0 = &c; // 3 != 4, 4 != 3
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c 
b/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
new file mode 100644
index 00000000000..068e9a57e65
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid assignment of a pointer to a 2D VLA to pointers
+ to 2D arrays of variable length. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ char d[u][v];
+ char (*pd0)[4][3];
+ char (*pd1)[u][3]; 
+ char (*pd2)[4][v];
+ char (*pd3)[u][v];
+
+ char e[4][v];
+ char (*pe0)[4][3];
+
+ char f[u][3];
+ char (*pf)[4][3];
+ char (*qf)[3][3];
+
+ /* assignment */
+
+ pd0 = &d;
+ pd1 = &d;
+ pd2 = &d;
+ pd3 = &d;
+ pe0 = &e;
+
+ pf = &f;
+ qf = &f; // 3 != 4
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c 
b/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
new file mode 100644
index 00000000000..b274e237eba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
@@ -0,0 +1,26 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test assignment of pointer to function returning 2D array to pointer to
+ function returning a pointer to a variable length array. */
+
+static void handler(int) { exit(0); }
+
+int m, n;
+static char (*z0(void))[5][5] { char (*p)[m][n] = 0; return p; }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ m = 4, n = 3;
+
+ int u = 4;
+ int v = 3;
+
+ char (*(*p)(void))[u][v] = &z0; // 4 != 5, 3 != 5
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-func-1.c 
b/gcc/testsuite/gcc.dg/vla-bounds-func-1.c
new file mode 100644
index 00000000000..8256999fc50
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-func-1.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-options "-fvla-bounds" } */
+
+// make sure we do not ICE on any of these
+
+const char* name = "hallo";
+
+typedef void (*ht)(int n, int m, char x[n][m]);
+void e(ht) { }
+
+int n, m;
+static void f0(char a[n][m]) { }
+static void f1(int u, int v, char a[u][v]) { }
+static void f2(int u, int v, char a[u][v]) { }
+
+void f(void)
+{
+ int x = 1;
+ int (*m)[x] = 0;
+ m = ({ long* d2; (int (*)[d2[0]])(0); });
+
+ /* function pointer assignments */
+
+ void (*gp)(char x[4][3]) = f0;
+ void (*hp)(int n, int m, char x[n][m]) = f1;
+ ht hp2 = f1;
+ e(f1);
+
+ /* composite type */
+
+ int u = 3; int v = 4;
+ char a[u][v];
+ (1 ? f1 : f2)(u, v, a);
+}
+
+/* size expression in parameter */
+
+extern void a(long N, char (*a)[N]);
+
+static void b(void)
+{
+ a(1, ({ int d = 0; (char (*)[d])0; }) );
+}
+
+/* composite type */
+
+int c(int u, char (*a)[u]);
+int c(int u, char (*a)[u]) { }
+
+int d(void)
+{
+ char a[3];
+ c(3, &a);
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-1.c 
b/gcc/testsuite/gcc.dg/vla-bounds-init-1.c
new file mode 100644
index 00000000000..3e608ba0c34
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-init-1.c
@@ -0,0 +1,26 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid initialization of pointers to arrays of
+ variable length. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ char b[u];
+ const char (*pb)[u] = &b;
+
+ char a[4];
+ char (*pa)[u] = &a;
+ char (*qa)[v] = &a; // 3 != 4
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-2.c 
b/gcc/testsuite/gcc.dg/vla-bounds-init-2.c
new file mode 100644
index 00000000000..d26683e6c97
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-init-2.c
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid initialization of pointers to 2D arrays of
+ variable length with pointer to 2D regular array. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ char c[4][3];
+ char (*pc1)[u][3] = &c;
+ char (*pc2)[4][v] = &c;
+ char (*pc0)[u][v] = &c;
+ char (*qc0)[v][u] = &c; // 3 != 4, 4 != 3
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-3.c 
b/gcc/testsuite/gcc.dg/vla-bounds-init-3.c
new file mode 100644
index 00000000000..7ab706af62e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-init-3.c
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid initialization of pointers to 2D arrays of
+ variable length with incomplete first dimension. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ /* initialization */
+
+ char c[4][3];
+ char (*pc3)[][v] = &c; 
+ char (*qc3)[][u] = &c; // 4 != 3
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-4.c 
b/gcc/testsuite/gcc.dg/vla-bounds-init-4.c
new file mode 100644
index 00000000000..f20de91c156
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-init-4.c
@@ -0,0 +1,27 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid initialization of pointers to 2D arrays of
+ variable length with pointer to 2D VLA. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ /* initialization */
+ char d[u][v];
+ char (*pd0)[4][3] = &d;
+ char (*pd1)[u][3] = &d; 
+ char (*pd2)[4][v] = &d;
+ char (*pd3)[u][v] = &d;
+ char (*qd0)[3][4] = &d; // 3 != 4, 4 != 3
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c 
b/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
new file mode 100644
index 00000000000..7f33bc95e29
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+/* { dg-require-effective-target trampolines } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid assignment of a nested function mit variably
+ modified return type. */
+
+void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int n = 3;
+ char b[4];
+ char (*f())[++n] { return &b; }
+
+ if (4 != sizeof(*f()))
+ abort();
+
+ char (*(*p)())[n++] = &f;
+
+ if (4 != sizeof(*(*p)()))
+ abort();
+
+ char (*(*p2)())[++n] = &f;
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c 
b/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
new file mode 100644
index 00000000000..13f77d4e185
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
@@ -0,0 +1,26 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+/* { dg-require-effective-target trampolines } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid assignment of regular function pointer
+ return array to pointer to function with variably modified
+ return type. */
+
+void handler(int) { exit(0); }
+
+static char bb[4][4];
+static char (*g())[4][4] { return &bb; }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int n = 4;
+
+ char (*(*p)())[n][4] = &g;
+ char (*(*q)())[++n][4] = &g;;
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c 
b/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
new file mode 100644
index 00000000000..43919927ab8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
@@ -0,0 +1,27 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test returning a pointer to a 2D variable length array in
+ a function returning a pointer to a regular 2D array. */
+
+static void handler(int) { exit(0); }
+
+int m, n;
+
+static char (*z0(void))[5][5] { char (*p)[m][n] = 0; return p; }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ m = 5, n = 5;
+ z0();
+
+ m = 4, n = 3;
+ z0(); // 5 != 3, 5, != 3
+ abort();
+}
+
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c 
b/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
new file mode 100644
index 00000000000..af8475e9a5f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test returning a pointer to a 2D variable length array with
+ a fist constant dimension in a function returning a pointer
+ to a regular 2D array. */
+
+static void handler(int) { exit(0); }
+
+int n, m;
+
+static char (*z1(void))[5][5] { char (*p)[5][n] = 0; return p; }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ m = 5, n = 5;
+ z1();
+
+ m = 4, n = 3;
+ z1(); // 4 != 3
+ abort();
+}
+
-- 
2.39.2



Reply via email to