C++ PATCH: Improve error recovery

Mark Mitchell mark@codesourcery.com
Fri Aug 4 11:51:00 GMT 2000


This patch avoids a crash when a user defines a member function that
has never been declared.

--
Mark Mitchell                   mark@codesourcery.com
CodeSourcery, LLC               http://www.codesourcery.com

2000-08-04  Mark Mitchell  <mark@codesourcery.com>

	* cp-tree.h (add_method): Change prototype.
	* class.c (add_method): Remove FIELDS parameter.  Add ERROR_P.
	Don't double the size of the method vector in the error case.
	(handle_using_decl): Adjust call to add_method.
	(add_implicitly_declared_members): Likewise.
	(clone_function_decl): Likewise.
	* decl2.c (check_classfn): Likewise.
	* semantics.c (finish_member_declaration): Likewise.
	
*** /dev/null	Tue May  5 13:32:27 1998
--- testsuite/g++.old-deja/g++.other/crash21.C	Fri Aug  4 11:48:52 2000
***************
*** 0 ****
--- 1,14 ----
+ // Build don't link:
+ // Origin: Gabriel Dos Reis <gdr@codesourcery.com>
+ 
+ struct  A {
+   virtual void f(int&) const;
+ };
+ 
+ struct B : public A {
+   int x;
+ };
+ 
+ void B::f(int& t) { // ERROR - undeclared method
+   x = t;
+ }

