/*
* 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/redeclare.c,v 1.10 2005/11/07 18:42:37 stefanf Exp $
*/
#include "config.h"
#include "producer.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "err_ops.h"
#include "exp_ops.h"
#include "graph_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "member_ops.h"
#include "off_ops.h"
#include "nspace_ops.h"
#include "str_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "access.h"
#include "basetype.h"
#include "char.h"
#include "check.h"
#include "chktype.h"
#include "class.h"
#include "compile.h"
#include "constant.h"
#include "copy.h"
#include "declare.h"
#include "derive.h"
#include "dump.h"
#include "file.h"
#include "function.h"
#include "identifier.h"
#include "instance.h"
#include "literal.h"
#include "namespace.h"
#include "option.h"
#include "overload.h"
#include "predict.h"
#include "redeclare.h"
#include "syntax.h"
#include "template.h"
#include "tokdef.h"
#include "token.h"
#include "ustring.h"
/*
* CURRENT LINKAGE SPECIFIER
*
* The current linkage specifier is handled by means of this global
* variable. The default, of no linkage being given, is interpreted
* according to the source language.
*/
DECL_SPEC crt_linkage = dspec_none;
DECL_SPEC new_linkage = dspec_none;
/*
* FIND A LINKAGE SPECIFIER
*
* This routine translates the string literal expression e into a linkage
* specifier. The only recognised strings are "C" and "C++".
*/
DECL_SPEC
find_linkage(EXP e)
{
STRING s = DEREF_str (exp_string_lit_str (e));
unsigned kind = DEREF_unsigned (str_simple_kind (s));
/* Can only occur in namespace scope */
if (in_function_defn || in_class_defn) {
report (crt_loc, ERR_dcl_link_scope ());
}
/* Check the linkage string */
if (kind == STRING_NONE) {
char *t = strlit (DEREF_string (str_simple_text (s)));
unsigned long len = DEREF_ulong (str_simple_len (s));
if (len == 1 && streq (t, "C")) {
return (dspec_c);
}
if (len == 3 && streq (t, "C++")) {
return (dspec_cpp);
}
}
/* Report unknown strings */
report (crt_loc, ERR_dcl_link_unknown (s));
return (crt_linkage);
}
/*
* FIND A LINKAGE STRING
*
* This routine returns the string corresponding to the linkage specifiers
* given by ds and cv.
*/
string
linkage_string(DECL_SPEC ds, CV_SPEC cv)
{
const char *str;
if ((ds & dspec_c) || (cv & cv_c)) {
str = "C";
} else {
str = "C++";
}
return (ustrlit (str));
}
/*
* ADJUST DECLARATION SPECIFIER FOR LANGUAGE
*
* This routine adds the current language specifier to the declaration
* specifier ds. mem is true for a member declaration (which always
* has C++ linkage). ds may contain a language specifier from a
* previous declaration, otherwise it is deduced from crt_linkage.
*/
DECL_SPEC
adjust_linkage(DECL_SPEC ds, int mem)
{
DECL_SPEC rs = (ds & ~dspec_language);
if (mem) {
/* Members have C++ linkage */
rs |= dspec_cpp;
} else if (rs & dspec_linkage) {
/* Only applies to objects with linkage */
DECL_SPEC ln = (ds | crt_linkage);
if (ln & dspec_c) {
rs |= dspec_c;
} else {
rs |= dspec_cpp;
}
}
return (rs);
}
/*
* CHECK C LINKAGE DECLARATIONS
*
* This routine checks the identifier id declared with C linkage. Objects
* with C linkage in different namespaces are actually the same.
*/
void
c_linkage(IDENTIFIER id, int def)
{
TYPE t = NULL_type;
unsigned tag = TAG_id (id);
if (tag == id_function_tag) {
/* Template functions can't have C linkage */
t = DEREF_type (id_function_type (id));
if (IS_type_templ (t)) {
report (decl_loc, ERR_temp_decl_linkage ());
}
} else if (tag == id_variable_tag) {
t = DEREF_type (id_variable_type (id));
}
if (!IS_NULL_type (t)) {
NAMESPACE ns = c_namespace;
if (!IS_NULL_nspace (ns)) {
HASHID nm = DEREF_hashid (id_name (id));
MEMBER mem = search_member (ns, nm, 1);
IDENTIFIER pid = DEREF_id (member_id (mem));
if (!IS_NULL_id (pid) && !EQ_id (id, pid)) {
NAMESPACE cns = DEREF_nspace (id_parent (id));
NAMESPACE pns = DEREF_nspace (id_parent (pid));
if (!EQ_nspace (cns, pns)) {
DECL_SPEC cl = crt_linkage;
QUALIFIER cq = crt_id_qualifier;
DECL_SPEC ds = DEREF_dspec (id_storage (id));
crt_linkage = (ds & dspec_language);
crt_id_qualifier = qual_none;
pid = redecl_id (ds, t, pid, 3, -1);
if (!IS_NULL_id (pid)) {
/* Set up alias */
if (IS_id_function (pid)) {
TYPE s = DEREF_type (id_function_type (pid));
s = redecl_func_type (pid, s, t, def, 0);
COPY_type (id_function_type (pid), s);
}
ds |= dspec_alias;
COPY_dspec (id_storage (id), ds);
pid = DEREF_id (id_alias (pid));
COPY_id (id_alias (id), pid);
if (do_dump) dump_alias (id, pid, &decl_loc);
id = pid;
}
crt_id_qualifier = cq;
crt_linkage = cl;
}
}
COPY_id (member_id (mem), id);
}
}
return;
}
/*
* FIND A PREVIOUS DECLARATION
*
* If a declaration in block scope is declared extern then it has external
* linkage unless the declaration matches a visible declaration of namespace
* scope. This routine finds such a declaration for the identifier id
* of type t.
*/
IDENTIFIER
find_previous(TYPE t, IDENTIFIER id)
{
if (crt_id_qualifier == qual_none) {
NAMESPACE ns = nonblock_namespace;
HASHID nm = DEREF_hashid (id_name (id));
IDENTIFIER pid = find_extern_id (nm, ns, 0);
if (!IS_NULL_id (pid)) {
TYPE s;
DECL_SPEC st;
switch (TAG_id (pid)) {
case id_variable_tag : {
/* Variables may be redeclared */
s = DEREF_type (id_variable_type (pid));
break;
}
case id_function_tag : {
/* Functions may be redeclared */
#if LANGUAGE_CPP
int eq = 0;
LIST (IDENTIFIER) pids = NULL_list (IDENTIFIER);
pid = resolve_func (pid, t, 0, 0, pids, &eq);
if (IS_NULL_id (pid)) return (NULL_id);
if (!IS_id_function (pid)) return (NULL_id);
#endif
s = DEREF_type (id_function_type (pid));
break;
}
default : {
/* Nothing else can be redeclared */
return (NULL_id);
}
}
st = DEREF_dspec (id_storage (pid));
if (st & dspec_linkage) {
/* Previous declaration must have linkage */
s = type_composite (s, t, 0, 0, KILL_err, 0);
if (!IS_NULL_type (s)) return (pid);
}
}
}
return (NULL_id);
}
/*
* UNIFY AN EXTERNAL IDENTIFIER
*
* This routine checks the identifier id, which is a variable declared
* extern in a block, against conflicts with the namespace ns into
* which it is injected. p gives all the other extern block identifiers
* declared before id. The routine returns any previous identifier.
*/
IDENTIFIER
unify_extern(IDENTIFIER id, TYPE t, NAMESPACE ns, LIST (IDENTIFIER) p)
{
IDENTIFIER pid = NULL_id;
HASHID nm = DEREF_hashid (id_name (id));
LIST (IDENTIFIER) pids = NULL_list (IDENTIFIER);
while (!IS_NULL_list (p)) {
/* Check other block externs */
IDENTIFIER bid = DEREF_id (HEAD_list (p));
HASHID bnm = DEREF_hashid (id_name (bid));
if (EQ_hashid (bnm, nm)) {
bid = DEREF_id (id_alias (bid));
CONS_id (bid, pids, pids);
}
p = TAIL_list (p);
}
if (!IS_NULL_nspace (ns)) {
/* Check actual namespace */
MEMBER mem = search_member (ns, nm, 0);
if (!IS_NULL_member (mem)) {
IDENTIFIER bid = DEREF_id (member_id (mem));
if (!IS_NULL_id (bid)) {
bid = DEREF_id (id_alias (bid));
CONS_id (bid, pids, pids);
}
}
}
if (!IS_NULL_list (pids)) {
/* Match found */
if (IS_NULL_type (t)) {
if (IS_NULL_list (TAIL_list (pids))) {
pid = DEREF_id (HEAD_list (pids));
DESTROY_list (pids, SIZE_id);
} else {
DECL_SPEC ds;
pids = REVERSE_list (pids);
ds = find_ambig_dspec (pids);
MAKE_id_ambig (nm, ds, ns, crt_loc, pids, 1, pid);
}
} else {
LIST (IDENTIFIER) qids;
DECL_SPEC cl = crt_linkage;
QUALIFIER cq = crt_id_qualifier;
DECL_SPEC ds = DEREF_dspec (id_storage (id));
crt_linkage = (ds & dspec_language);
crt_id_qualifier = qual_none;
DEREF_loc (id_loc (id), decl_loc);
pids = REVERSE_list (pids);
qids = pids;
while (!IS_NULL_list (qids)) {
IDENTIFIER qid = DEREF_id (HEAD_list (qids));
qid = DEREF_id (id_alias (qid));
if (IS_type_func (t)) {
/* Check function redeclaration */
IDENTIFIER over = NULL_id;
unsigned tag = TAG_id (id);
qid = redecl_func (ds, t, qid, tag, &over, -1);
} else {
/* Check variable redeclaration */
qid = redecl_id (ds, t, qid, 0, -1);
}
if (!IS_NULL_id (qid)) pid = qid;
qids = TAIL_list (qids);
}
DESTROY_list (pids, SIZE_id);
crt_id_qualifier = cq;
crt_linkage = cl;
}
}
return (pid);
}
/*
* UNIFY A BLOCK DECLARATION WITH A PREVIOUS DECLARATION
*
* This routine is used to unify the external block declaration id of
* type t with its previous declaration pid (as returned by find_previous).
* def is true is id is a function definition. The routine returns id.
* If there is no previous declaration then one is created and added to
* the extra identifier list of the enclosing non-block namespace.
*/
IDENTIFIER
unify_previous(IDENTIFIER id, TYPE t, IDENTIFIER pid, int def)
{
/* Unify external linkage */
if (IS_NULL_id (pid)) {
LIST (IDENTIFIER) p;
NAMESPACE ns = nonblock_namespace;
p = DEREF_list (nspace_named_etc_extra (ns));
pid = unify_extern (id, t, ns, p);
if (IS_NULL_id (pid)) {
if (!is_templ_depend (t)) {
/* Declare new external object */
DECL_SPEC ds;
pid = copy_id (id, 0);
ds = DEREF_dspec (id_storage (pid));
if (!(ds & dspec_linkage)) ds |= dspec_extern;
ds &= ~dspec_alias;
COPY_dspec (id_storage (pid), ds);
COPY_nspace (id_parent (pid), ns);
COPY_id (id_alias (pid), pid);
CONS_id (pid, p, p);
COPY_list (nspace_named_etc_extra (ns), p);
}
/* Check object type */
if (!is_global_type (t)) {
report (crt_loc, ERR_basic_link_none (t, id));
}
}
}
/* Alias id to be pid */
if (!IS_NULL_id (pid)) {
if (IS_id_function_etc (pid)) {
TYPE s = DEREF_type (id_function_etc_type (pid));
s = redecl_func_type (pid, s, t, def, 0);
COPY_type (id_function_etc_type (pid), s);
}
pid = DEREF_id (id_alias (pid));
COPY_id (id_alias (id), pid);
}
return (id);
}
/*
* UNIFY A DECLARATION WITH A PREVIOUS BLOCK DECLARATION
*
* This routine is used to unify the external declaration id of type
* t with any previous external block declaration of the same object.
* def is true if id is a function definition. The routine returns id.
*/
IDENTIFIER
unify_subsequent(IDENTIFIER id, TYPE t, int def)
{
NAMESPACE ns = DEREF_nspace (id_parent (id));
if (IS_nspace_named_etc (ns)) {
LIST (IDENTIFIER) p;
p = DEREF_list (nspace_named_etc_extra (ns));
if (!IS_NULL_list (p)) {
IDENTIFIER pid = unify_extern (id, t, NULL_nspace, p);
if (!IS_NULL_id (pid)) {
/* Alias id to be pid */
if (IS_id_function_etc (pid)) {
TYPE s = DEREF_type (id_function_etc_type (pid));
s = redecl_func_type (pid, s, t, def, 0);
COPY_type (id_function_etc_type (pid), s);
}
pid = DEREF_id (id_alias (pid));
COPY_id (id_alias (id), pid);
}
}
}
return (id);
}
/*
* CHECK FOR CLASS-LIKE TYPEDEF NAMES
*
* This routine checks whether the typedef name id behaves like a class
* or an object with respect to name hiding. It is not entirely clear
* whether it is just original class and enumeration names or all class
* and enumeration names (including those introduced using typedef)
* which behave in this way.
*/
int
is_tagged_type(IDENTIFIER id)
{
switch (TAG_id (id)) {
case id_class_name_tag :
case id_enum_name_tag : {
/* Original class and enumeration names */
return (1);
}
case id_class_alias_tag :
case id_enum_alias_tag :
case id_type_alias_tag : {
/* Type aliases */
return (0);
}
}
return (0);
}
/*
* REPORT AN OVERLOADING ERROR
*
* This routine reports the overloading error err for the function id
* which cannot be overloaded for the reason corresponding to reason.
* It returns the severity of the error.
*/
static int
overload_error(IDENTIFIER id, ERROR err, int reason)
{
int sev = ERROR_NONE;
switch (reason) {
case 1 : {
/* Two functions with C linkage */
err = concat_error (err, ERR_dcl_link_over ());
break;
}
case 2 : {
/* Two functions with indistinguishable parameters */
err = concat_error (err, ERR_over_load_pars ());
break;
}
case 3 : {
/* Two objects with C linkage */
PTR (LOCATION) loc = id_loc (id);
HASHID nm = DEREF_hashid (id_name (id));
err = concat_error (err, ERR_dcl_link_redecl (nm, loc));
break;
}
}
if (!IS_NULL_err (err)) {
sev = DEREF_int (err_severity (err));
report (decl_loc, err);
}
return (sev);
}
/*
* REDECLARE AN OBJECT IDENTIFIER
*
* This routine checks the redeclaration of the identifier id as an object
* with declaration specifiers ds and type t. It returns id for a valid
* redeclaration and the null identifier otherwise, reporting any errors
* if necessary. Note that it is possible to reserve an identifier using
* the reserve declaration specifier. At present this is only done for
* the fields of an anonymous union and enumerators. Also a class or
* enumeration name can be hidden by an object in the same scope. A
* redeclared identifier will be marked as defined according to whether
* the redeclaration is a definition. Whether the initial declaration
* was a definition may be determined from the initialiser expression.
*/
IDENTIFIER
redecl_id(DECL_SPEC ds, TYPE t, IDENTIFIER id, int reason, int def)
{
TYPE s;
DECL_SPEC ln;
PTR (TYPE) pt;
int changed = 0;
int is_member = 0;
int is_function = 0;
ERROR err = NULL_err;
DECL_SPEC ds_old, ln_old;
/* Check previous definition */
switch (TAG_id (id)) {
case id_variable_tag : {
/* Variables may be redeclared */
pt = id_variable_type (id);
break;
}
case id_function_tag : {
/* Functions may be redeclared */
is_function = 1;
pt = id_function_type (id);
break;
}
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 : {
/* Unqualified class and enumeration names can be hidden */
PTR (LOCATION) loc;
if (!is_tagged_type (id)) {
loc = id_loc (id);
report (decl_loc, ERR_basic_odr_diff (id, loc));
return (NULL_id);
}
if (crt_id_qualifier == qual_none) {
/* Check for templates */
ds_old = DEREF_dspec (id_storage (id));
if (!(ds_old & dspec_template)) return (NULL_id);
}
loc = id_loc (id);
report (decl_loc, ERR_basic_odr_diff (id, loc));
return (NULL_id);
}
case id_stat_member_tag : {
/* Members may be defined outside their class */
is_member = 1;
if (crt_id_qualifier == qual_none) goto error_lab;
pt = id_stat_member_type (id);
break;
}
case id_mem_func_tag :
case id_stat_mem_func_tag : {
/* Members may be defined outside their class */
is_member = 1;
is_function = 1;
if (crt_id_qualifier == qual_none) goto error_lab;
pt = id_function_etc_type (id);
break;
}
case id_member_tag : {
/* Non-static members cannot be redeclared */
is_member = 1;
if (crt_id_qualifier == qual_none) goto error_lab;
report (decl_loc, ERR_class_mem_def (id));
return (NULL_id);
}
case id_token_tag : {
/* Allow for token definitions */
return (NULL_id);
}
case id_undef_tag :
case id_ambig_tag : {
/* Allow for error propagation */
return (NULL_id);
}
default :
error_lab : {
/* No other identifiers can be redeclared */
if (is_member) {
/* Member redeclaration */
err = ERR_class_mem_redecl (id, id_loc (id));
} else {
/* Object redeclaration */
err = ERR_basic_odr_decl (id, id_loc (id));
}
IGNORE overload_error (id, err, reason);
return (NULL_id);
}
}
/* Check declaration specifiers */
ds_old = DEREF_dspec (id_storage (id));
if ((ds | ds_old) & dspec_reserve) {
/* Reserved names can't be redeclared */
reason = 0;
goto error_lab;
}
/* Check for objects with no linkage */
ln = (ds & dspec_linkage);
ln_old = (ds_old & dspec_linkage);
if (ln == dspec_none || ln_old == dspec_none) {
/* Can't redeclare objects with no linkage */
if (!is_function) {
reason = 0;
goto error_lab;
}
}
/* Check previous type */
s = DEREF_type (pt);
s = check_compatible (s, t, 0, &err, 1);
if (!IS_NULL_err (err)) {
/* Incompatible declaration */
PTR (LOCATION) loc = id_loc (id);
err = concat_error (err, ERR_basic_link_decl_type (id, loc));
if ((ds | ds_old) & dspec_token) {
/* Allow for interface declarations */
err = set_severity (err, OPT_interf_incompat, -1);
}
if (overload_error (id, err, reason) == ERROR_SERIOUS) {
return (NULL_id);
}
}
if (is_function) {
/* Sanity check for error types */
if (type_tag (s) != type_func_tag) return (NULL_id);
} else {
if (type_tag (s) == type_func_tag) return (NULL_id);
}
if (def >= 0) COPY_type (pt, s);
/* Check for redeclaration of aliases */
if ((ds | ds_old) & dspec_alias) {
PTR (LOCATION) loc = id_loc (id);
report (decl_loc, ERR_dcl_nspace_udecl_redecl (id, loc));
}
/* Check for inconsistent linkage */
if (ln != ln_old) {
ERROR err1;
DECL_SPEC ln_new;
PTR (LOCATION) loc = id_loc (id);
if (ln_old == dspec_static) {
err1 = ERR_dcl_stc_internal (id, loc);
} else {
err1 = ERR_dcl_stc_external (id, loc);
if (is_member) {
/* Members have external linkage */
ERROR err2 = ERR_basic_link_mem_extern (id);
err1 = concat_error (err2, err1);
}
}
if (reason == 3) {
/* Identification of objects with C linkage */
HASHID nm = DEREF_hashid (id_name (id));
ERROR err2 = ERR_dcl_link_redecl (nm, loc);
err1 = concat_error (err1, err2);
}
if (def == -1 && option (OPT_link_internal) == OPTION_OFF) {
ln_new = dspec_extern;
} else {
ln_new = dspec_static;
}
ds_old = (ln_new | (ds_old & ~dspec_linkage));
report (decl_loc, err1);
changed = 1;
}
/* Check language specifier */
ln = crt_linkage;
if (ln != dspec_none) {
/* Check against the current language */
ln_old = (ds_old & dspec_language);
if ((ds_old & dspec_extern) && ln != ln_old) {
/* Report inconsistent linkage */
if (!is_member) {
/* Should this only apply to functions? */
PTR (LOCATION) loc = id_loc (id);
string lang = linkage_string (ln_old, cv_none);
report (decl_loc, ERR_dcl_link_lang (id, lang, loc));
ds_old = adjust_linkage (ds_old, is_member);
changed = 1;
}
}
}
/* Check for inline specifier */
if (ds & dspec_inline) {
ds_old |= dspec_inline;
changed = 1;
}
/* Mark whether this declaration is a definition */
if (def >= 0) {
if (ds & dspec_defn) {
ds_old |= dspec_defn;
} else {
ds_old &= ~dspec_defn;
}
}
/* Compatible redeclaration */
COPY_dspec (id_storage (id), ds_old);
if (changed) update_tag (id, 0);
return (id);
}
/*
* REDECLARE A FUNCTION IDENTIFIER
*
* This routine is similar to redecl_id except that it allows for function
* overloading. As before it returns id if this is a redeclaration of an
* existing function, and the null identifier otherwise. However in the
* latter case any functions overloaded by the declaration are returned
* via over.
*/
IDENTIFIER
redecl_func(DECL_SPEC ds, TYPE t, IDENTIFIER id, unsigned tag, IDENTIFIER *over,
int def)
{
int reason = 0;
IDENTIFIER fid = id;
#if LANGUAGE_CPP
if (IS_id_function_etc (fid)) {
DECL_SPEC ds_old;
*over = fid;
/* Scan through overloaded functions for a match */
while (!IS_NULL_id (fid)) {
int m;
TYPE s;
int mq = 1;
if ((ds & dspec_extern) && crt_linkage == dspec_c) {
/* Two functions with C linkage are the same */
ds_old = DEREF_dspec (id_storage (fid));
if ((ds_old & dspec_c) && IS_id_function (fid)) {
reason = 1;
break;
}
}
/* Two functions with the same parameters are the same */
s = DEREF_type (id_function_etc_type (fid));
if (tag == id_stat_mem_func_tag) mq = 0;
if (IS_id_stat_mem_func (fid)) mq = 0;
m = eq_func_type (t, s, mq, 0);
if (m) {
/* Function types basically match */
if (m == 1) {
/* Return types don't match */
reason = 2;
}
break;
}
fid = DEREF_id (id_function_etc_over (fid));
}
if (IS_NULL_id (fid)) {
/* No match found */
IDENTIFIER tid = find_template (id, 0);
if (!IS_NULL_id (tid)) {
/* Must have match with template specialisation */
report (decl_loc, ERR_temp_spec_type (t, id));
return (NULL_id);
}
if (crt_id_qualifier != qual_none) {
/* Must have match with qualified identifier */
if (def == -2 && tag == id_function_tag) {
/* Allow for name injection */
/* EMPTY */
} else {
report (decl_loc, ERR_basic_link_unmatch (t, id));
}
return (NULL_id);
}
if (reason == 0) return (NULL_id);
fid = id;
}
/* Match found */
ds_old = DEREF_dspec (id_storage (fid));
if ((ds_old & dspec_implicit) && !(ds & dspec_implicit)) {
if (IS_id_mem_func (fid)) {
/* Matches implicitly declared member function */
report (decl_loc, ERR_class_special_decl (fid));
return (NULL_id);
}
}
if (ds_old & dspec_inherit) {
/* Inherited functions (including aliases) are hidden */
return (NULL_id);
}
/* *over = NULL_id; */
}
#else
/* Don't check overloading in C */
*over = NULL_id;
UNUSED (tag);
#endif
/* Redeclare id */
fid = redecl_id (ds, t, fid, reason, def);
if (!IS_NULL_id (fid)) {
TYPE form = DEREF_type (id_function_etc_form (id));
if (def >= 0) {
/* Allow for default arguments etc. */
TYPE s = DEREF_type (id_function_etc_type (fid));
s = redecl_func_type (fid, s, t, def, 1);
COPY_type (id_function_etc_type (fid), s);
}
if (!IS_NULL_type (form) && IS_type_token (form)) {
IDENTIFIER ext = DEREF_id (type_token_tok (form));
if (!IS_NULL_id (ext) && IS_id_token (ext)) {
/* Check for tokenised functions */
ds = DEREF_dspec (id_storage (ext));
ds |= dspec_explicit;
COPY_dspec (id_storage (ext), ds);
if (def) {
/* Check for token definitions */
IGNORE define_func_token (ext, fid);
if (ds & dspec_pure) {
report (decl_loc, ERR_token_def_not (ext));
}
}
}
}
}
return (fid);
}
/*
* REDECLARE AN INHERITED OR ALIASED MEMBER
*
* This routine is used to allow for declarations of class members to
* override any inherited value of the member. id gives the inherited
* value, mem is true for a member declaration, fn is true for a function
* declaration or a declaration which can't coexist with a function
* declaration. The null identifier is returned to indicate that id is
* to be overridden.
*/
IDENTIFIER
redecl_inherit(IDENTIFIER id, QUALIFIER qual, int mem, int fn)
{
if (!IS_NULL_id (id)) {
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds & dspec_alias) {
if (fn && IS_id_function_etc (id)) {
/* Everything is a function */
return (id);
}
if (mem && IS_id_class_name (id)) {
/* Allow for injected type names */
if (ds & dspec_implicit) return (id);
}
if (qual == qual_none) {
/* New declaration */
PTR (LOCATION) loc = id_loc (id);
report (decl_loc, ERR_dcl_nspace_udecl_multi (id, loc));
return (NULL_id);
}
}
if (ds & dspec_inherit) {
NAMESPACE ns;
if (mem) return (NULL_id);
ns = DEREF_nspace (id_parent (id));
id = DEREF_id (id_alias (id));
report (decl_loc, ERR_lookup_qual_decl (id, ns));
}
}
return (id);
}
/*
* COPY AN IDENTIFIER
*
* This routine creates a copy of the identifier id. If type is 1
* then any type components in id are copied using copy_typedef, if it
* is 2 they are further expanded using expand_type.
*/
IDENTIFIER
copy_id(IDENTIFIER id, int type)
{
TYPE t;
ulong no;
ulong dno;
HASHID nm;
unsigned tag;
LOCATION loc;
NAMESPACE ns;
DECL_SPEC ds;
IDENTIFIER lid;
IDENTIFIER cid = id;
/* Examine various cases */
if (IS_NULL_id (cid)) return (NULL_id);
tag = TAG_id (cid);
switch (tag) {
case id_class_name_tag :
case id_class_alias_tag :
case id_enum_name_tag :
case id_enum_alias_tag :
case id_type_alias_tag : {
/* Types */
BASE_TYPE bt;
DECONS_id_class_name_etc (nm, ds, ns, loc, lid, no,
dno, t, bt, cid);
if (type) {
t = copy_typedef (cid, t, cv_none);
if (type == 2) {
t = expand_type (t, 1);
if (tag == id_class_name_tag) {
/* Name already copied by copy_class */
CLASS_TYPE ct;
while (IS_type_templ (t)) {
t = DEREF_type (type_templ_defn (t));
}
ct = DEREF_ctype (type_compound_defn (t));
cid = DEREF_id (ctype_name (ct));
if (!EQ_id (cid, id)) {
COPY_hashid (id_name (cid), nm);
COPY_dspec (id_storage (cid), ds);
COPY_nspace (id_parent (cid), ns);
COPY_loc (id_loc (cid), loc);
break;
}
}
if (tag != id_enum_name_tag) {
/* Find type alias tag */
unsigned ta = type_tag (t);
if (ta == type_compound_tag) {
tag = id_class_alias_tag;
} else if (ta == type_enumerate_tag) {
tag = id_enum_alias_tag;
} else {
tag = id_type_alias_tag;
}
}
}
}
MAKE_id_class_name_etc (tag, nm, ds, ns, loc, t, cid);
COPY_btype (id_class_name_etc_rep (cid), bt);
break;
}
case id_variable_tag :
case id_parameter_tag :
case id_stat_member_tag : {
/* Objects */
EXP a, b;
DECONS_id_variable_etc (nm, ds, ns, loc, lid, no, dno, t,
a, b, cid);
if (type) {
t = copy_typedef (cid, t, cv_none);
if (type == 2) t = expand_type (t, 1);
}
MAKE_id_variable_etc (tag, nm, ds, ns, loc, t, cid);
COPY_exp (id_variable_etc_init (cid), a);
COPY_exp (id_variable_etc_term (cid), b);
break;
}
case id_function_tag :
case id_mem_func_tag :
case id_stat_mem_func_tag : {
/* Functions */
EXP a;
TYPE form;
IDENTIFIER over;
LIST (CLASS_TYPE) fr;
int idef;
IDENTIFIER sdef, sref;
DECONS_id_function_etc (nm, ds, ns, loc, lid, no, dno, t,
over, form, fr, idef, sdef, sref, a, cid);
if (type) {
t = copy_typedef (cid, t, cv_none);
if (type == 2) t = expand_type (t, 1);
}
MAKE_id_function_etc (tag, nm, ds, ns, loc, t, over, cid);
COPY_type (id_function_etc_form (cid), form);
COPY_int (id_function_etc_inline_def (cid), idef);
COPY_id (id_function_etc_static_def (cid), sdef);
COPY_id (id_function_etc_static_ref (cid), sref);
COPY_exp (id_function_etc_defn (cid), a);
if (type == 2) {
/* Copy friend classes */
while (!IS_NULL_list (fr)) {
TYPE r = NULL_type;
CLASS_TYPE cr = DEREF_ctype (HEAD_list (fr));
cr = expand_ctype (cr, 2, &r);
friend_function (cr, cid, 0);
fr = TAIL_list (fr);
}
} else {
COPY_list (id_function_etc_chums (cid), fr);
}
break;
}
case id_member_tag : {
/* Members */
GRAPH gr;
OFFSET off;
DECONS_id_member (nm, ds, ns, loc, lid, no, dno, t, off,
gr, cid);
if (type) {
t = copy_typedef (cid, t, cv_none);
if (type == 2) {
if (IS_hashid_anon (nm)) {
/* Allow for anonymous bitfields */
expand_anon_bitfield = 1;
}
t = expand_type (t, 1);
expand_anon_bitfield = 0;
}
}
MAKE_id_member (nm, ds, ns, loc, t, cid);
COPY_graph (id_member_base (cid), gr);
COPY_off (id_member_off (cid), off);
break;
}
case id_enumerator_tag : {
/* Enumerators */
EXP a;
ERROR err = NULL_err;
DECONS_id_enumerator (nm, ds, ns, loc, lid, no, dno,
t, a, cid);
if (type == 2) {
/* Copy enumerator value */
TYPE s = expand_type (t, 1);
a = copy_exp (a, t, s);
IGNORE make_nat_exp (a, &err);
t = s;
}
MAKE_id_enumerator (nm, ds, ns, loc, t, a, cid);
if (!IS_NULL_err (err)) {
err = concat_error (err, ERR_dcl_enum_const (cid));
report (crt_loc, err);
}
break;
}
case id_token_tag : {
/* Tokens */
TOKEN sort;
IDENTIFIER alt;
DECONS_id_token (nm, ds, ns, loc, lid, no, dno,
sort, alt, cid);
if (type == 2) {
/* Expand token sort */
sort = expand_sort (sort, 1, 1);
}
MAKE_id_token (nm, ds, ns, loc, sort, alt, cid);
break;
}
default : {
/* Don't copy other identifiers */
return (cid);
}
}
if (type != 2) {
COPY_id (id_alias (cid), lid);
COPY_ulong (id_no (cid), no);
COPY_ulong (id_dump (cid), dno);
}
return (cid);
}
/*
* CREATE AN IDENTIFIER ALIAS
*
* This routine creates an alias for the identifier id in the namespace
* ns. fn gives a list of function which the alias will overload if
* it is a function.
*/
IDENTIFIER
alias_id(IDENTIFIER id, NAMESPACE ns, IDENTIFIER fn, int rec)
{
IDENTIFIER cid = copy_id (id, 1);
if (!EQ_id (cid, id)) {
DECL_SPEC ds = DEREF_dspec (id_storage (cid));
DECL_SPEC acc = (ds & dspec_access);
if (acc) {
IDENTIFIER sid = DEREF_id (nspace_name (ns));
immediate_access (sid, cid);
}
ds = ((ds & ~dspec_access) | dspec_alias | crt_access);
COPY_dspec (id_storage (cid), ds);
COPY_nspace (id_parent (cid), ns);
if (do_dump) dump_alias (cid, id, &crt_loc);
if (IS_id_function_etc (cid)) {
/* Deal with overloaded functions */
IDENTIFIER over;
if (rec) {
over = DEREF_id (id_function_etc_over (cid));
if (IS_NULL_id (over)) {
over = fn;
} else {
over = alias_id (over, ns, fn, rec);
}
} else {
over = fn;
}
COPY_id (id_function_etc_over (cid), over);
}
}
return (cid);
}
/*
* DUMMY DECLARATION SPECIFIER
*
* This value is used as a dummy declaration specifier in the adjusting
* of overloaded functions.
*/
#define dspec_mark ((DECL_SPEC) 0x7fffffff)
/*
* REMOVE HIDDEN FUNCTIONS
*
* This routine adjusts the set of overloaded functions id by removing
* any with storage field equal to dspec_mark.
*/
static IDENTIFIER
remove_functions(IDENTIFIER id)
{
if (!IS_NULL_id (id)) {
DECL_SPEC ds = DEREF_dspec (id_storage (id));
IDENTIFIER over = DEREF_id (id_function_etc_over (id));
over = remove_functions (over);
if (ds == dspec_mark) {
id = over;
} else {
COPY_id (id_function_etc_over (id), over);
}
}
return (id);
}
/*
* COMPARE HIDING FUNCTIONS
*
* This routine compares two functions id and over, one declared in the
* normal fashion and the other by a using-declaration. It returns
* true if the former overrides the latter.
*/
static int
compare_functions(IDENTIFIER id, IDENTIFIER over, int mem)
{
TYPE t = DEREF_type (id_function_etc_type (id));
TYPE s = DEREF_type (id_function_etc_type (over));
int eq = eq_func_type (t, s, 1, 0);
if (eq) {
/* Equal parameter types */
if (mem) return (1);
}
if (eq >= 2) {
/* Equal types */
PTR (LOCATION) loc = id_loc (over);
report (crt_loc, ERR_dcl_nspace_udecl_multi (over, loc));
return (1);
}
return (0);
}
/*
* MARK HIDDEN FUNCTIONS
*
* This routine marks any functions which hide, or are hidden by, id in
* its set of overloaded functions. mem is true for member functions.
* Any hidden function is marked by setting its storage field to the
* value dspec_mark.
*/
static int
mark_functions(IDENTIFIER id, int mem)
{
int ret = 0;
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds != dspec_mark) {
IDENTIFIER over = DEREF_id (id_function_etc_over (id));
while (!IS_NULL_id (over)) {
DECL_SPEC pds = DEREF_dspec (id_storage (over));
if (pds != dspec_mark) {
if (ds & dspec_alias) {
if (pds & dspec_alias) {
/* Both are using declarations */
IDENTIFIER a = DEREF_id (id_alias (id));
IDENTIFIER b = DEREF_id (id_alias (over));
if (EQ_id (a, b)) {
/* Duplicate declarations */
if (mem) {
ERROR err;
PTR (LOCATION) loc = id_loc (over);
err = ERR_class_mem_redecl (over, loc);
report (crt_loc, err);
}
COPY_dspec (id_storage (over), dspec_mark);
ret++;
}
} else {
/* The first is a using declaration */
if (compare_functions (id, over, mem)) {
COPY_dspec (id_storage (id), dspec_mark);
ret++;
}
}
} else if (pds & dspec_alias) {
/* The second is a using declaration */
if (compare_functions (id, over, mem)) {
COPY_dspec (id_storage (over), dspec_mark);
ret++;
break;
}
}
}
over = DEREF_id (id_function_etc_over (over));
}
}
return (ret);
}
/*
* HANDLE HIDDEN FUNCTIONS WITH USING DECLARATIONS
*
* The interaction of using declarations with the hiding and overriding
* of member functions is somewhat complex. It is implemented by this
* routine which adjusts the declarations of the overloaded functions id
* up to the existing declarations over. mem is true for member functions.
*/
IDENTIFIER
hide_functions(IDENTIFIER id, IDENTIFIER over, int mem)
{
if (!IS_NULL_id (over)) {
int marked = 0;
IDENTIFIER pid = id;
while (!EQ_id (pid, over)) {
marked += mark_functions (pid, mem);
pid = DEREF_id (id_function_etc_over (pid));
}
if (marked) id = remove_functions (id);
pid = id;
while (!IS_NULL_id (pid)) {
/* Mark template functions */
DECL_SPEC ds = DEREF_dspec (id_storage (pid));
if (ds & dspec_template) {
templ_func_decl (id);
break;
}
pid = DEREF_id (id_function_etc_over (pid));
}
}
return (id);
}
/*
* CHECK THE VISIBILITY OF AN IDENTIFIER
*
* A member name in a using declaration should be visible from a direct
* base class. This routine checks whether the member id meets this
* criterion by comparing it with its look-up in the direct base
* classes, pid.
*/
static int
using_visible(IDENTIFIER id, IDENTIFIER pid)
{
while (!IS_NULL_id (pid)) {
IDENTIFIER qid = DEREF_id (id_alias (pid));
if (EQ_id (qid, id)) return (1);
switch (TAG_id (pid)) {
case id_function_tag :
case id_mem_func_tag :
case id_stat_mem_func_tag : {
/* Check overloaded functions */
pid = DEREF_id (id_function_etc_over (pid));
break;
}
case id_ambig_tag : {
/* Check ambiguous identifiers */
LIST (IDENTIFIER) pids;
pids = DEREF_list (id_ambig_ids (pid));
while (!IS_NULL_list (pids)) {
pid = DEREF_id (HEAD_list (pids));
if (using_visible (id, pid)) return (1);
pids = TAIL_list (pids);
}
return (0);
}
default : {
/* Other identifiers */
return (0);
}
}
}
return (0);
}
/*
* PROCESS A CLASS USING DECLARATION
*
* This routine processes a using-declaration of the identifier id in
* the case when this declaration is a member-declaration.
*/
static IDENTIFIER
using_member(IDENTIFIER id, int type)
{
MEMBER mem;
IDENTIFIER aid;
IDENTIFIER pid;
/* Check the identifier */
NAMESPACE cns = crt_namespace;
HASHID nm = DEREF_hashid (id_name (id));
GRAPH gr = is_subfield (cns, id);
if (IS_NULL_graph (gr)) {
/* id is not a member of a base class */
CLASS_TYPE ct = crt_class;
report (crt_loc, ERR_dcl_nspace_udecl_base (id, ct));
return (NULL_id);
} else {
GRAPH gu = DEREF_graph (graph_up (gr));
GRAPH gt = DEREF_graph (graph_top (gr));
if (EQ_graph (gt, gr)) {
/* id is a member of the current class */
report (crt_loc, ERR_dcl_nspace_udecl_mem (id));
return (id);
}
if (!EQ_graph (gt, gu)) {
/* Not a member of a direct base class */
IDENTIFIER bid = search_base_field (cns, nm, type, 0);
aid = id;
while (!IS_NULL_id (aid)) {
IDENTIFIER qid = find_template (aid, 1);
if (IS_NULL_id (qid)) qid = aid;
qid = DEREF_id (id_alias (qid));
if (!using_visible (qid, bid)) {
CLASS_TYPE ct = crt_class;
report (crt_loc, ERR_dcl_nspace_udecl_vis (aid, ct));
break;
}
if (!IS_id_function_etc (aid)) break;
aid = DEREF_id (id_function_etc_over (aid));
}
}
}
/* Check declarations */
mem = search_member (cns, nm, 1);
if (type) {
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds & dspec_template) {
pid = DEREF_id (member_id (mem));
} else {
pid = type_member (mem, 3);
}
} else {
pid = DEREF_id (member_id (mem));
if (!IS_NULL_id (pid) && is_tagged_type (pid)) {
/* Allow hiding of non-template classes */
DECL_SPEC ds = DEREF_dspec (id_storage (pid));
if (!(ds & dspec_template)) pid = NULL_id;
}
}
if (!IS_NULL_id (pid)) {
DECL_SPEC ds = DEREF_dspec (id_storage (pid));
if ((ds & dspec_inherit) && !(ds & dspec_alias)) {
/* Ignore inherited members */
pid = NULL_id;
} else {
IDENTIFIER rid = DEREF_id (id_alias (id));
IDENTIFIER qid = DEREF_id (id_alias (pid));
if (EQ_id (rid, qid)) {
/* Redeclaration of existing meaning */
if (!type) {
PTR (LOCATION) loc = id_loc (pid);
ERROR err = ERR_class_mem_redecl (pid, loc);
report (crt_loc, err);
}
adjust_access (pid, crt_access, 1);
return (pid);
}
if (IS_id_function_etc (id) && IS_id_function_etc (pid)) {
/* Both new and old meanings are functions */
/* EMPTY */
} else {
/* One meaning is not a function */
PTR (LOCATION) loc = id_loc (pid);
ERROR err = ERR_dcl_nspace_udecl_multi (pid, loc);
report (crt_loc, err);
return (NULL_id);
}
}
}
/* Find inherited member */
id = search_subfield (cns, gr, id);
aid = alias_id (id, cns, pid, 1);
if (!IS_NULL_id (pid)) {
/* Deal with function hiding */
aid = hide_functions (aid, pid, 1);
}
adjust_access (id, crt_access, 1);
if (type) {
set_type_member (mem, aid);
} else {
set_member (mem, aid);
}
return (aid);
}
/*
* PROCESS A NAMESPACE USING DECLARATION
*
* This routine processes a using-declaration of the identifier id in
* the case when this declaration is not a member-declaration.
*/
static IDENTIFIER
using_name(IDENTIFIER id)
{
int type;
HASHID nm;
MEMBER mem;
IDENTIFIER pid;
/* Check the identifier */
NAMESPACE cns = crt_namespace;
NAMESPACE ns = DEREF_nspace (id_parent (id));
if (IS_nspace_ctype (ns)) {
/* id denotes a class member */
report (crt_loc, ERR_dcl_nspace_udecl_id (id));
switch (TAG_id (id)) {
case id_member_tag :
case id_stat_member_tag :
case id_mem_func_tag :
case id_stat_mem_func_tag : {
/* Don't even try in these cases */
return (NULL_id);
}
}
}
/* Check declarations */
nm = DEREF_hashid (id_name (id));
mem = search_member (cns, nm, 1);
type = is_tagged_type (id);
if (type) {
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds & dspec_template) {
pid = DEREF_id (member_id (mem));
} else {
pid = type_member (mem, 3);
}
} else {
pid = DEREF_id (member_id (mem));
if (!IS_id_nspace_name_etc (id)) {
if (!IS_NULL_id (pid) && is_tagged_type (pid)) {
/* Allow hiding of non-template classes */
DECL_SPEC ds = DEREF_dspec (id_storage (pid));
if (!(ds & dspec_template)) pid = NULL_id;
}
}
}
if (!IS_NULL_id (pid)) {
IDENTIFIER qid = DEREF_id (id_alias (pid));
if (EQ_id (id, qid)) {
/* Redeclaration of existing meaning */
if (EQ_id (id, pid)) {
report (crt_loc, ERR_dcl_nspace_udecl_mem (id));
}
return (pid);
}
if (IS_id_function_etc (id) && IS_id_function_etc (pid)) {
/* Both new and old meanings are functions */
/* EMPTY */
} else {
/* Invalid redeclaration */
PTR (LOCATION) loc = id_loc (pid);
ERROR err = ERR_dcl_nspace_udecl_multi (pid, loc);
report (crt_loc, err);
pid = NULL_id;
}
}
/* Create the alias */
id = alias_id (id, cns, pid, 1);
if (!IS_NULL_id (pid)) {
/* Deal with function hiding */
id = hide_functions (id, pid, 0);
}
if (type) {
set_type_member (mem, id);
} else {
set_member (mem, id);
}
return (id);
}
/*
* PROCESS A USING DECLARATION
*
* This routine processes a using-declaration of the identifier id. Note
* that this includes the access declarations used to modify access to
* class members.
*/
IDENTIFIER
using_identifier(IDENTIFIER id)
{
/* Identifier must be qualified */
MEMBER mem;
HASHID unm;
IDENTIFIER cid;
IDENTIFIER uid = id;
NAMESPACE uns = DEREF_nspace (id_parent (uid));
uid = constr_name (uns, uid);
unm = DEREF_hashid (id_name (uid));
if (crt_id_qualifier == qual_none) {
report (crt_loc, ERR_dcl_nspace_udecl_unqual ());
return (uid);
}
/* Report undefined and ambiguous identifiers */
switch (TAG_id (uid)) {
case id_ambig_tag : {
/* Introduce all the ambiguous meanings */
LIST (IDENTIFIER) pids = DEREF_list (id_ambig_ids (uid));
while (!IS_NULL_list (pids)) {
IDENTIFIER pid = DEREF_id (HEAD_list (pids));
IGNORE using_identifier (pid);
pids = TAIL_list (pids);
}
uid = find_qual_id (crt_namespace, unm, 0, 0);
return (uid);
}
case id_undef_tag : {
/* Report undeclared identifiers */
report (crt_loc, ERR_lookup_qual_undef (unm, uns));
return (uid);
}
}
/* Can have constructors or destructors */
switch (TAG_hashid (unm)) {
case hashid_constr_tag :
case hashid_destr_tag : {
report (crt_loc, ERR_dcl_nspace_udecl_constr (uid));
return (uid);
}
}
/* Check for hidden type names */
mem = search_member (uns, unm, 0);
cid = type_member (mem, 1);
if (EQ_id (cid, uid)) cid = NULL_id;
/* Process the declaration */
if (in_class_defn) {
if (!IS_NULL_id (cid)) {
IGNORE using_member (cid, 1);
}
uid = using_member (uid, 0);
} else {
if (!IS_NULL_id (cid)) {
cid = DEREF_id (id_alias (cid));
IGNORE using_name (cid);
}
uid = DEREF_id (id_alias (uid));
uid = using_name (uid);
}
if (IS_NULL_id (uid)) {
uid = id;
} else {
/* Check for hiding */
if (option (OPT_decl_hide)) {
switch (TAG_id (id)) {
case id_variable_tag :
case id_function_tag : {
check_hiding (uid);
break;
}
}
}
}
return (uid);
}
/*
* PROCESS A USING TYPENAME DECLARATION
*
* This routine processes a using-declaration involving the type t
* declared using typename.
*/
void
using_typename(TYPE t)
{
UNUSED (t);
return;
}
/*
* REDECLARE AN IDENTIFIER
*
* This routine redeclares the identifier id in the namespace ns.
*/
IDENTIFIER
redeclare_id(NAMESPACE ns, IDENTIFIER id)
{
IDENTIFIER old_id;
int cl = is_tagged_type (id);
HASHID nm = DEREF_hashid (id_name (id));
MEMBER mem = search_member (ns, nm, 1);
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (cl) {
old_id = type_member (mem, 3);
} else {
old_id = DEREF_id (member_id (mem));
}
if (!IS_NULL_id (old_id)) {
PTR (LOCATION) loc = id_loc (old_id);
report (crt_loc, ERR_basic_odr_decl (old_id, loc));
}
if (cl) {
set_type_member (mem, id);
} else {
set_member (mem, id);
}
ds |= dspec_reserve;
COPY_dspec (id_storage (id), ds);
COPY_nspace (id_parent (id), ns);
return (id);
}
/*
* CHECK ANONYMOUS UNION MEMBER
*
* This routine checks whether the identifier id is member of an
* anonymous union.
*/
int
is_anon_member(IDENTIFIER id)
{
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds & dspec_reserve) {
IDENTIFIER pid = DEREF_id (id_alias (id));
if (!EQ_id (pid, id)) {
TYPE s = DEREF_type (id_variable_etc_type (pid));
if (IS_type_compound (s)) {
TYPE t = DEREF_type (id_variable_etc_type (id));
if (!eq_type (s, t)) return (1);
}
}
}
return (0);
}
/*
* REDECLARE A MEMBER OF AN ANONYMOUS UNION
*
* This routine redeclares the member id of an anonymous union. The
* remaining arguments are as in redecl_anon_union.
*/
static IDENTIFIER
redecl_anon_member(IDENTIFIER id, CLASS_TYPE ct, DECL_SPEC ds, IDENTIFIER obj)
{
IDENTIFIER pid = NULL_id;
HASHID nm = DEREF_hashid (id_name (id));
if (!IS_hashid_anon (nm)) {
switch (TAG_id (id)) {
case id_member_tag : {
/* Redeclare a type member */
TYPE t = DEREF_type (id_member_type (id));
DEREF_loc (id_loc (id), crt_loc);
if (IS_id_member (obj)) {
NAMESPACE cns = crt_namespace;
OFFSET off1 = DEREF_off (id_member_off (id));
OFFSET off2 = DEREF_off (id_member_off (obj));
id = find_id (nm);
id = constr_name (cns, id);
id = make_member_decl (ds, t, id, 0);
MAKE_off_plus (off2, off1, off1);
COPY_off (id_member_off (id), off1);
} else {
id = make_object_decl (ds, t, id, 0);
obj = DEREF_id (id_alias (obj));
COPY_id (id_alias (id), obj);
}
pid = id;
break;
}
case id_class_name_tag : {
/* Redeclare a class name */
int templ = 0;
CLASS_TYPE cs;
TYPE t = DEREF_type (id_class_name_defn (id));
while (IS_type_templ (t)) {
templ = 1;
t = DEREF_type (type_templ_defn (t));
}
cs = DEREF_ctype (type_compound_defn (t));
if (!eq_ctype (ct, cs)) {
NAMESPACE cns = crt_namespace;
if (templ) {
/* Shouldn't be a template class */
LOCATION loc;
DEREF_loc (id_loc (id), loc);
report (loc, ERR_temp_decl_bad ());
}
pid = redeclare_id (cns, id);
}
break;
}
case id_enum_name_tag :
case id_class_alias_tag :
case id_enum_alias_tag :
case id_type_alias_tag :
case id_enumerator_tag : {
/* Redeclare other identifiers */
NAMESPACE cns = crt_namespace;
pid = redeclare_id (cns, id);
break;
}
}
}
return (pid);
}
/*
* REDECLARE AN ANONYMOUS UNION
*
* This routine redeclares all the members of the anonymous union obj of
* type ct in the current namespace using the declaration specifiers ds.
* The routine returns false if there are no members to redeclare. The
* redeclared members are added to the extra field of the namespace given
* by ct.
*/
int
redecl_anon_union(CLASS_TYPE ct, DECL_SPEC ds, IDENTIFIER obj)
{
int ok = 0;
MEMBER mem;
NAMESPACE ns;
LOCATION old_loc;
bad_crt_loc++;
old_loc = crt_loc;
ds |= dspec_reserve;
crt_id_qualifier = qual_none;
crt_templ_qualifier = 0;
ns = DEREF_nspace (ctype_member (ct));
mem = DEREF_member (nspace_ctype_first (ns));
while (!IS_NULL_member (mem)) {
IDENTIFIER id = DEREF_id (member_id (mem));
IDENTIFIER aid = DEREF_id (member_alt (mem));
if (!IS_NULL_id (aid) && !EQ_id (aid, id)) {
aid = redecl_anon_member (aid, ct, ds, obj);
if (!IS_NULL_id (aid)) ok = 1;
}
if (!IS_NULL_id (id)) {
id = redecl_anon_member (id, ct, ds, obj);
if (!IS_NULL_id (id)) ok = 1;
}
mem = DEREF_member (member_next (mem));
}
crt_loc = old_loc;
bad_crt_loc--;
return (ok);
}
syntax highlighted by Code2HTML, v. 0.9.1