[prev in list] [next in list] [prev in thread] [next in thread] 

List:       gcc-patches
Subject:    C++ PATCH for Re: non-compliance of egcs/gcc/g++ with standard
From:       Mark Mitchell <mark () markmitchell ! com>
Date:       1998-10-30 19:18:56
[Download RAW message or body]


Darel --

  Yes, you have found a serious bug in EGCS.  (Multi-dimensional
arrays are not fully constructed in the presence of
brace-initializers.)  

  By inspecting the relevant code I also noticed a serious
exception-handling problem: if one of the brace-initializers causes an
exception to be thrown the destructors for the already constructed
elements are not called.

  This patch fixes both of these problems.

-- 
Mark Mitchell 			mark@markmitchell.com
Mark Mitchell Consulting	http://www.markmitchell.com

1998-10-30  Mark Mitchell  <mark@markmitchell.com>

	* decl2.c (delete_sanity): Pass integer_zero_node, not
	integer_two_node, to build_vec_delete.
	* init.c (build_array_eh_cleanup): Remove.
	(expand_vec_init_try_block): New function.
	(expand_vec_init_catch_clause): Likewise.
	(build_vec_delete_1): Don't deal with case that auto_delete_vec
	might be integer_two_node anymore.
	(expand_vec_init): Rework for initialization-correctness and
	exception-correctness.
	* typeck2.c (process_init_constructor): Make mutual exclusivity
	of cases more obvious.
	
