This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCH: PR 21440
- From: Mark Mitchell <mark at codesourcery dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Mon, 5 Sep 2005 09:05:33 -0700
- Subject: C++ PATCH: PR 21440
- Reply-to: mark at codesourcery dot com
This patch fixes a regression involving statement-expressions in C++.
The approach being used was rather ad-hoc; this more principled
version should be more robust. In particular, we now transform the
last statement of a statement expression from "e;" into "t = e;" where
"t" is an new temporary in the scope surrounding the
statement-expression. (Of course, if the expression has class type,
then this is really initialization with a constructor, not a bitwise
copy.) In looking at the current code, I realized we'd stopped
applying array-to-pointer and function-to-pointer conversions to the
value of a statement-expression, which is a divergence from the C
front end, so I've corrected that, and added a test case.
As with the recent static variable stuff, this is a significant enough
change that I would not be surprised if there was some amount of
fall-out, but the overall approach is much sounder.
Tested on x86_64-unknown-linux-gnu, applied on the mainline and on the
4.0 branch.
--
Mark Mitchell
CodeSourcery, LLC
mark@codesourcery.com
2005-09-05 Mark Mitchell <mark@codesourcery.com>
PR c++/21440
* semantics.c (finish_stmt_expr_expr): Add an explicit
initialization to the last statement in the statement-expression.
* (finish_stmt_expr): Adjust accordingly.
2005-09-05 Mark Mitchell <mark@codesourcery.com>
PR c++/21440
* g++.dg/ext/stmtexpr5.C: New test.
* g++.dg/ext/stmtexpr6.C: Likewise.
Index: gcc/cp/semantics.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/semantics.c,v
retrieving revision 1.484
diff -c -5 -p -r1.484 semantics.c
*** gcc/cp/semantics.c 3 Sep 2005 18:18:45 -0000 1.484
--- gcc/cp/semantics.c 5 Sep 2005 15:51:45 -0000
*************** begin_stmt_expr (void)
*** 1538,1704 ****
{
return push_stmt_list ();
}
/* Process the final expression of a statement expression. EXPR can be
! NULL, if the final expression is empty. Build up a TARGET_EXPR so
! that the result value can be safely returned to the enclosing
! expression. */
tree
finish_stmt_expr_expr (tree expr, tree stmt_expr)
{
- tree result = NULL_TREE;
-
if (error_operand_p (expr))
return error_mark_node;
if (expr)
{
! if (!processing_template_decl && !VOID_TYPE_P (TREE_TYPE (expr)))
! {
! tree type = TREE_TYPE (expr);
!
! if (TREE_CODE (type) == ARRAY_TYPE
! || TREE_CODE (type) == FUNCTION_TYPE)
! expr = decay_conversion (expr);
!
! expr = require_complete_type (expr);
!
type = TREE_TYPE (expr);
!
! /* Build a TARGET_EXPR for this aggregate. finish_stmt_expr
! will then pull it apart so the lifetime of the target is
! within the scope of the expression containing this statement
! expression. */
! if (TREE_CODE (expr) == TARGET_EXPR)
! ;
! else if (!IS_AGGR_TYPE (type) || TYPE_HAS_TRIVIAL_INIT_REF (type))
! expr = build_target_expr_with_type (expr, type);
else
{
! /* Copy construct. */
! expr = build_special_member_call
! (NULL_TREE, complete_ctor_identifier,
! build_tree_list (NULL_TREE, expr),
! type, LOOKUP_NORMAL);
! expr = build_cplus_new (type, expr);
! gcc_assert (TREE_CODE (expr) == TARGET_EXPR);
}
! }
!
! if (expr != error_mark_node)
! {
! result = build_stmt (EXPR_STMT, expr);
! EXPR_STMT_STMT_EXPR_RESULT (result) = 1;
! add_stmt (result);
}
}
! finish_stmt ();
! /* Remember the last expression so that finish_stmt_expr
! can pull it apart. */
! TREE_TYPE (stmt_expr) = result;
! return result;
}
/* Finish a statement-expression. EXPR should be the value returned
by the previous begin_stmt_expr. Returns an expression
representing the statement-expression. */
tree
finish_stmt_expr (tree stmt_expr, bool has_no_scope)
{
! tree result, result_stmt, type;
! tree *result_stmt_p = NULL;
! result_stmt = TREE_TYPE (stmt_expr);
! TREE_TYPE (stmt_expr) = void_type_node;
! result = pop_stmt_list (stmt_expr);
! if (!result_stmt || VOID_TYPE_P (result_stmt))
! type = void_type_node;
! else
! {
! /* We need to search the statement expression for the result_stmt,
! since we'll need to replace it entirely. */
! tree t;
! result_stmt_p = &result;
! while (1)
! {
! t = *result_stmt_p;
! if (t == result_stmt)
! break;
! switch (TREE_CODE (t))
! {
! case STATEMENT_LIST:
! {
! tree_stmt_iterator i = tsi_last (t);
! result_stmt_p = tsi_stmt_ptr (i);
! break;
! }
! case BIND_EXPR:
! result_stmt_p = &BIND_EXPR_BODY (t);
! break;
! case TRY_FINALLY_EXPR:
! case TRY_CATCH_EXPR:
! case CLEANUP_STMT:
! result_stmt_p = &TREE_OPERAND (t, 0);
! break;
! default:
! gcc_unreachable ();
! }
! }
! type = TREE_TYPE (EXPR_STMT_EXPR (result_stmt));
! }
if (processing_template_decl)
{
result = build_min (STMT_EXPR, type, result);
TREE_SIDE_EFFECTS (result) = 1;
STMT_EXPR_NO_SCOPE (result) = has_no_scope;
}
! else if (!VOID_TYPE_P (type))
{
! /* Pull out the TARGET_EXPR that is the final expression. Put
! the target's init_expr as the final expression and then put
! the statement expression itself as the target's init
! expr. Finally, return the target expression. */
! tree init, target_expr = EXPR_STMT_EXPR (result_stmt);
! gcc_assert (TREE_CODE (target_expr) == TARGET_EXPR);
!
! /* The initializer will be void if the initialization is done by
! AGGR_INIT_EXPR; propagate that out to the statement-expression as
! a whole. */
! init = TREE_OPERAND (target_expr, 1);
! type = TREE_TYPE (init);
!
! init = maybe_cleanup_point_expr (init);
! *result_stmt_p = init;
!
! if (VOID_TYPE_P (type))
! /* No frobbing needed. */;
! else if (TREE_CODE (result) == BIND_EXPR)
! {
! /* The BIND_EXPR created in finish_compound_stmt is void; if we're
! returning a value directly, give it the appropriate type. */
! if (VOID_TYPE_P (TREE_TYPE (result)))
! TREE_TYPE (result) = type;
! else
! gcc_assert (same_type_p (TREE_TYPE (result), type));
! }
! else if (TREE_CODE (result) == STATEMENT_LIST)
! /* We need to wrap a STATEMENT_LIST in a BIND_EXPR so it can have a
! type other than void. FIXME why can't we just return a value
! from STATEMENT_LIST? */
! result = build3 (BIND_EXPR, type, NULL, result, NULL);
!
! TREE_OPERAND (target_expr, 1) = result;
! result = target_expr;
}
return result;
}
--- 1538,1658 ----
{
return push_stmt_list ();
}
/* Process the final expression of a statement expression. EXPR can be
! NULL, if the final expression is empty. Return a STATEMENT_LIST
! containing all the statements in the statement-expression, or
! ERROR_MARK_NODE if there was an error. */
tree
finish_stmt_expr_expr (tree expr, tree stmt_expr)
{
if (error_operand_p (expr))
return error_mark_node;
+ /* If the last statement does not have "void" type, then the value
+ 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))
! {
! expr = decay_conversion (expr);
! if (error_operand_p (expr))
! return error_mark_node;
type = TREE_TYPE (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
! paticular 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;
}
/* Finish a statement-expression. EXPR should be the value returned
by the previous begin_stmt_expr. Returns an expression
representing the statement-expression. */
tree
finish_stmt_expr (tree stmt_expr, bool has_no_scope)
{
! tree type;
! tree result;
! if (error_operand_p (stmt_expr))
! return error_mark_node;
! gcc_assert (TREE_CODE (stmt_expr) == STATEMENT_LIST);
! type = TREE_TYPE (stmt_expr);
! result = pop_stmt_list (stmt_expr);
if (processing_template_decl)
{
result = build_min (STMT_EXPR, type, result);
TREE_SIDE_EFFECTS (result) = 1;
STMT_EXPR_NO_SCOPE (result) = has_no_scope;
}
! else if (!TYPE_P (type))
{
! gcc_assert (TREE_CODE (type) == TARGET_EXPR);
! TARGET_EXPR_INITIAL (type) = result;
! TREE_TYPE (result) = void_type_node;
! result = type;
}
return result;
}
Index: gcc/testsuite/g++.dg/ext/stmtexpr5.C
===================================================================
RCS file: gcc/testsuite/g++.dg/ext/stmtexpr5.C
diff -N gcc/testsuite/g++.dg/ext/stmtexpr5.C
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- gcc/testsuite/g++.dg/ext/stmtexpr5.C 5 Sep 2005 15:52:00 -0000
***************
*** 0 ****
--- 1,15 ----
+ // PR c++/21440
+ // { dg-options "" }
+
+ struct Foo {
+ ~Foo();
+ int i;
+ };
+
+ void bar() {
+ Foo foo = ({
+ Foo bletch;
+ bletch.i = 0;
+ bletch;
+ });
+ }
Index: gcc/testsuite/g++.dg/ext/stmtexpr6.C
===================================================================
RCS file: gcc/testsuite/g++.dg/ext/stmtexpr6.C
diff -N gcc/testsuite/g++.dg/ext/stmtexpr6.C
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- gcc/testsuite/g++.dg/ext/stmtexpr6.C 5 Sep 2005 15:52:00 -0000
***************
*** 0 ****
--- 1,11 ----
+ // { dg-do run }
+ // { dg-options "" }
+
+ int a[128];
+
+ int main() {
+ // Check that array-to-pointer conversion occurs in a
+ // statement-expression.
+ if (sizeof (({ a; })) != sizeof (int *))
+ return 1;
+ }