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]: Covariant returns


Hi,
this patch implement covariant return thunks. I used the generic thunk
machinery, so it won't work for varadic functions.

Covariant thunks are represented much like ordinary thunks, except that
they themselves can be thunked to. A this-pointer adjusting covarient
thunk looks like a this-thunk to a covariant-thunk to a base function.
I tidied up the naming of the thunk adjustment accessors to use
fixed_offset and virtual_offset appropriately (rather than delta and
vcall_offset).

There is an awkward little phasing problem when building up the vtables.
At the point we start modifying the vtable to slot in the covariant thunk,
we've not yet filled in the vtable vbase indices in the binfos. So I split
the thunk creation machinery to fix that up later. Regular thunks have a
similar issue, which is also appears to be split, but in that case the thunk
generation can be delayed to.

This fixes a couple of XFAILS, and passes the attached tests. Visual
examination of the generated vtables appears satisfactory.

This patch is against HEAD, but (if approved), I'll put it on bib.
Built and tested on i686-pc-linux-gnu, ok?

nathan
--
Dr Nathan Sidwell   ::   http://www.codesourcery.com   ::   CodeSourcery LLC
         'But that's a lie.' - 'Yes it is. What's your point?'
nathan@codesourcery.com : http://www.cs.bris.ac.uk/~nathan/ : nathan@acm.org

2002-11-28  Nathan Sidwell  <nathan@codesourcery.com>

	Implement covariant returns.
	* cp-tree.h (IS_AGGR_TYPE_2): Remove.
	(struct lang_decl_flags): Add this_thunk_p flag.
	Rename vcall_offset to virtual_offset.
	(struct lang_decl): Rename delta to fixed_offset.
	(DECL_THIS_THUNK_P, DECL_RESULT_THUNK_P): New #defines.
	(SET_DECL_THUNK_P): Add THIS_ADJUSTING arg.
	(THUNK_DELTA, THUNK_VCALL_OFFSET): Rename to ...
	(THUNK_FIXED_OFFSET, THUNK_VIRTUAL_OFFSET): ... here.
	(make_thunk): Add this_adjusting arg.
	(finish_thunk): Declare.
	(mangle_thunk): Add this_adjusting arg.
	* class.c (get_vcall_index): Use base function for lookup.
	(update_vtable_entry_for_fn): Generate covariant thunk.
	(finish_struct_1): Set DECL_VINDEX to NULL for thunks.
	(build_vtbl_initializer): Use base function for lookup.
	Finish covariant thunk here. Adjust thunk generation.
	* dump.c (cp_dump_tree): Simplify DECL_GLOBAL_[CD]TOR_P handling.
	Adjust thunk dumping.
	* mangle.c (mangle_call_offset): New function.
	(mangle_thunk): Adjust for covariant thunks.
	* method.c (make_thunk): Adjust. Do not set name here.
	(finish_thunk): New function. Set name here.
	(use_thunk): Generate covariant thunks too.
	(thunk_adjust): New function.
	* search.c (covariant_return_p): Remove. Fold into ...
	(check_final_overrider): ... here. Simplify.
	* semantics.c (emit_associated_thunks): Walk covariant thunk lists.
	
Index: cp/class.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/class.c,v
retrieving revision 1.496
diff -c -3 -p -r1.496 class.c
*** cp/class.c	26 Nov 2002 07:20:10 -0000	1.496
--- cp/class.c	27 Nov 2002 18:28:34 -0000
*************** get_vcall_index (tree fn, tree type)
*** 2326,2331 ****
--- 2326,2334 ----
  {
    tree v;
  
+   if (DECL_RESULT_THUNK_P (fn))
+     fn = TREE_OPERAND (DECL_INITIAL (fn), 0);
+ 
    for (v = CLASSTYPE_VCALL_INDICES (type); v; v = TREE_CHAIN (v))
      if ((DECL_DESTRUCTOR_P (fn) && DECL_DESTRUCTOR_P (TREE_PURPOSE (v)))
  	|| same_signature_p (fn, TREE_PURPOSE (v)))
*************** update_vtable_entry_for_fn (t, binfo, fn
*** 2373,2382 ****
    overrider = find_final_overrider (TYPE_BINFO (t), b, fn);
    if (overrider == error_mark_node)
      return;
  
!   /* Check for unsupported covariant returns again now that we've
!      calculated the base offsets.  */
!   check_final_overrider (TREE_PURPOSE (overrider), fn);
  
    /* Assume that we will produce a thunk that convert all the way to
       the final overrider, and not to an intermediate virtual base.  */
--- 2376,2428 ----
    overrider = find_final_overrider (TYPE_BINFO (t), b, fn);
    if (overrider == error_mark_node)
      return;
+   {
+     /* Check for adjusting covariant return types. */
+     tree over_return = TREE_TYPE (TREE_TYPE (TREE_PURPOSE (overrider)));
+     tree base_return = TREE_TYPE (TREE_TYPE (fn));
+ 
+     if (POINTER_TYPE_P (over_return)
+ 	&& TREE_CODE (over_return) == TREE_CODE (base_return)
+ 	&& CLASS_TYPE_P (TREE_TYPE (over_return))
+ 	&& CLASS_TYPE_P (TREE_TYPE (base_return)))
+       {
+ 	tree binfo;
+ 	base_kind kind;
  
! 	binfo = lookup_base (TREE_TYPE (over_return), TREE_TYPE (base_return),
! 			     ba_check | ba_quiet, &kind);
! 
! 	if (binfo && (kind == bk_via_virtual || !BINFO_OFFSET_ZEROP (binfo)))
! 	  {
! 	    tree fixed_offset = BINFO_OFFSET (binfo);
! 	    tree virtual_offset = NULL_TREE;
! 	    tree thunk;
! 	    
! 	    if (kind == bk_via_virtual)
! 	      {
! 		while (!TREE_VIA_VIRTUAL (binfo))
! 		  binfo = BINFO_INHERITANCE_CHAIN (binfo);
! 		
! 		/* If the covariant type is within the class hierarchy
! 		   we are currently laying out, the vbase index is not
! 		   yet known, so we have to remember the virtual base
! 		   binfo for the moment.  The thunk will be finished
! 		   in build_vtbl_initializer, where we'll know the
! 		   vtable index of the virtual base.  */
! 		virtual_offset = binfo_for_vbase (BINFO_TYPE (binfo), t);
! 	      }
! 	    
! 	    /* Replace the overriding function with a covariant thunk.
! 	       We will emit the overriding function in its own slot
! 	       as well. */
! 	    thunk = make_thunk (TREE_PURPOSE (overrider), /*this_adjusting=*/0,
! 				fixed_offset, virtual_offset);
! 	    TREE_PURPOSE (overrider) = thunk;
! 	    if (!virtual_offset && !DECL_NAME (thunk))
! 	      finish_thunk (thunk, fixed_offset, NULL_TREE);
! 	  }
!       }
!   }
  
    /* Assume that we will produce a thunk that convert all the way to
       the final overrider, and not to an intermediate virtual base.  */
*************** finish_struct_1 (t)
*** 5230,5237 ****
  	   fn = TREE_CHAIN (fn), 
  	     vindex += (TARGET_VTABLE_USES_DESCRIPTORS
  			? TARGET_VTABLE_USES_DESCRIPTORS : 1))
! 	if (TREE_CODE (DECL_VINDEX (BV_FN (fn))) != INTEGER_CST)
! 	  DECL_VINDEX (BV_FN (fn)) = build_shared_int_cst (vindex);
  
        /* Add this class to the list of dynamic classes.  */
        dynamic_classes = tree_cons (NULL_TREE, t, dynamic_classes);
--- 5276,5292 ----
  	   fn = TREE_CHAIN (fn), 
  	     vindex += (TARGET_VTABLE_USES_DESCRIPTORS
  			? TARGET_VTABLE_USES_DESCRIPTORS : 1))
! 	{
! 	  tree fndecl = BV_FN (fn);
! 
! 	  if (DECL_THUNK_P (fndecl))
! 	    /* A thunk. We should never be calling this entry directly
! 	       from this vtable -- we'd use the entry for the non
! 	       thunk base function.  */
! 	    DECL_VINDEX (fndecl) = NULL_TREE;
! 	  else if (TREE_CODE (DECL_VINDEX (fndecl)) != INTEGER_CST)
! 	    DECL_VINDEX (fndecl) = build_shared_int_cst (vindex);
! 	}
  
        /* Add this class to the list of dynamic classes.  */
        dynamic_classes = tree_cons (NULL_TREE, t, dynamic_classes);