Index: class.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/class.c,v
retrieving revision 1.327
diff -c -p -r1.327 class.c
*** class.c	2000/07/31 00:24:42	1.327
--- class.c	2000/08/04 18:35:56
*************** add_virtual_function (new_virtuals_p, ov
*** 1155,1344 ****
  
  extern struct obstack *current_obstack;
  
! /* Add method METHOD to class TYPE.
  
-    If non-NULL, FIELDS is the entry in the METHOD_VEC vector entry of
-    the class type where the method should be added.  */
- 
  void
! add_method (type, fields, method)
!      tree type, *fields, method;
  {
    int using = (DECL_CONTEXT (method) != type);
!   
!   if (fields && *fields)
!     *fields = build_overload (method, *fields);
!   else 
!     {
!       int len;
!       int slot;
!       tree method_vec;
! 
!       if (!CLASSTYPE_METHOD_VEC (type))
! 	/* Make a new method vector.  We start with 8 entries.  We must
! 	   allocate at least two (for constructors and destructors), and
! 	   we're going to end up with an assignment operator at some
! 	   point as well.  
! 
! 	   We could use a TREE_LIST for now, and convert it to a
! 	   TREE_VEC in finish_struct, but we would probably waste more
! 	   memory making the links in the list than we would by
! 	   over-allocating the size of the vector here.  Furthermore,
! 	   we would complicate all the code that expects this to be a
! 	   vector.  */
! 	CLASSTYPE_METHOD_VEC (type) = make_tree_vec (8);
! 
!       method_vec = CLASSTYPE_METHOD_VEC (type);
!       len = TREE_VEC_LENGTH (method_vec);
! 
!       /* Constructors and destructors go in special slots.  */
!       if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (method))
! 	slot = CLASSTYPE_CONSTRUCTOR_SLOT;
!       else if (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (method))
! 	slot = CLASSTYPE_DESTRUCTOR_SLOT;
!       else
! 	{
! 	  /* See if we already have an entry with this name.  */
! 	  for (slot = CLASSTYPE_FIRST_CONVERSION_SLOT; slot < len; ++slot)
! 	    if (!TREE_VEC_ELT (method_vec, slot)
! 		|| (DECL_NAME (OVL_CURRENT (TREE_VEC_ELT (method_vec, 
! 							  slot))) 
! 		    == DECL_NAME (method)))
! 	      break;
  		
! 	  if (slot == len)
  	    {
! 	      /* We need a bigger method vector.  */
! 	      tree new_vec = make_tree_vec (2 * len);
! 	      bcopy ((PTR) &TREE_VEC_ELT (method_vec, 0),
! 		     (PTR) &TREE_VEC_ELT (new_vec, 0),
! 		     len * sizeof (tree));
! 	      len = 2 * len;
! 	      method_vec = CLASSTYPE_METHOD_VEC (type) = new_vec;
! 	    }
! 
! 	  if (DECL_CONV_FN_P (method) && !TREE_VEC_ELT (method_vec, slot))
! 	    {
! 	      /* Type conversion operators have to come before
! 		 ordinary methods; add_conversions depends on this to
! 		 speed up looking for conversion operators.  So, if
! 		 necessary, we slide some of the vector elements up.
! 		 In theory, this makes this algorithm O(N^2) but we
! 		 don't expect many conversion operators.  */
! 	      for (slot = 2; slot < len; ++slot)
! 		{
! 		  tree fn = TREE_VEC_ELT (method_vec, slot);
    
! 		  if (!fn)
! 		    /* There are no more entries in the vector, so we
! 		       can insert the new conversion operator here.  */
! 		    break;
    		  
! 		  if (!DECL_CONV_FN_P (OVL_CURRENT (fn)))
! 		    /* We can insert the new function right at the
! 		       SLOTth position.  */
! 		    break;
! 		}
    
! 	      if (!TREE_VEC_ELT (method_vec, slot))
! 		/* There is nothing in the Ith slot, so we can avoid
! 		   moving anything.  */
  		; 
! 	      else
! 		{
! 		  /* We know the last slot in the vector is empty
! 		     because we know that at this point there's room
! 		     for a new function.  */
! 		  bcopy ((PTR) &TREE_VEC_ELT (method_vec, slot),
! 			 (PTR) &TREE_VEC_ELT (method_vec, slot + 1),
! 			 (len - slot - 1) * sizeof (tree));
! 		  TREE_VEC_ELT (method_vec, slot) = NULL_TREE;
! 		}
  	    }
  	}
        
!       if (template_class_depth (type))
! 	/* TYPE is a template class.  Don't issue any errors now; wait
! 	   until instantiation time to complain.  */
! 	  ;
!       else
! 	{
! 	  tree fns;
  
! 	  /* Check to see if we've already got this method.  */
! 	  for (fns = TREE_VEC_ELT (method_vec, slot);
! 	       fns;
! 	       fns = OVL_NEXT (fns))
! 	    {
! 	      tree fn = OVL_CURRENT (fns);
  		 
! 	      if (TREE_CODE (fn) != TREE_CODE (method))
! 		continue;
  
! 	      if (TREE_CODE (method) != TEMPLATE_DECL)
  		{
! 		  /* [over.load] Member function declarations with the
! 		     same name and the same parameter types cannot be
! 		     overloaded if any of them is a static member
! 		     function declaration.  */
! 		  if ((DECL_STATIC_FUNCTION_P (fn)
! 		       != DECL_STATIC_FUNCTION_P (method))
! 		      || using)
! 		    {
! 		      tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn));
! 		      tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (method));
  
! 		      if (! DECL_STATIC_FUNCTION_P (fn))
! 			parms1 = TREE_CHAIN (parms1);
! 		      if (! DECL_STATIC_FUNCTION_P (method))
! 			parms2 = TREE_CHAIN (parms2);
! 
! 		      if (compparms (parms1, parms2))
! 			{
! 			  if (using)
! 			    /* Defer to the local function.  */
! 			    return;
! 			  else
! 			    cp_error ("`%#D' and `%#D' cannot be overloaded",
! 				      fn, method);
! 			}
! 		    }
  
! 		  /* Since this is an ordinary function in a
! 		     non-template class, it's mangled name can be used
! 		     as a unique identifier.  This technique is only
! 		     an optimization; we would get the same results if
! 		     we just used decls_match here.  */
! 		  if (DECL_ASSEMBLER_NAME (fn) 
! 		      != DECL_ASSEMBLER_NAME (method))
! 		    continue;
  		}
- 	      else if (!decls_match (fn, method))
- 		continue;
  
! 	      /* There has already been a declaration of this method
! 		 or member template.  */
! 	      cp_error_at ("`%D' has already been declared in `%T'", 
! 			   method, type);
! 
! 	      /* We don't call duplicate_decls here to merge the
! 		 declarations because that will confuse things if the
! 		 methods have inline definitions.  In particular, we
! 		 will crash while processing the definitions.  */
! 	      return;
  	    }
  	}
  
