From c6c7698dfd268ad41c9ca00a8c49333d09c51e20 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 21 Aug 2006 16:54:57 -0400 Subject: [PATCH] re PR c++/27115 (ICE in cp_expr_size or miscompilation with statement expressions and constructors (and ?: )) 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. From-SVN: r116311 --- gcc/ChangeLog | 12 +++ gcc/cp/ChangeLog | 47 ++++++---- gcc/cp/cp-gimplify.c | 7 +- gcc/cp/except.c | 2 +- gcc/cp/semantics.c | 96 ++++++++------------ gcc/gimplify.c | 146 +++++++++++++++++-------------- gcc/testsuite/g++.dg/abi/forced-sticky.C | 62 +++++++++++++ gcc/testsuite/g++.dg/abi/forced.C | 25 ++++++ gcc/testsuite/g++.dg/ext/stmtexpr8.C | 28 ++++++ 9 files changed, 277 insertions(+), 148 deletions(-) create mode 100644 gcc/testsuite/g++.dg/abi/forced-sticky.C create mode 100644 gcc/testsuite/g++.dg/abi/forced.C create mode 100644 gcc/testsuite/g++.dg/ext/stmtexpr8.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index dc3fef9..685b4b6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,15 @@ +2006-08-21 Jason Merrill + + 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 * config/sh/lib1funcs-Os-4-200.asm: Guard entire file with diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 758f877..fa7d6bc 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,16 +1,27 @@ +2006-08-21 Jason Merrill + + 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 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 @@ -189,18 +200,18 @@ 2006-07-28 Lee Millward - 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 diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index ca8ff5f..bdb2edf 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 17166d9..71b433f 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 fb4ea0a..56dbe6f 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 6ade10c..fd9e1e5 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 0000000..0d31ee4 --- /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 +#include +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 0000000..7a9c359 --- /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 +#include +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 0000000..8e5d0dd --- /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; }); +} + -- 2.7.4