*************** build_vtbl_initializer (binfo, orig_binf
*** 7654,7664 ****
      {
        tree delta;
        tree vcall_index;
!       tree fn;
        tree init = NULL_TREE;
        
        fn = BV_FN (v);
! 
        /* If the only definition of this function signature along our
  	 primary base chain is from a lost primary, this vtable slot will
  	 never be used, so just zero it out.  This is important to avoid
--- 7709,7732 ----
      {
        tree delta;
        tree vcall_index;
!       tree fn, fn_original;
        tree init = NULL_TREE;
        
        fn = BV_FN (v);
!       fn_original = (DECL_RESULT_THUNK_P (fn)
! 		     ? TREE_OPERAND (DECL_INITIAL (fn), 0)
! 		     : fn);
!       /* Finish an unfinished covariant thunk. */
!       if (DECL_RESULT_THUNK_P (fn) && !DECL_NAME (fn))
! 	{
! 	  tree binfo = THUNK_VIRTUAL_OFFSET (fn);
! 	  tree fixed_offset = size_int (THUNK_FIXED_OFFSET (fn));
! 	  tree virtual_offset = BINFO_VPTR_FIELD (binfo);
! 	  
! 	  fixed_offset = size_diffop (fixed_offset, BINFO_OFFSET (binfo));
! 	  finish_thunk (fn, fixed_offset, virtual_offset);
! 	}
!       
        /* If the only definition of this function signature along our
  	 primary base chain is from a lost primary, this vtable slot will
  	 never be used, so just zero it out.  This is important to avoid
*************** build_vtbl_initializer (binfo, orig_binf
*** 7672,7678 ****
        for (b = binfo; ; b = get_primary_binfo (b))
  	{
  	  /* We found a defn before a lost primary; go ahead as normal.  */
! 	  if (look_for_overrides_here (BINFO_TYPE (b), fn))
  	    break;
  
  	  /* The nearest definition is from a lost primary; clear the
--- 7740,7746 ----
        for (b = binfo; ; b = get_primary_binfo (b))
  	{
  	  /* We found a defn before a lost primary; go ahead as normal.  */
! 	  if (look_for_overrides_here (BINFO_TYPE (b), fn_original))
  	    break;
  
  	  /* The nearest definition is from a lost primary; clear the
*************** build_vtbl_initializer (binfo, orig_binf
*** 7696,7705 ****
  
  	  /* You can't call an abstract virtual function; it's abstract.
  	     So, we replace these functions with __pure_virtual.  */
! 	  if (DECL_PURE_VIRTUAL_P (fn))
  	    fn = abort_fndecl;
  	  else if (!integer_zerop (delta) || vcall_index)
! 	    fn = make_thunk (fn, delta, vcall_index);
  	  /* Take the address of the function, considering it to be of an
  	     appropriate generic type.  */
  	  init = build1 (ADDR_EXPR, vfunc_ptr_type_node, fn);
--- 7764,7777 ----
  
  	  /* You can't call an abstract virtual function; it's abstract.
  	     So, we replace these functions with __pure_virtual.  */
! 	  if (DECL_PURE_VIRTUAL_P (fn_original))
  	    fn = abort_fndecl;
  	  else if (!integer_zerop (delta) || vcall_index)
! 	    {
! 	      fn = make_thunk (fn, /*this_adjusting=*/1, delta, vcall_index);
! 	      if (!DECL_NAME (fn))
! 		finish_thunk (fn, delta, THUNK_VIRTUAL_OFFSET (fn));
! 	    }
  	  /* Take the address of the function, considering it to be of an
  	     appropriate generic type.  */
  	  init = build1 (ADDR_EXPR, vfunc_ptr_type_node, fn);
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.771
diff -c -3 -p -r1.771 cp-tree.h
*** cp/cp-tree.h	26 Nov 2002 17:40:55 -0000	1.771
--- cp/cp-tree.h	27 Nov 2002 18:28:48 -0000
*************** enum languages { lang_c, lang_cplusplus,
*** 1002,1010 ****
    (IS_AGGR_TYPE_CODE (TREE_CODE (T)) && IS_AGGR_TYPE (T))
  
  #define IS_AGGR_TYPE_CODE(T)	((T) == RECORD_TYPE || (T) == UNION_TYPE)
- #define IS_AGGR_TYPE_2(TYPE1, TYPE2) \
-   (TREE_CODE (TYPE1) == TREE_CODE (TYPE2)	\
-    && IS_AGGR_TYPE (TYPE1) && IS_AGGR_TYPE (TYPE2))
  #define TAGGED_TYPE_P(T) \
    (CLASS_TYPE_P (T) || TREE_CODE (T) == ENUMERAL_TYPE)
  #define IS_OVERLOAD_TYPE(T) TAGGED_TYPE_P (T)
--- 1002,1007 ----
*************** struct lang_decl_flags GTY(())
*** 1740,1746 ****
    unsigned u1sel : 1;
    unsigned u2sel : 1;
    unsigned can_be_full : 1;
!   unsigned unused : 1; /* One unused bit.  */
  
    union lang_decl_u {
      /* In a FUNCTION_DECL, VAR_DECL, TYPE_DECL, or TEMPLATE_DECL, this
--- 1737,1743 ----
    unsigned u1sel : 1;
    unsigned u2sel : 1;
    unsigned can_be_full : 1;
!   unsigned this_thunk_p : 1;
  
    union lang_decl_u {
      /* In a FUNCTION_DECL, VAR_DECL, TYPE_DECL, or TEMPLATE_DECL, this
*************** struct lang_decl_flags GTY(())
*** 1759,1766 ****
      int discriminator;
  
      /* In a FUNCTION_DECL for which DECL_THUNK_P holds, this is
!        THUNK_VCALL_OFFSET.  */
!     tree GTY((tag ("2"))) vcall_offset;
    } GTY ((desc ("%1.u2sel"))) u2;
  };
  
--- 1756,1763 ----
      int discriminator;
  
      /* In a FUNCTION_DECL for which DECL_THUNK_P holds, this is
!        THUNK_VIRTUAL_OFFSET.  */
!     tree GTY((tag ("2"))) virtual_offset;
    } GTY ((desc ("%1.u2sel"))) u2;
  };
  
*************** struct lang_decl GTY(())
*** 1776,1790 ****
  	
  	/* For a non-virtual FUNCTION_DECL, this is
  	   DECL_FRIEND_CONTEXT.  For a virtual FUNCTION_DECL for which
! 	   DECL_THUNK_P does not hold, this is DECL_THUNKS.  */
  	tree context;
  
  	/* In a FUNCTION_DECL, this is DECL_CLONED_FUNCTION.  */
  	tree cloned_function;
  	
  	/* In a FUNCTION_DECL for which THUNK_P holds, this is
! 	   THUNK_DELTA.  */
! 	HOST_WIDE_INT delta;
  
  	/* In an overloaded operator, this is the value of
  	   DECL_OVERLOADED_OPERATOR_P.  */
--- 1773,1790 ----
  	
  	/* For a non-virtual FUNCTION_DECL, this is
  	   DECL_FRIEND_CONTEXT.  For a virtual FUNCTION_DECL for which
! 	   DECL_THIS_THUNK_P does not hold, this is DECL_THUNKS. Both
! 	   this pointer and result pointer adjusting thunks are
! 	   chained here.  This pointer thunks to return pointer thunks
! 	   will be chained on the return pointer thunk. */
  	tree context;
  
  	/* In a FUNCTION_DECL, this is DECL_CLONED_FUNCTION.  */
  	tree cloned_function;
  	
  	/* In a FUNCTION_DECL for which THUNK_P holds, this is
! 	   THUNK_FIXED_OFFSET.  */
! 	HOST_WIDE_INT fixed_offset;
  
  	/* In an overloaded operator, this is the value of
  	   DECL_OVERLOADED_OPERATOR_P.  */
*************** struct lang_decl GTY(())
*** 2066,2073 ****
  #define DECL_NEEDS_FINAL_OVERRIDER_P(NODE) \
    (DECL_LANG_SPECIFIC (NODE)->decl_flags.needs_final_overrider)
  
