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