/*
* Copyright (c) 2002, The Tendra Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Crown Copyright (c) 1997
*
* This TenDRA(r) Computer Program is subject to Copyright
* owned by the United Kingdom Secretary of State for Defence
* acting through the Defence Evaluation and Research Agency
* (DERA). It is made available to Recipients with a
* royalty-free licence for its use, reproduction, transfer
* to other parties and amendment for any purpose not excluding
* product development provided that any such use et cetera
* shall be deemed to be acceptance of the following conditions:-
*
* (1) Its Recipients shall ensure that this Notice is
* reproduced upon any copies or amended versions of it;
*
* (2) Any amended version of it shall be clearly marked to
* show both the nature of and the organisation responsible
* for the relevant amendment or amendments;
*
* (3) Its onward transfer from a recipient to another
* party shall be deemed to be that party's acceptance of
* these conditions;
*
* (4) DERA gives no warranty or assurance as to its
* quality or suitability for any purpose and DERA accepts
* no liability whatsoever in relation to any use to which
* it may be put.
*
* $TenDRA: tendra/src/producers/common/construct/derive.c,v 1.7 2004/08/14 15:15:36 bp Exp $
*/
#include "config.h"
#include "producer.h"
#include
#include "c_types.h"
#include "ctype_ops.h"
#include "etype_ops.h"
#include "exp_ops.h"
#include "graph_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "off_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "access.h"
#include "check.h"
#include "chktype.h"
#include "class.h"
#include "derive.h"
#include "dump.h"
#include "identifier.h"
#include "instance.h"
#include "namespace.h"
#include "redeclare.h"
#include "syntax.h"
#include "template.h"
#include "token.h"
#include "virtual.h"
/*
* ARE TWO GRAPHS EQUAL?
*
* This routine checks whether the graphs gr and gs are equal. Allowance
* is made for the equivalence of virtual bases.
*/
int
eq_graph(GRAPH gr, GRAPH gs)
{
GRAPH hr = gr;
GRAPH hs = gs;
while (!IS_NULL_graph (gr)) {
/* Calculate canonical form for virtual bases */
hr = gr;
gr = DEREF_graph (graph_equal (gr));
}
while (!IS_NULL_graph (gs)) {
/* Calculate canonical form for virtual bases */
hs = gs;
gs = DEREF_graph (graph_equal (gs));
}
return (EQ_graph (hr, hs));
}
/*
* SEARCH A GRAPH FOR A BASE
*
* This routine searches the graph gr for the base class ct. It returns
* the first node encountered which matches ct, or the null graph if no
* such match is found. Note that the search algorithm is based on a
* depth-first left-to-right traversal of the graph.
*/
static GRAPH
search_graph(GRAPH gr, CLASS_TYPE ct)
{
DECL_SPEC acc;
/* Check the head of gr */
CLASS_TYPE cr = DEREF_ctype (graph_head (gr));
if (eq_ctype (cr, ct)) return (gr);
/* Only search first instance */
acc = DEREF_dspec (graph_access (gr));
if (acc & dspec_defn) {
/* Search the branches of gr */
LIST (GRAPH) br = DEREF_list (graph_tails (gr));
while (!IS_NULL_list (br)) {
GRAPH gs = DEREF_graph (HEAD_list (br));
gs = search_graph (gs, ct);
if (!IS_NULL_graph (gs)) return (gs);
br = TAIL_list (br);
}
}
return (NULL_graph);
}
/*
* IS ONE GRAPH A SUBGRAPH OF ANOTHER?
*
* This routine checks whether the graph gs is a subgraph of gr (allowing
* for the identification of virtual bases).
*/
int
is_subgraph(GRAPH gr, GRAPH gs)
{
unsigned nt, ns;
CLASS_TYPE ct, cs;
if (EQ_graph (gr, gs)) return (1);
ct = DEREF_ctype (graph_head (gr));
cs = DEREF_ctype (graph_head (gs));
nt = DEREF_unsigned (ctype_no_bases (ct));
ns = DEREF_unsigned (ctype_no_bases (cs));
if (nt == ns) {
/* Graphs are the same size */
return (eq_graph (gr, gs));
}
if (nt > ns) {
/* gr is bigger than gs */
LIST (GRAPH) br = DEREF_list (graph_tails (gr));
while (!IS_NULL_list (br)) {
GRAPH gt = DEREF_graph (HEAD_list (br));
if (is_subgraph (gt, gs)) return (1);
br = TAIL_list (br);
}
}
return (0);
}
/*
* COPY A GRAPH
*
* This routine recursively copies the graph gr into the base class graph
* of the current class, with access specifiers adjusted by acc and virtual
* specifier virt. indir is true if the graph is a subgraph of a virtual
* base and templ is true if the graph represents a template parameter.
*/
static GRAPH
copy_graph(GRAPH gr, OFFSET off, DECL_SPEC acc, int virt, int indir, int templ)
{
OFFSET offa;
DECL_SPEC as;
GRAPH gc, gp, gs;
LIST (GRAPH) bs;
/* Decompose head of graph */
CLASS_TYPE ct = DEREF_ctype (graph_head (gr));
LIST (GRAPH) bt = DEREF_list (graph_tails (gr));
/* Adjust access specifiers */
DECL_SPEC at = DEREF_dspec (graph_access (gr));
as = join_access (at, acc);
as |= shadow_access (at);
if (at & dspec_virtual) virt = 1;
if (virt) {
/* Mark virtual bases */
as |= dspec_virtual;
indir = 1;
}
if (indir) {
/* Mark indirect bases */
as |= dspec_mutable;
}
if (templ) {
/* Mark template parameter bases */
as |= dspec_template;
}
as |= dspec_inherit;
as &= ~dspec_done;
/* Search existing graph for ct */
gc = DEREF_graph (ctype_base (crt_class));
gp = search_graph (gc, ct);
if (IS_NULL_graph (gp)) as |= dspec_defn;
/* Copy components */
bs = NULL_list (GRAPH);
while (!IS_NULL_list (bt)) {
GRAPH gt = DEREF_graph (HEAD_list (bt));
gt = copy_graph (gt, off, as, 0, indir, templ);
CONS_graph (gt, bs, bs);
bt = TAIL_list (bt);
}
bs = REVERSE_list (bs);
/* Create new graph */
MAKE_graph_basic (ct, as, gs);
COPY_list (graph_tails (gs), bs);
COPY_graph (graph_top (gs), gc);
/* Set base offset */
offa = DEREF_off (graph_off (gr));
if (!IS_NULL_off (offa)) {
MAKE_off_deriv (gs, off, offa, off);
}
COPY_off (graph_off (gs), off);
/* Set up-field of bases */
while (!IS_NULL_list (bs)) {
GRAPH gt = DEREF_graph (HEAD_list (bs));
COPY_graph (graph_up (gt), gs);
bs = TAIL_list (bs);
}
return (gs);
}
/*
* FIND A COPY OF A SUBGRAPH
*
* Given a graph gs, a subgraph hs of gs, and a copy gr of gs (produced
* by copy_graph above), this routine returns the subgraph of gr which is
* the image of hs under this copy operation, i.e. it is the subgraph
* hr which makes the following diagram commutative:
*
* copy
* gr ------ gs
* | |
* incl | | incl
* | |
* hr ------ hs
* copy
*/
GRAPH
find_subgraph(GRAPH gr, GRAPH gs, GRAPH hs)
{
if (EQ_graph (gs, hs)) return (gr);
if (!IS_NULL_graph (gr)) {
LIST (GRAPH) br = DEREF_list (graph_tails (gr));
LIST (GRAPH) bs = DEREF_list (graph_tails (gs));
while (!IS_NULL_list (br)) {
GRAPH ar = DEREF_graph (HEAD_list (br));
GRAPH as = DEREF_graph (HEAD_list (bs));
GRAPH hr = find_subgraph (ar, as, hs);
if (!IS_NULL_graph (hr)) return (hr);
br = TAIL_list (br);
bs = TAIL_list (bs);
}
}
return (NULL_graph);
}
/*
* IDENTIFY TWO GRAPHS
*
* This routine identifies the graph gs with the graph gr by linking
* them by means of their equal fields.
*/
static void
identify_graph(GRAPH gr, GRAPH gs)
{
DECL_SPEC acc;
LIST (GRAPH) br, bs;
GRAPH gu = DEREF_graph (graph_equal (gs));
while (!IS_NULL_graph (gu)) {
gs = gu;
gu = DEREF_graph (graph_equal (gs));
}
br = DEREF_list (graph_tails (gr));
bs = DEREF_list (graph_tails (gs));
while (!IS_NULL_list (br)) {
GRAPH hr = DEREF_graph (HEAD_list (br));
GRAPH hs = DEREF_graph (HEAD_list (bs));
identify_graph (hr, hs);
br = TAIL_list (br);
bs = TAIL_list (bs);
}
COPY_graph (graph_equal (gs), gr);
acc = DEREF_dspec (graph_access (gr));
acc |= dspec_done;
COPY_dspec (graph_access (gr), acc);
return;
}
/*
* BASE CLASS INFORMATION
*
* The variable base_info holds information about the graph being analysed
* by fold_graph, for example, does it have a virtual or an ambiguous base
* class?
*/
static CLASS_INFO base_info = cinfo_none;
static LIST (GRAPH) virtual_bases = NULL_list (GRAPH);
/*
* FOLD A GRAPH
*
* This routine folds the graph gr by identifying all of its equal virtual
* bases. It also defines values representing the offset of each base
* from the start of its class. It returns a list of all the base classes
* in gr (allowing for virtual bases). All bases added to the list are
* marked as explicit bases.
*/
static LIST (GRAPH)
fold_graph(GRAPH gr, LIST (GRAPH) br)
{
CLASS_TYPE ct = DEREF_ctype (graph_head (gr));
DECL_SPEC acc = DEREF_dspec (graph_access (gr));
/* Scan for previous uses of this base */
LIST (GRAPH) bs = br;
while (!IS_NULL_list (bs)) {
GRAPH gs = DEREF_graph (HEAD_list (bs));
CLASS_TYPE cs = DEREF_ctype (graph_head (gs));
if (eq_ctype (cs, ct)) {
/* Duplicate base found */
DECL_SPEC acs = DEREF_dspec (graph_access (gs));
if ((acs & dspec_virtual) && (acc & dspec_virtual)) {
/* Both bases are virtual */
if (!eq_graph (gr, gs)) {
/* Identify graphs if not already done */
identify_graph (gr, gs);
}
/* Propagate ambiguous bases */
if (acs & dspec_alias) {
acc |= dspec_alias;
} else if (acc & dspec_alias) {
while (!IS_NULL_graph (gs)) {
acs = DEREF_dspec (graph_access (gs));
acs |= dspec_alias;
COPY_dspec (graph_access (gs), acs);
gs = DEREF_graph (graph_equal (gs));
}
}
acc |= dspec_done;
COPY_dspec (graph_access (gr), acc);
return (br);
}
/* Ambiguous base */
base_info |= cinfo_ambiguous;
acc |= dspec_alias;
acs |= dspec_alias;
COPY_dspec (graph_access (gs), acs);
}
bs = TAIL_list (bs);
}
/* Add base class to list */
acc |= (dspec_main | dspec_done);
COPY_dspec (graph_access (gr), acc);
CONS_graph (gr, br, br);
/* Fold subgraphs */
bs = DEREF_list (graph_tails (gr));
while (!IS_NULL_list (bs)) {
GRAPH gs = DEREF_graph (HEAD_list (bs));
br = fold_graph (gs, br);
bs = TAIL_list (bs);
}
/* Build list of virtual classes */
if (acc & dspec_virtual) {
base_info |= cinfo_virtual_base;
CONS_graph (gr, virtual_bases, virtual_bases);
}
return (br);
}
/*
* FIND A SINGLE INHERITANCE BASE CLASS
*
* This routine returns the single inheritance base class of gr or the
* null graph if there is no such base. The single inheritance base
* is the first direct base, provided this is not virtual. Derived
* classes are laid out so that their single inheritance base classes
* are at zero offset from the start of the class, thereby minimising
* the cost of a base class conversion.
*/
static GRAPH
find_first_base(GRAPH gr)
{
LIST (GRAPH) br = DEREF_list (graph_tails (gr));
if (!IS_NULL_list (br)) {
GRAPH gs = DEREF_graph (HEAD_list (br));
DECL_SPEC acc = DEREF_dspec (graph_access (gs));
if (!(acc & dspec_virtual)) {
return (gs);
}
}
return (NULL_graph);
}
/*
* END BASE CLASS DEFINITIONS
*
* This routine is called after all the base classes of a class have been
* processed. ok is false for an empty base class list. The routine
* folds the base class graph of the current class and records the total
* number of bases. It also initialises the virtual function table.
*/
void
end_base_class(CLASS_TYPE ct, int ok)
{
if (!IS_NULL_ctype (ct)) {
/* Scan for virtual bases */
CLASS_INFO ci = DEREF_cinfo (ctype_info (ct));
GRAPH gr = DEREF_graph (ctype_base (ct));
LIST (GRAPH) bs = DEREF_list (graph_tails (gr));
unsigned nd = LENGTH_list (bs);
LIST (GRAPH) br = fold_graph (gr, NULL_list (GRAPH));
unsigned nb = LENGTH_list (br);
IGNORE check_value (OPT_VAL_base_classes, (ulong) (nb - 1));
IGNORE check_value (OPT_VAL_direct_bases, (ulong) nd);
COPY_unsigned (ctype_no_bases (ct), nb);
while (!IS_NULL_list (br)) {
GRAPH gs;
nb--;
DESTROY_CONS_graph (destroy, gs, br, br);
do {
COPY_unsigned (graph_no (gs), nb);
gs = DEREF_graph (graph_equal (gs));
} while (!IS_NULL_graph (gs));
}
br = REVERSE_list (virtual_bases);
COPY_list (ctype_vbase (ct), br);
nd = LENGTH_list (br);
IGNORE check_value (OPT_VAL_virtual_bases, (ulong) nd);
virtual_bases = NULL_list (GRAPH);
/* Mark single inheritance bases */
do {
DECL_SPEC acc = DEREF_dspec (graph_access (gr));
acc |= dspec_ignore;
COPY_dspec (graph_access (gr), acc);
gr = find_first_base (gr);
} while (!IS_NULL_graph (gr));
/* Set up base class namespaces */
while (!IS_NULL_list (bs)) {
GRAPH gs = DEREF_graph (HEAD_list (bs));
CLASS_TYPE cs = DEREF_ctype (graph_head (gs));
NAMESPACE ns = DEREF_nspace (ctype_member (cs));
NAMESPACE nt = DEREF_nspace (ctype_member (ct));
IGNORE use_namespace (ns, nt, nt);
uncache_namespace (ns, 0);
bs = TAIL_list (bs);
}
/* Record class information */
if (!ok) report (crt_loc, ERR_class_derived_empty (ct));
ci |= base_info;
COPY_cinfo (ctype_info (ct), ci);
base_info = cinfo_none;
if (do_dump) dump_base (ct);
/* Set up virtual function table */
begin_virtual (ct);
}
return;
}
/*
* ADD A BASE CLASS TO A CLASS DEFINITION
*
* This routine adds a base class bid with access specifier acc to the
* current class. virt is true to indicate a virtual base class.
*/
void
add_base_class(IDENTIFIER bid, DECL_SPEC acc, int virt)
{
GRAPH gt;
int ok = 1;
CLASS_INFO cj;
int templ = 0;
LIST (GRAPH) bt;
TYPE s = NULL_type;
CLASS_TYPE cs = NULL_ctype;
/* Examine the derived class */
CLASS_TYPE ct = crt_class;
CLASS_INFO ci = DEREF_cinfo (ctype_info (ct));
if (ci & cinfo_union) {
/* Derived class can't be a union */
report (crt_loc, ERR_class_union_deriv (ct));
ok = 0;
}
/* Examine the base class */
if (IS_id_class_name_etc (bid)) {
s = DEREF_type (id_class_name_etc_defn (bid));
} else {
IDENTIFIER tid;
HASHID bnm = DEREF_hashid (id_name (bid));
if (crt_id_qualifier == qual_none) {
tid = find_type_id (bnm, 1);
} else {
NAMESPACE bns = DEREF_nspace (id_parent (bid));
tid = find_qual_id (bns, bnm, 0, 1);
if (IS_NULL_id (tid)) {
/* Allow for implicit typename */
s = check_typename (bns, bid, btype_class);
}
}
if (!IS_NULL_id (tid)) {
if (IS_id_ambig (tid)) {
tid = report_ambiguous (tid, 0, 1, 1);
}
if (IS_id_class_name_etc (tid)) {
s = DEREF_type (id_class_name_etc_defn (tid));
bid = tid;
}
}
}
if (!IS_NULL_type (s)) {
/* Declared base type */
s = expand_type (s, 2);
if (IS_type_compound (s)) {
cs = DEREF_ctype (type_compound_defn (s));
} else if (IS_type_token (s)) {
/* Allow for template parameters */
IDENTIFIER pid = DEREF_id (type_token_tok (s));
cs = find_class (pid);
templ = 1;
}
if (IS_NULL_ctype (cs)) {
/* Inappropriate base class type */
if (!IS_type_error (s)) {
report (crt_loc, ERR_class_derived_class (s));
}
ok = 0;
} else {
/* Examine base class type */
bid = DEREF_id (ctype_name (cs));
cj = DEREF_cinfo (ctype_info (cs));
if (cj & cinfo_union) {
/* Base class can't be a union */
report (crt_loc, ERR_class_union_base (cs));
ok = 0;
} else {
/* Base class must be complete */
ERROR err = check_incomplete (s);
if (!IS_NULL_err (err)) {
ERROR err2 = ERR_class_derived_incompl ();
err = concat_error (err, err2);
report (crt_loc, err);
ok = 0;
}
}
}
} else {
/* Undeclared base type */
report (crt_loc, ERR_dcl_type_simple_undef (bid));
ok = 0;
}
/* Check the access specifier */
if (acc == dspec_none) {
/* No access specifier given */
acc = crt_access;
if (acc != prev_access) {
/* Warn about 'class C : public A, B' */
report (crt_loc, ERR_class_access_base_acc (bid, acc));
}
}
prev_access = acc;
if (virt) prev_access |= dspec_virtual;
/* Check for duplicate base classes */
gt = DEREF_graph (ctype_base (ct));
if (ok) {
bt = DEREF_list (graph_tails (gt));
while (!IS_NULL_list (bt)) {
GRAPH gu = DEREF_graph (HEAD_list (bt));
CLASS_TYPE cu = DEREF_ctype (graph_head (gu));
if (eq_ctype (cs, cu)) {
report (crt_loc, ERR_class_mi_dup (ct, cs));
ok = 0;
break;
}
bt = TAIL_list (bt);
}
}
/* Add base class to graph */
if (ok) {
OFFSET off;
LIST (GRAPH) bs = NULL_list (GRAPH);
GRAPH gs = DEREF_graph (ctype_base (cs));
MAKE_off_base (gs, off);
gs = copy_graph (gs, off, acc, virt, virt, templ);
COPY_graph (off_base_graph (off), gs);
COPY_graph (graph_up (gs), gt);
CONS_graph (gs, bs, bs);
bt = DEREF_list (graph_tails (gt));
if (!IS_NULL_list (bt)) {
/* Mark multiple inheritance */
ci |= cinfo_multiple_base;
}
bt = APPEND_list (bt, bs);
COPY_list (graph_tails (gt), bt);
/* Adjust class information */
cj = DEREF_cinfo (ctype_info (cs));
ci = check_class_info (ci, cj, 1, acc);
COPY_cinfo (ctype_info (ct), ci);
/* Use class name */
use_id (bid, 0);
}
return;
}
/*
* FIND A BASE CLASS
*
* This routine finds whether cs is a base class of ct, returning the
* corresponding node of the base class graph, or the null graph. The
* first guess is based on the fact that if cs is a base class of ct
* then ct has at least as many base classes as cs. The way to remember
* which way round the arguments go is that 'find_base_class (ct, cs)'
* checks for 'class ct : public cs {}'. If ct is a template class
* then def indicates whether this operation implies that ct should
* be defined (not properly implemented yet).
*/
GRAPH
find_base_class(CLASS_TYPE ct, CLASS_TYPE cs, int def)
{
unsigned nt, ns;
complete_class (ct, 1);
nt = DEREF_unsigned (ctype_no_bases (ct));
ns = DEREF_unsigned (ctype_no_bases (cs));
if (nt >= ns) {
/* ct is bigger than cs */
GRAPH gr = DEREF_graph (ctype_base (ct));
return (search_graph (gr, cs));
}
UNUSED (def);
return (NULL_graph);
}
/*
* COMPARE TWO CLASSES
*
* This routine finds whether cs is a base class of ct, or ct is a base
* class of cs. It returns whichever class is the base class, or the null
* class if the two classes are not so related. The first guess is based
* on the relative numbers of base classes of each type. def is as in
* find_base_class.
*/
CLASS_TYPE
compare_base_class(CLASS_TYPE ct, CLASS_TYPE cs, int def)
{
unsigned nt, ns;
complete_class (ct, 1);
complete_class (cs, 1);
nt = DEREF_unsigned (ctype_no_bases (ct));
ns = DEREF_unsigned (ctype_no_bases (cs));
if (nt >= ns) {
/* ct is bigger than cs */
GRAPH gr = DEREF_graph (ctype_base (ct));
gr = search_graph (gr, cs);
if (!IS_NULL_graph (gr)) return (cs);
} else {
/* ct is smaller than cs */
GRAPH gr = DEREF_graph (ctype_base (cs));
gr = search_graph (gr, ct);
if (!IS_NULL_graph (gr)) return (ct);
}
UNUSED (def);
return (NULL_ctype);
}
/*
* CHECK AN AMBIGUOUS BASE CLASS
*
* This routine checks whether the base class corresponding to the node
* gr is ambiguous. It returns a suitable error message.
*/
ERROR
check_ambig_base(GRAPH gr)
{
if (!IS_NULL_graph (gr)) {
DECL_SPEC acc = DEREF_dspec (graph_access (gr));
if (acc & dspec_alias) {
/* Ambiguous base class */
GRAPH gt = DEREF_graph (graph_top (gr));
CLASS_TYPE ct = DEREF_ctype (graph_head (gt));
CLASS_TYPE cs = DEREF_ctype (graph_head (gr));
ERROR err = ERR_class_member_lookup_ambig (cs, ct);
return (err);
}
}
return (NULL_err);
}
/*
* CHECK A VIRTUAL BASE CLASS
*
* This routine checks whether the base class corresponding to the node
* gr is a virtual base of a base class of a virtual base. It returns a
* suitable error message.
*/
ERROR
check_virt_base(GRAPH gr)
{
if (!IS_NULL_graph (gr)) {
DECL_SPEC acc = DEREF_dspec (graph_access (gr));
if (acc & dspec_mutable) {
/* Virtual base class */
GRAPH gt = DEREF_graph (graph_top (gr));
CLASS_TYPE ct = DEREF_ctype (graph_head (gt));
CLASS_TYPE cs = DEREF_ctype (graph_head (gr));
ERROR err = ERR_class_derived_virt (cs, ct);
return (err);
}
}
return (NULL_err);
}
/*
* FIND A UNIQUE BASE CLASS
*
* This routine returns the graph representing the unique direct base
* class of ct if this exists. Otherwise it adds an error to err and
* returns the null graph.
*/
GRAPH
uniq_base_class(CLASS_TYPE ct, ERROR *err)
{
GRAPH gr = DEREF_graph (ctype_base (ct));
LIST (GRAPH) br = DEREF_list (graph_tails (gr));
if (LENGTH_list (br) == 1) {
GRAPH gs = DEREF_graph (HEAD_list (br));
return (gs);
}
add_error (err, ERR_class_base_init_uniq (ct));
return (NULL_graph);
}
/*
* FIND THE SHORTEST ROUTE TO A BASE CLASS
*
* There may be several path for accessing a virtual base class. This
* routine finds the shortest such path in terms of virtual base
* indirections for reaching the base gr.
*/
GRAPH
min_base_class(GRAPH gr)
{
GRAPH gs = gr;
unsigned n = (unsigned) UINT_MAX;
while (!IS_NULL_graph (gs)) {
GRAPH gt = gs;
unsigned m = 0;
while (!IS_NULL_graph (gt)) {
/* Find base class depth */
DECL_SPEC acc = DEREF_dspec (graph_access (gt));
if (acc & dspec_virtual) m++;
gt = DEREF_graph (graph_up (gt));
}
if (m < n) {
/* Shortest path so far */
gr = gs;
n = m;
}
gs = DEREF_graph (graph_equal (gs));
}
return (gr);
}
/*
* FIND A DIRECT OR VIRTUAL BASE CLASS
*
* This routine checks whether the class cs denotes a direct or a virtual
* base class of ct. If so it returns the corresponding graph. If it is
* both then an error is added to err and the direct base is returned.
* Otherwise an error is added to err and the null graph is returned.
*/
GRAPH
direct_base_class(CLASS_TYPE ct, CLASS_TYPE cs, ERROR *err)
{
GRAPH gt = NULL_graph;
GRAPH gr = DEREF_graph (ctype_base (ct));
LIST (GRAPH) bd = DEREF_list (graph_tails (gr));
LIST (GRAPH) bv = DEREF_list (ctype_vbase (ct));
/* Scan through direct base classes */
while (!IS_NULL_list (bd)) {
GRAPH gs = DEREF_graph (HEAD_list (bd));
CLASS_TYPE cr = DEREF_ctype (graph_head (gs));
if (eq_ctype (cr, cs)) {
gt = gs;
break;
}
bd = TAIL_list (bd);
}
/* Scan through virtual base classes */
while (!IS_NULL_list (bv)) {
GRAPH gs = DEREF_graph (HEAD_list (bv));
CLASS_TYPE cr = DEREF_ctype (graph_head (gs));
if (eq_ctype (cr, cs)) {
if (IS_NULL_graph (gt)) {
gt = gs;
} else if (!eq_graph (gt, gs)) {
/* Both a direct and a virtual base */
add_error (err, ERR_class_base_init_ambig (cs));
}
break;
}
bv = TAIL_list (bv);
}
/* Return result */
if (IS_NULL_graph (gt)) {
/* Neither a direct nor a virtual base */
add_error (err, ERR_class_base_init_base (cs));
}
return (gt);
}
/*
* FIND AN AMBIGUOUS BASE CLASS
*
* This routine searches the graph gr for any ambiguous base class,
* returning the corresponding node if it is found, or the null graph
* otherwise.
*/
GRAPH
find_ambig_base(GRAPH gr)
{
DECL_SPEC acc = DEREF_dspec (graph_access (gr));
if (acc & dspec_alias) {
/* Ambiguous classes are marked */
return (gr);
}
if (acc & dspec_defn) {
/* Only search the first occurrence of each class */
LIST (GRAPH) br = DEREF_list (graph_tails (gr));
while (!IS_NULL_list (br)) {
GRAPH gs = DEREF_graph (HEAD_list (br));
gs = find_ambig_base (gs);
if (!IS_NULL_graph (gs)) return (gs);
br = TAIL_list (br);
}
}
return (NULL_graph);
}
/*
* EXPAND A BASE CLASS GRAPH
*
* This routine expands any token definitions in the base class graph
* gr. The null graph is returned if the result is not a valid base
* class.
*/
GRAPH
expand_graph(GRAPH gr, int rec)
{
if (!IS_NULL_graph (gr)) {
TYPE t = NULL_type;
GRAPH gt = DEREF_graph (graph_top (gr));
CLASS_TYPE ct = DEREF_ctype (graph_head (gt));
CLASS_TYPE cs = DEREF_ctype (graph_head (gr));
ct = expand_ctype (ct, rec, &t);
if (IS_NULL_ctype (ct)) return (NULL_graph);
cs = expand_ctype (cs, rec, &t);
if (IS_NULL_ctype (cs)) return (NULL_graph);
gr = find_base_class (ct, cs, 1);
}
return (gr);
}
/*
* CONSTRUCT AN INHERITED IDENTIFIER
*
* This routine constructs an identifier representing the inherited value
* of id in the namespace ns. gr and off give the corresponding base class
* if appropriate.
*/
static IDENTIFIER
inherit_id(NAMESPACE ns, GRAPH gr, OFFSET off, IDENTIFIER id, int prev)
{
HASHID nm;
DECL_SPEC ds;
unsigned tag;
NAMESPACE pns;
IDENTIFIER cid;
LIST (IDENTIFIER) p = NULL_list (IDENTIFIER);
/* Check for trivial inheritance */
if (IS_NULL_id (id)) return (NULL_id);
pns = DEREF_nspace (id_parent (id));
if (EQ_nspace (pns, ns)) return (id);
/* Check for previous declarations */
tag = TAG_id (id);
nm = DEREF_hashid (id_name (id));
if (!IS_NULL_graph (gr) && !prev) {
LIST (IDENTIFIER) q;
p = DEREF_list (graph_member (gr));
q = p;
while (!IS_NULL_list (q)) {
IDENTIFIER qid = DEREF_id (HEAD_list (q));
HASHID qnm = DEREF_hashid (id_name (qid));
if (EQ_hashid (qnm, nm) && tag == TAG_id (qid)) {
/* Already have inherited member */
return (qid);
}
q = TAIL_list (q);
}
}
/* Calculate inherited access */
ds = DEREF_dspec (id_storage (id));
if (!IS_NULL_graph (gr)) {
int adjust = 1;
if (tag == id_class_name_tag) {
CLASS_TYPE cs = DEREF_ctype (graph_head (gr));
TYPE t = DEREF_type (id_class_name_defn (id));
if (IS_type_compound (t)) {
CLASS_TYPE ct = DEREF_ctype (type_compound_defn (t));
if (eq_ctype (cs, ct)) {
/* This is an injected base class name */
adjust = 0;
}
}
}
if (adjust) {
DECL_SPEC acc = DEREF_dspec (graph_access (gr));
DECL_SPEC acc2 = unshadow_access (acc);
acc = join_access (ds, acc);
acc2 = join_access (ds, acc2);
ds &= ~(dspec_access | dspec_access2);
ds |= acc;
ds |= shadow_access (acc2);
}
}
ds |= dspec_inherit;
ds &= ~dspec_alias;
/* Copy the identifier */
cid = copy_id (id, 0);
if (!EQ_id (cid, id)) {
switch (tag) {
case id_function_tag :
case id_mem_func_tag :
case id_stat_mem_func_tag : {
/* Functions */
IDENTIFIER over;
over = DEREF_id (id_function_etc_over (cid));
if (!IS_NULL_id (over)) {
/* Overloaded functions */
over = inherit_id (ns, gr, off, over, 1);
COPY_id (id_function_etc_over (cid), over);
}
break;
}
case id_member_tag : {
/* Members */
if (!IS_NULL_graph (gr)) {
/* Add base class offset */
OFFSET off1 = DEREF_off (id_member_off (cid));
MAKE_off_plus (off, off1, off1);
COPY_off (id_member_off (cid), off1);
COPY_graph (id_member_base (cid), gr);
}
break;
}
}
COPY_nspace (id_parent (cid), ns);
COPY_dspec (id_storage (cid), ds);
}
/* Add to list of inherited members */
if (!IS_NULL_graph (gr) && !prev) {
CONS_id (cid, p, p);
COPY_list (graph_member (gr), p);
}
return (cid);
}
/*
* TYPE REPRESENTING A SET OF MEMBER LOOK-UPS
*
* This structure is used to represent a list of class member look-ups.
* Each look-up consists of a member identifier plus a base class graph
* indicating from which base class it is inherited.
*/
typedef struct {
LIST (IDENTIFIER) ids;
LIST (GRAPH) bases;
} MEMBER_LOOKUP;
/*
* RESOLVE AMBIGUOUS MEMBER LOOK-UP
*
* This routine resolves the potentially ambiguous member look-up for the
* member named nm in the namespace ns given by mems. If form is not
* the null type then it gives a list of template arguments to be
* applied to nm. The routine also destroys mems.
*/
static IDENTIFIER
resolve_member(NAMESPACE ns, HASHID nm, MEMBER_LOOKUP *mems, TYPE form,
int dominate)
{
IDENTIFIER id = NULL_id;
LIST (IDENTIFIER) ambig = NULL_list (IDENTIFIER);
LIST (IDENTIFIER) pids = mems->ids;
LIST (GRAPH) grs = mems->bases;
/* Check for empty lists */
if (IS_NULL_list (pids)) return (NULL_id);
/* Scan through various look-ups */
while (!IS_NULL_list (pids)) {
IDENTIFIER pid = DEREF_id (HEAD_list (pids));
GRAPH gp = DEREF_graph (HEAD_list (grs));
if (!IS_NULL_id (pid)) {
int ok = 1;
int dep = IS_id_member (pid);
IDENTIFIER pal = DEREF_id (id_alias (pid));
DECL_SPEC pds = DEREF_dspec (id_storage (pid));
DECL_SPEC gds = DEREF_dspec (graph_access (gp));
DECL_SPEC acc = join_access (pds, gds);
OFFSET off = DEREF_off (graph_off (gp));
/* Mark equivalent look-ups */
LIST (IDENTIFIER) qids = TAIL_list (pids);
LIST (GRAPH) hrs = TAIL_list (grs);
while (!IS_NULL_list (qids)) {
IDENTIFIER qid = DEREF_id (HEAD_list (qids));
IDENTIFIER qal = DEREF_id (id_alias (qid));
GRAPH hp = DEREF_graph (HEAD_list (hrs));
if (EQ_id (pal, qal)) {
if (!dep || eq_graph (gp, hp)) {
/* This represents the same member */
DECL_SPEC acc2;
COPY_id (HEAD_list (qids), NULL_id);
pds = DEREF_dspec (id_storage (qid));
gds = DEREF_dspec (graph_access (hp));
acc2 = join_access (pds, gds);
if (acc2 < acc) {
/* More accessible by this route */
gp = hp;
acc = acc2;
}
}
} else {
/* Check for dominating bases */
if (is_subgraph (gp, hp)) {
if (dominate) {
COPY_id (HEAD_list (qids), NULL_id);
}
} else if (is_subgraph (hp, gp)) {
ok = 0;
break;
}
}
qids = TAIL_list (qids);
hrs = TAIL_list (hrs);
}
/* Record look-up */
if (ok) {
int prev = 0;
if (!IS_NULL_type (form) && IS_type_token (form)) {
/* Allow for templates */
LIST (TOKEN) args;
args = DEREF_list (type_token_args (form));
pid = apply_template (pid, args, 0, 0);
prev = 1;
}
pid = inherit_id (ns, gp, off, pid, prev);
if (IS_NULL_id (id)) {
id = pid;
} else {
CONS_id (pid, ambig, ambig);
}
}
}
pids = TAIL_list (pids);
grs = TAIL_list (grs);
}
/* Construct the result */
if (!IS_NULL_list (ambig)) {
DECL_SPEC ds;
CONS_id (id, ambig, ambig);
ds = find_ambig_dspec (ambig);
ds |= dspec_inherit;
MAKE_id_ambig (nm, ds, ns, crt_loc, ambig, 1, id);
}
/* Destroy lists */
DESTROY_list (mems->ids, SIZE_id);
DESTROY_list (mems->bases, SIZE_graph);
return (id);
}
/*
* SEARCH A BASE CLASS GRAPH FOR AN IDENTIFIER
*
* This routine searches the graph gr for all visible members named nm,
* storing the result in mems. If cs is not the null class then only
* base classes lying below such a class are searched. If type is nonzero
* then only type members are found. depth gives a depth count and is
* used because the top level is searched elsewhere.
*/
static void
search_base(GRAPH gr, HASHID nm, MEMBER_LOOKUP *mems, CLASS_TYPE cs, int type,
int depth)
{
LIST (GRAPH) br;
LIST (GRAPH) gres = NULL_list (GRAPH);
LIST (IDENTIFIER) res = NULL_list (IDENTIFIER);
DECL_SPEC acc = DEREF_dspec (graph_access (gr));
if (!(acc & dspec_done)) {
/* Not yet in scope */
mems->ids = res;
mems->bases = gres;
return;
}
/* Search derived class */
if (depth) {
CLASS_TYPE ct = DEREF_ctype (graph_head (gr));
if (IS_NULL_ctype (cs) || eq_ctype (ct, cs)) {
NAMESPACE ns = DEREF_nspace (ctype_member (ct));
IDENTIFIER id = search_id (ns, nm, 0, type);
if (!IS_NULL_id (id)) {
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if ((ds & dspec_inherit) && !(ds & dspec_alias)) {
/* Ignore inherited members */
/* EMPTY */
} else {
CONS_id (id, res, res);
CONS_graph (gr, gres, gres);
mems->ids = res;
mems->bases = gres;
return;
}
}
cs = NULL_ctype;
}
}
/* Search base classes */
br = DEREF_list (graph_tails (gr));
while (!IS_NULL_list (br)) {
MEMBER_LOOKUP bmems;
GRAPH gs = DEREF_graph (HEAD_list (br));
search_base (gs, nm, &bmems, cs, type, depth + 1);
if (!IS_NULL_list (bmems.ids)) {
res = APPEND_list (res, bmems.ids);
gres = APPEND_list (gres, bmems.bases);
}
br = TAIL_list (br);
}
mems->ids = res;
mems->bases = gres;
return;
}
/*
* SEARCH BASE CLASSES FOR A CLASS MEMBER
*
* This routine searches the base classes of the class namespace ns
* for a member named nm.
*/
IDENTIFIER
search_base_field(NAMESPACE ns, HASHID nm, int type, int dominate)
{
GRAPH gr;
IDENTIFIER id;
CLASS_TYPE ct;
MEMBER_LOOKUP mems;
IDENTIFIER cid = DEREF_id (nspace_name (ns));
TYPE t = DEREF_type (id_class_name_etc_defn (cid));
while (IS_type_templ (t)) {
t = DEREF_type (type_templ_defn (t));
}
ct = DEREF_ctype (type_compound_defn (t));
gr = DEREF_graph (ctype_base (ct));
search_base (gr, nm, &mems, NULL_ctype, type, 0);
id = resolve_member (ns, nm, &mems, NULL_type, dominate);
return (id);
}
/*
* SEARCH FOR A CLASS MEMBER
*
* This routine searches the class namespace ns and its base classes
* for a member named nm, which is returned if found. Otherwise if
* create is true then a dummy identifier is created and returned.
* Otherwise the null identifier is returned. If type is true then only
* type and namespace names are considered.
*/
IDENTIFIER
search_field(NAMESPACE ns, HASHID nm, int create, int type)
{
/* Search main class */
IDENTIFIER id;
MEMBER mem = search_member (ns, nm, 1);
if (type) {
id = type_member (mem, type);
} else {
id = DEREF_id (member_id (mem));
}
/* Search base classes */
if (IS_NULL_id (id)) {
id = search_base_field (ns, nm, type, 1);
if (!IS_NULL_id (id)) {
if (type) {
set_type_member (mem, id);
} else {
set_member (mem, id);
}
if (IS_hashid_destr (nm)) {
/* Check destructor names */
report (crt_loc, ERR_class_dtor_inherit (nm, ns));
}
} else {
if (create) {
/* Create dummy identifier if necessary */
MAKE_id_undef (nm, dspec_none, ns, crt_loc, id);
}
if (IS_hashid_destr (nm)) {
/* Check destructor names */
TYPE s = DEREF_type (hashid_destr_type (nm));
IDENTIFIER tid = DEREF_id (nspace_name (ns));
TYPE t = DEREF_type (id_class_name_etc_defn (tid));
if (!eq_type (s, t)) {
report (crt_loc, ERR_expr_pseudo_type (t, s));
}
}
}
}
return (id);
}
/*
* IS AN IDENTIFIER A MEMBER OF A CLASS?
*
* This routine checks whether the identifier id is a member of the class
* namespace ns or any base class of ns. It returns the corresponding
* base class graph, or the null graph if id is not such a member.
*/
GRAPH
is_subfield(NAMESPACE ns, IDENTIFIER id)
{
CLASS_TYPE ct = namespace_class (ns);
if (!IS_NULL_ctype (ct)) {
NAMESPACE cns = DEREF_nspace (id_parent (id));
if (!IS_NULL_nspace (cns) && IS_nspace_ctype (cns)) {
CLASS_TYPE cs = namespace_class (cns);
GRAPH gr = find_base_class (ct, cs, 0);
return (gr);
}
}
return (NULL_graph);
}
/*
* SEARCH FOR A SUBFIELD
*
* This routine finds the member of a class corresponding to the member
* id of the base class of ns indicated by gr.
*/
IDENTIFIER
search_subfield(NAMESPACE ns, GRAPH gr, IDENTIFIER id)
{
GRAPH gt = DEREF_graph (graph_top (gr));
if (!EQ_graph (gr, gt)) {
int def = 0;
IDENTIFIER pid;
MEMBER_LOOKUP mems;
TYPE form = find_form (id, &def);
HASHID nm = DEREF_hashid (id_name (id));
DECL_SPEC acc = DEREF_dspec (graph_access (gr));
if (acc & dspec_alias) {
/* Ambiguous base class */
CLASS_TYPE cs = DEREF_ctype (graph_head (gr));
search_base (gt, nm, &mems, cs, 0, 1);
} else {
/* Unambiguous base class */
search_base (gr, nm, &mems, NULL_ctype, 0, 1);
}
pid = resolve_member (ns, nm, &mems, form, 1);
if (!IS_NULL_id (pid)) id = pid;
}
return (id);
}
/*
* PROCESS INHERITANCE FOR A CLASS
*
* This routine is called at the end of a class definition to handle
* any inheritance issues. At present an inherited member is only
* recorded as it is looked up. However the lists of all virtual
* functions and conversion functions on the class need to take account
* of inheritance immediately.
*/
void
inherit_class()
{
CLASS_TYPE ct = crt_class;
NAMESPACE ns = crt_namespace;
GRAPH gr = DEREF_graph (ctype_base (ct));
LIST (GRAPH) bases = DEREF_list (graph_tails (gr));
LIST (IDENTIFIER) pc = DEREF_list (ctype_conv (ct));
/* Scan through base classes */
while (!IS_NULL_list (bases)) {
GRAPH gs = DEREF_graph (HEAD_list (bases));
CLASS_TYPE cs = DEREF_ctype (graph_head (gs));
/* Deal with conversion functions */
LIST (IDENTIFIER) qc = DEREF_list (ctype_conv (cs));
while (!IS_NULL_list (qc)) {
/* Deal with each inherited conversion */
IDENTIFIER qid = DEREF_id (HEAD_list (qc));
HASHID qnm = DEREF_hashid (id_name (qid));
LIST (IDENTIFIER) r = pc;
while (!IS_NULL_list (r)) {
/* Check whether already declared */
IDENTIFIER rid = DEREF_id (HEAD_list (r));
HASHID rnm = DEREF_hashid (id_name (rid));
if (EQ_hashid (rnm, qnm)) break;
r = TAIL_list (r);
}
if (IS_NULL_list (r)) {
/* Construct inherited member */
qid = search_field (ns, qnm, 0, 0);
if (!IS_NULL_id (qid)) {
if (IS_id_ambig (qid)) {
LIST (IDENTIFIER) qids;
qids = DEREF_list (id_ambig_ids (qid));
while (!IS_NULL_list (qids)) {
qid = DEREF_id (HEAD_list (qids));
CONS_id (qid, pc, pc);
qids = TAIL_list (qids);
}
} else {
CONS_id (qid, pc, pc);
}
}
}
qc = TAIL_list (qc);
}
/* Process next base class */
bases = TAIL_list (bases);
}
/* Record modified information */
COPY_list (ctype_conv (ct), pc);
/* Deal with virtual functions */
end_virtual (ct);
return;
}