summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>2006-08-21 20:54:57 +0000
committerjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>2006-08-21 20:54:57 +0000
commitc3d09d4da1e9d3d59cf01acc2c4e52c6c764f383 (patch)
tree38fad6e6bb826a12c58767da330650091c1f7cd9
parent0dc9b1a54fab6cd36d3ef64d1f05c03718fd5172 (diff)
downloadgcc-c3d09d4da1e9d3d59cf01acc2c4e52c6c764f383.tar.gz
PR c++/27115
* gimplify.c (voidify_wrapper_expr): Handle STATEMENT_LIST as a wrapper. Loop to handle nested wrappers. (gimplify_bind_expr): Remove temp parameter. (gimplify_modify_expr_rhs): Handle CLEANUP_POINT_EXPR, BIND_EXPR and STATEMENT_LIST on the rhs. (gimplify_statement_list): Voidify the STATEMENT_LIST. (gimplify_expr): Pass pre_p to gimplify_statement_list. (gimplify_target_expr): Remove special BIND_EXPR handling. * cp/semantics.c (finish_stmt_expr_expr): Don't try to voidify here, just leave the expression as it is. (finish_stmt_expr): If the statement-expression has class type, wrap it in a TARGET_EXPR. * cp/cp-gimplify.c (cp_gimplify_init_expr): Don't bother with CLEANUP_POINT_EXPR. * cp/except.c (build_throw): Give the CLEANUP_POINT_EXPR void type. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@116311 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog12
-rw-r--r--gcc/cp/ChangeLog47
-rw-r--r--gcc/cp/cp-gimplify.c7
-rw-r--r--gcc/cp/except.c2
-rw-r--r--gcc/cp/semantics.c96
-rw-r--r--gcc/gimplify.c146
-rw-r--r--gcc/testsuite/g++.dg/abi/forced-sticky.C62
-rw-r--r--gcc/testsuite/g++.dg/abi/forced.C25
-rw-r--r--gcc/testsuite/g++.dg/ext/stmtexpr8.C28
9 files changed, 277 insertions, 148 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index dc3fef912f1..685b4b6daf9 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,15 @@
+2006-08-21 Jason Merrill <jason@redhat.com>
+
+ PR c++/27115
+ * gimplify.c (voidify_wrapper_expr): Handle STATEMENT_LIST as a
+ wrapper. Loop to handle nested wrappers.
+ (gimplify_bind_expr): Remove temp parameter.
+ (gimplify_modify_expr_rhs): Handle CLEANUP_POINT_EXPR, BIND_EXPR
+ and STATEMENT_LIST on the rhs.
+ (gimplify_statement_list): Voidify the STATEMENT_LIST.
+ (gimplify_expr): Pass pre_p to gimplify_statement_list.
+ (gimplify_target_expr): Remove special BIND_EXPR handling.
+
2006-08-21 J"orn Rennecke <joern.rennecke@st.com>
* config/sh/lib1funcs-Os-4-200.asm: Guard entire file with
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 758f877187f..fa7d6bc0e65 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,16 +1,27 @@
+2006-08-21 Jason Merrill <jason@redhat.com>
+
+ PR c++/27115
+ * semantics.c (finish_stmt_expr_expr): Don't try to voidify here,
+ just leave the expression as it is.
+ (finish_stmt_expr): If the statement-expression has class type,
+ wrap it in a TARGET_EXPR.
+ * cp-gimplify.c (cp_gimplify_init_expr): Don't bother with
+ CLEANUP_POINT_EXPR.
+ * except.c (build_throw): Give the CLEANUP_POINT_EXPR void type.
+
2006-08-21 Lee Millward <lee.millward@codesourcery.com>
PR c++/26269
- * decl.c (duplicate_decls): Return early if either
- newdecl or olddecl is error_mark_node.
+ * decl.c (duplicate_decls): Return early if either
+ newdecl or olddecl is error_mark_node.
PR c++/28505
- * decl.c (grokdeclarator): Return early after
- issuing diagnostic about an incomplete type.
+ * decl.c (grokdeclarator): Return early after
+ issuing diagnostic about an incomplete type.
PR c++/28741
- * tree.c (decl_anon_ns_mem_p): Robustify.
- * decl2.c (determine_visibility): Likewise.
+ * tree.c (decl_anon_ns_mem_p): Robustify.
+ * decl2.c (determine_visibility): Likewise.
2006-08-20 Mark Mitchell <mark@codesourcery.com>
@@ -189,18 +200,18 @@
2006-07-28 Lee Millward <lee.millward@codesourcery.com>
- PR c++/27668
- PR c++/27962
- * pt.c (process_template_parm) Store invalid template
- parameters as error_mark_node in the paramater list.
- (push_inline_template_parms_recursive): Handle invalid
- template parameters.
- (comp_template_parms): Likewise.
- (check_default_tmpl_arg): Likewise.
- (coerce_template_template_parms): Likewise.
- (mangle_class_name_for_template): Likewise.
- (tsubst_template_parms): Likewise.
- * error.c (dump_template_argument_list): Likewise.
+ PR c++/27668
+ PR c++/27962
+ * pt.c (process_template_parm) Store invalid template
+ parameters as error_mark_node in the paramater list.
+ (push_inline_template_parms_recursive): Handle invalid
+ template parameters.
+ (comp_template_parms): Likewise.
+ (check_default_tmpl_arg): Likewise.
+ (coerce_template_template_parms): Likewise.
+ (mangle_class_name_for_template): Likewise.
+ (tsubst_template_parms): Likewise.
+ * error.c (dump_template_argument_list): Likewise.
2006-07-28 Kazu Hirata <kazu@codesourcery.com>
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index ca8ff5fcfde..bdb2edf2f41 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -391,18 +391,15 @@ cp_gimplify_init_expr (tree *expr_p, tree *pre_p, tree *post_p)
tree to = TREE_OPERAND (*expr_p, 0);
tree sub;
- /* If we are initializing something from a TARGET_EXPR, strip the
- TARGET_EXPR and initialize it directly. */
/* What about code that pulls out the temp and uses it elsewhere? I
think that such code never uses the TARGET_EXPR as an initializer. If
I'm wrong, we'll abort because the temp won't have any RTL. In that
case, I guess we'll need to replace references somehow. */
if (TREE_CODE (from) == TARGET_EXPR)
from = TARGET_EXPR_INITIAL (from);
- if (TREE_CODE (from) == CLEANUP_POINT_EXPR)
- from = TREE_OPERAND (from, 0);
- /* Look through any COMPOUND_EXPRs. */
+ /* Look through any COMPOUND_EXPRs, since build_compound_expr pushes them
+ inside the TARGET_EXPR. */
sub = expr_last (from);
/* If we are initializing from an AGGR_INIT_EXPR, drop the INIT_EXPR and
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index 17166d92af0..71b433f1560 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -744,7 +744,7 @@ build_throw (tree exp)
/* Wrap the initialization in a CLEANUP_POINT_EXPR so that cleanups
for temporaries within the initialization are run before the one
for the exception object, preserving LIFO order. */
- exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), exp);
+ exp = build1 (CLEANUP_POINT_EXPR, void_type_node, exp);
if (elided)
exp = build2 (TRY_CATCH_EXPR, void_type_node, exp,
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index fb4ea0a79df..56dbe6fddad 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1612,70 +1612,46 @@ finish_stmt_expr_expr (tree expr, tree stmt_expr)
of the last statement is the value of the entire expression. */
if (expr)
{
- tree type;
- type = TREE_TYPE (expr);
- if (!dependent_type_p (type) && !VOID_TYPE_P (type))
+ tree type = TREE_TYPE (expr);
+
+ if (processing_template_decl)
+ {
+ expr = build_stmt (EXPR_STMT, expr);
+ expr = add_stmt (expr);
+ /* Mark the last statement so that we can recognize it as such at
+ template-instantiation time. */
+ EXPR_STMT_STMT_EXPR_RESULT (expr) = 1;
+ }
+ else if (VOID_TYPE_P (type))
{
- expr = decay_conversion (expr);
+ /* Just treat this like an ordinary statement. */
+ expr = finish_expr_stmt (expr);
+ }
+ else
+ {
+ /* It actually has a value we need to deal with. First, force it
+ to be an rvalue so that we won't need to build up a copy
+ constructor call later when we try to assign it to something. */
+ expr = force_rvalue (expr);
if (error_operand_p (expr))
return error_mark_node;
+
+ /* Update for array-to-pointer decay. */
type = TREE_TYPE (expr);
+
+ /* Wrap it in a CLEANUP_POINT_EXPR and add it to the list like a
+ normal statement, but don't convert to void or actually add
+ the EXPR_STMT. */
+ if (TREE_CODE (expr) != CLEANUP_POINT_EXPR)
+ expr = maybe_cleanup_point_expr (expr);
+ add_stmt (expr);
}
+
/* The type of the statement-expression is the type of the last
expression. */
TREE_TYPE (stmt_expr) = type;
- /* We must take particular care if TYPE is a class type. In
- particular if EXPR creates a temporary of class type, then it
- must be destroyed at the semicolon terminating the last
- statement -- but we must make a copy before that happens.
-
- This problem is solved by using a TARGET_EXPR to initialize a
- new temporary variable. The TARGET_EXPR itself is placed
- outside the statement-expression. However, the last
- statement in the statement-expression is transformed from
- EXPR to (approximately) T = EXPR, where T is the new
- temporary variable. Thus, the lifetime of the new temporary
- extends to the full-expression surrounding the
- statement-expression. */
- if (!processing_template_decl && !VOID_TYPE_P (type))
- {
- tree target_expr;
- if (CLASS_TYPE_P (type)
- && !TYPE_HAS_TRIVIAL_INIT_REF (type))
- {
- target_expr = build_target_expr_with_type (expr, type);
- expr = TARGET_EXPR_INITIAL (target_expr);
- }
- else
- {
- /* Normally, build_target_expr will not create a
- TARGET_EXPR for scalars. However, we need the
- temporary here, in order to solve the scoping
- problem described above. */
- target_expr = force_target_expr (type, expr);
- expr = TARGET_EXPR_INITIAL (target_expr);
- expr = build2 (INIT_EXPR,
- type,
- TARGET_EXPR_SLOT (target_expr),
- expr);
- }
- TARGET_EXPR_INITIAL (target_expr) = NULL_TREE;
- /* Save away the TARGET_EXPR in the TREE_TYPE field of the
- STATEMENT_EXPR. We will retrieve it in
- finish_stmt_expr. */
- TREE_TYPE (stmt_expr) = target_expr;
- }
}
- /* Having modified EXPR to reflect the extra initialization, we now
- treat it just like an ordinary statement. */
- expr = finish_expr_stmt (expr);
-
- /* Mark the last statement so that we can recognize it as such at
- template-instantiation time. */
- if (expr && processing_template_decl)
- EXPR_STMT_STMT_EXPR_RESULT (expr) = 1;
-
return stmt_expr;
}
@@ -1696,6 +1672,7 @@ finish_stmt_expr (tree stmt_expr, bool has_no_scope)
type = TREE_TYPE (stmt_expr);
result = pop_stmt_list (stmt_expr);
+ TREE_TYPE (result) = type;
if (processing_template_decl)
{
@@ -1703,12 +1680,13 @@ finish_stmt_expr (tree stmt_expr, bool has_no_scope)
TREE_SIDE_EFFECTS (result) = 1;
STMT_EXPR_NO_SCOPE (result) = has_no_scope;
}
- else if (!TYPE_P (type))
+ else if (CLASS_TYPE_P (type))
{
- gcc_assert (TREE_CODE (type) == TARGET_EXPR);
- TARGET_EXPR_INITIAL (type) = result;
- TREE_TYPE (result) = void_type_node;
- result = type;
+ /* Wrap the statement-expression in a TARGET_EXPR so that the
+ temporary object created by the final expression is destroyed at
+ the end of the full-expression containing the
+ statement-expression. */
+ result = force_target_expr (type, result);
}
return result;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 6ade10cca29..fd9e1e56157 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -957,71 +957,71 @@ voidify_wrapper_expr (tree wrapper, tree temp)
{
if (!VOID_TYPE_P (TREE_TYPE (wrapper)))
{
- tree *p, sub = wrapper;
+ tree type = TREE_TYPE (wrapper);
+ tree *p;
- restart:
- /* Set p to point to the body of the wrapper. */
- switch (TREE_CODE (sub))
- {
- case BIND_EXPR:
- /* For a BIND_EXPR, the body is operand 1. */
- p = &BIND_EXPR_BODY (sub);
- break;
-
- default:
- p = &TREE_OPERAND (sub, 0);
- break;
- }
-
- /* Advance to the last statement. Set all container types to void. */
- if (TREE_CODE (*p) == STATEMENT_LIST)
- {
- tree_stmt_iterator i = tsi_last (*p);
- p = tsi_end_p (i) ? NULL : tsi_stmt_ptr (i);
- }
- else
+ /* Set p to point to the body of the wrapper. Loop until we find
+ something that isn't a wrapper. */
+ for (p = &wrapper; p && *p; )
{
- for (; TREE_CODE (*p) == COMPOUND_EXPR; p = &TREE_OPERAND (*p, 1))
+ switch (TREE_CODE (*p))
{
+ case BIND_EXPR:
TREE_SIDE_EFFECTS (*p) = 1;
TREE_TYPE (*p) = void_type_node;
+ /* For a BIND_EXPR, the body is operand 1. */
+ p = &BIND_EXPR_BODY (*p);
+ break;
+
+ case CLEANUP_POINT_EXPR:
+ case TRY_FINALLY_EXPR:
+ case TRY_CATCH_EXPR:
+ TREE_SIDE_EFFECTS (*p) = 1;
+ TREE_TYPE (*p) = void_type_node;
+ p = &TREE_OPERAND (*p, 0);
+ break;
+
+ case STATEMENT_LIST:
+ {
+ tree_stmt_iterator i = tsi_last (*p);
+ TREE_SIDE_EFFECTS (*p) = 1;
+ TREE_TYPE (*p) = void_type_node;
+ p = tsi_end_p (i) ? NULL : tsi_stmt_ptr (i);
+ }
+ break;
+
+ case COMPOUND_EXPR:
+ /* Advance to the last statement. Set all container types to void. */
+ for (; TREE_CODE (*p) == COMPOUND_EXPR; p = &TREE_OPERAND (*p, 1))
+ {
+ TREE_SIDE_EFFECTS (*p) = 1;
+ TREE_TYPE (*p) = void_type_node;
+ }
+ break;
+
+ default:
+ goto out;
}
}
+ out:
if (p == NULL || IS_EMPTY_STMT (*p))
- ;
- /* Look through exception handling. */
- else if (TREE_CODE (*p) == TRY_FINALLY_EXPR
- || TREE_CODE (*p) == TRY_CATCH_EXPR)
+ temp = NULL_TREE;
+ else if (temp)
{
- sub = *p;
- goto restart;
- }
- /* The C++ frontend already did this for us. */
- else if (TREE_CODE (*p) == INIT_EXPR
- || TREE_CODE (*p) == TARGET_EXPR)
- temp = TREE_OPERAND (*p, 0);
- /* If we're returning a dereference, move the dereference
- outside the wrapper. */
- else if (TREE_CODE (*p) == INDIRECT_REF)
- {
- tree ptr = TREE_OPERAND (*p, 0);
- temp = create_tmp_var (TREE_TYPE (ptr), "retval");
- *p = build2 (MODIFY_EXPR, TREE_TYPE (ptr), temp, ptr);
- temp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (temp)), temp);
- /* If this is a BIND_EXPR for a const inline function, it might not
- have TREE_SIDE_EFFECTS set. That is no longer accurate. */
- TREE_SIDE_EFFECTS (wrapper) = 1;
+ /* The wrapper is on the RHS of an assignment that we're pushing
+ down. */
+ gcc_assert (TREE_CODE (temp) == INIT_EXPR
+ || TREE_CODE (temp) == MODIFY_EXPR);
+ TREE_OPERAND (temp, 1) = *p;
+ *p = temp;
}
else
{
- if (!temp)
- temp = create_tmp_var (TREE_TYPE (wrapper), "retval");
- *p = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, *p);
- TREE_SIDE_EFFECTS (wrapper) = 1;
+ temp = create_tmp_var (type, "retval");
+ *p = build2 (INIT_EXPR, type, temp, *p);
}
- TREE_TYPE (wrapper) = void_type_node;
return temp;
}
@@ -1050,13 +1050,13 @@ build_stack_save_restore (tree *save, tree *restore)
/* Gimplify a BIND_EXPR. Just voidify and recurse. */
static enum gimplify_status
-gimplify_bind_expr (tree *expr_p, tree temp, tree *pre_p)
+gimplify_bind_expr (tree *expr_p, tree *pre_p)
{
tree bind_expr = *expr_p;
bool old_save_stack = gimplify_ctxp->save_stack;
tree t;
- temp = voidify_wrapper_expr (bind_expr, temp);
+ tree temp = voidify_wrapper_expr (bind_expr, NULL);
/* Mark variables seen in this bind expr. */
for (t = BIND_EXPR_VARS (bind_expr); t ; t = TREE_CHAIN (t))
@@ -3408,6 +3408,20 @@ gimplify_modify_expr_rhs (tree *expr_p, tree *from_p, tree *to_p, tree *pre_p,
ret = GS_UNHANDLED;
break;
+ /* If we're initializing from a container, push the initialization
+ inside it. */
+ case CLEANUP_POINT_EXPR:
+ case BIND_EXPR:
+ case STATEMENT_LIST:
+ {
+ tree wrap = *from_p;
+ tree t = voidify_wrapper_expr (wrap, *expr_p);
+ gcc_assert (t == *expr_p);
+
+ *expr_p = wrap;
+ return GS_OK;
+ }
+
default:
ret = GS_UNHANDLED;
break;
@@ -3681,8 +3695,10 @@ gimplify_compound_expr (tree *expr_p, tree *pre_p, bool want_value)
enlightened front-end, or by shortcut_cond_expr. */
static enum gimplify_status
-gimplify_statement_list (tree *expr_p)
+gimplify_statement_list (tree *expr_p, tree *pre_p)
{
+ tree temp = voidify_wrapper_expr (*expr_p, NULL);
+
tree_stmt_iterator i = tsi_start (*expr_p);
while (!tsi_end_p (i))
@@ -3703,6 +3719,13 @@ gimplify_statement_list (tree *expr_p)
tsi_next (&i);
}
+ if (temp)
+ {
+ append_to_statement_list (*expr_p, pre_p);
+ *expr_p = temp;
+ return GS_OK;
+ }
+
return GS_ALL_DONE;
}
@@ -4184,16 +4207,9 @@ gimplify_target_expr (tree *expr_p, tree *pre_p, tree *post_p)
ret = gimplify_expr (&init, pre_p, post_p, is_gimple_stmt, fb_none);
else
{
- /* Special handling for BIND_EXPR can result in fewer temps. */
- ret = GS_OK;
- if (TREE_CODE (init) == BIND_EXPR)
- gimplify_bind_expr (&init, temp, pre_p);
- if (init != temp)
- {
- init = build2 (INIT_EXPR, void_type_node, temp, init);
- ret = gimplify_expr (&init, pre_p, post_p, is_gimple_stmt,
- fb_none);
- }
+ init = build2 (INIT_EXPR, void_type_node, temp, init);
+ ret = gimplify_expr (&init, pre_p, post_p, is_gimple_stmt,
+ fb_none);
}
if (ret == GS_ERROR)
return GS_ERROR;
@@ -5507,7 +5523,7 @@ gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p,
break;
case BIND_EXPR:
- ret = gimplify_bind_expr (expr_p, NULL, pre_p);
+ ret = gimplify_bind_expr (expr_p, pre_p);
break;
case LOOP_EXPR:
@@ -5654,7 +5670,7 @@ gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p,
break;
case STATEMENT_LIST:
- ret = gimplify_statement_list (expr_p);
+ ret = gimplify_statement_list (expr_p, pre_p);
break;
case WITH_SIZE_EXPR:
diff --git a/gcc/testsuite/g++.dg/abi/forced-sticky.C b/gcc/testsuite/g++.dg/abi/forced-sticky.C
new file mode 100644
index 00000000000..0d31ee43547
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/forced-sticky.C
@@ -0,0 +1,62 @@
+// Test for "sticky cancel": if a catch (...) block discards the
+// cancellation exception, a new one is raised at the next cancellation
+// point.
+
+// This test only applies to glibc targets.
+// { dg-do run { target *-*-linux* } }
+// { dg-options "-pthread" }
+
+#include <pthread.h>
+#include <cxxabi.h>
+extern "C" int printf (const char *, ...);
+
+void* thread_main(void*)
+{
+ try
+ {
+ // Spin until we get cancelled.
+ while (1)
+ pthread_testcancel();
+ }
+ catch (...)
+ {
+ // Catch and discard the forced unwind.
+ printf ("caught ...\n");
+ }
+
+ try
+ {
+ // Start unwinding again.
+ pthread_testcancel();
+ }
+ catch (...)
+ {
+ // Catch and discard again. This time the thread exits before the
+ // next cancellation point, so we're done.
+ printf ("caught ... again\n");
+ return 0;
+ }
+
+ return (void*)4;
+}
+
+int main()
+{
+ pthread_t thread;
+ int r;
+ void *p;
+
+ r = pthread_create (&thread, NULL, thread_main, NULL);
+ if (r)
+ return 1;
+
+ r = pthread_cancel (thread);
+ if (r)
+ return 2;
+
+ r = pthread_join (thread, &p);
+ if (r)
+ return 3;
+
+ return (int)p;
+}
diff --git a/gcc/testsuite/g++.dg/abi/forced.C b/gcc/testsuite/g++.dg/abi/forced.C
new file mode 100644
index 00000000000..7a9c35964f7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/forced.C
@@ -0,0 +1,25 @@
+// This test only applies to glibc (NPTL) targets.
+// { dg-do run { target *-*-linux* } }
+// { dg-options "-pthread" }
+
+#include <pthread.h>
+#include <cxxabi.h>
+extern "C" int printf (const char *, ...);
+
+int main()
+{
+ try
+ {
+ pthread_exit (0);
+ }
+ catch (abi::__forced_unwind &)
+ {
+ printf ("caught forced unwind\n");
+ throw;
+ }
+ catch (...)
+ {
+ printf ("caught ...\n");
+ return 1;
+ }
+}
diff --git a/gcc/testsuite/g++.dg/ext/stmtexpr8.C b/gcc/testsuite/g++.dg/ext/stmtexpr8.C
new file mode 100644
index 00000000000..8e5d0ddcba2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/stmtexpr8.C
@@ -0,0 +1,28 @@
+// PR c++/27115
+
+// { dg-do run }
+// { dg-options "" }
+
+struct A
+{
+ int i;
+ A (int j) : i(j) {}
+ A (const A &j) : i(j.i) {}
+ A& operator= (const A &j) { i = j.i; return *this; }
+};
+
+A foo(int j)
+{
+ return ({ j ? A(1) : A(0); });
+}
+
+int main()
+{
+ return foo(1).i-1;
+}
+
+void foo2()
+{
+ A b = ({ A a(1); a; });
+}
+