Here if we return immediately after the diagnostic about unsupported
variably-modified type capture, we avoid the crash.

I'm also changing the diagnostic from error to sorry, since it would
make sense to support this usage and apparently Clang already does.

The second patch below is the beginning of work for more general
variably-modified type capture, based on the approach of capturing and
remapping all the uses of outer automatic vars in the VLA type when
the VLA variable is captured.  I've also thought about handling this
later, when we actually do something that involves the dimensions, but
currently that's mostly hidden in ARRAY_REF.

First patch tested x86_64-pc-linux-gnu, applying to trunk.
commit c77d362bbe6abdfdf486b061cf2ec5897723dba9
Author: Jason Merrill <ja...@redhat.com>
Date:   Sun Apr 8 14:15:35 2018 -0400

            PR c++/85256 - ICE capturing pointer to VLA.
    
            * lambda.c (add_capture): Distinguish between variable-size and
            variably-modified types.

diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 374060626c1..e9b962a8f33 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -554,13 +554,13 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
   else if (!dependent_type_p (type)
 	   && variably_modified_type_p (type, NULL_TREE))
     {
-      error ("capture of variable-size type %qT that is not an N3639 array "
+      sorry ("capture of variably-modified type %qT that is not an N3639 array "
 	     "of runtime bound", type);
       if (TREE_CODE (type) == ARRAY_TYPE
 	  && variably_modified_type_p (TREE_TYPE (type), NULL_TREE))
 	inform (input_location, "because the array element type %qT has "
 		"variable size", TREE_TYPE (type));
-      type = error_mark_node;
+      return error_mark_node;
     }
   else
     {
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-vla2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-vla2.C
index d4de131fc23..aee9694852d 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-vla2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-vla2.C
@@ -7,6 +7,6 @@ void f() {
   int m = 1;
   int d[n][m];
   [&]() {
-    return d[1];		// { dg-error "variabl" }
+    return d[1];		// { dg-prune-output "sorry" }
   }();
 }
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-vla3.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-vla3.C
new file mode 100644
index 00000000000..eebdbcdbb93
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-vla3.C
@@ -0,0 +1,10 @@
+// PR c++/85256
+// { dg-do compile { target c++11 } }
+// { dg-additional-options -Wno-vla }
+
+void foo(int i)
+{
+  int (*x)[i];
+  [=]{ [=]{ 0 ? x : x; }; };	// { dg-bogus "sorry" "" { xfail *-*-* } }
+
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/vla7.C b/gcc/testsuite/g++.dg/cpp1y/vla7.C
index df34c8219db..afa5fac508d 100644
--- a/gcc/testsuite/g++.dg/cpp1y/vla7.C
+++ b/gcc/testsuite/g++.dg/cpp1y/vla7.C
@@ -6,7 +6,7 @@ int main(int argc, char** argv)
 {
   int x[1][argc];
 
-  [&x](int i) {			// { dg-error "variable.size" }
-    x[0][i] = 0;	     	// { dg-prune-output "assignment" }
+  [&x](int i) {			// { dg-prune-output "sorry" }
+    x[0][i] = 0;	     	// { dg-prune-output "not captured" }
   }(5);
 }
diff --git a/gcc/testsuite/g++.dg/cpp1y/vla9.C b/gcc/testsuite/g++.dg/cpp1y/vla9.C
index 939de30a3c1..2c5b3a5404e 100644
--- a/gcc/testsuite/g++.dg/cpp1y/vla9.C
+++ b/gcc/testsuite/g++.dg/cpp1y/vla9.C
@@ -25,7 +25,7 @@ int main(){
     fa[0][1]=1.8;
     auto fx=[&](){
         for(int c=0; c<2; c++){
-            printf("use me", fa[0][c]);	// { dg-error "capture of variable-size type" }
+            printf("use me", fa[0][c]);	// { dg-prune-output "sorry" }
         }
     };
     call(fx);
commit 2918e20db4270ced5705eabac37162c7068c1be7
Author: Jason Merrill <ja...@redhat.com>
Date:   Sun Apr 8 14:19:00 2018 -0400

    no-vla

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 204791e51cf..a7db19da65f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -441,7 +441,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
    5: CLASS_TYPE_P (in RECORD_TYPE and UNION_TYPE)
       ENUM_FIXED_UNDERLYING_TYPE_P (in ENUMERAL_TYPE)
       AUTO_IS_DECLTYPE (in TEMPLATE_TYPE_PARM)
-      REFERENCE_VLA_OK (in REFERENCE_TYPE)
    6: TYPE_DEPENDENT_P_VALID
 
    Usage of DECL_LANG_FLAG_?:
@@ -455,7 +454,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       DECL_TEMPLATE_INSTANTIATED (in a VAR_DECL or a FUNCTION_DECL)
       DECL_MEMBER_TEMPLATE_P (in TEMPLATE_DECL)
       USING_DECL_TYPENAME_P (in USING_DECL)
-      DECL_VLA_CAPTURE_P (in FIELD_DECL)
       DECL_ARRAY_PARAMETER_P (in PARM_DECL)
       LABEL_DECL_CONTINUE (in LABEL_DECL)
    2: DECL_THIS_EXTERN (in VAR_DECL or FUNCTION_DECL).
@@ -3632,11 +3630,6 @@ extern void decl_shadowed_for_var_insert (tree, tree);
    && (TREE_CODE (TREE_TYPE (TREE_OPERAND ((NODE), 0)))	\
        == REFERENCE_TYPE))
 
-/* True if NODE is a REFERENCE_TYPE which is OK to instantiate to be a
-   reference to VLA type, because it's used for VLA capture.  */
-#define REFERENCE_VLA_OK(NODE) \
-  (TYPE_LANG_FLAG_5 (REFERENCE_TYPE_CHECK (NODE)))
-
 #define NEW_EXPR_USE_GLOBAL(NODE) \
   TREE_LANG_FLAG_0 (NEW_EXPR_CHECK (NODE))
 #define DELETE_EXPR_USE_GLOBAL(NODE) \
@@ -4403,11 +4396,6 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define DECL_THIS_STATIC(NODE) \
   DECL_LANG_FLAG_6 (VAR_FUNCTION_OR_PARM_DECL_CHECK (NODE))
 
-/* Nonzero for FIELD_DECL node means that this field is a lambda capture
-   field for an array of runtime bound.  */
-#define DECL_VLA_CAPTURE_P(NODE) \
-  DECL_LANG_FLAG_1 (FIELD_DECL_CHECK (NODE))
-
 /* Nonzero for PARM_DECL node means that this is an array function
    parameter, i.e, a[] rather than *a.  */
 #define DECL_ARRAY_PARAMETER_P(NODE) \
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index e9b962a8f33..ea1641f1690 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -89,7 +89,6 @@ build_lambda_object (tree lambda_expr)
       if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE)
 	val = build_array_copy (val);
       else if (DECL_NORMAL_CAPTURE_P (field)
-	       && !DECL_VLA_CAPTURE_P (field)
 	       && TREE_CODE (TREE_TYPE (field)) != REFERENCE_TYPE)
 	{
 	  /* "the entities that are captured by copy are used to
@@ -275,10 +274,6 @@ is_normal_capture_proxy (tree decl)
     /* It's not a capture proxy.  */
     return false;
 
-  if (variably_modified_type_p (TREE_TYPE (decl), NULL_TREE))
-    /* VLA capture.  */
-    return true;
-
   /* It is a capture proxy, is it a normal capture?  */
   tree val = DECL_VALUE_EXPR (decl);
   if (val == error_mark_node)
@@ -418,20 +413,6 @@ build_capture_proxy (tree member, tree init)
       object = build_fold_addr_expr_with_type (object, type);
     }
 
-  if (DECL_VLA_CAPTURE_P (member))
-    {
-      /* Rebuild the VLA type from the pointer and maxindex.  */
-      tree field = next_initializable_field (TYPE_FIELDS (type));
-      tree ptr = build_simple_component_ref (object, field);
-      field = next_initializable_field (DECL_CHAIN (field));
-      tree max = build_simple_component_ref (object, field);
-      type = build_cplus_array_type (TREE_TYPE (TREE_TYPE (ptr)),
-				     build_index_type (max));
-      type = build_reference_type (type);
-      REFERENCE_VLA_OK (type) = true;
-      object = convert (type, ptr);
-    }
-
   complete_type (type);
 
   var = build_decl (input_location, VAR_DECL, name, type);
@@ -443,18 +424,8 @@ build_capture_proxy (tree member, tree init)
 
   if (DECL_NORMAL_CAPTURE_P (member))
     {
-      if (DECL_VLA_CAPTURE_P (member))
-	{
-	  init = CONSTRUCTOR_ELT (init, 0)->value;
-	  init = TREE_OPERAND (init, 0); // Strip ADDR_EXPR.
-	  init = TREE_OPERAND (init, 0); // Strip ARRAY_REF.
-	}
-      else
-	{
-	  if (PACK_EXPANSION_P (init))
-	    init = PACK_EXPANSION_PATTERN (init);
-	}
-
+      if (PACK_EXPANSION_P (init))
+	init = PACK_EXPANSION_PATTERN (init);
       if (INDIRECT_REF_P (init))
 	init = TREE_OPERAND (init, 0);
       STRIP_NOPS (init);
@@ -480,31 +451,6 @@ build_capture_proxy (tree member, tree init)
   return var;
 }
 
-static GTY(()) tree ptr_id;
-static GTY(()) tree max_id;
-
-/* Return a struct containing a pointer and a length for lambda capture of
-   an array of runtime length.  */
-
-static tree
-vla_capture_type (tree array_type)
-{
-  tree type = xref_tag (record_type, make_anon_name (), ts_current, false);
-  xref_basetypes (type, NULL_TREE);
-  type = begin_class_definition (type);
-  if (!ptr_id)
-    {
-      ptr_id = get_identifier ("ptr");
-      max_id = get_identifier ("max");
-    }
-  tree ptrtype = build_pointer_type (TREE_TYPE (array_type));
-  tree field = build_decl (input_location, FIELD_DECL, ptr_id, ptrtype);
-  finish_member_declaration (field);
-  field = build_decl (input_location, FIELD_DECL, max_id, sizetype);
-  finish_member_declaration (field);
-  return finish_struct (type, NULL_TREE);
-}
-
 /* From an ID and INITIALIZER, create a capture (by reference if
    BY_REFERENCE_P is true), add it to the capture-list for LAMBDA,
    and return it.  If ID is `this', BY_REFERENCE_P says whether
@@ -516,7 +462,6 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
 {
   char *buf;
   tree type, member, name;
-  bool vla = false;
   bool variadic = false;
   tree initializer = orig_init;
 
@@ -535,67 +480,46 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
   if (type == error_mark_node)
     return error_mark_node;
 
-  if (array_of_runtime_bound_p (type))
+  if (!by_reference_p
+      && !dependent_type_p (type)
+      && !TREE_CONSTANT (TYPE_SIZE (type)))
     {
-      vla = true;
-      if (!by_reference_p)
-	error ("array of runtime bound cannot be captured by copy, "
-	       "only by reference");
-
-      /* For a VLA, we capture the address of the first element and the
-	 maximum index, and then reconstruct the VLA for the proxy.  */
-      tree elt = cp_build_array_ref (input_location, initializer,
-				     integer_zero_node, tf_warning_or_error);
-      initializer = build_constructor_va (init_list_type_node, 2,
-					  NULL_TREE, build_address (elt),
-					  NULL_TREE, array_type_nelts (type));
-      type = vla_capture_type (type);
+      error ("variable-size type cannot be captured by copy, "
+	     "only by reference");
+      by_reference_p = true;
     }
-  else if (!dependent_type_p (type)
-	   && variably_modified_type_p (type, NULL_TREE))
+
+  type = lambda_capture_field_type (initializer, explicit_init_p,
+				    by_reference_p);
+  if (type == error_mark_node)
+    return error_mark_node;
+
+  if (id == this_identifier && !by_reference_p)
     {
-      sorry ("capture of variably-modified type %qT that is not an N3639 array "
-	     "of runtime bound", type);
-      if (TREE_CODE (type) == ARRAY_TYPE
-	  && variably_modified_type_p (TREE_TYPE (type), NULL_TREE))
-	inform (input_location, "because the array element type %qT has "
-		"variable size", TREE_TYPE (type));
-      return error_mark_node;
+      gcc_assert (POINTER_TYPE_P (type));
+      type = TREE_TYPE (type);
+      initializer = cp_build_fold_indirect_ref (initializer);
     }
-  else
-    {
-      type = lambda_capture_field_type (initializer, explicit_init_p,
-					by_reference_p);
-      if (type == error_mark_node)
-	return error_mark_node;
-
-      if (id == this_identifier && !by_reference_p)
-	{
-	  gcc_assert (POINTER_TYPE_P (type));
-	  type = TREE_TYPE (type);
-	  initializer = cp_build_fold_indirect_ref (initializer);
-	}
 
-      if (dependent_type_p (type))
-	;
-      else if (id != this_identifier && by_reference_p)
+  if (dependent_type_p (type))
+    ;
+  else if (id != this_identifier && by_reference_p)
+    {
+      if (!lvalue_p (initializer))
 	{
-	  if (!lvalue_p (initializer))
-	    {
-	      error ("cannot capture %qE by reference", initializer);
-	      return error_mark_node;
-	    }
+	  error ("cannot capture %qE by reference", initializer);
+	  return error_mark_node;
 	}
-      else
+    }
+  else
+    {
+      /* Capture by copy requires a complete type.  */
+      type = complete_type (type);
+      if (!COMPLETE_TYPE_P (type))
 	{
-	  /* Capture by copy requires a complete type.  */
-	  type = complete_type (type);
-	  if (!COMPLETE_TYPE_P (type))
-	    {
-	      error ("capture by copy of incomplete type %qT", type);
-	      cxx_incomplete_type_inform (type);
-	      return error_mark_node;
-	    }
+	  error ("capture by copy of incomplete type %qT", type);
+	  cxx_incomplete_type_inform (type);
+	  return error_mark_node;
 	}
     }
 
@@ -627,7 +551,6 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
 
   /* Make member variable.  */
   member = build_decl (input_location, FIELD_DECL, name, type);
-  DECL_VLA_CAPTURE_P (member) = vla;
 
   if (!explicit_init_p)
     /* Normal captures are invisible to name lookup but uses are replaced

commit 2ff0bb13b2d6152e1247818dfc5c320c20845134
Author: Jason Merrill <ja...@redhat.com>
Date:   Sat Apr 7 00:33:28 2018 -0400

    vla-cap

diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index ea1641f1690..7bc88be4b0b 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "cgraph.h"
 #include "tree-iterator.h"
+#include "tree-inline.h"
 #include "toplev.h"
 #include "gimplify.h"
 
@@ -279,6 +280,7 @@ is_normal_capture_proxy (tree decl)
   if (val == error_mark_node)
     return true;
 
+  STRIP_NOPS (val);
   if (TREE_CODE (val) == ADDR_EXPR)
     val = TREE_OPERAND (val, 0);
   gcc_assert (TREE_CODE (val) == COMPONENT_REF);
@@ -379,6 +381,64 @@ lambda_proxy_type (tree ref)
   return type;
 }
 
+/* Data for captured_vla_type.  */
+
+struct vla_cap_data
+{
+  copy_body_data id;
+  hash_set<tree> visited;
+  hash_map<tree,tree> decl_map;
+
+  vla_cap_data() : id()
+  {
+    id.dst_fn = current_function_decl;
+    id.decl_map = &decl_map;
+    id.do_not_unshare = true;
+    id.prevent_decl_creation_for_types = true;
+  }
+};
+
+/* Tree walker for captured_vla_type.  */
+
+static tree
+capture_outer_vars_r (tree *tp, int *walk_subtrees, void *data_)
+{
+  vla_cap_data *data = reinterpret_cast<vla_cap_data*>(data_);
+  tree t = *tp;
+  if (TYPE_P (t))
+    {
+      /* walk_type_fields doesn't walk into the TYPE_MAX_VALUE of an
+	 INTEGER_TYPE, so we need to do something explicit.  Walking into
+	 TYPE_SIZE of the outermost variable-size type should do the trick.  */
+      tree size = TYPE_SIZE (t);
+      if (!TREE_CONSTANT (size))
+	{
+	  cp_walk_tree (&size, capture_outer_vars_r, data, &data->visited);
+	  *walk_subtrees = 0;
+	}
+    }
+  else if (outer_automatic_var_p (t))
+    {
+      tree r = process_outer_var_ref (t, tf_warning_or_error, /*odr*/false);
+      insert_decl_map (&data->id, t, r);
+    }
+  return NULL_TREE;
+}
+
+/* TYPE is a variably-modified type; replace all occurrences of outer automatic
+   variables with their capture proxies.  */
+
+static tree
+captured_vla_type (tree type)
+{
+  vla_cap_data data;
+
+  cp_walk_tree (&type, capture_outer_vars_r, &data, &data.visited);
+
+  tree r = remap_type (type, &data.id);
+  return r;
+}
+
 /* MEMBER is a capture field in a lambda closure class.  Now that we're
    inside the operator(), build a placeholder var for future lookups and
    debugging.  */
@@ -413,6 +473,13 @@ build_capture_proxy (tree member, tree init)
       object = build_fold_addr_expr_with_type (object, type);
     }
 
+  if (!dependent_type_p (type)
+      && variably_modified_type_p (type, NULL_TREE))
+    {
+      type = captured_vla_type (type);
+      object = build_nop (type, object);
+    }
+
   complete_type (type);
 
   var = build_decl (input_location, VAR_DECL, name, type);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 59cac77f6b7..b71c5dc0e6e 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3343,8 +3343,10 @@ outer_automatic_var_p (tree decl)
 tree
 process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use)
 {
-  if (cp_unevaluated_operand)
-    /* It's not a use (3.2) if we're in an unevaluated context.  */
+  if (cp_unevaluated_operand
+      && !variably_modified_type_p (TREE_TYPE (decl), NULL_TREE))
+    /* It's not a use (3.2) if we're in an unevaluated context.
+       Except that sizeof a C VLA is actually evaluated.  */
     return decl;
   if (decl == error_mark_node)
     return decl;
diff --git a/gcc/testsuite/g++.dg/ext/vla20.C b/gcc/testsuite/g++.dg/ext/vla20.C
new file mode 100644
index 00000000000..0009099a30d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/vla20.C
@@ -0,0 +1,11 @@
+// PR c++/85256
+// { dg-do compile { target c++11 } }
+// { dg-additional-options -Wno-vla }
+
+void foo(int i)
+{
+  int (*x)[i][i];
+  [=]{
+    (*x)[1][1] = 42;
+  }();
+}
diff --git a/gcc/tree.c b/gcc/tree.c
index 8ae9ec800b8..8ca39df6d19 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -8757,7 +8757,8 @@ get_type_static_bounds (const_tree type, mpz_t min, mpz_t max)
 bool
 auto_var_in_fn_p (const_tree var, const_tree fn)
 {
-  return (DECL_P (var) && DECL_CONTEXT (var) == fn
+  return (DECL_P (var)
+	  && (fn == NULL_TREE || DECL_CONTEXT (var) == fn)
 	  && ((((VAR_P (var) && ! DECL_EXTERNAL (var))
 		|| TREE_CODE (var) == PARM_DECL)
 	       && ! TREE_STATIC (var))

Reply via email to