! /* The thunks associated with NODE, a FUNCTION_DECL that is not itself
!    a thunk.  */
  #define DECL_THUNKS(NODE) \
    (DECL_LANG_SPECIFIC (NODE)->u.f.context)
  
--- 2066,2072 ----
  #define DECL_NEEDS_FINAL_OVERRIDER_P(NODE) \
    (DECL_LANG_SPECIFIC (NODE)->decl_flags.needs_final_overrider)
  
! /* The thunks associated with NODE, a FUNCTION_DECL.  */
  #define DECL_THUNKS(NODE) \
    (DECL_LANG_SPECIFIC (NODE)->u.f.context)
  
*************** struct lang_decl GTY(())
*** 2076,2081 ****
--- 2075,2088 ----
    (TREE_CODE (NODE) == FUNCTION_DECL		\
     && DECL_LANG_FLAG_7 (NODE))
  
+ /* Nonzero if NODE is a this pointer adjusting thunk.  */
+ #define DECL_THIS_THUNK_P(NODE)			\
+   (DECL_THUNK_P (NODE) && DECL_LANG_SPECIFIC (NODE)->decl_flags.this_thunk_p)
+ 
+ /* Nonzero if NODE is a result pointer adjusting thunk.  */
+ #define DECL_RESULT_THUNK_P(NODE)			\
+   (DECL_THUNK_P (NODE) && !DECL_LANG_SPECIFIC (NODE)->decl_flags.this_thunk_p)
+ 
  /* Nonzero if NODE is a FUNCTION_DECL, but not a thunk.  */
  #define DECL_NON_THUNK_FUNCTION_P(NODE)				\
    (TREE_CODE (NODE) == FUNCTION_DECL && !DECL_THUNK_P (NODE))
*************** struct lang_decl GTY(())
*** 2089,2097 ****
    (DECL_NON_THUNK_FUNCTION_P (NODE) && DECL_EXTERN_C_P (NODE))
  
  /* Set DECL_THUNK_P for node.  */
! #define SET_DECL_THUNK_P(NODE)					\
    (DECL_LANG_FLAG_7 (NODE) = 1, 				\
!    DECL_LANG_SPECIFIC (NODE)->u.f.u3sel = 1)
  
  /* Nonzero if this DECL is the __PRETTY_FUNCTION__ variable in a
     template function.  */
--- 2096,2105 ----
    (DECL_NON_THUNK_FUNCTION_P (NODE) && DECL_EXTERN_C_P (NODE))
  
  /* Set DECL_THUNK_P for node.  */
! #define SET_DECL_THUNK_P(NODE, THIS_ADJUSTING)			\
    (DECL_LANG_FLAG_7 (NODE) = 1, 				\
!    DECL_LANG_SPECIFIC (NODE)->u.f.u3sel = 1,			\
!    DECL_LANG_SPECIFIC (NODE)->decl_flags.this_thunk_p = (THIS_ADJUSTING))
  
  /* Nonzero if this DECL is the __PRETTY_FUNCTION__ variable in a
     template function.  */
*************** struct lang_decl GTY(())
*** 2921,2953 ****
     A thunk is an alternate entry point for an ordinary FUNCTION_DECL.
     The address of the ordinary FUNCTION_DECL is given by the
     DECL_INITIAL, which is always an ADDR_EXPR whose operand is a
!    FUNCTION_DECL.  The job of the thunk is to adjust the `this'
!    pointer before transferring control to the FUNCTION_DECL.
! 
     A thunk may perform either, or both, of the following operations:
  
!    o Adjust the `this' pointer by a constant offset.
!    o Adjust the `this' pointer by looking up a vcall-offset
       in the vtable.
  
!    If both operations are performed, then the constant adjument to
!    `this' is performed first.
! 
!    The constant adjustment is given by THUNK_DELTA.  If the
!    vcall-offset is required, the index into the vtable is given by
!    THUNK_VCALL_OFFSET.  */
  
  /* An integer indicating how many bytes should be subtracted from the
!    `this' pointer when this function is called.  */
! #define THUNK_DELTA(DECL) \
!   (DECL_LANG_SPECIFIC (DECL)->u.f.delta)
! 
! /* A tree indicating how many bytes should be subtracted from the
!    vtable for the `this' pointer to find the vcall offset.  (The vptr
!    is always located at offset zero from the f `this' pointer.)  If
!    NULL, then there is no vcall offset.  */
! #define THUNK_VCALL_OFFSET(DECL) \
!   (LANG_DECL_U2_CHECK (DECL, 0)->vcall_offset)
  
  /* These macros provide convenient access to the various _STMT nodes
     created when parsing template declarations.  */
--- 2929,2970 ----
     A thunk is an alternate entry point for an ordinary FUNCTION_DECL.
     The address of the ordinary FUNCTION_DECL is given by the
     DECL_INITIAL, which is always an ADDR_EXPR whose operand is a
!    FUNCTION_DECL.  The job of the thunk is to either adjust the this
!    pointer before transferring control to the FUNCTION_DECL, or call
!    FUNCTION_DECL and then adjust the result value. Note, the result
!    pointer adjusting thunk must perform a call to the thunked
!    function, (or be implemented via passing some invisible parameter
!    to the thunked function, which is modified to perform the
!    adjustment just before returning).
!    
     A thunk may perform either, or both, of the following operations:
  
!    o Adjust the this or result pointer by a constant offset.
!    o Adjust the this or result pointer by looking up a vcall or vbase offset
       in the vtable.
  
!    A this pointer adjusting thunk converts from a base to a derived
!    class, and hence adds the offsets. A result pointer adjusting thunk
!    converts from a derived class to a base, and hence subtracts the
!    offsets.  If both operations are performed, then the constant
!    adjument is performed first for this pointer adjustment and last
!    for the result pointer adjustment.
! 
!    The constant adjustment is given by THUNK_FIXED_OFFSET.  If the
!    vcall or vbase offset is required, the index into the vtable is given by
!    THUNK_VIRTUAL_OFFSET.  */
  
  /* An integer indicating how many bytes should be subtracted from the
!    this or result pointer when this function is called.  */
! #define THUNK_FIXED_OFFSET(DECL) \
!   (DECL_LANG_SPECIFIC (DECL)->u.f.fixed_offset)
! 
! /* A tree indicating how many bytes should be added to the
!    vtable for the this or result pointer to find the vcall or vbase
!    offset.  (The vptr is always located at offset zero from the
!    this or result pointer.)  If NULL, then there is no virtual adjust.  */
! #define THUNK_VIRTUAL_OFFSET(DECL) \
!   (LANG_DECL_U2_CHECK (DECL, 0)->virtual_offset)
  
  /* These macros provide convenient access to the various _STMT nodes
     created when parsing template declarations.  */