!       /* Actually insert the new method.  */
!       TREE_VEC_ELT (method_vec, slot) 
! 	= build_overload (method, TREE_VEC_ELT (method_vec, slot));
  
        /* Add the new binding.  */ 
!       if (!DECL_CONSTRUCTOR_P (method)
! 	  && !DECL_DESTRUCTOR_P (method))
! 	push_class_level_binding (DECL_NAME (method),
! 				  TREE_VEC_ELT (method_vec, slot));
!     }
  }
  
  /* Subroutines of finish_struct.  */
--- 1155,1353 ----
  
  extern struct obstack *current_obstack;
  
! /* Add method METHOD to class TYPE.  If ERROR_P is true, we are adding
!    the method after the class has already been defined because a
!    declaration for it was seen.  (Even though that is erroneous, we
!    add the method for improved error recovery.)  */
  
  void
! add_method (type, method, error_p)
!      tree type;
!      tree method;
!      int error_p;
  {
    int using = (DECL_CONTEXT (method) != type);
!   int len;
!   int slot;
!   tree method_vec;
! 
!   if (!CLASSTYPE_METHOD_VEC (type))
!     /* Make a new method vector.  We start with 8 entries.  We must
!        allocate at least two (for constructors and destructors), and
!        we're going to end up with an assignment operator at some point
!        as well.
!        
!        We could use a TREE_LIST for now, and convert it to a TREE_VEC
!        in finish_struct, but we would probably waste more memory
!        making the links in the list than we would by over-allocating
!        the size of the vector here.  Furthermore, we would complicate
!        all the code that expects this to be a vector.  */
!     CLASSTYPE_METHOD_VEC (type) = make_tree_vec (8);
! 
!   method_vec = CLASSTYPE_METHOD_VEC (type);
!   len = TREE_VEC_LENGTH (method_vec);
! 
!   /* Constructors and destructors go in special slots.  */
!   if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (method))
!     slot = CLASSTYPE_CONSTRUCTOR_SLOT;
!   else if (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (method))
!     slot = CLASSTYPE_DESTRUCTOR_SLOT;
!   else
!     {
!       /* See if we already have an entry with this name.  */
!       for (slot = CLASSTYPE_FIRST_CONVERSION_SLOT; slot < len; ++slot)
! 	if (!TREE_VEC_ELT (method_vec, slot)
! 	    || (DECL_NAME (OVL_CURRENT (TREE_VEC_ELT (method_vec, 
! 						      slot))) 
! 		== DECL_NAME (method)))
! 	  break;
  		
!       if (slot == len)
! 	{
! 	  /* We need a bigger method vector.  */
! 	  int new_len;
! 	  tree new_vec;
! 
! 	  /* In the non-error case, we are processing a class
! 	     definition.  Double the size of the vector to give room
! 	     for new methods.  */
! 	  if (!error_p)
! 	    new_len = 2 * len;
! 	  /* In the error case, the vector is already complete.  We
! 	     don't expect many errors, and the rest of the front-end
! 	     will get confused if there are empty slots in the vector.  */
! 	  else
! 	    new_len = len + 1;
! 
! 	  new_vec = make_tree_vec (new_len);
! 	  bcopy ((PTR) &TREE_VEC_ELT (method_vec, 0),
! 		 (PTR) &TREE_VEC_ELT (new_vec, 0),
! 		 len * sizeof (tree));
! 	  len = new_len;
! 	  method_vec = CLASSTYPE_METHOD_VEC (type) = new_vec;
! 	}
! 
!       if (DECL_CONV_FN_P (method) && !TREE_VEC_ELT (method_vec, slot))
! 	{
! 	  /* Type conversion operators have to come before ordinary
! 	     methods; add_conversions depends on this to speed up
! 	     looking for conversion operators.  So, if necessary, we
! 	     slide some of the vector elements up.  In theory, this
! 	     makes this algorithm O(N^2) but we don't expect many
! 	     conversion operators.  */
! 	  for (slot = 2; slot < len; ++slot)
  	    {
! 	      tree fn = TREE_VEC_ELT (method_vec, slot);
    
! 	      if (!fn)
! 		/* There are no more entries in the vector, so we
! 		   can insert the new conversion operator here.  */
! 		break;
    		  
! 	      if (!DECL_CONV_FN_P (OVL_CURRENT (fn)))
! 		/* We can insert the new function right at the
! 		   SLOTth position.  */
! 		break;
! 	    }
    
! 	  if (!TREE_VEC_ELT (method_vec, slot))
! 	    /* There is nothing in the Ith slot, so we can avoid
! 	       moving anything.  */
  		; 
! 	  else
! 	    {
! 	      /* We know the last slot in the vector is empty
! 		 because we know that at this point there's room
! 		 for a new function.  */
! 	      bcopy ((PTR) &TREE_VEC_ELT (method_vec, slot),
! 		     (PTR) &TREE_VEC_ELT (method_vec, slot + 1),
! 		     (len - slot - 1) * sizeof (tree));
! 	      TREE_VEC_ELT (method_vec, slot) = NULL_TREE;
  	    }
  	}
+     }
        
