This PR points out that nested functions returning VM types don't work as
expected (yeah, go figure).  We got an ICE on the testcase because we were
trying to allocate variable-sized temporary instead of using __builtin_alloca
or its kin.  Jakub suggested to follow what the C++ front end does here.  It
seems to be the case that it creates a TARGET_EXPR if the call doesn't have
a LHS.  That seems to work out well.  The run-time testcase sanity-checks that
we do something reasonable.

Not a regression, but on the other hand the patch doesn't change anything for
99.9% programs out there.

Bootstrapped/regtested on x86_64-linux.

2016-03-08  Marek Polacek  <pola...@redhat.com>

        PR c/70093
        * c-typeck.c (build_function_call_vec): Create a TARGET_EXPR for
        nested functions returning VM types.

        * gcc.dg/nested-func-9.c: New test.

diff --git gcc/c/c-typeck.c gcc/c/c-typeck.c
index 6aa0f03..de9d465 100644
--- gcc/c/c-typeck.c
+++ gcc/c/c-typeck.c
@@ -3068,6 +3068,16 @@ build_function_call_vec (location_t loc, vec<location_t> 
arg_loc,
     result = build_call_array_loc (loc, TREE_TYPE (fntype),
                                   function, nargs, argarray);
 
+  /* In this improbable scenario, a nested function returns a VM type.
+     Create a TARGET_EXPR so that the call always has a LHS, much as
+     what the C++ FE does for functions returning non-PODs.  */
+  if (variably_modified_type_p (TREE_TYPE (fntype), NULL_TREE))
+    {
+      tree tmp = create_tmp_var_raw (TREE_TYPE (fntype));
+      result = build4 (TARGET_EXPR, TREE_TYPE (fntype), tmp, result,
+                      NULL_TREE, NULL_TREE);
+    }
+
   if (VOID_TYPE_P (TREE_TYPE (result)))
     {
       if (TYPE_QUALS (TREE_TYPE (result)) != TYPE_UNQUALIFIED)
diff --git gcc/testsuite/gcc.dg/nested-func-9.c 
gcc/testsuite/gcc.dg/nested-func-9.c
index e69de29..b703f3a 100644
--- gcc/testsuite/gcc.dg/nested-func-9.c
+++ gcc/testsuite/gcc.dg/nested-func-9.c
@@ -0,0 +1,45 @@
+/* PR c/70093 */
+/* { dg-do run } */
+/* { dg-options "" } */
+
+void
+foo (int n)
+{
+  struct S { int a[n]; };
+
+  struct S
+  fn (void)
+  {
+    struct S s;
+    s.a[0] = 42;
+    return s;
+  }
+
+  auto struct S
+  fn2 (void)
+  {
+    return fn ();
+  }
+
+  struct S x;
+  x = fn ();
+
+  if (x.a[0] != 42)
+    __builtin_abort ();
+
+  if (fn ().a[0] != 42)
+    __builtin_abort ();
+
+  __typeof__ (fn ()) *p = &x;
+  if (p->a[0] != 42)
+    __builtin_abort ();
+
+  if (fn2 ().a[0] != 42)
+    __builtin_abort ();
+}
+
+int
+main (void)
+{
+  foo (1);
+}

        Marek

Reply via email to