*************** extern void init_method				PARAMS ((void
*** 3950,3956 ****
  extern void set_mangled_name_for_decl           PARAMS ((tree));
  extern tree build_opfncall			PARAMS ((enum tree_code, int, tree, tree, tree));
  extern tree hack_identifier			PARAMS ((tree, tree));
! extern tree make_thunk				PARAMS ((tree, tree, tree));
  extern void use_thunk				PARAMS ((tree, int));
  extern void synthesize_method			PARAMS ((tree));
  extern tree implicitly_declare_fn               PARAMS ((special_function_kind, tree, int));
--- 3967,3974 ----
  extern void set_mangled_name_for_decl           PARAMS ((tree));
  extern tree build_opfncall			PARAMS ((enum tree_code, int, tree, tree, tree));
  extern tree hack_identifier			PARAMS ((tree, tree));
! extern tree make_thunk				PARAMS ((tree, int, tree, tree));
! extern void finish_thunk			PARAMS ((tree, tree, tree));
  extern void use_thunk				PARAMS ((tree, int));
  extern void synthesize_method			PARAMS ((tree));
  extern tree implicitly_declare_fn               PARAMS ((special_function_kind, tree, int));
*************** extern tree mangle_typeinfo_string_for_t
*** 4384,4390 ****
  extern tree mangle_vtbl_for_type                PARAMS ((tree));
  extern tree mangle_vtt_for_type                 PARAMS ((tree));
  extern tree mangle_ctor_vtbl_for_type           PARAMS ((tree, tree));
! extern tree mangle_thunk                        PARAMS ((tree, tree, tree));
  extern tree mangle_conv_op_name_for_type        PARAMS ((tree));
  extern tree mangle_guard_variable               PARAMS ((tree));
  extern tree mangle_ref_init_variable            PARAMS ((tree));
--- 4402,4408 ----
  extern tree mangle_vtbl_for_type                PARAMS ((tree));
  extern tree mangle_vtt_for_type                 PARAMS ((tree));
  extern tree mangle_ctor_vtbl_for_type           PARAMS ((tree, tree));
! extern tree mangle_thunk                        PARAMS ((tree, int, tree, tree));
  extern tree mangle_conv_op_name_for_type        PARAMS ((tree));
  extern tree mangle_guard_variable               PARAMS ((tree));
  extern tree mangle_ref_init_variable            PARAMS ((tree));
Index: cp/dump.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/dump.c,v
retrieving revision 1.64
diff -c -3 -p -r1.64 dump.c
*** cp/dump.c	31 Oct 2002 16:07:32 -0000	1.64
--- cp/dump.c	27 Nov 2002 18:28:49 -0000
*************** cp_dump_tree (dump_info, t)
*** 317,337 ****
  	    dump_string (di, "destructor");
  	  if (DECL_CONV_FN_P (t))
  	    dump_string (di, "conversion");
! 	  if (DECL_GLOBAL_CTOR_P (t) || DECL_GLOBAL_DTOR_P (t))
! 	    {
! 	      if (DECL_GLOBAL_CTOR_P (t))
! 		dump_string (di, "global init");
! 	      if (DECL_GLOBAL_DTOR_P (t))
! 		dump_string (di, "global fini");
! 	    }
  	  if (DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t))
  	    dump_string (di, "pseudo tmpl");
  	}
        else
  	{
  	  dump_string (di, "thunk");
! 	  dump_int (di, "dlta", THUNK_DELTA (t));
! 	  dump_child ("vcll", THUNK_VCALL_OFFSET (t));
  	  dump_child ("fn", DECL_INITIAL (t));
  	}
        break;
--- 317,338 ----
  	    dump_string (di, "destructor");
  	  if (DECL_CONV_FN_P (t))
  	    dump_string (di, "conversion");
! 	  if (DECL_GLOBAL_CTOR_P (t))
! 	    dump_string (di, "global init");
! 	  if (DECL_GLOBAL_DTOR_P (t))
! 	    dump_string (di, "global fini");
  	  if (DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t))
  	    dump_string (di, "pseudo tmpl");
  	}
        else
  	{
  	  dump_string (di, "thunk");
! 	  if (DECL_THIS_THUNK_P (t))
! 	    dump_string (di, "this adjusting");
! 	  else
! 	    dump_string (di, "result adjusting");
! 	  dump_int (di, "fixd", THUNK_FIXED_OFFSET (t));
! 	  dump_child ("virt", THUNK_VIRTUAL_OFFSET (t));
  	  dump_child ("fn", DECL_INITIAL (t));
  	}
        break;