!   if (template_class_depth (type))
!     /* TYPE is a template class.  Don't issue any errors now; wait
!        until instantiation time to complain.  */
!     ;
!   else
!     {
!       tree fns;
  
!       /* Check to see if we've already got this method.  */
!       for (fns = TREE_VEC_ELT (method_vec, slot);
! 	   fns;
! 	   fns = OVL_NEXT (fns))
! 	{
! 	  tree fn = OVL_CURRENT (fns);
  		 
! 	  if (TREE_CODE (fn) != TREE_CODE (method))
! 	    continue;
  
! 	  if (TREE_CODE (method) != TEMPLATE_DECL)
! 	    {
! 	      /* [over.load] Member function declarations with the
! 		 same name and the same parameter types cannot be
! 		 overloaded if any of them is a static member
! 		 function declaration.  */
! 	      if ((DECL_STATIC_FUNCTION_P (fn)
! 		   != DECL_STATIC_FUNCTION_P (method))
! 		  || using)
  		{
! 		  tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn));
! 		  tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (method));
  
! 		  if (! DECL_STATIC_FUNCTION_P (fn))
! 		    parms1 = TREE_CHAIN (parms1);
! 		  if (! DECL_STATIC_FUNCTION_P (method))
! 		    parms2 = TREE_CHAIN (parms2);
  
! 		  if (compparms (parms1, parms2))
! 		    {
! 		      if (using)
! 			/* Defer to the local function.  */
! 			return;
! 		      else
! 			cp_error ("`%#D' and `%#D' cannot be overloaded",
! 				  fn, method);
! 		    }
  		}
  
! 	      /* Since this is an ordinary function in a
! 		 non-template class, it's mangled name can be used
! 		 as a unique identifier.  This technique is only
! 		 an optimization; we would get the same results if
! 		 we just used decls_match here.  */
! 	      if (DECL_ASSEMBLER_NAME (fn) 
! 		  != DECL_ASSEMBLER_NAME (method))
! 		continue;
  	    }
