Article View: gnu.g++.bug
Article #2018g++ 1.35 virtual base class bug.
From: Michael Tiemann
Date: Fri, 19 May 1989 21:28
Date: Fri, 19 May 1989 21:28
1268 lines
43594 bytes
43594 bytes
I fixed some minor glitches in GNU C++ 1.35.0, mostly relating to
error recovery and multiple inheritance. There are other, harder bugs
which I am fixing now, but I don't want to tie everyone up waiting for
those. Also, if `#pragma once' bites you (there was a bug in the GCC
1.35 release of cccp.c which failed to close files after processing
#pragma once), you can get a copy of the fixed file in
yahi:~ftp/pub/cccp.c.Z .
yahi% diff -rc2H g++-1.35.0 g++-1.35.0+
diff -rc2H g++-1.35.0/ChangeLog g++-1.35.0+/ChangeLog
*** g++-1.35.0/ChangeLog Tue May 16 23:45:39 1989
--- g++-1.35.0+/ChangeLog Fri May 19 14:22:31 1989
***************
*** 1,2 ****
--- 1,66 ----
+ Fri May 19 13:20:07 1989 Michael Tiemann (tiemann at yahi)
+
+ * cplus-class.c: handle case where user requests member function
+ using object, i.e., `x.f'. If f is virtual, return the function
+ found in the virtual function table.
+
+ * stor-layout.c: language dependent code removed from
+ layout_basetypes, put in cplus-class.c
+
+ * stmt.c: if cleanups were called on leaving a binding contour,
+ and EXIT_IGNORE_STACK is not defined, call
+ do_pending_stack_adjust (expand_goto_internal,
+ expand_end_bindings, fixup_cleanups).
+
+ * gcc.c: recognize ".cxx" as a C++ file name extension.
+
+ * cplus-typeck.c: convert_for_initialization,
+ convert_for_assignment convert MEMBER_REFs, if possible, to
+ members or component refs as appropriate.
+
+ * cplus-typeck.c: build_member_ref recognizes member refs which
+ are not associated with any object in particular (i.e., could
+ be, but need not be `this').
+
+ * cplus-search.c: get_vbase_types now orders the virtual
+ base classes so that initialization and anti-initialization are
+ performed in correct order. A virtual baseclass is not
+ destroyed until all parts of the object which could be using
+ that virtual base class are destroyed.
+
+ * cplus-parse.y: call build_vbase_delete if appropriate.
+
+ * cplus-init.c: expand_delete now calls build_vbase_delete if
+ appropriate. build_delete no longer deletes parts of objects
+ via virtual base classes.
+
+ * cplus-init.c: new function build_vbase_delete, performs
+ destructors on objects with virtual base classes. new function
+ build_x_delete calls free-store deallocator on objects without
+ running destructors.
+
+ * cplus-decl.c: make pushdecl, shadow_tag, and xref_tag have
+ C-like behavior in "C" language scope vis a vis type
+ declarations.
+
+ * cplus-decl.c: define macros PUSH_BINDING_LEVEL and
+ POP_BINDING_LEVEL. Use these macros where appropriate. Fixed
+ bug in popping binding contours in the presence of syntax
+ errors.
+
+ * README, config.g++, make-links.g++: fixed minor glitches
+
+ Wed May 17 20:34:29 1989 Michael Tiemann (tiemann at yahi)
+
+ * stor-layout.c: move code which deletes an incomplete base type
+ to cplus-class.c.
+
+ * cplus-class.c: changed `finish_struct' to call
+ `propagate_basetype_offsets'.
+
+ * cplus-class.c: new function `propagate_basetype_offsets' gives
+ correct offsets to base types which are not immediate base types
+ in a multiple inheritance lattice.
+
Tue May 16 09:40:00 1989 Michael Tiemann (tiemann at yahi)
diff -rc2H g++-1.35.0/HINTS g++-1.35.0+/HINTS
*** g++-1.35.0/HINTS Tue May 16 23:45:38 1989
--- g++-1.35.0+/HINTS Fri May 19 14:22:31 1989
***************
*** 73,74 ****
--- 73,87 ----
tiemann@lurch.stanford.edu
----------------------------------------------------------------
+ The default LINK_SPEC in gcc.c tells the linker to link
+ with crt0+.o. Many machine-specific files define their own
+ LINK_SPECs. A strategy which worked until tm-sun?-nfp-os? came along
+ was to edit the tm-*.h file into tm-*+.h, and replace the string
+ "crt0.o" with "crt0+.o". This strategy is defeated with one tm-*.h
+ file includes a file defining LINK_SPEC. I will fix this in release
+ 1.35.1. In the mean time, copy LINK_SPEC from the file that is being
+ included, #undef it and redef it in the top level tm-*.h file.
+
+
+ Michael Tieman
+ tieman@lurch.stanford.edu
+ ----------------------------------------------------------------
diff -rc2H g++-1.35.0/Makefile g++-1.35.0+/Makefile
*** g++-1.35.0/Makefile Tue May 16 23:45:19 1989
--- g++-1.35.0+/Makefile Fri May 19 14:22:31 1989
***************
*** 53,59 ****
# Variables that exist for you to override.
! CFLAGS = -g -DSOS -DESKIT
# CFLAGS = -g -O
! CC = ../gcc/gcc -B../gcc/
BISON = bison
BISONFLAGS = -v -d
--- 53,59 ----
# Variables that exist for you to override.
! CFLAGS = -g -DSOS -DESKIT -O
# CFLAGS = -g -O
! CC = gcc
BISON = bison
BISONFLAGS = -v -d
***************
*** 555,559 ****
-ln -s $(DIR)/move-if-change .
$(MAKE) clean
! make-links.g++
-ln -s $(DIR)/gen*.c .
-ln -s $(TDIR)/obstack.o $(TDIR)/rtl.o $(TDIR)/emit-rtl.o \
--- 555,559 ----
-ln -s $(DIR)/move-if-change .
$(MAKE) clean
! ./make-links.g++
-ln -s $(DIR)/gen*.c .
-ln -s $(TDIR)/obstack.o $(TDIR)/rtl.o $(TDIR)/emit-rtl.o \
diff -rc2H g++-1.35.0/README g++-1.35.0+/README
*** g++-1.35.0/README Tue May 16 23:45:20 1989
--- g++-1.35.0+/README Fri May 19 14:22:31 1989
***************
*** 137,141 ****
install% pwd
! install% ./g++
*Very Important*: Because GNU C++ now uses its own crt0.c, borrowed
--- 137,142 ----
install% pwd
! ./g++
! install%
*Very Important*: Because GNU C++ now uses its own crt0.c, borrowed
***************
*** 144,153 ****
have GNU Emacs, and you are not compiling to a SUN, consult your local
GNU Emacs guru, to see what sort of #defines are required for proper
! operation. Then, just type `make'. If you are not using a SUN, you
! will need to use the appropriate machine dependent files, as per GNU
! CC. If you do not provide a proper crt0.c, any executable produced
! by GNU C++ may fail to run at all. Conversely, if you have a program
! which does not make it as far as the first line in main (), you have
! probably failed to provide the correct flags to crt0.c.
You have now just made GNU C++. Having done that, you should now
--- 145,158 ----
have GNU Emacs, and you are not compiling to a SUN, consult your local
GNU Emacs guru, to see what sort of #defines are required for proper
! operation. Then, just type `make':
!
! install% make
!
! If you are not using a SUN, you will need to use the appropriate
! machine dependent files, as per GNU CC. If you do not provide a
! proper crt0.c, any executable produced by GNU C++ may fail to run at
! all. Conversely, if you have a program which does not make it as far
! as the first line in main (), you have probably failed to provide the
! correct flags to crt0.c.
You have now just made GNU C++. Having done that, you should now
diff -rc2H g++-1.35.0/config.g++ g++-1.35.0+/config.g++
*** g++-1.35.0/config.g++ Tue May 16 23:45:32 1989
--- g++-1.35.0+/config.g++ Fri May 19 14:22:32 1989
***************
*** 126,130 ****
sun3-os4 | sun-3-os4)
cpu_type=m68k
! sed 's/-e start//' < config/xm-sunos4.h > config/xm-sunos4+.h
configuration_file=xm-sunos4+.h
machine_type=sun3
--- 126,130 ----
sun3-os4 | sun-3-os4)
cpu_type=m68k
! sed 's/-e start/-e __start/' < config/xm-sunos4.h > config/xm-sunos4+.h
configuration_file=xm-sunos4+.h
machine_type=sun3
***************
*** 132,136 ****
sun3-nfp-os4 | sun-3-nfp-os4)
cpu_type=m68k
! sed 's/-e start//' < config/xm-sunos4.h > config/xm-sunos4+.h
configuration_file=xm-sunos4.h
machine_type=sun3-nfp
--- 132,137 ----
sun3-nfp-os4 | sun-3-nfp-os4)
cpu_type=m68k
! sed 's/-e start/-e __start/' < config/xm-sunos4.h > config/xm-sunos4+.h
! configuration_file=xm-sunos4+.h
configuration_file=xm-sunos4.h
machine_type=sun3-nfp
diff -rc2H g++-1.35.0/cplus-class.c g++-1.35.0+/cplus-class.c
*** g++-1.35.0/cplus-class.c Tue May 16 23:45:21 1989
--- g++-1.35.0+/cplus-class.c Fri May 19 14:22:33 1989
***************
*** 645,648 ****
--- 645,689 ----
}
+ /* Add OFFSET to all child types of T.
+
+ OFFSET, which is a type offset, is number of bytes.
+
+ Note that we don't have to worry about having two paths to the
+ same base type, since virtual baseclasses are avoided,
+ and basetypes become unique. */
+ static void
+ propagate_basetype_offsets (t, offset)
+ tree t;
+ tree offset;
+ {
+ int i, n_baselinks = CLASSTYPE_N_BASECLASSES (t);
+
+ for (i = 1; i <= n_baselinks; i++)
+ if (! CLASSTYPE_VIA_VIRTUAL (t, i))
+ {
+ tree basetype = CLASSTYPE_THIS_BASECLASS (t, i);
+
+ if (CLASSTYPE_OFFSET (basetype) == integer_zero_node)
+ basetype = build_classtype_variant (basetype, offset, 0);
+ else
+ basetype = build_classtype_variant (basetype,
+ genop (PLUS_EXPR, CLASSTYPE_OFFSET (basetype), offset), 0);
+ /* Now make our own copy of this base type we can munge. */
+ basetype = copy_node (basetype);
+ CLASSTYPE_THIS_BASECLASS (t, i) = basetype;
+ copy_type_lang_specific (basetype);
+ TYPE_NAME (basetype) = copy_node (TYPE_NAME (basetype));
+ TREE_TYPE (TYPE_NAME (basetype)) = basetype;
+ DECL_OFFSET (TYPE_NAME (basetype))
+ = TREE_INT_CST_LOW (convert_units (CLASSTYPE_OFFSET (basetype), BITS_PER_UNIT, 1));
+ propagate_basetype_offsets (basetype, offset);
+ if (i < n_baselinks)
+ {
+ offset = genop (PLUS_EXPR,
+ convert_units (TYPE_SIZE (basetype), TYPE_SIZE_UNIT (basetype), BITS_PER_UNIT), offset);
+ }
+ }
+ }
+
/* Change the visibility of T::FDECL to VISIBILITY.
Return 1 if change was legit, otherwise return 0. */
***************
*** 934,997 ****
/* If the type of basetype is incomplete, then
! we already complained about that fact. */
! if (TYPE_SIZE (basetype))
{
! TYPE_USES_VIRTUAL_BASECLASSES (t)
! |= (TYPE_USES_VIRTUAL_BASECLASSES (basetype) | CLASSTYPE_VIA_VIRTUAL (t, i));
! CLASSTYPE_HAS_UNINHERITABLE_VIRTUALS (t)
! |= CLASSTYPE_HAS_UNINHERITABLE_VIRTUALS (basetype) | (CLASSTYPE_UNINHERITED_VIRTUALS (basetype) != 0);
! CLASSTYPE_ALTERS_VISIBILITIES_P (t)
! |= CLASSTYPE_ALTERS_VISIBILITIES_P (basetype);
!
! /* This does not work if the MI lattice has
! virtual baseclasses. Must use a bfs or dfs
! routine instead. */
! n_ancestors += CLASSTYPE_N_SUPERCLASSES (basetype);
! needs_ctor |= TYPE_NEEDS_CONSTRUCTOR (basetype);
! needs_dtor |= TYPE_NEEDS_DESTRUCTOR (basetype);
! needs_wrapper |= TYPE_NEEDS_WRAPPER (basetype) | TYPE_HAS_WRAPPER (basetype);
! any_assigns_this |= TREE_ANY_ASSIGNS_THIS (basetype);
! gets_assignment |= TREE_GETS_ASSIGNMENT (basetype);
!
! TYPE_OVERLOADS_CALL_EXPR (t) |= TYPE_OVERLOADS_CALL_EXPR (basetype);
! TYPE_OVERLOADS_ARRAY_REF (t) |= TYPE_OVERLOADS_ARRAY_REF (basetype);
! if (TYPE_VIRTUAL_P (basetype))
! {
! /* We cannot share virtual information for any node
! which has a non-zero offset. This is because for
! such nodes, we rewrite that node's virtual information
! in place--mostly. */
! if (CLASSTYPE_OFFSET (basetype) != integer_zero_node
! || CLASSTYPE_VIA_VIRTUAL (t, i))
! {
! CLASSTYPE_THIS_BASECLASS (t, i) = basetype = copy_node (basetype);
! TREE_VIA_VIRTUAL (basetype) = CLASSTYPE_VIA_VIRTUAL (t, i);
! copy_type_lang_specific (basetype);
! }
! if (first_vfn_base_index == 0)
! {
! first_vfn_base_index = i;
!
! /* The size of the virtual function table we get
! from the base class is this. */
! max_has_virtual = has_virtual = CLASSTYPE_VSIZE (basetype);
!
! /* Don't borrow vfields from virtual baseclasses,
! but do borrow everything else. */
! if (! TREE_VIA_VIRTUAL (basetype))
! {
! vfield = CLASSTYPE_VFIELD (basetype);
! CLASSTYPE_VFIELD (t) = vfield;
! static_vfield_name = DECL_NAME (vfield);
! }
! }
! else
! {
! if (CLASSTYPE_VSIZE (basetype) > max_has_virtual)
! max_has_virtual = CLASSTYPE_VSIZE (basetype);
! }
}
}
- else continue;
}
--- 975,1060 ----
/* If the type of basetype is incomplete, then
! we already complained about that fact
! (and we should have fixed it up as well). */
! if (TYPE_SIZE (basetype) == 0)
! {
! int j;
! /* The base type is of incomplete type. It is
! probably best to pretend that it does not
! exist. */
! if (i == n_baseclasses)
! CLASSTYPE_THIS_BASECLASS (t, i) = NULL_TREE;
! CLASSTYPE_N_BASECLASSES (t) -= 1;
! n_baseclasses -= 1;
! for (j = i; j < n_baseclasses; j++)
! {
! CLASSTYPE_THIS_BASECLASS (t, j) = CLASSTYPE_THIS_BASECLASS (t, j+1);
! SET_CLASSTYPE_VIAS (t, j,
! CLASSTYPE_VIA_PUBLIC (t, j+1),
! CLASSTYPE_VIA_VIRTUAL (t, j+1));
! }
! }
!
! TYPE_USES_VIRTUAL_BASECLASSES (t)
! |= (TYPE_USES_VIRTUAL_BASECLASSES (basetype) | CLASSTYPE_VIA_VIRTUAL (t, i));
! CLASSTYPE_HAS_UNINHERITABLE_VIRTUALS (t)
! |= CLASSTYPE_HAS_UNINHERITABLE_VIRTUALS (basetype) | (CLASSTYPE_UNINHERITED_VIRTUALS (basetype) != 0);
! CLASSTYPE_ALTERS_VISIBILITIES_P (t)
! |= CLASSTYPE_ALTERS_VISIBILITIES_P (basetype);
!
! /* This does not work if the MI lattice has
! virtual baseclasses. Must use a bfs or dfs
! routine instead. */
! n_ancestors += CLASSTYPE_N_SUPERCLASSES (basetype);
! needs_ctor |= TYPE_NEEDS_CONSTRUCTOR (basetype);
! needs_dtor |= TYPE_NEEDS_DESTRUCTOR (basetype);
! needs_wrapper |= TYPE_NEEDS_WRAPPER (basetype) | TYPE_HAS_WRAPPER (basetype);
! any_assigns_this |= TREE_ANY_ASSIGNS_THIS (basetype);
! gets_assignment |= TREE_GETS_ASSIGNMENT (basetype);
!
! TYPE_OVERLOADS_CALL_EXPR (t) |= TYPE_OVERLOADS_CALL_EXPR (basetype);
! TYPE_OVERLOADS_ARRAY_REF (t) |= TYPE_OVERLOADS_ARRAY_REF (basetype);
!
! if (CLASSTYPE_OFFSET (basetype) != integer_zero_node)
! /* Propagate this offset through all the children. Do this
! before uniquizing baseclasses for virtual functions. */
! propagate_basetype_offsets (basetype, CLASSTYPE_OFFSET (basetype));
!
! if (TYPE_VIRTUAL_P (basetype))
{
! /* We cannot share virtual information for any node
! which has a non-zero offset. This is because for
! such nodes, we rewrite that node's virtual information
! in place--mostly. */
! if (CLASSTYPE_OFFSET (basetype) != integer_zero_node
! || CLASSTYPE_VIA_VIRTUAL (t, i))
! {
! CLASSTYPE_THIS_BASECLASS (t, i) = basetype = copy_node (basetype);
! TREE_VIA_VIRTUAL (basetype) = CLASSTYPE_VIA_VIRTUAL (t, i);
! copy_type_lang_specific (basetype);
! }
! if (first_vfn_base_index == 0)
! {
! first_vfn_base_index = i;
!
! /* The size of the virtual function table we get
! from the base class is this. */
! max_has_virtual = has_virtual = CLASSTYPE_VSIZE (basetype);
!
! /* Don't borrow vfields from virtual baseclasses,
! but do borrow everything else. */
! if (! TREE_VIA_VIRTUAL (basetype))
! {
! vfield = CLASSTYPE_VFIELD (basetype);
! CLASSTYPE_VFIELD (t) = vfield;
! static_vfield_name = DECL_NAME (vfield);
! }
! }
! else
! {
! if (CLASSTYPE_VSIZE (basetype) > max_has_virtual)
! max_has_virtual = CLASSTYPE_VSIZE (basetype);
}
}
}
***************
*** 4189,4192 ****
--- 4252,4275 ----
{
tree field = TREE_OPERAND (rhs, 1);
+ if (TREE_CODE (field) == TREE_LIST)
+ {
+ tree function = instantiate_type (lhstype, field, complain);
+ if (function == error_mark_node)
+ return error_mark_node;
+ assert (TREE_CODE (function) == FUNCTION_DECL);
+ if (DECL_VIRTUAL_P (function))
+ {
+ tree base = TREE_OPERAND (rhs, 0);
+ tree base_ptr = build_unary_op (ADDR_EXPR, base, 0);
+ if (base_ptr == error_mark_node)
+ return error_mark_node;
+ base_ptr = convert_to_nonzero_pointer (TYPE_POINTER_TO (DECL_VCONTEXT (function)), base_ptr);
+ if (base_ptr == error_mark_node)
+ return error_mark_node;
+ return build_vfn_ref (&base_ptr, base, DECL_VINDEX (function));
+ }
+ return function;
+ }
+
assert (TREE_CODE (field) == FIELD_DECL);
assert (!(TREE_CODE (TREE_TYPE (field)) == FUNCTION_TYPE
***************
*** 4245,4249 ****
{
if (complain)
! error ("invalid type combination in `instantiate_type'");
return error_mark_node;
}
--- 4328,4332 ----
{
if (complain)
! error ("invalid type combination type instantiation");
return error_mark_node;
}
***************
*** 4295,4299 ****
else if (TREE_CODE (TREE_VALUE (rhs)) == TREE_LIST)
{
! char *name = IDENTIFIER_POINTER (TREE_PURPOSE (rhs));
/* rhs is a BASELINK from derived downto base types. */
while (rhs)
--- 4378,4382 ----
else if (TREE_CODE (TREE_VALUE (rhs)) == TREE_LIST)
{
! char *name = IDENTIFIER_POINTER (TREE_PURPOSE (TREE_VALUE (rhs)));
/* rhs is a BASELINK from derived downto base types. */
while (rhs)
diff -rc2H g++-1.35.0/cplus-decl.c g++-1.35.0+/cplus-decl.c
*** g++-1.35.0/cplus-decl.c Tue May 16 23:45:22 1989
--- g++-1.35.0+/cplus-decl.c Fri May 19 14:22:35 1989
***************
*** 594,597 ****
--- 594,637 ----
static struct binding_level clear_binding_level
= {NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0};
+
+ #define PUSH_BINDING_LEVEL(NEWLEVEL, TAG_TRANSPARENT, TEMPORARY) \
+ do { \
+ /* Add this level to the front of the chain (stack) of levels that \
+ are active. */ \
+ *NEWLEVEL = clear_binding_level; \
+ if (class_binding_level) \
+ { \
+ NEWLEVEL->level_chain = class_binding_level; \
+ class_binding_level = 0; \
+ } \
+ else \
+ { \
+ NEWLEVEL->level_chain = current_binding_level; \
+ } \
+ current_binding_level = NEWLEVEL; \
+ NEWLEVEL->tag_transparent = TAG_TRANSPARENT; \
+ NEWLEVEL->more_cleanups_ok = 1; \
+ NEWLEVEL->temporary = TEMPORARY; \
+ } while (0)
+
+ #define POP_BINDING_LEVEL \
+ do { \
+ /* Pop the current level, and free the structure for reuse. */ \
+ { \
+ register struct binding_level *level = current_binding_level; \
+ current_binding_level = current_binding_level->level_chain; \
+ level->level_chain = free_binding_level; \
+ free_binding_level = level; \
+ if (current_binding_level->parm_flag < 0) \
+ { \
+ class_binding_level = current_binding_level; \
+ do \
+ { \
+ current_binding_level = current_binding_level->level_chain; \
+ } \
+ while (current_binding_level->parm_flag < 0); \
+ } \
+ } \
+ } while (0)
/* Create a new `struct binding_level'. */
***************
*** 644,665 ****
newlevel = make_binding_level ();
}
!
! /* Add this level to the front of the chain (stack) of levels that
! are active. */
!
! *newlevel = clear_binding_level;
! if (class_binding_level)
! {
! newlevel->level_chain = class_binding_level;
! class_binding_level = 0;
! }
! else
! {
! newlevel->level_chain = current_binding_level;
! }
! current_binding_level = newlevel;
! newlevel->tag_transparent = tag_transparent;
! newlevel->more_cleanups_ok = 1;
! newlevel->temporary = 0;
}
--- 684,688 ----
newlevel = make_binding_level ();
}
! PUSH_BINDING_LEVEL (newlevel, tag_transparent, 0);
}
***************
*** 813,835 ****
}
- /* Pop the current level, and free the structure for reuse. */
tmp = current_binding_level->temporary;
! {
! register struct binding_level *level = current_binding_level;
! current_binding_level = current_binding_level->level_chain;
!
! level->level_chain = free_binding_level;
! free_binding_level = level;
! if (current_binding_level->parm_flag < 0)
! {
! class_binding_level = current_binding_level;
! do
! {
! current_binding_level = current_binding_level->level_chain;
! }
! while (current_binding_level->parm_flag < 0);
! }
! }
!
if (functionbody > 0)
{
--- 836,841 ----
}
tmp = current_binding_level->temporary;
! POP_BINDING_LEVEL;
if (functionbody > 0)
{
***************
*** 895,899 ****
{
register struct binding_level *level = class_binding_level;
! class_binding_level = class_binding_level->level_chain;
level->level_chain = free_binding_level;
--- 901,914 ----
{
register struct binding_level *level = class_binding_level;
!
! if (level == 0)
! {
! while (current_binding_level && class_binding_level == 0)
! poplevel (0, 0, 0);
! if (current_binding_level == 0)
! fatal ("syntax error too serious");
! level = class_binding_level;
! }
! class_binding_level = level->level_chain;
level->level_chain = free_binding_level;
***************
*** 1325,1329 ****
for (prev = 0, vars = global_binding_level->names;
vars;
! prev = vars, vars = TREE_CHAIN (vars))
{
if (TREE_CODE (vars) == TYPE_DECL
--- 1340,1344 ----
for (prev = 0, vars = global_binding_level->names;
vars;
! vars = TREE_CHAIN (vars))
{
if (TREE_CODE (vars) == TYPE_DECL
***************
*** 1359,1362 ****
--- 1374,1379 ----
global_binding_level->names = TREE_CHAIN (global_binding_level->names);
}
+ else
+ prev = vars;
}
}
***************
*** 1820,1836 ****
if (TREE_CODE (x) == TYPE_DECL)
{
! if (TYPE_NAME (TREE_TYPE (x)) == 0
! || TREE_CODE (TYPE_NAME (TREE_TYPE (x))) != TYPE_DECL)
! TYPE_NAME (TREE_TYPE (x)) = x;
! else if (current_lang_name == lang_name_cplusplus
! && IS_AGGR_TYPE (TREE_TYPE (x))
! && ANON_AGGRNAME_P (DECL_NAME (TYPE_NAME (TREE_TYPE (x)))))
{
! /* do gratuitous C++ typedefing, and make sure that
! we access this type either through TREE_TYPE field
! or via the tags list. */
! TYPE_NAME (TREE_TYPE (x)) = x;
! pushtag (DECL_NAME (x), TREE_TYPE (x));
}
}
--- 1837,1861 ----
if (TREE_CODE (x) == TYPE_DECL)
{
! if (current_lang_name == lang_name_cplusplus)
{
! if (TYPE_NAME (TREE_TYPE (x)) == 0
! || TREE_CODE (TYPE_NAME (TREE_TYPE (x))) != TYPE_DECL)
! {
! /* If these are different names, make two equivalent
! definitions. */
! TYPE_NAME (TREE_TYPE (x)) = x;
! }
! else if (IS_AGGR_TYPE (TREE_TYPE (x))
! && ANON_AGGRNAME_P (DECL_NAME (TYPE_NAME (TREE_TYPE (x)))))
! {
! /* do gratuitous C++ typedefing, and make sure that
! we access this type either through TREE_TYPE field
! or via the tags list. */
! TYPE_NAME (TREE_TYPE (x)) = x;
! pushtag (DECL_NAME (x), TREE_TYPE (x));
! }
}
+ else if (TYPE_NAME (TREE_TYPE (x)) == 0)
+ TYPE_NAME (TREE_TYPE (x)) = x;
}
***************
*** 3050,3055 ****
#endif
! if (TREE_CODE (decl) == FUNCTION_DECL
! && TREE_OVERLOADED (decl))
/* @@ Also done in start_function. */
push_overloaded_decl (tem);
--- 3075,3079 ----
#endif
! if (TREE_CODE (decl) == FUNCTION_DECL && TREE_OVERLOADED (decl))
/* @@ Also done in start_function. */
push_overloaded_decl (tem);
***************
*** 3134,3138 ****
DECL_INITIAL (decl) = init = 0;
}
! if (IS_AGGR_TYPE (TREE_TYPE (decl)))
{
if (TREE_TYPE (DECL_NAME (decl)))
--- 3158,3163 ----
DECL_INITIAL (decl) = init = 0;
}
! if (current_lang_name == lang_name_cplusplus
! && IS_AGGR_TYPE (TREE_TYPE (decl)))
{
if (TREE_TYPE (DECL_NAME (decl)))
***************
*** 4961,4965 ****
refer to it, so nothing needs know about the name change.
The TYPE_NAME field was filled in by build_struct_xref. */
! if (IS_AGGR_TYPE (type)
&& TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
&& ANON_AGGRNAME_P (DECL_NAME (TYPE_NAME (type))))
--- 4986,4991 ----
refer to it, so nothing needs know about the name change.
The TYPE_NAME field was filled in by build_struct_xref. */
! if (current_lang_name == lang_name_cplusplus
! && IS_AGGR_TYPE (type)
&& TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
&& ANON_AGGRNAME_P (DECL_NAME (TYPE_NAME (type))))
***************
*** 8047,8068 ****
IDENTIFIER_LOCAL_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link);
! /* Pop the current level, and free the structure for reuse. */
!
! {
! register struct binding_level *level = current_binding_level;
! current_binding_level = current_binding_level->level_chain;
!
! level->level_chain = free_binding_level;
! free_binding_level = level;
! if (current_binding_level->parm_flag < 0)
! {
! class_binding_level = current_binding_level;
! do
! {
! current_binding_level = current_binding_level->level_chain;
! }
! while (current_binding_level->parm_flag < 0);
! }
! }
}
--- 8073,8077 ----
IDENTIFIER_LOCAL_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link);
! POP_BINDING_LEVEL;
}
diff -rc2H g++-1.35.0/cplus-init.c g++-1.35.0+/cplus-init.c
*** g++-1.35.0/cplus-init.c Tue May 16 23:45:23 1989
--- g++-1.35.0+/cplus-init.c Fri May 19 14:22:36 1989
***************
*** 47,50 ****
--- 47,51 ----
void expand_aggr_init ();
tree build_virtual_init ();
+ tree build_vbase_delete ();
static void expand_aggr_init_1 ();
***************
*** 191,195 ****
an immediate base class, complain. */
for (i = n_baseclasses; i > 0; i--)
! if (basetype == CLASSTYPE_BASECLASS (current_class_type, i))
break;
if (i == 0)
--- 192,196 ----
an immediate base class, complain. */
for (i = n_baseclasses; i > 0; i--)
! if (basetype == CLASSTYPE_THIS_BASECLASS (current_class_type, i))
break;
if (i == 0)
***************
*** 2533,2536 ****
--- 2534,2540 ----
}
+ /* Delete an object at top level. This means we delete both the
+ object and its virtual base classes. */
+
void
expand_delete (type, addr, auto_delete, protect)
***************
*** 2563,2570 ****
else
{
rval = build_delete (type, addr, auto_delete, protect);
! if (rval != error_mark_node)
! expand_expr_stmt (rval);
! else
/* Have to emit something, since it might be a cleanup. */
emit_note (0, -1);
--- 2567,2580 ----
else
{
+ struct rtx_def *get_last_insn ();
+ struct rtx_def *last_insn = get_last_insn ();
+
rval = build_delete (type, addr, auto_delete, protect);
! expand_expr_stmt (rval);
! if (TYPE_NEEDS_DESTRUCTOR (type)
! && TYPE_USES_VIRTUAL_BASECLASSES (type))
! expand_expr_stmt (build_vbase_delete (type, addr));
!
! if (get_last_insn () == last_insn)
/* Have to emit something, since it might be a cleanup. */
emit_note (0, -1);
***************
*** 2572,2578 ****
}
/* Generate a call to a destructor. TYPE is the type to cast ADDR to.
ADDR is an expression which yields the store to be destroyed.
! AUTO_DELETE is nonzero if a call to DELETE should be made or not. */
tree
build_delete (type, addr, auto_delete, protect)
--- 2582,2615 ----
}
+ /* Free up storage of type TYPE, at address ADDR.
+ TYPE is a POINTER_TYPE.
+
+ This does not call any destructors. */
+ tree
+ build_x_delete (type, addr)
+ tree type, addr;
+ {
+ tree rval;
+
+ if (TREE_GETS_DELETE (TREE_TYPE (type)))
+ rval = build_opfncall (DELETE_EXPR, addr, addr);
+ else
+ {
+ rval = build (CALL_EXPR, void_type_node, BID,
+ build_tree_list (NULL_TREE, addr), 0);
+ TREE_VOLATILE (rval) = 1;
+ }
+ return rval;
+ }
+
/* Generate a call to a destructor. TYPE is the type to cast ADDR to.
ADDR is an expression which yields the store to be destroyed.
! AUTO_DELETE is nonzero if a call to DELETE should be made or not.
!
! PROTECT is nonzero if we protect access to the virtual baseclass
! members.
!
! This function does not delete an object's virtual base classes.
! Call `expand_delete' for that. */
tree
build_delete (type, addr, auto_delete, protect)
***************
*** 2753,2757 ****
}
else if (basetype == NULL_TREE
! || ! TYPE_NEEDS_DESTRUCTOR (basetype))
{
cond = build (COND_EXPR, void_type_node,
--- 2790,2795 ----
}
else if (basetype == NULL_TREE
! || (CLASSTYPE_VIA_VIRTUAL (type, 1) == 0
! && ! TYPE_NEEDS_DESTRUCTOR (basetype)))
{
cond = build (COND_EXPR, void_type_node,
***************
*** 2765,2769 ****
exprstmt = build_tree_list (NULL_TREE, cond);
! if (basetype && TYPE_NEEDS_DESTRUCTOR (basetype))
{
expr = build_delete (basetype, ref, parent_auto_delete, protect);
--- 2803,2809 ----
exprstmt = build_tree_list (NULL_TREE, cond);
! if (basetype
! && ! CLASSTYPE_VIA_VIRTUAL (type, 1)
! && TYPE_NEEDS_DESTRUCTOR (basetype))
{
expr = build_delete (basetype, ref, parent_auto_delete, protect);
***************
*** 2776,2780 ****
{
basetype = CLASSTYPE_BASECLASS (type, i);
! if (! TYPE_NEEDS_DESTRUCTOR (basetype))
continue;
--- 2816,2821 ----
{
basetype = CLASSTYPE_BASECLASS (type, i);
! if (! TYPE_NEEDS_DESTRUCTOR (basetype)
! || CLASSTYPE_VIA_VIRTUAL (type, i))
continue;
***************
*** 2794,2799 ****
if (TYPE_NEEDS_DESTRUCTOR (TREE_TYPE (member)))
{
! expr = build_delete (TREE_TYPE (member),
! build_component_ref (ref, DECL_NAME (member), 0, 0),
integer_zero_node, 1);
if (expr == error_mark_node)
--- 2835,2840 ----
if (TYPE_NEEDS_DESTRUCTOR (TREE_TYPE (member)))
{
! tree this_member = build_component_ref (ref, DECL_NAME (member), 0, 0);
! expr = build_delete (TREE_TYPE (member), this_member,
integer_zero_node, 1);
if (expr == error_mark_node)
***************
*** 2800,2803 ****
--- 2841,2851 ----
return error_mark_node;
exprstmt = tree_cons (NULL_TREE, expr, exprstmt);
+ if (TYPE_USES_VIRTUAL_BASECLASSES (TREE_TYPE (member)))
+ {
+ expr = build_vbase_delete (TREE_TYPE (member), this_member);
+ if (expr == error_mark_node)
+ return error_mark_node;
+ exprstmt = tree_cons (NULL_TREE, expr, exprstmt);
+ }
}
}
***************
*** 2805,2814 ****
if (exprstmt)
return build_compound_expr (exprstmt);
! else
! {
! compiler_error ("build delete does nothing");
! return error_mark_node;
! }
}
}
--- 2853,2880 ----
if (exprstmt)
return build_compound_expr (exprstmt);
! /* Virtual base classes make this function do nothing. */
! return build (NOP_EXPR, void_type_node, integer_zero_node);
! }
! }
!
! /* For type TYPE, delete the virtual baseclass objects of DECL. */
!
! tree
! build_vbase_delete (type, decl)
! tree type, decl;
! {
! tree vbases = CLASSTYPE_VBASECLASSES (type);
! tree result = NULL_TREE;
! tree addr = build_unary_op (ADDR_EXPR, decl, 0);
! if (addr == error_mark_node)
! return error_mark_node;
! while (vbases)
! {
! result = tree_cons (NULL_TREE,
! build_delete (TYPE_POINTER_TO (TREE_VALUE (vbases)),
! addr, integer_zero_node, 1), result);
! vbases = TREE_CHAIN (vbases);
}
+ return build_compound_expr (nreverse (result));
}
***************
*** 2821,2825 ****
This function expands (or synthesizes) these calls itself.
AUTO_DELETE_VEC says whether the container (vector) should be deallocated.
! AUTO_DELETE say whether each item in the container should be deallocated. */
void
expand_vec_delete (base, maxindex, elt_size, dtor_dummy, auto_delete_vec, auto_delete)
--- 2887,2893 ----
This function expands (or synthesizes) these calls itself.
AUTO_DELETE_VEC says whether the container (vector) should be deallocated.
! AUTO_DELETE say whether each item in the container should be deallocated.
!
! This also calls delete for virtual baseclasses of elements of the vector. */
void
expand_vec_delete (base, maxindex, elt_size, dtor_dummy, auto_delete_vec, auto_delete)
***************
*** 2912,2916 ****
This generates a call to the function BIVD, which passes the
destructor across the vector. This is needed only when
! expanding such an action is unacceptable. */
tree
build_vec_delete (base, maxindex, elt_size, dtor_dummy, auto_delete_vec, auto_delete)
--- 2980,2986 ----
This generates a call to the function BIVD, which passes the
destructor across the vector. This is needed only when
! expanding such an action is unacceptable.
!
! This does not call delete on the elements' virtual baseclasses. */
tree
build_vec_delete (base, maxindex, elt_size, dtor_dummy, auto_delete_vec, auto_delete)
diff -rc2H g++-1.35.0/cplus-parse.y g++-1.35.0+/cplus-parse.y
*** g++-1.35.0/cplus-parse.y Tue May 16 23:45:24 1989
--- g++-1.35.0+/cplus-parse.y Fri May 19 14:22:38 1989
***************
*** 623,628 ****
$$ = error_mark_node;
}
! else
$$ = build_delete (type, expr, integer_one_node, 1);
}
| DELETE '[' expr ']' expr_no_commas %prec UNARY
--- 623,637 ----
$$ = error_mark_node;
}
! else if (! IS_AGGR_TYPE (TREE_TYPE (type))
! || ! TYPE_USES_VIRTUAL_BASECLASSES (TREE_TYPE (type)))
$$ = build_delete (type, expr, integer_one_node, 1);
+ else
+ {
+ $$ = build (COMPOUND_EXPR, void_type_node,
+ build (COMPOUND_EXPR, void_type_node,
+ build_delete (type, expr, integer_zero_node, 1),
+ build_vbase_delete (type, expr)),
+ build_x_delete (type, expr));
+ }
}
| DELETE '[' expr ']' expr_no_commas %prec UNARY
diff -rc2H g++-1.35.0/cplus-search.c g++-1.35.0+/cplus-search.c
*** g++-1.35.0/cplus-search.c Tue May 16 23:45:25 1989
--- g++-1.35.0+/cplus-search.c Fri May 19 14:22:39 1989
***************
*** 2027,2030 ****
--- 2027,2036 ----
}
+ /* Some virtual baseclasses might be virtual baseclasses for
+ other virtual baseclasses. We sort the virtual baseclasses
+ topologically: in the list returned, the first virtual base
+ classes have no virtual baseclasses themselves, and any entry
+ on the list has no dependency on virtual base classes later in the
+ list. */
tree
get_vbase_types (type)
***************
*** 2034,2038 ****
dfs_walk (type, dfs_get_vbase_types, unmarkedp);
! dfs_walk (type, dfs_unmark12, markedp);
return vbase_types;
--- 2040,2081 ----
dfs_walk (type, dfs_get_vbase_types, unmarkedp);
! dfs_walk (type, dfs_unmark, markedp);
!
! if (vbase_types)
! {
! /* Now sort these types. This is essentially a bubble sort. */
! tree vbases = vbase_types;
!
! while (vbases)
! {
! tree prev = NULL_TREE;
! while (vbases
! && ! TYPE_USES_VIRTUAL_BASECLASSES (TREE_VALUE (vbases)))
! {
! CLASSTYPE_MARKED2 (TREE_VALUE (vbases)) = 0;
! prev = vbases;
! vbases = TREE_CHAIN (vbases);
! }
! if (vbases)
! {
! /* If all our virtual baseclasses are unmarked, ok. */
! tree t = CLASSTYPE_VBASECLASSES (TREE_VALUE (vbases));
! while (t && CLASSTYPE_MARKED2 (TREE_VALUE (vbases)) == 0)
! t = TREE_CHAIN (t);
! if (t != NULL_TREE)
! {
! /* This virtual base class still depends on others. */
! tree next = TREE_CHAIN (vbases);
! assert (prev != NULL_TREE);
! TREE_CHAIN (prev) = next;
! TREE_CHAIN (vbases) = TREE_CHAIN (next);
! TREE_CHAIN (next) = vbases;
! vbases = next;
! continue;
! }
! }
! break;
! }
! }
return vbase_types;
diff -rc2H g++-1.35.0/cplus-typeck.c g++-1.35.0+/cplus-typeck.c
*** g++-1.35.0/cplus-typeck.c Tue May 16 23:45:27 1989
--- g++-1.35.0+/cplus-typeck.c Fri May 19 14:22:41 1989
***************
*** 3708,3712 ****
t = TREE_OPERAND (arg, 1);
! if (TREE_OPERAND (arg, 0))
{
/* Don't know if this should return address to just
--- 3708,3714 ----
t = TREE_OPERAND (arg, 1);
! if (TREE_OPERAND (arg, 0)
! && (TREE_CODE (TREE_OPERAND (arg, 0)) != NOP_EXPR
! || TREE_OPERAND (TREE_OPERAND (arg, 0), 0) != error_mark_node))
{
/* Don't know if this should return address to just
***************
*** 4788,4791 ****
--- 4790,4802 ----
rhs = TREE_OPERAND (rhs, 0);
+ if (TREE_CODE (TREE_TYPE (rhs)) == OFFSET_TYPE)
+ {
+ rhs = resolve_member_ref (rhs);
+ if (rhs == error_mark_node)
+ return error_mark_node;
+ rhstype = TREE_TYPE (rhs);
+ coder = TREE_CODE (rhstype);
+ }
+
if (TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE
***************
*** 5047,5050 ****
--- 5058,5070 ----
&& TREE_TYPE (rhs) == TREE_TYPE (TREE_OPERAND (rhs, 0)))
rhs = TREE_OPERAND (rhs, 0);
+
+ if (TREE_CODE (TREE_TYPE (rhs)) == OFFSET_TYPE)
+ {
+ rhs = resolve_member_ref (rhs);
+ if (rhs == error_mark_node)
+ return error_mark_node;
+ rhstype = TREE_TYPE (rhs);
+ coder = TREE_CODE (rhstype);
+ }
if (TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE
diff -rc2H g++-1.35.0/gcc.c g++-1.35.0+/gcc.c
*** g++-1.35.0/gcc.c Tue May 16 23:45:31 1989
--- g++-1.35.0+/gcc.c Fri May 19 14:22:45 1989
***************
*** 278,281 ****
--- 278,297 ----
%{!pipe:%g.s}\
%{c:%{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o}\n }}}"},
+ {".cxx",
+ "cpp -+ %{nostdinc} %{C} %{v} %{D*} %{U*} %{I*} %{M*} %{T}\
+ -undef -D__GNU__ -D__GNUG__ -D__cplusplus %{ansi:-T -D__STRICT_ANSI__} %{!ansi:%p} %P\
+ %c %{O:-D__OPTIMIZE__} %{traditional} %{pedantic}\
+ %{Wcomment*} %{Wtrigraphs} %{Wall} %C\
+ %i %{!M*:%{!E:%{!pipe:%g.cpp}}}%{E:%{o*}}%{M*:%{o*}} |\n\
+ %{!M*:%{!E:cc1plus %{!pipe:%g.cpp} %1 \
+ %{!Q:-quiet} -dumpbase %i %{Y*} %{d*} %{m*} %{f*} %{a}\
+ %{W*} %{w} %{pedantic} %{ansi} %{traditional}\
+ %{O:-opt}%{!O:-noreg}\
+ %{v:-version} %{g:-G} %{g0:-G0} %{pg:-p} %{p}\
+ %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
+ %{S:%{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\
+ %{!S:as %{R} %{j} %{J} %{h} %{d2} %a\
+ %{!pipe:%g.s}\
+ %{c:%{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o}\n }}}"},
{".i",
"cc1 %i %1 %{!Q:-quiet} %{Y*} %{d*} %{m*} %{f*} %{a}\
diff -rc2H g++-1.35.0/make-links.g++ g++-1.35.0+/make-links.g++
*** g++-1.35.0/make-links.g++ Tue May 16 23:45:38 1989
--- g++-1.35.0+/make-links.g++ Fri May 19 14:22:47 1989
***************
*** 34,41 ****
then
DIR="../gcc"
- CDIR="../../gcc/config"
else
- CDIR="${DIR}/config"
fi
progname=$0
--- 34,40 ----
then
DIR="../gcc"
else
fi
+ CDIR="../${DIR}/config"
progname=$0
***************
*** 61,65 ****
insn-output.c insn-flags.h insn-config.h insn-codes.h"
! $remove -f config
$make_directory config
$change_directory config
--- 60,64 ----
insn-output.c insn-flags.h insn-config.h insn-codes.h"
! $remove -rf config
$make_directory config
$change_directory config
diff -rc2H g++-1.35.0/stmt.c g++-1.35.0+/stmt.c
*** g++-1.35.0/stmt.c Tue May 16 23:45:34 1989
--- g++-1.35.0+/stmt.c Fri May 19 14:22:50 1989
***************
*** 511,514 ****
--- 511,518 ----
if (stack_level)
emit_move_insn (stack_pointer_rtx, stack_level);
+ #ifndef EXIT_IGNORE_STACK
+ else
+ do_pending_stack_adjust ();
+ #endif
if (body != 0 && TREE_PACKED (body))
***************
*** 1754,1757 ****
--- 1758,1765 ----
thisblock->data.block.stack_level);
}
+ #ifndef EXIT_IGNORE_STACK
+ else
+ do_pending_stack_adjust ();
+ #endif
/* Any gotos out of this block must also do these things.
***************
*** 2077,2080 ****
--- 2085,2092 ----
expand_cleanups (list, 0);
+ #ifndef EXIT_IGNORE_STACK
+ do_pending_stack_adjust ();
+ #endif
+
new_before_jump = get_last_insn ();
diff -rc2H g++-1.35.0/stor-layout.c g++-1.35.0+/stor-layout.c
*** g++-1.35.0/stor-layout.c Tue May 16 23:45:35 1989
--- g++-1.35.0+/stor-layout.c Fri May 19 14:22:51 1989
***************
*** 439,458 ****
if (TYPE_SIZE (basetype) == 0)
{
! int j;
! /* The base type is of incomplete type. It is
! probably best to pretend that it does not
! exist. */
! error ("base class `%s' has incomplete type", TYPE_NAME_STRING (basetype));
! if (i == n_baseclasses)
! CLASSTYPE_THIS_BASECLASS (rec, i) = NULL_TREE;
! CLASSTYPE_N_BASECLASSES (rec) -= 1;
! n_baseclasses -= 1;
! for (j = i; j < n_baseclasses; j++)
! {
! CLASSTYPE_THIS_BASECLASS (rec, j) = CLASSTYPE_THIS_BASECLASS (rec, j+1);
! SET_CLASSTYPE_VIAS (rec, j,
! CLASSTYPE_VIA_PUBLIC (rec, j+1),
! CLASSTYPE_VIA_VIRTUAL (rec, j+1));
! }
continue;
}
--- 439,443 ----
if (TYPE_SIZE (basetype) == 0)
{
! error_with_aggr_type (basetype, "base class `%s' has incomplete type");
continue;
}
diff -rc2H g++-1.35.0/version.c g++-1.35.0+/version.c
*** g++-1.35.0/version.c Tue May 16 23:45:37 1989
--- g++-1.35.0+/version.c Fri May 19 14:22:54 1989
***************
*** 1,1 ****
! char *version_string = "1.35.0";
--- 1,1 ----
! char *version_string = "1.35.0+";
yahi%
If these diffs make anything worse, I need to know.
Michael
Technical Details
Message-ID:
<8905192128.AA11255@yahi.stanford.edu>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!YAHI.STANFORD.EDU!tiemannReferences:
<8905191336.AA04634@nada.kth.se>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!YAHI.STANFORD.EDU!tiemann From: tiemann@YAHI.STANFORD.EDU (Michael Tiemann) Newsgroups: gnu.g++.bug Subject: g++ 1.35 virtual base class bug. Message-ID: <8905192128.AA11255@yahi.stanford.edu> Date: 19 May 89 21:28:43 GMT References: <8905191336.AA04634@nada.kth.se> Sender: daemon@tut.cis.ohio-state.edu Reply-To: tiemann@lurch.stanford.edu Distribution: gnu Organization: GNUs Not Usenet Lines: 1268