Index: cp/mangle.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/mangle.c,v
retrieving revision 1.63
diff -c -3 -p -r1.63 mangle.c
*** cp/mangle.c	9 Nov 2002 11:53:16 -0000	1.63
--- cp/mangle.c	27 Nov 2002 18:28:54 -0000
*************** static void add_substitution PARAMS ((tr
*** 154,159 ****
--- 154,160 ----
  static inline int is_std_substitution PARAMS ((tree, substitution_identifier_index_t));
  static inline int is_std_substitution_char PARAMS ((tree, substitution_identifier_index_t));
  static int find_substitution PARAMS ((tree));
+ static void mangle_call_offset PARAMS ((tree, tree));
  
  /* Functions for emitting mangled representations of things.  */
  
*************** mangle_ctor_vtbl_for_type (type, binfo)
*** 2532,2576 ****
    return get_identifier (result);
  }
  
! /* Return an identifier for the mangled name of a thunk to FN_DECL.
!    OFFSET is the initial adjustment to this used to find the vptr.  If
!    VCALL_OFFSET is non-NULL, this is a virtual thunk, and it is the
!    vtbl offset in bytes.  
! 
!     <special-name> ::= Th <offset number> _ <base encoding>
!                    ::= Tv <offset number> _ <vcall offset number> _
! 		                                            <base encoding>
  */
  
  tree
! mangle_thunk (fn_decl, offset, vcall_offset)
       tree fn_decl;
!      tree offset;
!      tree vcall_offset;
  {
    const char *result;
    
    start_mangling (fn_decl);
  
    write_string ("_Z");
-   /* The <special-name> for virtual thunks is Tv, for non-virtual
-      thunks Th.  */
    write_char ('T');
!   if (vcall_offset != 0)
!     write_char ('v');
    else
-     write_char ('h');
- 
-   /* For either flavor, write the offset to this.  */
-   write_integer_cst (offset);
-   write_char ('_');
- 
-   /* For a virtual thunk, add the vcall offset.  */
-   if (vcall_offset)
      {
!       /* Virtual thunk.  Write the vcall offset and base type name.  */
!       write_integer_cst (vcall_offset);
!       write_char ('_');
      }
  
    /* Scoped name.  */
--- 2533,2611 ----
    return get_identifier (result);
  }
  
! /* Mangle a this pointer or result pointer adjustment.
!    
!    <call-offset> ::= h <fixed offset number> _
! 		 ::= v <fixed offset number> _ <virtual offset number> _ */
!    
! static void
! mangle_call_offset (fixed_offset, virtual_offset)
!      tree fixed_offset;
!      tree virtual_offset;
! {
!   if (virtual_offset)
!     write_char (virtual_offset ? 'v' : 'h');
!   else
!     write_char ('h');
! 
!   /* For either flavor, write the fixed offset.  */
!   write_integer_cst (fixed_offset);
!   write_char ('_');
! 
!   /* For a virtual thunk, add the virtual offset.  */
!   if (virtual_offset)
!     {
!       write_integer_cst (virtual_offset);
!       write_char ('_');
!     }
! }
! 
! /* Return an identifier for the mangled name of a this-adjusting or
!    covariant thunk to FN_DECL.  FIXED_OFFSET is the initial adjustment
!    to this used to find the vptr.  If VIRTUAL_OFFSET is non-NULL, this
!    is a virtual thunk, and it is the vtbl offset in
!    bytes. THIS_ADJUSTING is non-zero for a this adjusting thunk and
!    zero for a covariant thunk. Note, that FN_DECL might be a covariant
!    thunk itself. A covariant thunk name always includes the adjustment
!    for the this pointer, even if there is none.
! 
!    <special-name> ::= T <call-offset> <base encoding>
!                   ::= Tc <this_adjust call-offset> <result_adjust call-offset>
! 		  			<base encoding>
  */
  
  tree
! mangle_thunk (fn_decl, this_adjusting, fixed_offset, virtual_offset)
       tree fn_decl;
!      int this_adjusting;
!      tree fixed_offset;
!      tree virtual_offset;
  {
    const char *result;
    
    start_mangling (fn_decl);
  
    write_string ("_Z");
    write_char ('T');
!   
!   if (this_adjusting && !DECL_RESULT_THUNK_P (fn_decl))
!     /* Plain this adjusting thunk.  */
!     mangle_call_offset (fixed_offset, virtual_offset);
!   else if (!this_adjusting)
!     {
!       /* Covariant thunk with no this adjustment */
!       write_char ('c');
!       mangle_call_offset (integer_zero_node, NULL_TREE);
!       mangle_call_offset (fixed_offset, virtual_offset);
!     }
    else
      {
!       /* This adjusting thunk to covariant thunk.  */
!       write_char ('c');
!       mangle_call_offset (fixed_offset, virtual_offset);
!       mangle_call_offset (ssize_int (THUNK_FIXED_OFFSET (fn_decl)),
! 			  THUNK_VIRTUAL_OFFSET (fn_decl));
!       fn_decl = TREE_OPERAND (DECL_INITIAL (fn_decl), 0);
      }
  
    /* Scoped name.  */
Index: cp/method.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/method.c,v
retrieving revision 1.237
diff -c -3 -p -r1.237 method.c
*** cp/method.c	25 Oct 2002 19:39:47 -0000	1.237
--- cp/method.c	27 Nov 2002 18:28:56 -0000
*************** enum mangling_flags
*** 54,59 ****
--- 54,60 ----
  
  typedef enum mangling_flags mangling_flags;
  
+ static tree thunk_adjust PARAMS ((tree, int, HOST_WIDE_INT, tree));
  static void do_build_assign_ref PARAMS ((tree));
  static void do_build_copy_constructor PARAMS ((tree));
  static tree synthesize_exception_spec PARAMS ((tree, tree (*) (tree, void *), void *));
*************** request for member `%D' is ambiguous in 
*** 265,309 ****
  }
  
  
! /* Return a thunk to FUNCTION.  For a virtual thunk, DELTA is the
!    offset to this used to locate the vptr, and VCALL_INDEX is used to
!    look up the eventual subobject location.  For a non-virtual thunk,
!    DELTA is the offset to this and VCALL_INDEX is NULL.  */
  
  tree
! make_thunk (function, delta, vcall_index)
       tree function;
!      tree delta;
!      tree vcall_index;
  {
-   tree thunk_id;
-   tree thunk;
-   tree vcall_offset;
    HOST_WIDE_INT d;
  
    my_friendly_assert (TREE_CODE (function) == FUNCTION_DECL, 20021025);
  
!   /* Scale the VCALL_INDEX to be in terms of bytes.  */
!   if (vcall_index)
!     vcall_offset 
        = size_binop (MULT_EXPR,
! 		    vcall_index,
  		    convert (ssizetype,
  			     TYPE_SIZE_UNIT (vtable_entry_type)));
-   else
-     vcall_offset = NULL_TREE;
  
!   d = tree_low_cst (delta, 0);
  
!   /* See if we already have the thunk in question.  */
    for (thunk = DECL_THUNKS (function); thunk; thunk = TREE_CHAIN (thunk))
!     if (THUNK_DELTA (thunk) == d
! 	&& ((THUNK_VCALL_OFFSET (thunk) != NULL_TREE)
! 	    == (vcall_offset != NULL_TREE))
! 	&& (THUNK_VCALL_OFFSET (thunk)
! 	    ? tree_int_cst_equal (THUNK_VCALL_OFFSET (thunk), 
! 				  vcall_offset)
! 	    : true))
        return thunk;
  
    /* All thunks must be created before FUNCTION is actually emitted;
--- 266,317 ----
  }
  
  
! /* Return a this adjusting thunk to FUNCTION.  THIS_ADJUSTING
!    indicates whether it is a this or result adjusting thunk.
!    FIXED_OFFSET and VIRTUAL_OFFSET indicate how to do the adjustment
!    (see thunk_adjust).  VIRTUAL_OFFSET can be NULL, but FIXED_OFFSET
!    never is.  VIRTUAL_OFFSET is the /index/ into the vtable for this
!    adjusting thunks, we scale it to a byte offset. For covariant
!    thunks VIRTUAL_OFFSET is the virtual binfo.  You must post process
!    the returned thunk with finish_thunk.  */
  
  tree
! make_thunk (function, this_adjusting, fixed_offset, virtual_offset)
       tree function;
!      int this_adjusting;
!      tree fixed_offset;
!      tree virtual_offset;
  {
    HOST_WIDE_INT d;
+   tree thunk;
  
    my_friendly_assert (TREE_CODE (function) == FUNCTION_DECL, 20021025);
+   /* We can have this thunks to covariant thunks, but not vice versa. */
+   my_friendly_assert (!DECL_THIS_THUNK_P (function), 20021127);
  
!   /* Scale the VIRTUAL_OFFSET to be in terms of bytes.  */
!   if (this_adjusting && virtual_offset)
!     virtual_offset 
        = size_binop (MULT_EXPR,
! 		    virtual_offset,
  		    convert (ssizetype,
  			     TYPE_SIZE_UNIT (vtable_entry_type)));
  
!   d = tree_low_cst (fixed_offset, 0);
  
!   /* See if we already have the thunk in question.  For this_adjusting
!      thunks VIRTUAL_OFFSET will be an INTEGER_CST, for covariant thunks it
!      will be a BINFO (because of the organization of the layout
!      algorithm). */
    for (thunk = DECL_THUNKS (function); thunk; thunk = TREE_CHAIN (thunk))
!     if (DECL_THIS_THUNK_P (thunk) == this_adjusting
! 	&& THUNK_FIXED_OFFSET (thunk) == d
! 	&& (this_adjusting
! 	    ? (!THUNK_VIRTUAL_OFFSET (thunk) == !virtual_offset
! 	       && (!virtual_offset
! 		   || tree_int_cst_equal (THUNK_VIRTUAL_OFFSET (thunk), 
! 					  virtual_offset)))
! 	    : THUNK_VIRTUAL_OFFSET (thunk) == virtual_offset))
        return thunk;
  
    /* All thunks must be created before FUNCTION is actually emitted;
*************** make_thunk (function, delta, vcall_index
*** 311,331 ****
       function to which they transfer control.  */
    my_friendly_assert (!TREE_ASM_WRITTEN (function), 20021025);
  
!   thunk_id = mangle_thunk (function, delta, vcall_offset);
!   thunk = build_decl (FUNCTION_DECL, thunk_id, TREE_TYPE (function));
    DECL_LANG_SPECIFIC (thunk) = DECL_LANG_SPECIFIC (function);
    cxx_dup_lang_specific_decl (function);
-   SET_DECL_ASSEMBLER_NAME (thunk, thunk_id);
    DECL_CONTEXT (thunk) = DECL_CONTEXT (function);
    TREE_READONLY (thunk) = TREE_READONLY (function);
    TREE_THIS_VOLATILE (thunk) = TREE_THIS_VOLATILE (function);
    TREE_PUBLIC (thunk) = TREE_PUBLIC (function);
    if (flag_weak)
      comdat_linkage (thunk);
!   SET_DECL_THUNK_P (thunk);
    DECL_INITIAL (thunk) = build1 (ADDR_EXPR, vfunc_ptr_type_node, function);
!   THUNK_DELTA (thunk) = d;
!   THUNK_VCALL_OFFSET (thunk) = vcall_offset;
    /* The thunk itself is not a constructor or destructor, even if
       the thing it is thunking to is.  */
    DECL_INTERFACE_KNOWN (thunk) = 1;
--- 319,338 ----
       function to which they transfer control.  */
    my_friendly_assert (!TREE_ASM_WRITTEN (function), 20021025);
  
!   thunk = build_decl (FUNCTION_DECL, NULL_TREE, TREE_TYPE (function));
    DECL_LANG_SPECIFIC (thunk) = DECL_LANG_SPECIFIC (function);
    cxx_dup_lang_specific_decl (function);
    DECL_CONTEXT (thunk) = DECL_CONTEXT (function);
    TREE_READONLY (thunk) = TREE_READONLY (function);
    TREE_THIS_VOLATILE (thunk) = TREE_THIS_VOLATILE (function);
    TREE_PUBLIC (thunk) = TREE_PUBLIC (function);
    if (flag_weak)
      comdat_linkage (thunk);
!   SET_DECL_THUNK_P (thunk, this_adjusting);
    DECL_INITIAL (thunk) = build1 (ADDR_EXPR, vfunc_ptr_type_node, function);
!   THUNK_FIXED_OFFSET (thunk) = tree_low_cst (fixed_offset, 0);
!   THUNK_VIRTUAL_OFFSET (thunk) = virtual_offset;
!   
    /* The thunk itself is not a constructor or destructor, even if
       the thing it is thunking to is.  */
    DECL_INTERFACE_KNOWN (thunk) = 1;
*************** make_thunk (function, delta, vcall_index
*** 351,358 ****
    return thunk;
  }
  
! /* Emit the definition of a C++ multiple inheritance vtable thunk.  If
!    EMIT_P is nonzero, the thunk is emitted immediately.  */
  
  void
  use_thunk (thunk_fndecl, emit_p)
--- 358,437 ----
    return thunk;
  }
  
! /* Finish THUNK, a thunk decl. FIXED_OFFSET and VIRTUAL_OFFSET are the
!    adjustments to apply.  */
! 
! void
! finish_thunk (thunk, fixed_offset, virtual_offset)
!      tree thunk;
!      tree fixed_offset;
!      tree virtual_offset;
! {
!   tree function, name;
!   
!   my_friendly_assert (!DECL_NAME (thunk) && DECL_THUNK_P (thunk), 20021127);
!   function = TREE_OPERAND (DECL_INITIAL (thunk), 0);
!   name = mangle_thunk (function, DECL_THIS_THUNK_P (thunk),
! 			    fixed_offset, virtual_offset);
!   THUNK_FIXED_OFFSET (thunk) = tree_low_cst (fixed_offset, 0);
!   THUNK_VIRTUAL_OFFSET (thunk) = virtual_offset;
!   DECL_NAME (thunk) = name;
!   SET_DECL_ASSEMBLER_NAME (thunk, name);
! }
! 
! /* Adjust PTR by the constant FIXED_OFFSET, and by the vtable
!    offset indicated by VIRTUAL_OFFSET, if that is
!    non-null. THIS_ADJUSTING is non-zero for a this adjusting thunk and
!    zero for a result adjusting thunk. */
! 
! static tree
! thunk_adjust (ptr, this_adjusting, fixed_offset, virtual_offset)
!      tree ptr;
!      int this_adjusting;
!      HOST_WIDE_INT fixed_offset;
!      tree virtual_offset;
! {
!   if (this_adjusting)
!     /* Adjust the pointer by the constant.  */
!     ptr = fold (build (PLUS_EXPR, TREE_TYPE (ptr), ptr,
! 		       ssize_int (fixed_offset)));
! 
!   /* If there's a virtual offset, look up that value in the vtable and
!      adjust the pointer again.  */
!   if (virtual_offset)
!     {
!       tree vtable;
! 
!       /* It shouldn't be a binfo any more. */
!       my_friendly_assert (TREE_CODE (virtual_offset) == INTEGER_CST, 20021127);
!       
!       ptr = save_expr (ptr);
!       /* The vptr is always at offset zero in the object.  */
!       vtable = build1 (NOP_EXPR,
! 		       build_pointer_type (build_pointer_type 
! 					   (vtable_entry_type)),
! 		       ptr);
!       /* Form the vtable address.  */
!       vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
!       /* Find the entry with the vcall offset.  */
!       vtable = build (PLUS_EXPR, TREE_TYPE (vtable), vtable, virtual_offset);
!       /* Get the offset itself.  */
!       vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
!       /* Adjust the `this' pointer.  */
!       ptr = fold (build (PLUS_EXPR, TREE_TYPE (ptr), ptr, vtable));
!     }
!   
!   if (!this_adjusting)
!     /* Adjust the pointer by the constant.  */
!     ptr = fold (build (PLUS_EXPR, TREE_TYPE (ptr), ptr,
! 		       ssize_int (fixed_offset)));
! 
!   return ptr;
! }
! 
! /* Emit the definition of a C++ multiple inheritance or covariant
!    return vtable thunk.  If EMIT_P is nonzero, the thunk is emitted
!    immediately.  */
  
  void
  use_thunk (thunk_fndecl, emit_p)