+ 	  else if (!decls_match (fn, method))
+ 	    continue;
+ 
+ 	  /* There has already been a declaration of this method
+ 	     or member template.  */
+ 	  cp_error_at ("`%D' has already been declared in `%T'", 
+ 		       method, type);
+ 
+ 	  /* We don't call duplicate_decls here to merge the
+ 	     declarations because that will confuse things if the
+ 	     methods have inline definitions.  In particular, we
+ 	     will crash while processing the definitions.  */
+ 	  return;
  	}
+     }
  
!   /* Actually insert the new method.  */
!   TREE_VEC_ELT (method_vec, slot) 
!     = build_overload (method, TREE_VEC_ELT (method_vec, slot));
  
        /* Add the new binding.  */ 
!   if (!DECL_CONSTRUCTOR_P (method)
!       && !DECL_DESTRUCTOR_P (method))
!     push_class_level_binding (DECL_NAME (method),
! 			      TREE_VEC_ELT (method_vec, slot));
  }
  
  /* Subroutines of finish_struct.  */
*************** handle_using_decl (using_decl, t)
*** 1567,1573 ****
    if (flist)
      for (; flist; flist = OVL_NEXT (flist))
        {
! 	add_method (t, 0, OVL_CURRENT (flist));
  	alter_access (t, OVL_CURRENT (flist), access);
        }
    else
--- 1576,1582 ----
    if (flist)
      for (; flist; flist = OVL_NEXT (flist))
        {
! 	add_method (t, OVL_CURRENT (flist), /*error_p=*/0);
  	alter_access (t, OVL_CURRENT (flist), access);
        }
    else
*************** add_implicitly_declared_members (t, cant
*** 3146,3152 ****
    /* Now, hook all of the new functions on to TYPE_METHODS,
       and add them to the CLASSTYPE_METHOD_VEC.  */
    for (f = &implicit_fns; *f; f = &TREE_CHAIN (*f))
!     add_method (t, 0, *f);
    *f = TYPE_METHODS (t);
    TYPE_METHODS (t) = implicit_fns;
  
--- 3155,3161 ----
    /* Now, hook all of the new functions on to TYPE_METHODS,
       and add them to the CLASSTYPE_METHOD_VEC.  */
    for (f = &implicit_fns; *f; f = &TREE_CHAIN (*f))
!     add_method (t, *f, /*error_p=*/0);
    *f = TYPE_METHODS (t);
    TYPE_METHODS (t) = implicit_fns;
  
*************** clone_function_decl (fn, update_method_v
*** 4203,4212 ****
  	 and a not-in-charge version.  */
        clone = build_clone (fn, complete_ctor_identifier);
        if (update_method_vec_p)
! 	add_method (DECL_CONTEXT (clone), NULL, clone);
        clone = build_clone (fn, base_ctor_identifier);
        if (update_method_vec_p)
! 	add_method (DECL_CONTEXT (clone), NULL, clone);
      }
    else
      {
--- 4212,4221 ----
  	 and a not-in-charge version.  */
        clone = build_clone (fn, complete_ctor_identifier);
        if (update_method_vec_p)
! 	add_method (DECL_CONTEXT (clone), clone, /*error_p=*/0);
        clone = build_clone (fn, base_ctor_identifier);
        if (update_method_vec_p)
! 	add_method (DECL_CONTEXT (clone), clone, /*error_p=*/0);
      }
    else
      {
*************** clone_function_decl (fn, update_method_v
*** 4220,4232 ****
  	 function table.  */
        clone = build_clone (fn, deleting_dtor_identifier);
        if (update_method_vec_p)
! 	add_method (DECL_CONTEXT (clone), NULL, clone);
        clone = build_clone (fn, complete_dtor_identifier);
        if (update_method_vec_p)
! 	add_method (DECL_CONTEXT (clone), NULL, clone);
        clone = build_clone (fn, base_dtor_identifier);
        if (update_method_vec_p)
! 	add_method (DECL_CONTEXT (clone), NULL, clone);
      }
  }
  
--- 4229,4241 ----
  	 function table.  */
        clone = build_clone (fn, deleting_dtor_identifier);
        if (update_method_vec_p)
! 	add_method (DECL_CONTEXT (clone), clone, /*error_p=*/0);
        clone = build_clone (fn, complete_dtor_identifier);
        if (update_method_vec_p)
! 	add_method (DECL_CONTEXT (clone), clone, /*error_p=*/0);
        clone = build_clone (fn, base_dtor_identifier);
        if (update_method_vec_p)
! 	add_method (DECL_CONTEXT (clone), clone, /*error_p=*/0);
      }
  }
  
