This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
PATCH: Fix vcall offsets in construction vtables
- From: Mark Mitchell <mark at codesourcery dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: oldham at codesourcery dot com
- Date: Thu, 14 Nov 2002 08:56:04 -0800
- Subject: PATCH: Fix vcall offsets in construction vtables
- Reply-to: mark at codesourcery dot com
Jeffrey's tests found another defect in the way that we compute vcall
offsets in construction vtables, which could result in the `this'
pointer having the wrong value if a virtual function is called during
a constructor or destructor.
This statement
! /* The FN comes from BASE. So, we must calculate the
! adjustment from vid->vbase to BASE. We can just look for
! BASE in the complete object because we are converting
! from a virtual base, so if there were multiple copies,
! there would not be a unique final overrider and
! vid->derived would be ill-formed. */
Fixed with the attached patch.
Tested on i686-pc-linux-gnu, applied on the mainline.
--
Mark Mitchell mark@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
2002-11-14 Mark Mitchell <mark@codesourcery.com>
* class.c (dfs_find_final_overrider): Adjust so that the most
derived object is a binfo, rather than a class type.
(find_final_overrider): Likewise.
(add_vcall_offset_vtbl_entries_1): Simplify accordingly.
(add_vcall_offset): Likewise.
2002-11-14 Mark Mitchell <mark@codesourcery.com>
* g++.dg/abi/vcall1.C: New test.
Index: cp/class.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/class.c,v
retrieving revision 1.492
diff -c -5 -p -r1.492 class.c
*** cp/class.c 8 Nov 2002 02:16:45 -0000 1.492
--- cp/class.c 14 Nov 2002 16:49:27 -0000
*************** dfs_find_final_overrider (binfo, data)
*** 2218,2238 ****
/* We haven't found an overrider yet. */
method = NULL_TREE;
/* We've found a path to the declaring base. Walk down the path
looking for an overrider for FN. */
! for (path = reverse_path (binfo);
! path;
! path = TREE_CHAIN (path))
{
method = look_for_overrides_here (BINFO_TYPE (TREE_VALUE (path)),
ffod->fn);
if (method)
{
path = TREE_VALUE (path);
break;
}
}
/* If we found an overrider, record the overriding function, and
the base from which it came. */
if (path)
--- 2218,2242 ----
/* We haven't found an overrider yet. */
method = NULL_TREE;
/* We've found a path to the declaring base. Walk down the path
looking for an overrider for FN. */
! path = reverse_path (binfo);
! while (!same_type_p (BINFO_TYPE (TREE_VALUE (path)),
! ffod->most_derived_type))
! path = TREE_CHAIN (path);
! while (path)
{
method = look_for_overrides_here (BINFO_TYPE (TREE_VALUE (path)),
ffod->fn);
if (method)
{
path = TREE_VALUE (path);
break;
}
+
+ path = TREE_CHAIN (path);
}
/* If we found an overrider, record the overriding function, and
the base from which it came. */
if (path)
*************** dfs_find_final_overrider (binfo, data)
*** 2262,2277 ****
return NULL_TREE;
}
/* Returns a TREE_LIST whose TREE_PURPOSE is the final overrider for
FN and whose TREE_VALUE is the binfo for the base where the
! overriding occurs. BINFO (in the hierarchy dominated by T) is the
! base object in which FN is declared. */
static tree
! find_final_overrider (t, binfo, fn)
! tree t;
tree binfo;
tree fn;
{
find_final_overrider_data ffod;
--- 2266,2281 ----
return NULL_TREE;
}
/* Returns a TREE_LIST whose TREE_PURPOSE is the final overrider for
FN and whose TREE_VALUE is the binfo for the base where the
! overriding occurs. BINFO (in the hierarchy dominated by the binfo
! DERIVED) is the base object in which FN is declared. */
static tree
! find_final_overrider (derived, binfo, fn)
! tree derived;
tree binfo;
tree fn;
{
find_final_overrider_data ffod;
*************** find_final_overrider (t, binfo, fn)
*** 2293,2314 ****
The solution is to look at all paths to BINFO. If we find
different overriders along any two, then there is a problem. */
ffod.fn = fn;
ffod.declaring_base = binfo;
! ffod.most_derived_type = t;
ffod.candidates = NULL_TREE;
! dfs_walk (TYPE_BINFO (t),
dfs_find_final_overrider,
NULL,
&ffod);
/* If there was no winner, issue an error message. */
if (!ffod.candidates || TREE_CHAIN (ffod.candidates))
{
! error ("no unique final overrider for `%D' in `%T'", fn, t);
return error_mark_node;
}
return ffod.candidates;
}
--- 2297,2319 ----
The solution is to look at all paths to BINFO. If we find
different overriders along any two, then there is a problem. */
ffod.fn = fn;
ffod.declaring_base = binfo;
! ffod.most_derived_type = BINFO_TYPE (derived);
ffod.candidates = NULL_TREE;
! dfs_walk (derived,
dfs_find_final_overrider,
NULL,
&ffod);
/* If there was no winner, issue an error message. */
if (!ffod.candidates || TREE_CHAIN (ffod.candidates))
{
! error ("no unique final overrider for `%D' in `%T'", fn,
! BINFO_TYPE (derived));
return error_mark_node;
}
return ffod.candidates;
}
*************** update_vtable_entry_for_fn (t, binfo, fn
*** 2363,2373 ****
lost = true;
}
first_defn = b;
/* Find the final overrider. */
! overrider = find_final_overrider (t, b, fn);
if (overrider == error_mark_node)
return;
/* Check for unsupported covariant returns again now that we've
calculated the base offsets. */
--- 2368,2378 ----
lost = true;
}
first_defn = b;
/* Find the final overrider. */
! 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. */
*************** add_vcall_offset_vtbl_entries_r (binfo,
*** 7907,7924 ****
static void
add_vcall_offset_vtbl_entries_1 (binfo, vid)
tree binfo;
vtbl_init_data* vid;
{
- tree binfo_in_rtti;
-
- if (vid->ctor_vtbl_p)
- binfo_in_rtti = (get_original_base
- (binfo, TYPE_BINFO (BINFO_TYPE (vid->rtti_binfo))));
- else
- binfo_in_rtti = binfo;
-
/* Make entries for the rest of the virtuals. */
if (abi_version_at_least (2))
{
tree orig_fn;
--- 7912,7921 ----
*************** add_vcall_offset_vtbl_entries_1 (binfo,
*** 7926,7936 ****
order. G++ 3.2 used the order in the vtable. */
for (orig_fn = TYPE_METHODS (BINFO_TYPE (binfo));
orig_fn;
orig_fn = TREE_CHAIN (orig_fn))
if (DECL_VINDEX (orig_fn))
! add_vcall_offset (orig_fn, binfo_in_rtti, vid);
}
else
{
tree derived_virtuals;
tree base_virtuals;
--- 7923,7933 ----
order. G++ 3.2 used the order in the vtable. */
for (orig_fn = TYPE_METHODS (BINFO_TYPE (binfo));
orig_fn;
orig_fn = TREE_CHAIN (orig_fn))
if (DECL_VINDEX (orig_fn))
! add_vcall_offset (orig_fn, binfo, vid);
}
else
{
tree derived_virtuals;
tree base_virtuals;
*************** add_vcall_offset_vtbl_entries_1 (binfo,
*** 7991,8012 ****
function slots introduced in BINFO. So don't try to generate
one if the function isn't even defined in BINFO. */
if (!same_type_p (DECL_CONTEXT (orig_fn), BINFO_TYPE (binfo)))
continue;
! add_vcall_offset (orig_fn, binfo_in_rtti, vid);
}
}
}
! /* Add a vcall offset entry for ORIG_FN to the vtable. In a
! construction vtable, BINFO_IN_RTTI is the base corresponding to the
! vtable base in VID->RTTI_BINFO. */
static void
! add_vcall_offset (tree orig_fn, tree binfo_in_rtti,
! vtbl_init_data *vid)
{
size_t i;
tree vcall_offset;
/* If there is already an entry for a function with the same
--- 7988,8006 ----
function slots introduced in BINFO. So don't try to generate
one if the function isn't even defined in BINFO. */
if (!same_type_p (DECL_CONTEXT (orig_fn), BINFO_TYPE (binfo)))
continue;
! add_vcall_offset (orig_fn, binfo, vid);
}
}
}
! /* Add a vcall offset entry for ORIG_FN to the vtable. */
static void
! add_vcall_offset (tree orig_fn, tree binfo, vtbl_init_data *vid)
{
size_t i;
tree vcall_offset;
/* If there is already an entry for a function with the same
*************** add_vcall_offset (tree orig_fn, tree bin
*** 8043,8081 ****
VARRAY_PUSH_TREE (vid->fns, orig_fn);
if (vid->generate_vcall_entries)
{
tree base;
- tree base_binfo;
tree fn;
/* Find the overriding function. */
! fn = find_final_overrider (BINFO_TYPE (vid->rtti_binfo),
! binfo_in_rtti, orig_fn);
if (fn == error_mark_node)
vcall_offset = build1 (NOP_EXPR, vtable_entry_type,
integer_zero_node);
else
{
! fn = TREE_PURPOSE (fn);
! /* The FN comes from BASE. So, we must calculate the
! adjustment from vid->vbase to BASE. We can just look for
! BASE in the complete object because we are converting
! from a virtual base, so if there were multiple copies,
! there would not be a unique final overrider and
! vid->derived would be ill-formed. */
! base = DECL_CONTEXT (fn);
! base_binfo = lookup_base (vid->derived, base, ba_any, NULL);
!
! /* Compute the vcall offset. */
! /* As mentioned above, the vbase we're working on is a
! primary base of vid->binfo. But it might be a lost
! primary, so its BINFO_OFFSET might be wrong, so we just
! use the BINFO_OFFSET from vid->binfo. */
! vcall_offset = BINFO_OFFSET (vid->binfo);
! vcall_offset = size_diffop (BINFO_OFFSET (base_binfo),
! vcall_offset);
vcall_offset = fold (build1 (NOP_EXPR, vtable_entry_type,
vcall_offset));
}
/* Add the intiailizer to the vtable. */
*vid->last_init = build_tree_list (NULL_TREE, vcall_offset);
--- 8037,8063 ----
VARRAY_PUSH_TREE (vid->fns, orig_fn);
if (vid->generate_vcall_entries)
{
tree base;
tree fn;
/* Find the overriding function. */
! fn = find_final_overrider (vid->rtti_binfo, binfo, orig_fn);
if (fn == error_mark_node)
vcall_offset = build1 (NOP_EXPR, vtable_entry_type,
integer_zero_node);
else
{
! base = TREE_VALUE (fn);
!
! /* The vbase we're working on is a primary base of
! vid->binfo. But it might be a lost primary, so its
! BINFO_OFFSET might be wrong, so we just use the
! BINFO_OFFSET from vid->binfo. */
! vcall_offset = size_diffop (BINFO_OFFSET (base),
! BINFO_OFFSET (vid->binfo));
vcall_offset = fold (build1 (NOP_EXPR, vtable_entry_type,
vcall_offset));
}
/* Add the intiailizer to the vtable. */
*vid->last_init = build_tree_list (NULL_TREE, vcall_offset);
Index: testsuite/g++.dg/abi/vcall1.C
===================================================================
RCS file: testsuite/g++.dg/abi/vcall1.C
diff -N testsuite/g++.dg/abi/vcall1.C
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/g++.dg/abi/vcall1.C 14 Nov 2002 16:49:28 -0000
***************
*** 0 ****
--- 1,36 ----
+ // { dg-do run }
+ // { dg-options "-w" }
+
+ extern "C" void abort ();
+
+ struct B;
+
+ B* b;
+
+ struct A {
+ virtual void f () {}
+ };
+
+ struct B : virtual public A {
+ B () {
+ b = this;
+ ((A*) this)->f ();
+ }
+
+ virtual void f () {
+ if (this != b)
+ abort ();
+ }
+ };
+
+ struct C : public B {
+ };
+
+ struct D : public C, public B {
+ virtual void f () {}
+ };
+
+ int main () {
+ D d;
+ }
+