*************** use_thunk (thunk_fndecl, emit_p)
*** 361,368 ****
  {
    tree fnaddr;
    tree function;
!   tree vcall_offset;
!   HOST_WIDE_INT delta, vcall_value;
  
    if (TREE_ASM_WRITTEN (thunk_fndecl))
      return;
--- 440,450 ----
  {
    tree fnaddr;
    tree function;
!   tree virtual_offset;
!   HOST_WIDE_INT fixed_offset, virtual_value;
! 
!   /* We should have called finish_thunk to give it a name. */
!   my_friendly_assert (DECL_NAME (thunk_fndecl), 20021127);
  
    if (TREE_ASM_WRITTEN (thunk_fndecl))
      return;
*************** use_thunk (thunk_fndecl, emit_p)
*** 385,404 ****
    if (!emit_p)
      return;
  
!   delta = THUNK_DELTA (thunk_fndecl);
!   vcall_offset = THUNK_VCALL_OFFSET (thunk_fndecl);
! 
!   if (vcall_offset)
!     {
!       vcall_value = tree_low_cst (vcall_offset, /*pos=*/0);
! 
!       /* It is expected that a value of zero means no vcall.  */
!       if (!vcall_value)
! 	abort ();
!     }
!   else
!     vcall_value = 0;
  
    /* And, if we need to emit the thunk, it's used.  */
    mark_used (thunk_fndecl);
    /* This thunk is actually defined.  */
--- 467,479 ----
    if (!emit_p)
      return;
  
!   fixed_offset = THUNK_FIXED_OFFSET (thunk_fndecl);
!   virtual_offset = THUNK_VIRTUAL_OFFSET (thunk_fndecl);
  
+   virtual_value = (virtual_offset
+ 		   ? tree_low_cst (virtual_offset, /*pos=*/0) : 0);
+   my_friendly_assert (!virtual_offset || virtual_value, 20021026);
+   
    /* And, if we need to emit the thunk, it's used.  */
    mark_used (thunk_fndecl);
    /* This thunk is actually defined.  */
*************** use_thunk (thunk_fndecl, emit_p)
*** 421,428 ****
    BLOCK_VARS (DECL_INITIAL (thunk_fndecl)) 
      = DECL_ARGUMENTS (thunk_fndecl);
  
!   if (targetm.asm_out.can_output_mi_thunk (thunk_fndecl, delta,
! 					   vcall_value, function))
      {
        const char *fnname;
        current_function_decl = thunk_fndecl;
--- 496,504 ----
    BLOCK_VARS (DECL_INITIAL (thunk_fndecl)) 
      = DECL_ARGUMENTS (thunk_fndecl);
  
!   if (DECL_THIS_THUNK_P (thunk_fndecl)
!       && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
! 					      virtual_value, function))
      {
        const char *fnname;
        current_function_decl = thunk_fndecl;
*************** use_thunk (thunk_fndecl, emit_p)
*** 433,440 ****
        current_function_is_thunk = 1;
        assemble_start_function (thunk_fndecl, fnname);
  
!       targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl, delta,
! 				       vcall_value, function);
  
        assemble_end_function (thunk_fndecl, fnname);
        current_function_decl = 0;
--- 509,516 ----
        current_function_is_thunk = 1;
        assemble_start_function (thunk_fndecl, fnname);
  
!       targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
! 				       fixed_offset, virtual_value, function);
  
        assemble_end_function (thunk_fndecl, fnname);
        current_function_decl = 0;
*************** use_thunk (thunk_fndecl, emit_p)
*** 443,451 ****
      }
    else
      {
!       /* If we don't have the necessary code for efficient thunks,
! 	 generate a thunk function that just makes a call to the real
! 	 function.  Unfortunately, this doesn't work for varargs.  */
  
        tree a, t;
  
--- 519,528 ----
      }
    else
      {
!       /* If this is a covariant thunk, or we don't have the necessary
! 	 code for efficient thunks, generate a thunk function that
! 	 just makes a call to the real function.  Unfortunately, this
! 	 doesn't work for varargs.  */
  
        tree a, t;
  
*************** use_thunk (thunk_fndecl, emit_p)
*** 453,459 ****
  	error ("generic thunk code fails for method `%#D' which uses `...'",
  	       function);
  
!       /* Set up clone argument trees for the thunk.  */
        t = NULL_TREE;
        for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
  	{
--- 530,536 ----
  	error ("generic thunk code fails for method `%#D' which uses `...'",
  	       function);
  
!       /* Set up cloned argument trees for the thunk.  */
        t = NULL_TREE;
        for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
  	{
*************** use_thunk (thunk_fndecl, emit_p)
*** 469,510 ****
        start_function (NULL_TREE, thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
        /* We don't bother with a body block for thunks.  */
  
!       /* Adjust the this pointer by the constant.  */
!       t = ssize_int (delta);
!       t = fold (build (PLUS_EXPR, TREE_TYPE (a), a, t));
! 
!       /* If there's a vcall offset, look up that value in the vtable and
! 	 adjust the `this' pointer again.  */
!       if (vcall_offset && !integer_zerop (vcall_offset))
! 	{
! 	  tree orig_this;
! 
! 	  t = save_expr (t);
! 	  orig_this = t;
! 	  /* The vptr is always at offset zero in the object.  */
! 	  t = build1 (NOP_EXPR,
! 		      build_pointer_type (build_pointer_type 
! 					  (vtable_entry_type)),
! 		      t);
! 	  /* Form the vtable address.  */
! 	  t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
! 	  /* Find the entry with the vcall offset.  */
! 	  t = build (PLUS_EXPR, TREE_TYPE (t), t, vcall_offset);
! 	  /* Calculate the offset itself.  */
! 	  t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
! 	  /* Adjust the `this' pointer.  */
! 	  t = fold (build (PLUS_EXPR,
! 			   TREE_TYPE (orig_this),
! 			   orig_this,
! 			   t));
! 	}
! 
        /* Build up the call to the real function.  */
        t = tree_cons (NULL_TREE, t, NULL_TREE);
        for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a))
  	t = tree_cons (NULL_TREE, a, t);
        t = nreverse (t);
        t = build_call (function, t);
        if (VOID_TYPE_P (TREE_TYPE (t)))
  	finish_expr_stmt (t);
        else
--- 546,567 ----
        start_function (NULL_TREE, thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
        /* We don't bother with a body block for thunks.  */
  
!       t = a;
!       
!       if (DECL_THIS_THUNK_P (thunk_fndecl))
! 	t = thunk_adjust (t, /*this_adjusting=*/1,
! 			  fixed_offset, virtual_offset);
!       
        /* Build up the call to the real function.  */
        t = tree_cons (NULL_TREE, t, NULL_TREE);
        for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a))
  	t = tree_cons (NULL_TREE, a, t);
        t = nreverse (t);
        t = build_call (function, t);
+       if (DECL_RESULT_THUNK_P (thunk_fndecl))
+ 	t = thunk_adjust (t, /*this_adjusting=*/0,
+ 			  fixed_offset, virtual_offset);
+       
        if (VOID_TYPE_P (TREE_TYPE (t)))
  	finish_expr_stmt (t);
        else
Index: cp/search.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/search.c,v
retrieving revision 1.243
diff -c -3 -p -r1.243 search.c
*** cp/search.c	25 Nov 2002 09:25:31 -0000	1.243
--- cp/search.c	27 Nov 2002 18:29:02 -0000
*************** static tree dfs_push_type_decls PARAMS (
*** 97,103 ****
  static tree dfs_push_decls PARAMS ((tree, void *));
  static tree dfs_unuse_fields PARAMS ((tree, void *));
  static tree add_conversions PARAMS ((tree, void *));
- static int covariant_return_p PARAMS ((tree, tree));
  static int look_for_overrides_r PARAMS ((tree, tree));
  static struct search_level *push_search_level
  	PARAMS ((struct stack_level *, struct obstack *));
--- 97,102 ----
*************** dfs_walk (binfo, fn, qfn, data)
*** 1850,1907 ****
    return dfs_walk_real (binfo, 0, fn, qfn, data);
  }
  
- /* Returns > 0 if a function with type DRETTYPE overriding a function
-    with type BRETTYPE is covariant, as defined in [class.virtual].
- 
-    Returns 1 if trivial covariance, 2 if non-trivial (requiring runtime
-    adjustment), or -1 if pedantically invalid covariance.  */
- 
- static int
- covariant_return_p (brettype, drettype)
-      tree brettype, drettype;
- {
-   tree binfo;
-   base_kind kind;
- 
-   if (TREE_CODE (brettype) == FUNCTION_DECL)
-     {
-       brettype = TREE_TYPE (TREE_TYPE (brettype));
-       drettype = TREE_TYPE (TREE_TYPE (drettype));
-     }
-   else if (TREE_CODE (brettype) == METHOD_TYPE)
-     {
-       brettype = TREE_TYPE (brettype);
-       drettype = TREE_TYPE (drettype);
-     }
- 
-   if (same_type_p (brettype, drettype))
-     return 0;
- 
-   if (! (TREE_CODE (brettype) == TREE_CODE (drettype)
- 	 && (TREE_CODE (brettype) == POINTER_TYPE
- 	     || TREE_CODE (brettype) == REFERENCE_TYPE)
- 	 && TYPE_QUALS (brettype) == TYPE_QUALS (drettype)))
-     return 0;
- 
-   if (! can_convert (brettype, drettype))
-     return 0;
- 
-   brettype = TREE_TYPE (brettype);
-   drettype = TREE_TYPE (drettype);
- 
-   /* If not pedantic, allow any standard pointer conversion.  */
-   if (! IS_AGGR_TYPE (drettype) || ! IS_AGGR_TYPE (brettype))
-     return -1;
- 
-   binfo = lookup_base (drettype, brettype, ba_check | ba_quiet, &kind);
-   
-   if (!binfo)
-     return 0;
-   if (BINFO_OFFSET_ZEROP (binfo) && kind != bk_via_virtual)
-     return 1;
-   return 2;
- }
- 
  /* Check that virtual overrider OVERRIDER is acceptable for base function
     BASEFN. Issue diagnostic, and return zero, if unacceptable.  */
  
--- 1849,1854 ----
*************** check_final_overrider (overrider, basefn
*** 1915,1946 ****
    tree base_return = TREE_TYPE (base_type);
    tree over_throw = TYPE_RAISES_EXCEPTIONS (over_type);
    tree base_throw = TYPE_RAISES_EXCEPTIONS (base_type);
!   int i;
    
    if (same_type_p (base_return, over_return))
      /* OK */;
!   else if ((i = covariant_return_p (base_return, over_return)))
      {
!       if (i == 2)
! 	sorry ("adjusting pointers for covariant returns");
  
!       if (pedantic && i == -1)
  	{
! 	  cp_pedwarn_at ("invalid covariant return type for `%#D'", overrider);
! 	  cp_pedwarn_at ("  overriding `%#D' (must be pointer or reference to class)", basefn);
  	}
      }
!   else if (IS_AGGR_TYPE_2 (base_return, over_return)
! 	   && same_or_base_type_p (base_return, over_return))
!     {
!       cp_error_at ("invalid covariant return type for `%#D'", overrider);
!       cp_error_at ("  overriding `%#D' (must use pointer or reference)", basefn);
!       return 0;
!     }
!   else if (IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider)) == NULL_TREE)
      {
!       cp_error_at ("conflicting return type specified for `%#D'", overrider);
!       cp_error_at ("  overriding `%#D'", basefn);
        SET_IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider),
                                    DECL_CONTEXT (overrider));
        return 0;