Index: cp-tree.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/cp-tree.h,v
retrieving revision 1.507
diff -c -p -r1.507 cp-tree.h
*** cp-tree.h	2000/08/04 00:02:19	1.507
--- cp-tree.h	2000/08/04 18:35:59
*************** extern tree build_vbase_path			PARAMS ((
*** 3805,3811 ****
  extern tree build_vtbl_ref			PARAMS ((tree, tree));
  extern tree build_vfn_ref			PARAMS ((tree *, tree, tree));
  extern tree get_vtable_decl                     PARAMS ((tree, int));
! extern void add_method				PARAMS ((tree, tree *, tree));
  extern int currently_open_class			PARAMS ((tree));
  extern tree currently_open_derived_class	PARAMS ((tree));
  extern tree get_vfield_offset			PARAMS ((tree));
--- 3805,3811 ----
  extern tree build_vtbl_ref			PARAMS ((tree, tree));
  extern tree build_vfn_ref			PARAMS ((tree *, tree, tree));
  extern tree get_vtable_decl                     PARAMS ((tree, int));
! extern void add_method				PARAMS ((tree, tree, int));
  extern int currently_open_class			PARAMS ((tree));
  extern tree currently_open_derived_class	PARAMS ((tree));
  extern tree get_vfield_offset			PARAMS ((tree));
Index: decl2.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/decl2.c,v
retrieving revision 1.380
diff -c -p -r1.380 decl2.c
*** decl2.c	2000/08/03 08:26:36	1.380
--- decl2.c	2000/08/04 18:36:03
*************** check_classfn (ctype, function)
*** 1549,1555 ****
       case we'll only confuse ourselves when the function is declared
       properly within the class.  */
    if (COMPLETE_TYPE_P (ctype))
!     add_method (ctype, methods, function);
    return NULL_TREE;
  }
  
--- 1549,1555 ----
       case we'll only confuse ourselves when the function is declared
       properly within the class.  */
    if (COMPLETE_TYPE_P (ctype))
!     add_method (ctype, function, /*error_p=*/1);
    return NULL_TREE;
  }
  
Index: semantics.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/semantics.c,v
retrieving revision 1.162
diff -c -p -r1.162 semantics.c
*** semantics.c	2000/08/02 17:01:12	1.162
--- semantics.c	2000/08/04 18:36:04
*************** finish_member_declaration (decl)
*** 1969,1975 ****
      {
        /* We also need to add this function to the
  	 CLASSTYPE_METHOD_VEC.  */
!       add_method (current_class_type, 0, decl);
  
        TREE_CHAIN (decl) = TYPE_METHODS (current_class_type);
        TYPE_METHODS (current_class_type) = decl;
--- 1969,1975 ----
      {
        /* We also need to add this function to the
  	 CLASSTYPE_METHOD_VEC.  */
!       add_method (current_class_type, decl, /*error_p=*/0);
  
        TREE_CHAIN (decl) = TYPE_METHODS (current_class_type);
        TYPE_METHODS (current_class_type) = decl;


More information about the Gcc-patches mailing list