Index: testsuite/g++.old-deja/g++.other/array1.C
===================================================================
RCS file: array1.C
diff -N array1.C
*** /dev/null	Mon Dec 31 20:00:00 1979
--- array1.C	Fri Oct 30 10:57:07 1998
***************
*** 0 ****
--- 1,26 ----
+ int i;
+ 
+ struct S {
+   S (int) {
+     ++i;
+     if (i == 3)
+       throw 3;
+   };
+ 
+   S () {}
+ 
+   ~S() {
+     --i;
+   }
+ };
+ 
+ int main()
+ {
+   try {
+     S s[5] = { 0, 1, 2, 3, 4 };
+   } catch (...) {
+   }
+ 
+   if (i != 1)
+     return 1;
+ }
Index: testsuite/g++.old-deja/g++.other/array2.C
===================================================================
RCS file: array2.C
diff -N array2.C
*** /dev/null	Mon Dec 31 20:00:00 1979
--- array2.C	Fri Oct 30 10:57:07 1998
***************
*** 0 ****
--- 1,18 ----
+ int i;
+ 
+ struct S {
+   S () {
+     ++i;
+   };
+ 
+   S (int) {
+   };
+ };
+ 
+ int main()
+ {
+   S s[3][3] = { 2 };
+ 
+   if (i != 8)
+     return 1;
+ }
Index: testsuite/g++.old-deja/cp/decl2.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/decl2.c,v
retrieving revision 1.151
diff -c -p -r1.151 decl2.c
*** decl2.c	1998/10/28 20:29:08	1.151
--- decl2.c	1998/10/30 18:57:20
*************** delete_sanity (exp, size, doing_vec, use
*** 1211,1217 ****
  
    if (doing_vec)
      return build_vec_delete (t, maxindex, integer_one_node,
! 			     integer_two_node, use_global_delete);
    else
      {
        if (IS_AGGR_TYPE (TREE_TYPE (type))
--- 1211,1217 ----
  
    if (doing_vec)
      return build_vec_delete (t, maxindex, integer_one_node,
! 			     integer_zero_node, use_global_delete);
    else
      {
        if (IS_AGGR_TYPE (TREE_TYPE (type))
Index: testsuite/g++.old-deja/cp/init.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/init.c,v
retrieving revision 1.74
diff -c -p -r1.74 init.c
*** init.c	1998/10/28 03:36:56	1.74
--- init.c	1998/10/30 18:57:22
*************** static tree build_vec_delete_1 PROTO((tr
*** 54,65 ****
  static void perform_member_init PROTO((tree, tree, tree, int));
  static void sort_base_init PROTO((tree, tree *, tree *));
  static tree build_builtin_delete_call PROTO((tree));
- static tree build_array_eh_cleanup PROTO((tree, tree, tree));
  static int member_init_ok_or_else PROTO((tree, tree, char *));
  static void expand_virtual_init PROTO((tree, tree));
  static tree sort_member_init PROTO((tree));
  static tree build_partial_cleanup_for PROTO((tree));
  static tree initializing_context PROTO((tree));
  
  /* Cache the identifier nodes for the magic field of a new cookie.  */
  static tree nc_nelts_field_id;
--- 54,66 ----
  static void perform_member_init PROTO((tree, tree, tree, int));
  static void sort_base_init PROTO((tree, tree *, tree *));
  static tree build_builtin_delete_call PROTO((tree));
  static int member_init_ok_or_else PROTO((tree, tree, char *));
  static void expand_virtual_init PROTO((tree, tree));
  static tree sort_member_init PROTO((tree));
  static tree build_partial_cleanup_for PROTO((tree));
  static tree initializing_context PROTO((tree));
+ static void expand_vec_init_try_block PROTO((tree));
+ static void expand_vec_init_catch_clause PROTO((tree, tree, tree, tree));
  
  /* Cache the identifier nodes for the magic field of a new cookie.  */
  static tree nc_nelts_field_id;
*************** build_vec_delete_1 (base, maxindex, type
*** 2595,2602 ****
   no_destructor:
    /* If the delete flag is one, or anything else with the low bit set,
       delete the storage.  */
!   if (auto_delete_vec == integer_zero_node
!       || auto_delete_vec == integer_two_node)
      deallocate_expr = integer_zero_node;
    else
      {
--- 2596,2602 ----
   no_destructor:
    /* If the delete flag is one, or anything else with the low bit set,
       delete the storage.  */
!   if (auto_delete_vec == integer_zero_node)
      deallocate_expr = integer_zero_node;
    else
      {
*************** build_vec_delete_1 (base, maxindex, type
*** 2652,2669 ****
      return cp_convert (void_type_node, body);
  }
  
! /* Build a tree to cleanup partially built arrays.
!    BASE is that starting address of the array.
!    COUNT is the count of objects that have been built, that need destroying.
!    TYPE is the type of elements in the array.  */
  
! static tree
! build_array_eh_cleanup (base, count, type)
!      tree base, count, type;
  {
!   tree expr = build_vec_delete_1 (base, count, type, integer_two_node,
! 				  integer_zero_node, 0);
!   return expr;
  }
  
  /* `expand_vec_init' performs initialization of a vector of aggregate
--- 2652,2731 ----
      return cp_convert (void_type_node, body);
  }
  
! /* Protect the vector initialization with a try-block so that we can
!    destroy the first few elements if constructing a later element
!    causes an exception to be thrown.  TYPE is the type of the array
!    elements.  */
  
! static void
! expand_vec_init_try_block (type)
!      tree type;
  {
!   if (!TYPE_NEEDS_DESTRUCTOR (type) || !flag_exceptions)
!     return;
! 
!   /* The code we generate looks like:
! 
!        try {
!          // Initialize the vector.
!        } catch (...) {
!          // Destory the elements that need destroying.
! 	 throw;
!        } 
! 
!      Here we're just beginning the `try'.  */
! 
!   expand_eh_region_start ();
! }
! 
! /* Add code to destroy the array elements constructed so far if the
!    construction of some element in the array causes an exception to be
!    thrown.  RVAL is the address of the last element in the array.
!    TYPE is the type of the array elements.  MAXINDEX is the maximum
!    allowable index into the array.  ITERATOR is an integer variable
!    indicating how many elements remain to be constructed.  */
! 
! static void
! expand_vec_init_catch_clause (rval, type, maxindex, iterator)
!      tree rval;
!      tree type;
!      tree maxindex;
!      tree iterator;
! {
!   tree e;
!   tree cleanup;
! 
!   if (!TYPE_NEEDS_DESTRUCTOR (type) || !flag_exceptions)
!     return;
!     
!   /* We have to ensure that this can live to the cleanup expansion
!      time, since we know it is only ever needed once, generate code
!      now.  */
!   push_obstacks_nochange ();
!   resume_temporary_allocation ();
! 
!   cleanup = make_node (RTL_EXPR);
!   TREE_TYPE (cleanup) = void_type_node;
!   RTL_EXPR_RTL (cleanup) = const0_rtx;
!   TREE_SIDE_EFFECTS (cleanup) = 1;
!   do_pending_stack_adjust ();
!   start_sequence_for_rtl_expr (cleanup);
!     
!   e = build_vec_delete_1 (rval,
! 			  build_binary_op (MINUS_EXPR, maxindex, 
! 					   iterator, 1),
! 			  type,
! 			  /*auto_delete_vec=*/integer_zero_node,
! 			  /*auto_delete=*/integer_zero_node,
! 			  /*use_global_delete=*/0);
!   expand_expr (e, const0_rtx, VOIDmode, EXPAND_NORMAL);
! 
!   do_pending_stack_adjust ();
!   RTL_EXPR_SEQUENCE (cleanup) = get_insns ();
!   end_sequence ();
!   cleanup = protect_with_terminate (cleanup);
!   expand_eh_region_end (cleanup);
!   pop_obstacks ();
  }
  
  /* `expand_vec_init' performs initialization of a vector of aggregate
*************** expand_vec_init (decl, base, maxindex, i
*** 2689,2697 ****
       int from_array;
  {
    tree rval;
!   tree iterator, base2 = NULL_TREE;
    tree type = TREE_TYPE (TREE_TYPE (base));
    tree size;
  
    maxindex = cp_convert (ptrdiff_type_node, maxindex);
    if (maxindex == error_mark_node)
--- 2751,2762 ----
       int from_array;
  {
    tree rval;
!   tree base2 = NULL_TREE;
    tree type = TREE_TYPE (TREE_TYPE (base));
    tree size;
+   tree itype;
+   tree iterator;
+   int num_initialized_elts = 0;
  
    maxindex = cp_convert (ptrdiff_type_node, maxindex);
    if (maxindex == error_mark_node)
*************** expand_vec_init (decl, base, maxindex, i
*** 2708,2811 ****
  
    size = size_in_bytes (type);
  
-   /* Set to zero in case size is <= 0.  Optimizer will delete this if
-      it is not needed.  */
-   rval = get_temp_regvar (build_pointer_type (type),
- 			  cp_convert (build_pointer_type (type), null_pointer_node));
    base = default_conversion (base);
    base = cp_convert (build_pointer_type (type), base);
!   expand_assignment (rval, base, 0, 0);
    base = get_temp_regvar (build_pointer_type (type), base);
  
!   if (init != NULL_TREE
!       && TREE_CODE (init) == CONSTRUCTOR
!       && (! decl || TREE_TYPE (init) == TREE_TYPE (decl)))
      {
!       /* Initialization of array from {...}.  */
!       tree elts = CONSTRUCTOR_ELTS (init);
        tree baseref = build1 (INDIRECT_REF, type, base);
-       tree baseinc = build (PLUS_EXPR, build_pointer_type (type), base, size);
-       int host_i = TREE_INT_CST_LOW (maxindex);
  
!       if (IS_AGGR_TYPE (type))
  	{
! 	  while (elts)
! 	    {
! 	      host_i -= 1;
! 	      expand_aggr_init (baseref, TREE_VALUE (elts), 0);
  
! 	      expand_assignment (base, baseinc, 0, 0);
! 	      elts = TREE_CHAIN (elts);
! 	    }
! 	  /* Initialize any elements by default if possible.  */
! 	  if (host_i >= 0)
! 	    {
! 	      if (TYPE_NEEDS_CONSTRUCTING (type) == 0)
! 		{
! 		  if (obey_regdecls)
! 		    use_variable (DECL_RTL (base));
! 		  goto done_init;
! 		}
  
! 	      iterator = get_temp_regvar (ptrdiff_type_node,
! 					  build_int_2 (host_i, 0));
! 	      init = NULL_TREE;
! 	      goto init_by_default;
! 	    }
  	}
!       else
! 	while (elts)
! 	  {
! 	    expand_assignment (baseref, TREE_VALUE (elts), 0, 0);
! 
! 	    expand_assignment (base, baseinc, 0, 0);
! 	    elts = TREE_CHAIN (elts);
! 	  }
  
        if (obey_regdecls)
  	use_variable (DECL_RTL (base));
      }
!   else
      {
!       tree itype;
! 
!       iterator = get_temp_regvar (ptrdiff_type_node, maxindex);
! 
!     init_by_default:
!       itype = NULL_TREE;
! 
!       /* If initializing one array from another,
! 	 initialize element by element.  */
!       if (from_array)
  	{
! 	  /* We rely upon the below calls the do argument checking */
! 	  if (decl == NULL_TREE)
! 	    {
! 	      sorry ("initialization of array from dissimilar array type");
! 	      return error_mark_node;
! 	    }
! 	  if (init)
! 	    {
! 	      base2 = default_conversion (init);
! 	      itype = TREE_TYPE (base2);
! 	      base2 = get_temp_regvar (itype, base2);
! 	      itype = TREE_TYPE (itype);
! 	    }
! 	  else if (TYPE_LANG_SPECIFIC (type)
! 		   && TYPE_NEEDS_CONSTRUCTING (type)
! 		   && ! TYPE_HAS_DEFAULT_CONSTRUCTOR (type))
! 	    {
! 	      error ("initializer ends prematurely");
! 	      return error_mark_node;
! 	    }
  	}
  
!       expand_start_cond (build (GE_EXPR, boolean_type_node,
! 				iterator, integer_zero_node), 0);
!       if (TYPE_NEEDS_DESTRUCTOR (type))
! 	expand_eh_region_start ();
!       expand_start_loop_continue_elsewhere (1);
  
        /* The initialization of each array element is a full-expression.  */
        expand_start_target_temps ();
  
--- 2773,2867 ----
  
    size = size_in_bytes (type);
  
    base = default_conversion (base);
    base = cp_convert (build_pointer_type (type), base);
!   rval = get_temp_regvar (build_pointer_type (type), base);
    base = get_temp_regvar (build_pointer_type (type), base);
+   iterator = get_temp_regvar (ptrdiff_type_node, maxindex);
  
!   /* Protect the entire array initialization so that we can destroy
!      the partially constructed array if an exception is thrown.  */
!   expand_vec_init_try_block (type);
! 
!   if (init != NULL_TREE && TREE_CODE (init) == CONSTRUCTOR
!       && (!decl || comptypes (TREE_TYPE (init), 
! 			      TREE_TYPE (decl), 1)))
      {
!       /* Do non-default initialization resulting from brace-enclosed
! 	 initializers.  */
! 
!       tree elts;
        tree baseref = build1 (INDIRECT_REF, type, base);
  
!       for (elts = CONSTRUCTOR_ELTS (init); elts; elts = TREE_CHAIN (elts))
  	{
! 	  tree elt = TREE_VALUE (elts);
  
! 	  num_initialized_elts++;
  
! 	  if (IS_AGGR_TYPE (type) || TREE_CODE (type) == ARRAY_TYPE)
! 	    expand_aggr_init (baseref, elt, 0);
! 	  else
! 	    expand_assignment (baseref, elt, 0, 0);
! 
! 	  expand_assignment (base, 
! 			     build (PLUS_EXPR, build_pointer_type (type),
! 				    base, size),
! 			     0, 0);
! 	  expand_assignment (iterator,
! 			     build (MINUS_EXPR, ptrdiff_type_node,
! 				    iterator, integer_one_node),
! 			     0, 0);
  	}
! 
!       /* Clear out INIT so that we don't get confused below.  */
!       init = NULL_TREE;
  
        if (obey_regdecls)
  	use_variable (DECL_RTL (base));
      }
!   else if (from_array)
      {
!       /* If initializing one array from another, initialize element by
! 	 element.  We rely upon the below calls the do argument
! 	 checking.  */ 
!       if (decl == NULL_TREE)
  	{
! 	  sorry ("initialization of array from dissimilar array type");
! 	  return error_mark_node;
  	}
+       if (init)
+ 	{
+ 	  base2 = default_conversion (init);
+ 	  itype = TREE_TYPE (base2);
+ 	  base2 = get_temp_regvar (itype, base2);
+ 	  itype = TREE_TYPE (itype);
+ 	}
+       else if (TYPE_LANG_SPECIFIC (type)
+ 	       && TYPE_NEEDS_CONSTRUCTING (type)
+ 	       && ! TYPE_HAS_DEFAULT_CONSTRUCTOR (type))
+ 	{
+ 	  error ("initializer ends prematurely");
+ 	  return error_mark_node;
+ 	}
+     }
  
!   /* Now, default-initialize any remaining elements.  We don't need to
!      do that if a) the type does not need constructing, or b) we've
!      already initialized all the elements.  */
!   if (TYPE_NEEDS_CONSTRUCTING (type)
!       && !(TREE_CODE (maxindex) == INTEGER_CST
! 	   && num_initialized_elts == TREE_INT_CST_LOW (maxindex) + 1))
!     {
!       /* If the ITERATOR is equal to zero, then we don't have to loop;
! 	 we've already initialized all the elements.  */
!       expand_start_cond (build (NE_EXPR, boolean_type_node,
! 				iterator, integer_zero_node), 
! 			 0);
  
+       /* Otherwise, loop through the elements.  */
+       expand_start_loop_continue_elsewhere (1);
+   
        /* The initialization of each array element is a full-expression.  */
        expand_start_target_temps ();
  
*************** expand_vec_init (decl, base, maxindex, i
*** 2832,2901 ****
  	{
  	  if (init != 0)
  	    sorry ("cannot initialize multi-dimensional array with initializer");
! 	  expand_vec_init (decl, build1 (NOP_EXPR, build_pointer_type (TREE_TYPE (type)), base),
  			   array_type_nelts (type), 0, 0);
  	}
        else
  	expand_aggr_init (build1 (INDIRECT_REF, type, base), init, 0);
  
        expand_assignment (base,
! 			 build (PLUS_EXPR, build_pointer_type (type), base, size),
! 			 0, 0);
        if (base2)
  	expand_assignment (base2,
! 			   build (PLUS_EXPR, build_pointer_type (type), base2, size), 0, 0);
  
        /* Cleanup any temporaries needed for the initial value.  */
        expand_end_target_temps ();
! 
        expand_loop_continue_here ();
        expand_exit_loop_if_false (0, build (NE_EXPR, boolean_type_node,
! 					   build (PREDECREMENT_EXPR, ptrdiff_type_node, iterator, integer_one_node), minus_one));
! 
        if (obey_regdecls)
  	{
  	  use_variable (DECL_RTL (base));
  	  if (base2)
  	    use_variable (DECL_RTL (base2));
  	}
        expand_end_loop ();
-       if (TYPE_NEEDS_DESTRUCTOR (type) && flag_exceptions)
- 	{
- 	  /* We have to ensure that this can live to the cleanup
- 	     expansion time, since we know it is only ever needed
- 	     once, generate code now.  */
- 	  push_obstacks_nochange ();
- 	  resume_temporary_allocation ();
- 	  {
- 	    tree e1, cleanup = make_node (RTL_EXPR);
- 	    TREE_TYPE (cleanup) = void_type_node;
- 	    RTL_EXPR_RTL (cleanup) = const0_rtx;
- 	    TREE_SIDE_EFFECTS (cleanup) = 1;
- 	    do_pending_stack_adjust ();
- 	    start_sequence_for_rtl_expr (cleanup);
- 
- 	    e1 = build_array_eh_cleanup
- 	      (rval,
- 	       build_binary_op (MINUS_EXPR, maxindex, iterator, 1),
- 	       type);
- 	    expand_expr (e1, const0_rtx, VOIDmode, EXPAND_NORMAL);
- 	    do_pending_stack_adjust ();
- 	    RTL_EXPR_SEQUENCE (cleanup) = get_insns ();
- 	    end_sequence ();
- 
- 	    cleanup = protect_with_terminate (cleanup);
- 	    expand_eh_region_end (cleanup);
- 	  }
- 	  pop_obstacks ();
- 	}
        expand_end_cond ();
-       if (obey_regdecls)
- 	use_variable (DECL_RTL (iterator));
      }
-  done_init:
  
    if (obey_regdecls)
!     use_variable (DECL_RTL (rval));
    return rval;
  }
  
--- 2888,2942 ----
  	{
  	  if (init != 0)
  	    sorry ("cannot initialize multi-dimensional array with initializer");
! 	  expand_vec_init (decl, 
! 			   build1 (NOP_EXPR, 
! 				   build_pointer_type (TREE_TYPE
! 						       (type)),
! 				   base),
  			   array_type_nelts (type), 0, 0);
  	}
        else
  	expand_aggr_init (build1 (INDIRECT_REF, type, base), init, 0);
  
        expand_assignment (base,
! 			 build (PLUS_EXPR, build_pointer_type (type), 
! 				base, size), 0, 0);
        if (base2)
  	expand_assignment (base2,
! 			   build (PLUS_EXPR, build_pointer_type (type), 
! 				  base2, size), 0, 0);
  
        /* Cleanup any temporaries needed for the initial value.  */
        expand_end_target_temps ();
!   
        expand_loop_continue_here ();
        expand_exit_loop_if_false (0, build (NE_EXPR, boolean_type_node,
! 					   build (PREDECREMENT_EXPR, 
! 						  ptrdiff_type_node, 
! 						  iterator,
! 						  integer_one_node), 
! 					   minus_one));
!   
        if (obey_regdecls)
  	{
  	  use_variable (DECL_RTL (base));
  	  if (base2)
  	    use_variable (DECL_RTL (base2));
  	}
+ 
        expand_end_loop ();
        expand_end_cond ();
      }
  
+   /* Make sure to cleanup any partially constructed elements.  */
+   expand_vec_init_catch_clause (rval, type, maxindex, iterator);
+ 
    if (obey_regdecls)
!     {
!       use_variable (DECL_RTL (iterator));
!       use_variable (DECL_RTL (rval));
!     }
! 
    return rval;
  }
  
*************** build_delete (type, addr, auto_delete, f
*** 2990,2996 ****
  	  return error_mark_node;
  	}
        return build_vec_delete (addr, array_type_nelts (type),
! 			       auto_delete, integer_two_node,
  			       use_global_delete);
      }
    else
--- 3031,3037 ----
  	  return error_mark_node;
  	}
        return build_vec_delete (addr, array_type_nelts (type),
! 			       auto_delete, integer_zero_node,
  			       use_global_delete);
      }
    else
Index: testsuite/g++.old-deja/cp/typeck2.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/typeck2.c,v
retrieving revision 1.38
diff -c -p -r1.38 typeck2.c
*** typeck2.c	1998/10/27 22:33:40	1.38
--- typeck2.c	1998/10/30 18:57:23
*************** process_init_constructor (type, init, el
*** 1006,1012 ****
  	  members = expr_tree_cons (NULL_TREE, next1, members);
  	}
      }
!   if (TREE_CODE (type) == RECORD_TYPE)
      {
        register tree field;
  
--- 1006,1012 ----
  	  members = expr_tree_cons (NULL_TREE, next1, members);
  	}
      }
!   else if (TREE_CODE (type) == RECORD_TYPE)
      {
        register tree field;
  
*************** process_init_constructor (type, init, el
*** 1108,1115 ****
  		     IDENTIFIER_POINTER (DECL_NAME (field)));
  	}
      }
! 
!   if (TREE_CODE (type) == UNION_TYPE)
      {
        register tree field = TYPE_FIELDS (type);
        register tree next1;
--- 1108,1114 ----
  		     IDENTIFIER_POINTER (DECL_NAME (field)));
  	}
      }
!   else if (TREE_CODE (type) == UNION_TYPE)
      {
        register tree field = TYPE_FIELDS (type);
        register tree next1;

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic