This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCH: PR 21440


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;
+ }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]