--- 1862,1935 ----
    tree base_return = TREE_TYPE (base_type);
    tree over_throw = TYPE_RAISES_EXCEPTIONS (over_type);
    tree base_throw = TYPE_RAISES_EXCEPTIONS (base_type);
!   int fail = 0;
    
    if (same_type_p (base_return, over_return))
      /* OK */;
!   else if ((CLASS_TYPE_P (over_return) && CLASS_TYPE_P (base_return))
! 	   || (TREE_CODE (base_return) == TREE_CODE (over_return)
! 	       && POINTER_TYPE_P (base_return)))
      {
!       /* Potentially covariant. */
!       unsigned base_quals, over_quals;
!       
!       fail = !POINTER_TYPE_P (base_return);
!       if (!fail)
! 	{
! 	  fail = cp_type_quals (base_return) != cp_type_quals (over_return);
! 	  
! 	  base_return = TREE_TYPE (base_return);
! 	  over_return = TREE_TYPE (over_return);
! 	}
!       base_quals = cp_type_quals (base_return);
!       over_quals = cp_type_quals (over_return);
! 
!       if ((base_quals & over_quals) != over_quals)
! 	fail = 1;
!       
!       if (CLASS_TYPE_P (base_return) && CLASS_TYPE_P (over_return))
! 	{
! 	  tree binfo = lookup_base (over_return, base_return,
! 				    ba_check | ba_quiet, NULL);
  
! 	  if (!binfo)
! 	    fail = 1;
! 	}
!       else if (!pedantic
! 	       && can_convert (TREE_TYPE (base_type), TREE_TYPE (over_type)))
! 	/* GNU extension, allow trivial pointer conversions such as
! 	   converting to void *, or qualification conversion.  */
  	{
! 	  /* can_convert will permit user defined conversion from a
! 	     (reference to) class type. We must reject them. */
! 	  over_return = TREE_TYPE (over_type);
! 	  if (TREE_CODE (over_return) == REFERENCE_TYPE)
! 	    over_return = TREE_TYPE (over_return);
! 	  if (CLASS_TYPE_P (over_return))
! 	    fail = 2;
  	}
+       else
+ 	fail = 2;
      }
!   else
!     fail = 2;
!   if (!fail)
!     /* OK */;
!   else if (IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider)))
!     return 0;
!   else
      {
!       if (fail == 1)
! 	{
! 	  cp_error_at ("invalid covariant return type for `%#D'", overrider);
! 	  cp_error_at ("  overriding `%#D'", basefn);
! 	}
!       else
! 	{
! 	  cp_error_at ("conflicting return type specified for `%#D'",
! 		       overrider);
! 	  cp_error_at ("  overriding `%#D'", basefn);
! 	}
        SET_IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider),
                                    DECL_CONTEXT (overrider));
        return 0;
