Commit c6c7698d by Jason Merrill

re PR c++/27115 (ICE in cp_expr_size or miscompilation with statement…

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
parent 63b26393
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> 2006-08-21 J"orn Rennecke <joern.rennecke@st.com>
* config/sh/lib1funcs-Os-4-200.asm: Guard entire file with * config/sh/lib1funcs-Os-4-200.asm: Guard entire file with
......
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> 2006-08-21 Lee Millward <lee.millward@codesourcery.com>
PR c++/26269 PR c++/26269
* decl.c (duplicate_decls): Return early if either * decl.c (duplicate_decls): Return early if either
newdecl or olddecl is error_mark_node. newdecl or olddecl is error_mark_node.
PR c++/28505 PR c++/28505
* decl.c (grokdeclarator): Return early after * decl.c (grokdeclarator): Return early after
issuing diagnostic about an incomplete type. issuing diagnostic about an incomplete type.
PR c++/28741 PR c++/28741
* tree.c (decl_anon_ns_mem_p): Robustify. * tree.c (decl_anon_ns_mem_p): Robustify.
* decl2.c (determine_visibility): Likewise. * decl2.c (determine_visibility): Likewise.
2006-08-20 Mark Mitchell <mark@codesourcery.com> 2006-08-20 Mark Mitchell <mark@codesourcery.com>
...@@ -189,18 +200,18 @@ ...@@ -189,18 +200,18 @@
2006-07-28 Lee Millward <lee.millward@codesourcery.com> 2006-07-28 Lee Millward <lee.millward@codesourcery.com>
PR c++/27668 PR c++/27668
PR c++/27962 PR c++/27962
* pt.c (process_template_parm) Store invalid template * pt.c (process_template_parm) Store invalid template
parameters as error_mark_node in the paramater list. parameters as error_mark_node in the paramater list.
(push_inline_template_parms_recursive): Handle invalid (push_inline_template_parms_recursive): Handle invalid
template parameters. template parameters.
(comp_template_parms): Likewise. (comp_template_parms): Likewise.
(check_default_tmpl_arg): Likewise. (check_default_tmpl_arg): Likewise.
(coerce_template_template_parms): Likewise. (coerce_template_template_parms): Likewise.
(mangle_class_name_for_template): Likewise. (mangle_class_name_for_template): Likewise.
(tsubst_template_parms): Likewise. (tsubst_template_parms): Likewise.
* error.c (dump_template_argument_list): Likewise. * error.c (dump_template_argument_list): Likewise.
2006-07-28 Kazu Hirata <kazu@codesourcery.com> 2006-07-28 Kazu Hirata <kazu@codesourcery.com>
......
...@@ -391,18 +391,15 @@ cp_gimplify_init_expr (tree *expr_p, tree *pre_p, tree *post_p) ...@@ -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 to = TREE_OPERAND (*expr_p, 0);
tree sub; 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 /* 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 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 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. */ case, I guess we'll need to replace references somehow. */
if (TREE_CODE (from) == TARGET_EXPR) if (TREE_CODE (from) == TARGET_EXPR)
from = TARGET_EXPR_INITIAL (from); 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); sub = expr_last (from);
/* If we are initializing from an AGGR_INIT_EXPR, drop the INIT_EXPR and /* If we are initializing from an AGGR_INIT_EXPR, drop the INIT_EXPR and
......
...@@ -744,7 +744,7 @@ build_throw (tree exp) ...@@ -744,7 +744,7 @@ build_throw (tree exp)
/* Wrap the initialization in a CLEANUP_POINT_EXPR so that cleanups /* Wrap the initialization in a CLEANUP_POINT_EXPR so that cleanups
for temporaries within the initialization are run before the one for temporaries within the initialization are run before the one
for the exception object, preserving LIFO order. */ 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) if (elided)
exp = build2 (TRY_CATCH_EXPR, void_type_node, exp, exp = build2 (TRY_CATCH_EXPR, void_type_node, exp,
......
...@@ -1612,70 +1612,46 @@ finish_stmt_expr_expr (tree expr, tree stmt_expr) ...@@ -1612,70 +1612,46 @@ finish_stmt_expr_expr (tree expr, tree stmt_expr)
of the last statement is the value of the entire expression. */ of the last statement is the value of the entire expression. */
if (expr) if (expr)
{ {
tree type; tree type = TREE_TYPE (expr);
type = TREE_TYPE (expr);
if (!dependent_type_p (type) && !VOID_TYPE_P (type)) 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)) if (error_operand_p (expr))
return error_mark_node; return error_mark_node;
/* Update for array-to-pointer decay. */
type = TREE_TYPE (expr); 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 /* The type of the statement-expression is the type of the last
expression. */ expression. */
TREE_TYPE (stmt_expr) = type; 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; return stmt_expr;
} }
...@@ -1696,6 +1672,7 @@ finish_stmt_expr (tree stmt_expr, bool has_no_scope) ...@@ -1696,6 +1672,7 @@ finish_stmt_expr (tree stmt_expr, bool has_no_scope)
type = TREE_TYPE (stmt_expr); type = TREE_TYPE (stmt_expr);
result = pop_stmt_list (stmt_expr); result = pop_stmt_list (stmt_expr);
TREE_TYPE (result) = type;
if (processing_template_decl) if (processing_template_decl)
{ {
...@@ -1703,12 +1680,13 @@ finish_stmt_expr (tree stmt_expr, bool has_no_scope) ...@@ -1703,12 +1680,13 @@ finish_stmt_expr (tree stmt_expr, bool has_no_scope)
TREE_SIDE_EFFECTS (result) = 1; TREE_SIDE_EFFECTS (result) = 1;
STMT_EXPR_NO_SCOPE (result) = has_no_scope; 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); /* Wrap the statement-expression in a TARGET_EXPR so that the
TARGET_EXPR_INITIAL (type) = result; temporary object created by the final expression is destroyed at
TREE_TYPE (result) = void_type_node; the end of the full-expression containing the
result = type; statement-expression. */
result = force_target_expr (type, result);
} }
return result; return result;
......
...@@ -957,71 +957,71 @@ voidify_wrapper_expr (tree wrapper, tree temp) ...@@ -957,71 +957,71 @@ voidify_wrapper_expr (tree wrapper, tree temp)
{ {
if (!VOID_TYPE_P (TREE_TYPE (wrapper))) 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. Loop until we find
/* Set p to point to the body of the wrapper. */ something that isn't a wrapper. */
switch (TREE_CODE (sub)) for (p = &wrapper; p && *p; )
{
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
{ {
for (; TREE_CODE (*p) == COMPOUND_EXPR; p = &TREE_OPERAND (*p, 1)) switch (TREE_CODE (*p))
{ {
case BIND_EXPR:
TREE_SIDE_EFFECTS (*p) = 1; TREE_SIDE_EFFECTS (*p) = 1;
TREE_TYPE (*p) = void_type_node; 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)) if (p == NULL || IS_EMPTY_STMT (*p))
; temp = NULL_TREE;
/* Look through exception handling. */ else if (temp)
else if (TREE_CODE (*p) == TRY_FINALLY_EXPR
|| TREE_CODE (*p) == TRY_CATCH_EXPR)
{ {
sub = *p; /* The wrapper is on the RHS of an assignment that we're pushing
goto restart; down. */
} gcc_assert (TREE_CODE (temp) == INIT_EXPR
/* The C++ frontend already did this for us. */ || TREE_CODE (temp) == MODIFY_EXPR);
else if (TREE_CODE (*p) == INIT_EXPR TREE_OPERAND (temp, 1) = *p;
|| TREE_CODE (*p) == TARGET_EXPR) *p = temp;
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;
} }
else else
{ {
if (!temp) temp = create_tmp_var (type, "retval");
temp = create_tmp_var (TREE_TYPE (wrapper), "retval"); *p = build2 (INIT_EXPR, type, temp, *p);
*p = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, *p);
TREE_SIDE_EFFECTS (wrapper) = 1;
} }
TREE_TYPE (wrapper) = void_type_node;
return temp; return temp;
} }
...@@ -1050,13 +1050,13 @@ build_stack_save_restore (tree *save, tree *restore) ...@@ -1050,13 +1050,13 @@ build_stack_save_restore (tree *save, tree *restore)
/* Gimplify a BIND_EXPR. Just voidify and recurse. */ /* Gimplify a BIND_EXPR. Just voidify and recurse. */
static enum gimplify_status 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; tree bind_expr = *expr_p;
bool old_save_stack = gimplify_ctxp->save_stack; bool old_save_stack = gimplify_ctxp->save_stack;
tree t; tree t;
temp = voidify_wrapper_expr (bind_expr, temp); tree temp = voidify_wrapper_expr (bind_expr, NULL);
/* Mark variables seen in this bind expr. */ /* Mark variables seen in this bind expr. */
for (t = BIND_EXPR_VARS (bind_expr); t ; t = TREE_CHAIN (t)) 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, ...@@ -3408,6 +3408,20 @@ gimplify_modify_expr_rhs (tree *expr_p, tree *from_p, tree *to_p, tree *pre_p,
ret = GS_UNHANDLED; ret = GS_UNHANDLED;
break; 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: default:
ret = GS_UNHANDLED; ret = GS_UNHANDLED;
break; break;
...@@ -3681,8 +3695,10 @@ gimplify_compound_expr (tree *expr_p, tree *pre_p, bool want_value) ...@@ -3681,8 +3695,10 @@ gimplify_compound_expr (tree *expr_p, tree *pre_p, bool want_value)
enlightened front-end, or by shortcut_cond_expr. */ enlightened front-end, or by shortcut_cond_expr. */
static enum gimplify_status 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); tree_stmt_iterator i = tsi_start (*expr_p);
while (!tsi_end_p (i)) while (!tsi_end_p (i))
...@@ -3703,6 +3719,13 @@ gimplify_statement_list (tree *expr_p) ...@@ -3703,6 +3719,13 @@ gimplify_statement_list (tree *expr_p)
tsi_next (&i); tsi_next (&i);
} }
if (temp)
{
append_to_statement_list (*expr_p, pre_p);
*expr_p = temp;
return GS_OK;
}
return GS_ALL_DONE; return GS_ALL_DONE;
} }
...@@ -4184,16 +4207,9 @@ gimplify_target_expr (tree *expr_p, tree *pre_p, tree *post_p) ...@@ -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); ret = gimplify_expr (&init, pre_p, post_p, is_gimple_stmt, fb_none);
else else
{ {
/* Special handling for BIND_EXPR can result in fewer temps. */ init = build2 (INIT_EXPR, void_type_node, temp, init);
ret = GS_OK; ret = gimplify_expr (&init, pre_p, post_p, is_gimple_stmt,
if (TREE_CODE (init) == BIND_EXPR) fb_none);
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);
}
} }
if (ret == GS_ERROR) if (ret == GS_ERROR)
return GS_ERROR; return GS_ERROR;
...@@ -5507,7 +5523,7 @@ gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p, ...@@ -5507,7 +5523,7 @@ gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p,
break; break;
case BIND_EXPR: case BIND_EXPR:
ret = gimplify_bind_expr (expr_p, NULL, pre_p); ret = gimplify_bind_expr (expr_p, pre_p);
break; break;
case LOOP_EXPR: case LOOP_EXPR:
...@@ -5654,7 +5670,7 @@ gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p, ...@@ -5654,7 +5670,7 @@ gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p,
break; break;
case STATEMENT_LIST: case STATEMENT_LIST:
ret = gimplify_statement_list (expr_p); ret = gimplify_statement_list (expr_p, pre_p);
break; break;
case WITH_SIZE_EXPR: case WITH_SIZE_EXPR:
......
// 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;
}
// 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;
}
}
// 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; });
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment