/*
* Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
* 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/merge.c,v 1.5 2004/08/14 15:15:36 bp Exp $
*/
#include "config.h"
#include "producer.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "graph_ops.h"
#include "id_ops.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "access.h"
#include "chktype.h"
#include "class.h"
#include "declare.h"
#include "initialise.h"
#include "merge.h"
#include "namespace.h"
#include "redeclare.h"
#include "token.h"
/*
* NAME MERGING FLAG
*
* This flag is set to true to indicate that name merging is taking
* place.
*/
int force_merge = 0;
/*
* ARE TWO CLASSES LAYOUT COMPATIBLE?
*
* This routine checks whether the classes ct and cs are layout compatible,
* i.e. they have the same member types and accesses.
*/
int
compatible_class(CLASS_TYPE ct, CLASS_TYPE cs)
{
GRAPH gt, gs;
MEMBER mt, ms;
BASE_TYPE kt, ks;
NAMESPACE nt, ns;
LIST (GRAPH) bt, bs;
/* Check for obvious equality */
if (eq_ctype (ct, cs)) return (1);
/* Check class keys */
kt = find_class_key (ct);
ks = find_class_key (cs);
if (!equal_key (kt, ks)) return (0);
/* Check base classes */
gt = DEREF_graph (ctype_base (ct));
gs = DEREF_graph (ctype_base (cs));
bt = DEREF_list (graph_tails (gt));
bs = DEREF_list (graph_tails (gs));
while (!IS_NULL_list (bt) && !IS_NULL_list (bs)) {
GRAPH ht = DEREF_graph (HEAD_list (bt));
GRAPH hs = DEREF_graph (HEAD_list (bs));
CLASS_TYPE pt = DEREF_ctype (graph_head (ht));
CLASS_TYPE ps = DEREF_ctype (graph_head (hs));
DECL_SPEC at = DEREF_dspec (graph_access (ht));
DECL_SPEC as = DEREF_dspec (graph_access (hs));
at &= (dspec_access | dspec_virtual);
as &= (dspec_access | dspec_virtual);
if (at != as) return (0);
if (!compatible_class (pt, ps)) return (0);
bs = TAIL_list (bs);
bt = TAIL_list (bt);
}
if (!EQ_list (bt, bs)) return (0);
/* Check class members */
nt = DEREF_nspace (ctype_member (ct));
mt = DEREF_member (nspace_ctype_first (nt));
mt = next_data_member (mt, 1);
ns = DEREF_nspace (ctype_member (cs));
ms = DEREF_member (nspace_ctype_first (ns));
ms = next_data_member (ms, 1);
while (!IS_NULL_member (mt) && !IS_NULL_member (ms)) {
IDENTIFIER it = DEREF_id (member_id (mt));
IDENTIFIER is = DEREF_id (member_id (ms));
TYPE t = DEREF_type (id_member_type (it));
TYPE s = DEREF_type (id_member_type (is));
DECL_SPEC at = DEREF_dspec (id_storage (it));
DECL_SPEC as = DEREF_dspec (id_storage (is));
at &= dspec_access;
as &= dspec_access;
if (at != as) return (0);
if (!eq_type (t, s)) return (0);
ms = DEREF_member (member_next (ms));
ms = next_data_member (ms, 1);
mt = DEREF_member (member_next (mt));
mt = next_data_member (mt, 1);
}
if (!EQ_member (mt, ms)) return (0);
return (1);
}
/*
* ARE TWO TYPES TO BE MERGED?
*
* This routine checks whether the identifiers tid and sid are to be made
* equal by merge_id. This essentially means that they should be the
* same.
*/
int
merge_type(IDENTIFIER tid, IDENTIFIER sid)
{
while (!EQ_id (tid, sid)) {
unsigned tt, ts;
HASHID tnm, snm;
NAMESPACE nt, ns;
/* Check identifiers */
if (IS_NULL_id (tid)) return (0);
if (IS_NULL_id (sid)) return (0);
tt = TAG_id (tid);
ts = TAG_id (sid);
if (tt != ts) return (0);
/* Check identifier names */
tnm = DEREF_hashid (id_name (tid));
snm = DEREF_hashid (id_name (sid));
if (!EQ_hashid (tnm, snm)) return (0);
/* Check identifier namespaces */
nt = DEREF_nspace (id_parent (tid));
ns = DEREF_nspace (id_parent (sid));
if (EQ_nspace (nt, ns)) return (1);
if (IS_NULL_nspace (nt)) return (0);
if (IS_NULL_nspace (ns)) return (0);
tt = TAG_nspace (nt);
ts = TAG_nspace (ns);
if (tt != ts) return (0);
switch (tt) {
case nspace_global_tag : {
return (1);
}
case nspace_ctype_tag :
case nspace_named_tag :
case nspace_unnamed_tag : {
break;
}
default : {
return (0);
}
}
tid = DEREF_id (nspace_name (nt));
sid = DEREF_id (nspace_name (ns));
}
return (1);
}
/*
* CHECK FOR CONSISTENT REDEFINITION
*
* This routine checks whether the definitions of the objects pid and
* qid and consistent.
*/
static int
consistent_redef(IDENTIFIER pid, IDENTIFIER qid)
{
int ok = 1;
switch (TAG_id (pid)) {
case id_nspace_name_tag : {
/* Merge namespace definitions */
NAMESPACE pns = DEREF_nspace (id_nspace_name_defn (pid));
NAMESPACE qns = DEREF_nspace (id_nspace_name_defn (qid));
if (IS_nspace_named (pns)) {
COPY_nspace (id_nspace_name_defn (pid), qns);
merge_namespaces (qns, pns);
} else {
merge_namespaces (NULL_nspace, pns);
}
break;
}
case id_class_name_tag : {
BASE_TYPE bt, bs;
CLASS_TYPE ct, cs;
NAMESPACE pns, qns;
TYPE t = DEREF_type (id_class_name_defn (pid));
TYPE s = DEREF_type (id_class_name_defn (qid));
while (IS_type_templ (t)) {
t = DEREF_type (type_templ_defn (t));
}
ct = DEREF_ctype (type_compound_defn (t));
bt = find_class_key (ct);
pns = DEREF_nspace (ctype_member (ct));
while (IS_type_templ (s)) {
s = DEREF_type (type_templ_defn (s));
}
cs = DEREF_ctype (type_compound_defn (s));
bs = find_class_key (cs);
qns = DEREF_nspace (ctype_member (cs));
COPY_nspace (ctype_member (ct), qns);
if (!equal_key (bt, bs)) {
/* Inconsistent key */
PTR (LOCATION) loc = id_loc (qid);
ERROR err = ERR_dcl_type_elab_bad (bt, bs, qid, loc);
report (crt_loc, err);
}
merge_namespaces (qns, pns);
break;
}
case id_variable_tag :
case id_stat_member_tag : {
ok = 0;
break;
}
}
return (ok);
}
/*
* EXPAND AN IDENTIFIER
*
* This routine expands the identifier id read from a spec input file.
* This is necessary because id may still contain pending identifiers.
*/
static void
expand_id(IDENTIFIER id)
{
DEREF_loc (id_loc (id), crt_loc);
decl_loc = crt_loc;
return;
}
/*
* DECLARATION SPECIFIER MASK
*
* The macro dspec_mask is a mask which gives those declaration specifiers
* which should be the same for all declarations.
*/
#define dspec_mem_func\
(dspec_virtual | dspec_pure | dspec_explicit | dspec_implicit)
#define dspec_mask\
(dspec_storage | dspec_access | dspec_language | dspec_mem_func)
/*
* MERGE AN IDENTIFIER INTO A NAMESPACE
*
* This routine merges the identifier id read from a spec input file
* into the namespace ns.
*/
static void
merge_id(NAMESPACE ns, IDENTIFIER id)
{
int type;
HASHID nm;
MEMBER mem;
IDENTIFIER pid;
/* Allow for overloaded functions */
if (IS_id_function_etc (id)) {
IDENTIFIER over = DEREF_id (id_function_etc_over (id));
if (!IS_NULL_id (over)) merge_id (ns, over);
}
/* Find previous declaration */
expand_id (id);
if (IS_NULL_nspace (ns)) return;
nm = DEREF_hashid (id_name (id));
mem = search_member (ns, nm, 1);
if (is_tagged_type (id)) {
pid = type_member (mem, 3);
type = 1;
} else {
pid = DEREF_id (member_id (mem));
type = 0;
}
/* Check previous declaration */
if (!IS_NULL_id (pid)) {
unsigned tag = TAG_id (id);
unsigned ptag = TAG_id (pid);
if (tag == id_stat_mem_func_tag) tag = id_mem_func_tag;
if (ptag == id_stat_mem_func_tag) ptag = id_mem_func_tag;
if (tag == ptag) {
int ok = 1;
DECL_SPEC ds = DEREF_dspec (id_storage (id));
DECL_SPEC pds = DEREF_dspec (id_storage (pid));
PTR (TYPE) pt = NULL_ptr (TYPE);
PTR (TYPE) ps = NULL_ptr (TYPE);
switch (tag) {
case id_class_name_tag :
case id_enum_name_tag :
case id_class_alias_tag :
case id_enum_alias_tag :
case id_type_alias_tag : {
pt = id_class_name_etc_defn (id);
ps = id_class_name_etc_defn (pid);
break;
}
case id_variable_tag :
case id_parameter_tag :
case id_stat_member_tag : {
pt = id_variable_etc_type (id);
ps = id_variable_etc_type (pid);
break;
}
#if 0
case id_function_tag :
case id_mem_func_tag :
case id_stat_mem_func_tag : {
/* NOT YET IMPLEMENTED - function overloading */
pt = id_function_etc_type (id);
ps = id_function_etc_type (pid);
break;
}
#endif
case id_member_tag : {
pt = id_member_type (id);
ps = id_member_type (pid);
break;
}
case id_enumerator_tag : {
pt = id_enumerator_etype (id);
ps = id_enumerator_etype (pid);
break;
}
}
/* Check type compatibility */
if (!IS_NULL_ptr (pt) && !IS_NULL_ptr (ps)) {
ERROR err = NULL_err;
TYPE t = DEREF_type (pt);
TYPE s = DEREF_type (ps);
t = expand_type (t, 1);
s = expand_type (s, 1);
s = check_compatible (s, t, 0, &err, 1);
if (!IS_NULL_err (err)) {
/* Incompatible declaration */
PTR (LOCATION) ploc = id_loc (pid);
ERROR err2 = ERR_basic_link_decl_type (pid, ploc);
err = concat_error (err, err2);
report (crt_loc, err);
ok = 0;
} else {
COPY_type (pt, t);
COPY_type (ps, s);
}
}
/* Check declaration specifiers */
if (ok && (ds & dspec_mask) != (pds & dspec_mask)) {
PTR (LOCATION) ploc = id_loc (pid);
DECL_SPEC st = (ds & dspec_storage);
DECL_SPEC pst = (pds & dspec_storage);
if (st != pst) {
/* Inconsistent linkage */
ERROR err;
if (pst & dspec_static) {
err = ERR_dcl_stc_internal (pid, ploc);
} else {
err = ERR_dcl_stc_external (pid, ploc);
}
report (crt_loc, err);
}
st = (ds & dspec_access);
pst = (pds & dspec_access);
if (st != pst) {
/* Adjust access */
adjust_access (pid, st, 0);
}
st = (ds & dspec_language);
pst = (pds & dspec_language);
if (st != pst) {
/* Inconsistent language */
string lang = linkage_string (pst, cv_none);
ERROR err = ERR_dcl_link_lang (pid, lang, ploc);
report (crt_loc, err);
}
st = (ds & dspec_mem_func);
pst = (pds & dspec_mem_func);
if (st != pst) {
/* NOT YET IMPLEMENTED */
/* EMPTY */
}
}
/* Check for multiple definitions */
if (pds & dspec_defn) {
if (ds & dspec_defn) {
if (!consistent_redef (id, pid) && ok) {
PTR (LOCATION) ploc = id_loc (pid);
report (crt_loc, ERR_basic_odr_def (pid, ploc));
}
} else {
id = pid;
}
}
} else {
/* Redeclared as different type of object */
PTR (LOCATION) ploc = id_loc (pid);
report (crt_loc, ERR_basic_odr_diff (pid, ploc));
}
}
/* Set member */
COPY_nspace (id_parent (id), ns);
if (type) {
set_type_member (mem, id);
} else {
set_member (mem, id);
}
return;
}
/*
* MERGE TWO NAMESPACES
*
* This routine merges the namespace ns with the additional members
* read from a spec input file given by pns.
*/
void
merge_namespaces(NAMESPACE ns, NAMESPACE pns)
{
MEMBER mem = DEREF_member (nspace_named_etc_first (pns));
LIST (IDENTIFIER) ids = DEREF_list (nspace_named_etc_extra (pns));
force_merge++;
while (!IS_NULL_member (mem)) {
IDENTIFIER id = DEREF_id (member_id (mem));
IDENTIFIER alt = DEREF_id (member_alt (mem));
if (!IS_NULL_id (id)) {
merge_id (ns, id);
}
if (!IS_NULL_id (alt) && !EQ_id (id, alt)) {
merge_id (ns, alt);
}
mem = DEREF_member (member_next (mem));
}
while (!IS_NULL_list (ids)) {
IDENTIFIER id = DEREF_id (HEAD_list (ids));
expand_id (id);
ids = TAIL_list (ids);
}
force_merge--;
return;
}
syntax highlighted by Code2HTML, v. 0.9.1