*************** check_final_overrider (overrider, basefn
*** 1949,1958 ****
    /* Check throw specifier is at least as strict.  */
    if (!comp_except_specs (base_throw, over_throw, 0))
      {
!       cp_error_at ("looser throw specifier for `%#F'", overrider);
!       cp_error_at ("  overriding `%#F'", basefn);
        return 0;
      }
    return 1;
  }
  
--- 1938,1953 ----
    /* Check throw specifier is at least as strict.  */
    if (!comp_except_specs (base_throw, over_throw, 0))
      {
!       if (!IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider)))
! 	{
! 	  cp_error_at ("looser throw specifier for `%#F'", overrider);
! 	  cp_error_at ("  overriding `%#F'", basefn);
! 	  SET_IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider),
! 				      DECL_CONTEXT (overrider));
! 	}
        return 0;
      }
+   
    return 1;
  }
  
Index: cp/semantics.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/semantics.c,v
retrieving revision 1.282
diff -c -3 -p -r1.282 semantics.c
*** cp/semantics.c	30 Oct 2002 15:54:07 -0000	1.282
--- cp/semantics.c	27 Nov 2002 18:29:07 -0000
*************** emit_associated_thunks (fn)
*** 2275,2282 ****
    if (DECL_VIRTUAL_P (fn))
      {
        tree thunk;
        for (thunk = DECL_THUNKS (fn); thunk; thunk = TREE_CHAIN (thunk))
! 	use_thunk (thunk, /*emit_p=*/1);
      }
  }
  
--- 2275,2293 ----
    if (DECL_VIRTUAL_P (fn))
      {
        tree thunk;
+       
        for (thunk = DECL_THUNKS (fn); thunk; thunk = TREE_CHAIN (thunk))
! 	{
! 	  use_thunk (thunk, /*emit_p=*/1);
! 	  if (DECL_RESULT_THUNK_P (thunk))
! 	    {
! 	      tree probe;
! 
! 	      for (probe = DECL_THUNKS (thunk);
! 		   probe; probe = TREE_CHAIN (probe))
! 		use_thunk (probe, /*emit_p=*/1);
! 	    }
! 	}
      }
  }
  
Index: testsuite/g++.dg/inherit/covariant1.C
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.dg/inherit/covariant1.C,v
retrieving revision 1.1
diff -c -3 -p -r1.1 covariant1.C
*** testsuite/g++.dg/inherit/covariant1.C	25 Apr 2002 18:04:46 -0000	1.1
--- testsuite/g++.dg/inherit/covariant1.C	27 Nov 2002 18:29:23 -0000
***************
*** 1,12 ****
  // PR c++/5607
  
! // Currently we don't support covariant returns that would actually require
! // a pointer adjustment.  We were failing to recognize this as such a case,
! // so were silently generating bad code.  When we do support covariant
! // returns properly, the expected error should go away, and the testcase
! // should pass execution.
! 
! // { NOT YET dg-do run }
  
  class A {
  public:
--- 1,6 ----
  // PR c++/5607
  
! // { dg-do run }
  
  class A {
  public:
*************** public:
*** 19,25 ****
    virtual B* getThis() { return this; }
  };
  
! class AB : public A, public B {	// { dg-error "covariant" }
  public:
    virtual AB* getThis() { return this; }
  };
--- 13,19 ----
    virtual B* getThis() { return this; }
  };
  
! class AB : public A, public B {
  public:
    virtual AB* getThis() { return this; }
  };
// { dg-do run }

// Copyright (C) 2002 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 27 Nov 2002 <nathan@codesourcery.com>

// covariant returns. Fixed offset.

struct B1;
struct B2;
struct D;

struct B1
{
  virtual B1 *foo1 () {return this;}
  virtual B2 *foo2 (D *);
};
struct B2
{
  virtual B2 *baz1 () {return this;}
  virtual B1 *baz2 (D *);
};

struct D : B1, B2
{
  virtual D *foo1 () {return this;}
  virtual D *foo2 (D *d) {return d;}
  virtual D *baz1 () {return this;}
  virtual D *baz2 (D *d) {return d;}
};

B2 *B1::foo2 (D *d) {return d;}
B1 *B2::baz2 (D *d) {return d;}

int test (B1 *b1, B2 *b2, D *d)
{
  if (b1->foo1 () != b1)
    return 1;
  if (b2->baz1 () != b2)
    return 2;
  if (b1->foo2 (d) != b2)
    return 3;
  if (b2->baz2 (d) != b1)
    return 4;
  return 0;
}

int test (D *d)
{
  if (d->foo2 (d) != d)
    return 11;
  if (d->baz2 (d) != d)
    return 12;
  if (d->foo1 () != d)
    return 13;
  if (d->baz1 () != d)
    return 14;
  return 0;
}

int main ()
{
  D d;
  int r;
  
  if ((r = test (&d, &d, &d)))
    return r;
  if ((r = test (&d)))
    return r;
  return 0;
}
// { dg-do run }

// Copyright (C) 2002 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 27 Nov 2002 <nathan@codesourcery.com>

// covariant returns. Virtual offset.

struct B1;
struct B2;
struct D;

struct B1
{
  virtual B1 *foo1 () {return this;}
  virtual B2 *foo2 (D *);
};
struct B2
{
  virtual B2 *baz1 () {return this;}
  virtual B1 *baz2 (D *);
};

struct D : virtual B1, virtual B2
{
  virtual D *foo1 () {return this;}
  virtual D *foo2 (D *d) {return d;}
  virtual D *baz1 () {return this;}
  virtual D *baz2 (D *d) {return d;}
};

B2 *B1::foo2 (D *d) {return d;}
B1 *B2::baz2 (D *d) {return d;}

int test (B1 *b1, B2 *b2, D *d)
{
  if (b1->foo1 () != b1)
    return 1;
  if (b2->baz1 () != b2)
    return 2;
  if (b1->foo2 (d) != b2)
    return 3;
  if (b2->baz2 (d) != b1)
    return 4;
  return 0;
}

int test (D *d)
{
  if (d->foo2 (d) != d)
    return 11;
  if (d->baz2 (d) != d)
    return 12;
  if (d->foo1 () != d)
    return 13;
  if (d->baz1 () != d)
    return 14;
  return 0;
}

int main ()
{
  D d;
  int r;
  
  if ((r = test (&d, &d, &d)))
    return r;
  if ((r = test (&d)))
    return r;
  return 0;
}
// { dg-do run }

// Copyright (C) 2002 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 27 Nov 2002 <nathan@codesourcery.com>

// covariant returns. Fixed & virtual offset.

struct B1;
struct B2;
struct D;

struct B1
{
  virtual B1 *foo1 () {return this;}
  virtual B2 *foo2 (D *);
};

struct B2
{
  virtual B2 *baz1 () {return this;}
  virtual B1 *baz2 (D *);
};

struct Pad1 { virtual ~Pad1 (){}};
struct Pad2 { virtual ~Pad2 (){}};
struct Proxy1 : Pad1, B1 {};
struct Proxy2 : Pad2, B2 {};

struct D : virtual Proxy1, virtual Proxy2
{
  virtual D *foo1 () {return this;}
  virtual D *foo2 (D *d) {return d;}
  virtual D *baz1 () {return this;}
  virtual D *baz2 (D *d) {return d;}
};

B2 *B1::foo2 (D *d) {return d;}
B1 *B2::baz2 (D *d) {return d;}

int test (B1 *b1, B2 *b2, D *d)
{
  if (b1->foo1 () != b1)
    return 1;
  if (b2->baz1 () != b2)
    return 2;
  if (b1->foo2 (d) != b2)
    return 3;
  if (b2->baz2 (d) != b1)
    return 4;
  return 0;
}

int test (D *d)
{
  if (d->foo2 (d) != d)
    return 11;
  if (d->baz2 (d) != d)
    return 12;
  if (d->foo1 () != d)
    return 13;
  if (d->baz1 () != d)
    return 14;
  return 0;
}

int main ()
{
  D d;
  int r;
  
  if ((r = test (&d, &d, &d)))
    return r;
  if ((r = test (&d)))
    return r;
  return 0;
}

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