/*
* 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, 1998
*
* 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/declare.c,v 1.18 2005/11/09 17:09:47 stefanf Exp $
*/
#include "config.h"
#include "producer.h"
#include "msgcat.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "loc_ext.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "off_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "access.h"
#include "allocate.h"
#include "basetype.h"
#include "capsule.h"
#include "chktype.h"
#include "class.h"
#include "compile.h"
#include "construct.h"
#include "declare.h"
#include "derive.h"
#include "dump.h"
#include "exception.h"
#include "file.h"
#include "function.h"
#include "hash.h"
#include "identifier.h"
#include "initialise.h"
#include "instance.h"
#include "inttype.h"
#include "namespace.h"
#include "operator.h"
#include "overload.h"
#include "predict.h"
#include "preproc.h"
#include "redeclare.h"
#include "syntax.h"
#include "template.h"
#include "tokdef.h"
#include "token.h"
#include "virtual.h"
/*
* DECLARATION LOCATION
*
* This variable is used to record the location of the current declaration.
* This is primarily so that the location of a declaration can be set to
* the location of the declarator rather than the end of the corresponding
* initialiser.
*/
LOCATION decl_loc = NULL_loc;
int is_redeclared = 0;
/*
* DEFAULT LINKAGE FOR INLINE FUNCTIONS AND CONST OBJECTS
*
* These variables give the default linkages for inline functions and
* const objects and the default cv-qualifiers for external objects.
*/
DECL_SPEC inline_linkage = dspec_static;
DECL_SPEC const_linkage = dspec_static;
CV_SPEC cv_extern = cv_none;
/*
* DUMMY EMPTY DECLARATION SPECIFIER
*
* The value dspec_empty is used during the processing of declarations
* to mark any declaration specifier which does not contain an explicit
* specifier. The value dspec_nonempty gives those declaration
* specifiers which constitute an explicit specifier.
*/
#define dspec_empty dspec_pure
#define dspec_nonempty dspec_keyword
/*
* COMPLETE A DECLARATION SPECIFIER
*
* This routine completes the declaration specifier given by the
* specifiers ds, the type t and the cv-qualifier cv. If these are
* all empty then the result is marked using dspec_empty. The special
* case 'extern "LANG" typedef' is checked.
*/
DECL_SPEC
complete_dspec(DECL_SPEC ds, BASE_TYPE bt, TYPE t, CV_SPEC cv)
{
DECL_SPEC key = (ds & dspec_nonempty);
#if LANGUAGE_C
if (in_for_decl) {
/* Can't have specifiers other than 'auto' or 'register' */
DECL_SPEC mask = ~(dspec_auto | dspec_register);
if (ds & mask) {
report (crt_loc, ERR_stmt_for_storage (ds & mask));
ds &= ~mask;
}
}
#endif
if (key || bt || !IS_NULL_type (t) || cv) {
/* Have a declaration specifier */
if (ds & dspec_c) {
/* Have a linkage specification */
if (ds & dspec_typedef) ds &= ~dspec_extern;
ds &= ~dspec_c;
}
} else {
ds |= dspec_empty;
}
return (ds);
}
/*
* CHECK INFERRED OBJECT TYPES
*
* This routine checks the declaration specifiers ds and the type *p for
* inferred types and empty specifier lists. It is used in object,
* parameter and class member declarations.
*/
static DECL_SPEC
check_inferred_type(DECL_SPEC ds, TYPE *p, int mem)
{
int infer;
TYPE t = *p;
int empty = 0;
ERROR err = NULL_err;
/* Report if there are no declaration specifiers */
if (ds & dspec_empty) {
if (mem) {
err = ERR_class_mem_ds_empty ();
} else {
err = ERR_dcl_dcl_ds_empty ();
}
ds &= ~dspec_empty;
empty = 1;
}
/* Check on inferred types */
infer = is_type_inferred (t);
if (infer != INFERRED_NOT) {
ERROR err2;
t = clean_inferred_type (t);
if (empty) {
err2 = ERR_dcl_type_infer (t);
} else {
err2 = report_inferred_type (t, infer);
}
err = concat_error (err, err2);
*p = t;
}
if (!IS_NULL_err (err)) report (crt_loc, err);
return (ds);
}
/*
* CHECK INFERRED FUNCTION TYPES
*
* This routine checks the declaration specifiers ds and the function
* type t for inferred types and empty specifier lists. It is used in
* function declarations and definitions (as indicated by def).
*/
static DECL_SPEC
check_func_type(DECL_SPEC ds, TYPE t, int def, int chk, int mem)
{
int empty = 0;
ERROR err = NULL_err;
/* Report if there are no declaration specifiers */
if (ds & dspec_empty) {
if (mem) {
err = ERR_class_mem_ds_empty ();
} else if (def) {
err = ERR_dcl_dcl_ds_func ();
} else {
err = ERR_dcl_dcl_ds_empty ();
}
ds &= ~dspec_empty;
empty = 1;
}
/* Check for template types */
while (IS_type_templ (t)) {
ds |= dspec_template;
t = DEREF_type (type_templ_defn (t));
}
/* Check the return type */
if (chk) {
TYPE r = DEREF_type (type_func_ret (t));
int infer = is_type_inferred (r);
if (infer != INFERRED_NOT) {
ERROR err2;
r = clean_inferred_type (r);
if (empty) {
err2 = ERR_dcl_type_infer (r);
} else {
err2 = report_inferred_type (r, infer);
}
err = concat_error (err, err2);
COPY_type (type_func_ret (t), r);
}
}
if (!IS_NULL_err (err)) report (crt_loc, err);
return (ds);
}
/*
* SHIFT A STORAGE CLASS
*
* This macro is used to shift a storage class specifier into a more
* sensible range.
*/
#define DSPEC_STORAGE(A) ((A) >> 6)
/*
* CHECK STORAGE CLASS SPECIFIERS
*
* This routine extracts the storage class specifiers from the declaration
* specifiers ds in the context given by loc. It returns a valid storage
* class specifier.
*/
static DECL_SPEC
check_storage(DECL_SPEC ds, int loc, IDENTIFIER id)
{
DECL_SPEC st = (ds & dspec_storage);
/* Check on storage class */
switch_label : {
switch (DSPEC_STORAGE (st)) {
case DSPEC_STORAGE (dspec_none) : {
/* No storage class given */
break;
}
case DSPEC_STORAGE (dspec_auto) :
case DSPEC_STORAGE (dspec_register) : {
/* Deal with auto and register */
switch (loc) {
case CONTEXT_OBJECT : {
/* Objects declared in a block are alright */
if (!in_function_defn) goto bad_auto_lab;
break;
}
case CONTEXT_FUNCTION : {
/* Functions can't be auto */
if (!in_function_defn) goto bad_auto_lab;
report (crt_loc, ERR_dcl_stc_auto_func (st));
st = dspec_none;
break;
}
case CONTEXT_PARAMETER :
case CONTEXT_WEAK_PARAM : {
/* Function parameters are alright */
if (st == dspec_auto) {
/* Can't have auto parameters in C */
report (crt_loc, ERR_dcl_stc_auto_par ());
}
break;
}
case CONTEXT_TEMPL_PARAM : {
/* Template parameters can't have storage class */
report (crt_loc, ERR_temp_param_dcl_stc (st));
st = dspec_none;
break;
}
default :
bad_auto_lab : {
/* Anything outside a block can't be auto */
report (crt_loc, ERR_dcl_stc_auto_bad (st));
st = dspec_none;
break;
}
}
break;
}
case DSPEC_STORAGE (dspec_static) : {
/* Deal with static */
switch (loc) {
case CONTEXT_PARAMETER :
case CONTEXT_WEAK_PARAM : {
/* Function parameters can't be static */
report (crt_loc, ERR_dcl_stc_param (st));
st = dspec_none;
break;
}
case CONTEXT_TEMPL_PARAM : {
/* Template parameters can't have storage class */
report (crt_loc, ERR_temp_param_dcl_stc (st));
st = dspec_none;
break;
}
}
break;
}
case DSPEC_STORAGE (dspec_extern) : {
/* Deal with extern */
switch (loc) {
case CONTEXT_OBJECT :
case CONTEXT_FUNCTION : {
/* Objects and functions can be extern */
if (!IS_NULL_id (id)) {
switch (TAG_id (id)) {
case id_member_tag :
case id_mem_func_tag :
case id_stat_member_tag :
case id_stat_mem_func_tag : {
/* But not class members */
ERROR err = ERR_dcl_stc_ext_mem ();
report (crt_loc, err);
break;
}
}
}
break;
}
case CONTEXT_PARAMETER :
case CONTEXT_WEAK_PARAM : {
/* Function parameters can't be extern */
report (crt_loc, ERR_dcl_stc_param (st));
st = dspec_none;
break;
}
case CONTEXT_TEMPL_PARAM : {
/* Template parameters can't have storage class */
report (crt_loc, ERR_temp_param_dcl_stc (st));
st = dspec_none;
break;
}
default : {
/* Class members can't be extern */
report (crt_loc, ERR_dcl_stc_ext_mem ());
st = dspec_none;
break;
}
}
break;
}
case DSPEC_STORAGE (dspec_mutable) : {
/* Deal with mutable */
if (loc != CONTEXT_MEMBER) {
/* Only data members can be mutable */
report (crt_loc, ERR_dcl_stc_mut_bad ());
st = dspec_none;
}
break;
}
default : {
/* More than one storage class - select one */
DECL_SPEC nst = dspec_static;
while (!(st & nst)) {
ASSERT (nst != dspec_none);
nst <<= 1;
}
report (crt_loc, ERR_dcl_stc_dup (st, nst));
st = nst;
goto switch_label;
}
}
}
return (st);
}
/*
* CHECK STORAGE CLASS SPECIFIERS
*
* This routine extracts the function specifiers (plus friend specifiers)
* from the declaration specifiers ds in the context given by loc (as
* above). It returns a valid combination of function and friend
* specifiers.
*/
static DECL_SPEC
check_func_spec(DECL_SPEC ds, int loc)
{
DECL_SPEC fn = dspec_none;
/* Only functions can be inline */
if (ds & dspec_inline) {
if (loc == CONTEXT_FUNCTION || loc == CONTEXT_FUNC_MEMBER) {
fn |= dspec_inline;
} else {
report (crt_loc, ERR_dcl_fct_spec_inline_bad ());
}
}
/* Only function members can be virtual */
if (ds & dspec_virtual) {
if (loc == CONTEXT_FUNC_MEMBER) {
fn |= dspec_virtual;
} else {
report (crt_loc, ERR_dcl_fct_spec_virtual ());
}
}
/* Only function members can be explicit */
if (ds & dspec_explicit) {
if (loc == CONTEXT_FUNC_MEMBER) {
fn |= dspec_explicit;
} else {
report (crt_loc, ERR_dcl_fct_spec_explicit ());
}
}
/* Only functions declared in a class can be friends */
if (ds & dspec_friend) {
if (loc == CONTEXT_FUNCTION && in_class_defn) {
/* Don't add to specifier list */
/* EMPTY */
} else if (in_class_defn) {
report (crt_loc, ERR_class_friend_decl ());
} else {
report (crt_loc, ERR_dcl_friend_class ());
}
}
/* Allow for function discarding */
if (loc == CONTEXT_FUNCTION || loc == CONTEXT_FUNC_MEMBER) {
fn |= dspec_ignore;
}
return (fn);
}
/*
* CONSTRUCT A TYPE DECLARATION
*
* This routine constructs the declaration of a type with declaration
* specifiers ds (which will include typedef), type t and name id in the
* namespace ns. mem gives the member of ns corresponding to id or the
* null member for class member redeclarations.
*/
static IDENTIFIER
make_type_decl(NAMESPACE ns, DECL_SPEC ds, TYPE t, MEMBER mem, IDENTIFIER id)
{
int reported = 0;
IDENTIFIER old_id;
unsigned tag = TAG_type (t);
QUALIFIER cq = crt_id_qualifier;
HASHID nm = DEREF_hashid (id_name (id));
/* Can't have other declaration specifiers with typedef */
DECL_SPEC st = (ds & dspec_keyword);
if (st != dspec_typedef) {
st &= ~dspec_typedef;
report (crt_loc, ERR_dcl_typedef_dspec (st));
}
/* Check for function cv-qualifiers and exception specifiers */
object_type (t, id_type_alias_tag);
/* Check for previous declaration */
if (IS_NULL_member (mem)) {
mem = search_member (ns, nm, 1);
old_id = DEREF_id (member_id (mem));
old_id = redecl_inherit (old_id, cq, in_class_defn, 2);
if (IS_NULL_id (old_id)) {
report (crt_loc, ERR_lookup_qual_undef (nm, ns));
}
} else {
old_id = DEREF_id (member_id (mem));
old_id = redecl_inherit (old_id, cq, in_class_defn, 2);
}
/* Allow for type redeclarations */
#if LANGUAGE_CPP
id = type_member (mem, 3);
id = redecl_inherit (id, cq, in_class_defn, 2);
#else
id = old_id;
#endif
if (!IS_NULL_id (id) && IS_id_class_name_etc (id)) {
/* Already declared as a type name */
TYPE s;
ERROR err = NULL_err;
PTR (LOCATION) loc = id_loc (id);
unsigned long tokdefs = no_token_defns;
/* Check for reserved identifiers */
DECL_SPEC ods = DEREF_dspec (id_storage (id));
if ((ds | ods) & dspec_reserve) {
report (crt_loc, ERR_basic_odr_decl (id, loc));
return (id);
}
/* Check type compatibility */
s = DEREF_type (id_class_name_etc_defn (id));
s = check_compatible (s, t, 0, &err, 1);
/* QUERY: or is type equality required? */
/* Compatible redefinition */
if (IS_NULL_err (err)) {
NAMESPACE cns = crt_namespace;
if (cq != qual_none) {
/* Check qualified definitions */
check_decl_nspace (id, ns, 1, cns);
}
if (in_class_defn && EQ_nspace (ns, cns)) {
if (!is_tagged_type (id)) {
/* Still not allowed in a class definition */
report (crt_loc, ERR_class_mem_redecl (id, loc));
}
} else if (tokdefs == no_token_defns && !in_pragma_dir) {
/* Can't redeclare types in C */
report (crt_loc, ERR_basic_odr_typedef (id, loc));
}
if (tag == type_func_tag) {
/* Check function types */
s = redecl_func_type (id, s, t, 0, 1);
}
COPY_type (id_class_name_etc_defn (id), s);
adjust_access (id, crt_access, 0);
return (id);
}
/* Incompatible redefinition */
err = concat_error (err, ERR_basic_link_typedef (id, loc));
report (crt_loc, err);
reported = 1;
}
/* Declare the type */
id = make_typedef (ns, nm, t, dspec_none);
if (is_tagged_type (id)) {
/* Class-like typedef-names */
set_type_member (mem, id);
} else {
/* Object-like typedef-names */
if (!IS_NULL_id (old_id) && !reported) {
/* Already declared as an object */
PTR (LOCATION) loc = id_loc (old_id);
report (crt_loc, ERR_basic_odr_diff (old_id, loc));
}
set_member (mem, id);
}
if (tag == type_func_tag) {
/* Check function type */
decl_func_type (id, t, 0);
} else if (tag == type_templ_tag) {
IGNORE check_templ_params (t, id);
report (crt_loc, ERR_temp_decl_bad ());
}
return (id);
}
/*
* CREATE A SPECIAL TYPE DEFINITION
*
* This routine creates a typedef of the identifier id to the special
* type t.
*/
void
typedef_special(IDENTIFIER id, TYPE t)
{
int pushed = 0;
NAMESPACE ns = nonblock_namespace;
if (!EQ_nspace (ns, crt_namespace)) {
push_namespace (ns);
pushed = 1;
}
decl_loc = preproc_loc;
if (in_class_defn) {
id = make_member_decl (dspec_typedef, t, id, 0);
} else {
id = make_object_decl (dspec_typedef, t, id, 0);
}
if (do_dump) dump_declare (id, &decl_loc, 1);
if (pushed) {
IGNORE pop_namespace ();
}
return;
}
/*
* FIND THE LINKAGE OF AN IDENTIFIER
*
* This routine finds the linkage of the identifier id (including the
* language specifier), returning the default value st if id does not
* represent an object or function.
*/
static DECL_SPEC
find_storage(IDENTIFIER id, DECL_SPEC st, TYPE t)
{
if (!IS_NULL_id (id)) {
switch (TAG_id (id)) {
case id_variable_tag :
case id_parameter_tag :
case id_stat_member_tag :
case id_weak_param_tag : {
/* Objects */
DECL_SPEC ds = DEREF_dspec (id_storage (id));
st = (ds & (dspec_storage | dspec_language));
break;
}
case id_function_tag :
case id_mem_func_tag :
case id_stat_mem_func_tag : {
/* Functions */
DECL_SPEC ds;
#if LANGUAGE_CPP
LIST (IDENTIFIER) pids = NULL_list (IDENTIFIER);
if (!IS_NULL_type (t)) {
int eq = 0;
id = resolve_func (id, t, 0, 0, pids, &eq);
if (IS_NULL_id (id) || !IS_id_function_etc (id)) {
return (st);
}
}
#else
UNUSED (t);
#endif
ds = DEREF_dspec (id_storage (id));
st = (ds & (dspec_storage | dspec_language));
break;
}
}
}
return (st);
}
/*
* CHECK THE LOCATION OF A DECLARATION
*
* This routine checks whether the namespace cns is a suitable location
* for redeclaring the object id, which is a member of the namespace ns.
* def is true for a definition.
*/
void
check_decl_nspace(IDENTIFIER id, NAMESPACE ns, int def, NAMESPACE cns)
{
int func = 0;
int local_def = really_in_function_defn;
switch (TAG_id (id)) {
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 : {
/* Can define local types */
local_def = 0;
break;
}
case id_mem_func_tag :
case id_stat_mem_func_tag : {
/* Member function */
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds & dspec_implicit) {
/* Ignore implicit constructors etc */
return;
}
if (!def && !is_templ_nspace (ns)) {
/* Can't even redeclare in this case */
report (crt_loc, ERR_class_mfct_redecl (id));
return;
}
func = 1;
break;
}
case id_undef_tag : {
/* Report undeclared members */
HASHID nm = DEREF_hashid (id_name (id));
report (crt_loc, ERR_lookup_qual_undef (nm, ns));
return;
}
}
if (def) {
/* Check for enclosing namespace scope */
if (local_def || !is_subnspace (cns, ns)) {
/* Report badly placed definition */
ERROR err;
if (IS_nspace_ctype (ns)) {
if (func) {
/* Member functions */
err = ERR_class_mfct_scope (id);
} else {
/* Other class members */
err = ERR_class_static_data_scope (id);
}
} else {
/* Namespace members */
err = ERR_dcl_nspace_memdef_scope (id);
}
report (crt_loc, err);
}
}
return;
}
/*
* CHECK AN OBJECT TYPE
*
* This routine checks the type t for the object id with declaration
* specifiers ds.
*/
void
check_obj_decl(DECL_SPEC ds, TYPE t, IDENTIFIER id, int tentative)
{
unsigned tag = TAG_type (t);
if (tag == type_top_tag || tag == type_bottom_tag) {
/* Always report void declarations */
report (crt_loc, ERR_basic_fund_void_decl (id, t));
} else if (tag == type_templ_tag) {
/* Shouldn't have template type */
report (crt_loc, ERR_temp_decl_bad ());
} else if (ds & dspec_defn) {
/* Only check otherwise if this is a definition */
if (tag == type_array_tag) {
/* Arrays may be completed by the initialiser */
/* EMPTY */
} else if (tag == type_ref_tag) {
/* References don't need checking */
/* EMPTY */
} else {
ERROR err;
/* Tentative definitions with internal linkage must have
* complete type. */
if (!tentative || (ds & dspec_static)) {
err = check_complete (t);
if (!IS_NULL_err (err)) {
/* Other definitions should have complete type */
ERROR err2 = ERR_basic_types_def_incompl (id);
err = concat_error (err, err2);
report (crt_loc, err);
}
}
err = check_abstract (t);
if (!IS_NULL_err (err)) {
/* Objects can't have abstract type */
ERROR err2 = ERR_class_abstract_decl (id);
err = concat_error (err, err2);
report (crt_loc, err);
}
}
}
return;
}
/*
* DECLARE AN OBJECT
*
* This routine constructs the declaration of an object with declaration
* specifiers ds, type t and name id.
*/
IDENTIFIER
make_object_decl(DECL_SPEC ds, TYPE t, IDENTIFIER id, int def)
{
ERROR err;
MEMBER mem;
NAMESPACE ns;
int redef = 0;
int simple_id = 1;
int tentative = 0;
IDENTIFIER old_id;
IDENTIFIER alt_id;
DECL_SPEC st, df, rs;
IDENTIFIER prev_id = NULL_id;
unsigned tag = TAG_type (t);
unsigned itag = id_variable_tag;
HASHID nm = DEREF_hashid (id_name (id));
/* Check for template specialisations */
bound_specialise = 0;
if (is_templ_decl (id, NULL_type) || is_templ_spec (t)) {
t = bind_specialise (&id, t, ds, 0, 1, def);
if (IS_NULL_id (id)) {
/* Invalid specialisation */
crt_id_qualifier = qual_none;
crt_templ_qualifier = 0;
id = DEREF_id (hashid_id (nm));
while (IS_type_templ (t)) {
t = DEREF_type (type_templ_defn (t));
}
id = make_object_decl (ds, t, id, def);
return (id);
}
ns = DEREF_nspace (id_parent (id));
check_decl_nspace (id, ns, 0, crt_namespace);
old_id = id;
mem = NULL_member;
simple_id = 0;
} else {
/* Check on identifier name */
QUALIFIER cq = crt_id_qualifier;
err = check_id_name (id, CONTEXT_OBJECT);
if (!IS_NULL_err (err)) report (crt_loc, err);
/* Check for qualified identifiers */
if (cq == qual_none) {
/* Declaration of simple identifier */
ns = crt_namespace;
mem = search_member (ns, nm, 1);
old_id = DEREF_id (member_id (mem));
alt_id = DEREF_id (member_alt (mem));
if (!IS_NULL_id (old_id)) {
if (in_function_defn && (ds & dspec_extern)) {
/* Redeclaration of block external */
prev_id = find_previous (t, id);
if (EQ_id (prev_id, old_id)) old_id = NULL_id;
} else {
old_id = redecl_inherit (old_id, cq, 0, 2);
}
redef = 1;
} else if (!IS_NULL_id (alt_id)) {
redef = 1;
}
} else {
/* Redeclaration of class or namespace member */
ns = DEREF_nspace (id_parent (id));
check_decl_nspace (id, ns, 0, crt_namespace);
if (IS_id_undef (id)) {
if (IS_nspace_ctype (ns)) itag = id_stat_member_tag;
mem = search_member (ns, nm, 1);
old_id = NULL_id;
} else {
mem = NULL_member;
old_id = redecl_inherit (id, cq, 0, 2);
t = bind_specialise (&old_id, t, ds, 0, 0, def);
if (!IS_NULL_id (old_id)) {
id = old_id;
itag = TAG_id (id);
ns = DEREF_nspace (id_parent (id));
}
}
simple_id = 0;
}
}
/* Deal with inferred types */
ds = check_inferred_type (ds, &t, 0);
/* Check on storage class specifiers */
st = check_storage (ds, CONTEXT_OBJECT, old_id);
/* Deal with type definitions */
if (ds & dspec_typedef) {
id = make_type_decl (ns, ds, t, mem, id);
return (id);
}
/* Check on function specifiers */
IGNORE check_func_spec (ds, CONTEXT_OBJECT);
/* Find the object linkage and whether it is a definition */
if (st == dspec_extern) {
/* Explicit extern indicates a declaration (probably) */
df = dspec_none;
if (in_function_defn && simple_id) {
if (IS_NULL_id (prev_id)) {
prev_id = find_previous (t, id);
}
st = find_storage (prev_id, st, NULL_type);
} else {
prev_id = old_id;
st = find_storage (old_id, st, t);
}
} else {
if (tag == type_templ_tag && bound_specialise) {
/* A template specialisation is a declaration (probably) */
df = dspec_none;
} else {
/* Everything else is a definition */
df = dspec_defn;
}
if (in_function_defn) {
/* Objects declared in a block have no linkage */
if (IS_NULL_member (mem)) {
/* Use old linkage if explicitly qualified */
st = find_storage (old_id, dspec_none, t);
} else if (st == dspec_static) {
st = dspec_none;
} else if (st == dspec_register) {
st = (dspec_auto | dspec_register);
used_register = 1;
} else {
st = dspec_auto;
}
} else if (st == dspec_static) {
/* Check static declarations */
if (!def) tentative = LANGUAGE_C;
} else if (st == dspec_none) {
/* Objects declared const have internal linkage */
CV_SPEC qual = find_cv_qual (t);
if (qual & cv_const) {
st = const_linkage;
if (st & dspec_static) {
st = find_storage (old_id, st, t);
}
} else {
st = dspec_extern;
}
/* find_storage is not applied for objects */
if (!def) tentative = LANGUAGE_C;
}
}
/* Create the declaration */
t = lvalue_type (t);
rs = (ds & dspec_other);
ds = (st | df | rs);
if (!IS_NULL_id (old_id)) {
/* Check redeclarations */
old_id = redecl_id (ds, t, old_id, 0, 0);
if (IS_NULL_id (old_id) && IS_NULL_member (mem)) {
/* Bad redeclaration of class or namespace member */
nm = lookup_anon ();
mem = search_member (ns, nm, 1);
itag = id_stat_member_tag;
}
}
object_type (t, itag);
if (IS_NULL_id (old_id)) {
/* Construct the declaration */
ds = adjust_linkage (ds, 0);
MAKE_id_variable_etc (itag, nm, ds, ns, decl_loc, t, id);
if (in_function_defn) {
if (ds & dspec_linkage) {
/* Block function declarations */
id = unify_previous (id, t, prev_id, 0);
} else if (redef) {
/* Redeclarations of local variables */
mem = update_member (ns, mem);
}
} else {
id = unify_subsequent (id, t, 0);
}
set_member (mem, id);
if (itag == id_variable_tag && option (OPT_decl_hide)) {
check_hiding (id);
}
is_redeclared = 0;
} else {
/* Redeclare an existing object */
id = old_id;
if (IS_id_member (id)) {
t = DEREF_type (id_member_type (id));
} else {
t = DEREF_type (id_variable_etc_type (id));
}
ds = DEREF_dspec (id_storage (id));
is_redeclared = 1;
}
#if LANGUAGE_CPP
if (ds & dspec_c) c_linkage (id, 0);
#endif
check_obj_decl (ds, t, id, tentative);
return (id);
}
/*
* DECLARE A FUNCTION
*
* This routine constructs a function declaration for a function with
* declaration specifiers ds, type t and name id. The argument def is
* true to distinguish function definitions from function declarations.
*/
IDENTIFIER
make_func_decl(DECL_SPEC ds, TYPE t, IDENTIFIER id, int def)
{
ERROR err;
MEMBER mem;
int ok = 1;
int chk = 1;
unsigned it;
int simple_id = 1;
int main_func = 0;
IDENTIFIER old_id;
NAMESPACE ns, ens;
DECL_SPEC st, df, fn, rs;
IDENTIFIER over_id = NULL_id;
IDENTIFIER prev_id = NULL_id;
unsigned itag = id_function_tag;
HASHID nm = DEREF_hashid (id_name (id));
int inline_def = 0;
#if LANGUAGE_CPP
int allocator = 0;
#endif
/* Check for template specialisations */
if (is_templ_decl (id, t) || is_templ_spec (t)) {
t = bind_specialise (&id, t, ds, 0, 1, def);
if (IS_NULL_id (id)) {
/* Invalid specialisation */
crt_id_qualifier = qual_none;
crt_templ_qualifier = 0;
id = DEREF_id (hashid_id (nm));
while (IS_type_templ (t)) {
t = DEREF_type (type_templ_defn (t));
}
id = make_func_decl (ds, t, id, def);
return (id);
}
ns = DEREF_nspace (id_parent (id));
check_decl_nspace (id, ns, def, crt_namespace);
ens = ns;
mem = NULL_member;
old_id = id;
simple_id = 0;
} else {
/* Check on identifier name */
QUALIFIER cq = crt_id_qualifier;
err = check_id_name (id, CONTEXT_FUNCTION);
if (!IS_NULL_err (err)) {
report (crt_loc, err);
ok = 0;
}
/* Check for qualified identifiers */
if (cq == qual_none) {
/* Declaration of simple identifier */
ns = crt_namespace;
ens = nonblock_namespace;
mem = search_member (ns, nm, 1);
old_id = DEREF_id (member_id (mem));
if (!IS_NULL_id (old_id)) {
old_id = redecl_inherit (old_id, cq, 0, 1);
}
if (def && in_function_defn) {
/* Check for nested function definitions */
report (crt_loc, ERR_dcl_fct_def_scope ());
}
} else {
/* Redeclaration of class or namespace member */
ns = DEREF_nspace (id_parent (id));
check_decl_nspace (id, ns, def, crt_namespace);
ens = ns;
if (IS_id_undef (id)) {
if (IS_nspace_ctype (ns)) itag = id_mem_func_tag;
mem = search_member (ns, nm, 1);
old_id = NULL_id;
} else {
mem = NULL_member;
old_id = redecl_inherit (id, cq, 0, 1);
t = bind_specialise (&old_id, t, ds, 0, 0, def);
if (!IS_NULL_id (old_id)) {
id = old_id;
itag = TAG_id (id);
ns = DEREF_nspace (id_parent (id));
ens = ns;
}
}
simple_id = 0;
}
}
/* Allow for special functions */
if (EQ_KEYWORD (nm, lex_main) && IS_nspace_global (ens)) {
if (ds & dspec_typedef) {
/* Ignore type definition */
/* EMPTY */
} else {
t = check_main (t, nm);
main_func = 1;
}
}
it = TAG_hashid (nm);
#if LANGUAGE_CPP
switch (it) {
case hashid_constr_tag : {
t = check_constr (t, id, ns);
ds &= ~dspec_empty;
ds |= dspec_main;
break;
}
case hashid_destr_tag : {
t = check_destr (t, id, ns);
ds &= ~dspec_empty;
ds |= dspec_main;
break;
}
case hashid_op_tag : {
int cl = IS_nspace_ctype (ns);
t = check_operator (t, id, cl, &allocator);
if (!allocator) ds |= dspec_ignore;
break;
}
case hashid_conv_tag : {
t = check_conv (t, id);
ds &= ~dspec_empty;
chk = 0;
break;
}
}
#endif
/* Deal with inferred types */
ds = check_func_type (ds, t, def, chk, 0);
if (main_func) ds |= dspec_main;
/* Handle function definitions */
df = (def ? dspec_defn : dspec_none);
/* Check on storage class specifiers */
st = check_storage (ds, CONTEXT_FUNCTION, old_id);
if (st == dspec_static) {
/* Check on static functions */
if (main_func) {
err = ERR_basic_start_main_static (nm);
if (!IS_NULL_err (err)) {
report (crt_loc, err);
st = dspec_extern;
}
} else {
/* Check static declarations */
/* EMPTY */
}
} else if (ds & dspec_inline) {
if (st != dspec_extern) inline_def = 1;
/* Check on inline functions */
if (main_func) {
fn = dspec_inline;
report (crt_loc, ERR_basic_start_main_link (nm, fn));
ds &= ~dspec_inline;
} else {
if (st == dspec_extern && inline_linkage == dspec_static) {
/* Inline functions can't be extern */
report (crt_loc, ERR_dcl_stc_ext_inline ());
}
if (in_function_defn) {
/* Can't declare inline functions in a block */
report (crt_loc, ERR_dcl_fct_spec_block ());
st = dspec_none;
}
}
}
/* Deal with type definitions */
if (ds & dspec_typedef) {
/* Can only apply typedef to declarations, not definitions */
if (!def) {
if (ok) {
/* Recheck identifier name */
err = check_id_name (id, CONTEXT_OBJECT);
if (!IS_NULL_err (err)) report (crt_loc, err);
}
id = make_type_decl (ns, ds, t, mem, id);
return (id);
}
report (crt_loc, ERR_dcl_typedef_func ());
}
/* Check on function specifiers */
fn = check_func_spec (ds, CONTEXT_FUNCTION);
/* Find the function linkage */
if (st == dspec_extern) {
if (in_function_defn && simple_id) {
prev_id = find_previous (t, id);
st = find_storage (prev_id, st, NULL_type);
} else {
prev_id = old_id;
st = find_storage (old_id, st, t);
}
} else if (st == dspec_static) {
if (in_function_defn) {
/* Can't declare static functions in a block */
report (crt_loc, ERR_dcl_stc_stat_block ());
if (simple_id) {
prev_id = find_previous (t, id);
} else {
prev_id = old_id;
}
}
} else if (st == dspec_none) {
/* Inline functions have internal linkage */
if (fn & dspec_inline) {
st = inline_linkage;
} else {
st = dspec_extern;
}
if (in_function_defn) {
prev_id = find_previous (t, id);
st = find_storage (prev_id, st, NULL_type);
} else {
prev_id = old_id;
st = find_storage (old_id, st, t);
}
}
/* Create the declaration */
t = lvalue_type (t);
rs = (ds & dspec_other);
ds = (st | df | fn | rs);
if (!IS_NULL_id (old_id)) {
/* Check redeclarations */
old_id = redecl_func (ds, t, old_id, itag, &over_id, def);
if (IS_NULL_id (old_id) && IS_NULL_member (mem)) {
/* Bad redeclaration of class or namespace member */
nm = lookup_anon ();
mem = search_member (ns, nm, 1);
if (IS_nspace_ctype (ns)) itag = id_mem_func_tag;
over_id = NULL_id;
}
}
object_type (t, itag);
if (IS_NULL_id (old_id)) {
/* Declare the function */
ds = adjust_linkage (ds, 0);
MAKE_id_function_etc (itag, nm, ds, ns, decl_loc, t, over_id, id);
COPY_int (id_function_etc_inline_def (id), inline_def);
if (in_function_defn) {
id = unify_previous (id, t, prev_id, def);
} else {
id = unify_subsequent (id, t, def);
}
set_member (mem, id);
decl_func_type (id, t, def);
/* Check for conversion functions */
if (it == hashid_conv_tag && itag != id_mem_func_tag) {
report (crt_loc, ERR_class_conv_fct_mem ());
}
if (itag == id_function_tag && option (OPT_decl_hide)) {
check_hiding (id);
}
is_redeclared = 0;
} else {
/* Redeclare the function */
id = old_id;
inline_def &= DEREF_int (id_function_etc_inline_def (id));
COPY_int (id_function_etc_inline_def (id), inline_def);
is_redeclared = 1;
}
ds = DEREF_dspec (id_storage (id));
#if LANGUAGE_CPP
if (ds & dspec_c) c_linkage (id, def);
#endif
/* Allow for discarded functions */
if (!(rs & dspec_ignore) && option (OPT_discard_func)) {
ds &= ~dspec_ignore;
COPY_dspec (id_storage (id), ds);
}
#if LANGUAGE_CPP
if (allocator) recheck_allocator (id, allocator);
#endif
if (main_func) recheck_main (id);
return (id);
}
/*
* CHECK A PARAMETER DECLARATION
*
* This routine checks the type of the parameter id declared with type t.
*/
void
check_par_decl(TYPE t, IDENTIFIER id, int loc)
{
unsigned tag = TAG_type (t);
if (tag == type_compound_tag) {
/* Parameters can't have abstract type */
ERROR err = check_abstract (t);
if (!IS_NULL_err (err)) {
err = concat_error (err, ERR_class_abstract_par ());
report (crt_loc, err);
}
} else if (tag == type_templ_tag) {
/* Shouldn't have template type */
report (crt_loc, ERR_temp_decl_bad ());
}
if (loc == CONTEXT_WEAK_PARAM) {
/* Check for 'void' and other types */
ERROR err = check_param_type (id, t);
if (!IS_NULL_err (err)) report (crt_loc, err);
}
return;
}
/*
* DECLARE A FUNCTION PARAMETER
*
* This routine constructs the declaration of a function parameter or
* non-type template parameter (as indicated by loc) with declaration
* specifiers ds, type t and name id. Note that t is not checked - this
* is only a declaration, and t may still legitimately be void, however
* function and array parameters are adjusted to pointer parameters at
* this stage.
*/
IDENTIFIER
make_param_decl(DECL_SPEC ds, TYPE t, IDENTIFIER id, int loc)
{
ERROR err;
DECL_SPEC st, rs;
NAMESPACE ns = crt_namespace;
HASHID nm = DEREF_hashid (id_name (id));
MEMBER mem = search_member (ns, nm, 1);
IDENTIFIER old_id = DEREF_id (member_id (mem));
/* Check on identifier name */
err = check_id_name (id, loc);
if (!IS_NULL_err (err)) report (decl_loc, err);
/* Deal with inferred types */
ds = check_inferred_type (ds, &t, 0);
func_type_defn (1);
/* Check on storage class specifiers */
st = check_storage (ds, loc, old_id);
if (st == dspec_register) {
st = (dspec_auto | dspec_register);
used_register = 1;
} else {
st = dspec_auto;
}
/* Deal with type definitions */
if (ds & dspec_typedef) {
/* Can't have typedef in function parameters */
report (decl_loc, ERR_dcl_typedef_par ());
}
/* Check on function specifiers */
IGNORE check_func_spec (ds, loc);
/* Create the parameter declaration */
t = make_param_type (t, loc);
rs = (ds & dspec_other);
ds = (st | rs | dspec_defn);
if (!IS_NULL_id (old_id)) {
/* Check for redeclarations */
if (loc == CONTEXT_TEMPL_PARAM) {
report (decl_loc, ERR_temp_param_dup (nm));
nm = lookup_anon ();
mem = search_member (ns, nm, 1);
id = DEREF_id (hashid_id (nm));
} else {
if (loc == CONTEXT_WEAK_PARAM) {
if (IS_id_weak_param (old_id)) {
/* Check for order of declaration */
MEMBER mem1 = DEREF_member (nspace_last (ns));
while (!EQ_member (mem1, mem)) {
IDENTIFIER mid = DEREF_id (member_id (mem1));
if (!IS_NULL_id (mid) && IS_id_parameter (mid)) {
report (decl_loc, ERR_dcl_fct_par_order ());
break;
}
mem1 = DEREF_member (member_next (mem1));
}
old_id = NULL_id;
} else if (!IS_id_parameter (old_id)) {
report (decl_loc, ERR_dcl_fct_par_undecl (nm));
}
}
if (!IS_NULL_id (old_id)) {
if (IS_id_parameter (old_id)) {
/* Make up new name for parameter */
nm = lookup_anon ();
mem = search_member (ns, nm, 1);
}
IGNORE redecl_id (ds, t, old_id, 0, 0);
}
}
} else {
if (loc == CONTEXT_WEAK_PARAM) {
report (decl_loc, ERR_dcl_fct_par_undecl (nm));
}
}
ds = adjust_linkage (ds, 0);
if (loc == CONTEXT_TEMPL_PARAM) {
IDENTIFIER pid;
object_type (t, id_token_tag);
id = make_exp_param (t, id);
pid = DEREF_id (id_token_alt (id));
set_member (mem, pid);
} else {
t = lvalue_type (t);
object_type (t, id_parameter_tag);
MAKE_id_parameter (nm, ds, ns, decl_loc, t, id);
set_member (mem, id);
}
check_par_decl (t, id, loc);
is_redeclared = 0;
return (id);
}
/*
* DECLARE A NON-PROTOTYPE FUNCTION PARAMETER
*
* This routine is used to declare a non-prototype function parameter.
*/
IDENTIFIER
weak_param_decl(IDENTIFIER id)
{
NAMESPACE ns = crt_namespace;
HASHID nm = DEREF_hashid (id_name (id));
MEMBER mem = search_member (ns, nm, 1);
IDENTIFIER old_id = DEREF_id (member_id (mem));
if (!IS_NULL_id (old_id)) {
nm = lookup_anon ();
mem = search_member (ns, nm, 1);
IGNORE redecl_id (dspec_none, NULL_type, old_id, 0, 0);
}
MAKE_id_weak_param (nm, dspec_none, ns, decl_loc, id);
set_member (mem, id);
is_redeclared = 0;
return (id);
}
/**
* LOOK FOR A FLEXIBLE ARRAY MEMBER
*
* This routine recursively checks whether the type t contains a struct
* with a flexible array member. If one is found the function returns
* an error.
*/
ERROR
check_flex_mem(TYPE t)
{
ERROR err = NULL_err;
if (IS_type_compound (t)) {
CLASS_TYPE ct = DEREF_ctype (type_compound_defn (t));
if (!IS_NULL_id (DEREF_id (ctype_flex_mem (ct)))) {
err = ERR_class_mem_flex (t);
} else {
NAMESPACE ns = DEREF_nspace (ctype_member (ct));
MEMBER mem = DEREF_member (nspace_ctype_first (ns));
mem = next_data_member (mem, 2);
while (!IS_NULL_member (mem)) {
IDENTIFIER id = DEREF_id (member_id (mem));
TYPE s = DEREF_type (id_member_type (id));
err = check_flex_mem (s);
if (!IS_NULL_err (err)) break;
mem = DEREF_member (member_next (mem));
mem = next_data_member (mem, 2);
}
}
}
return (err);
}
/*
* CHECK A MEMBER DECLARATION
*
* This routine checks the declaration of the class member id with type
* t and declaration specifiers ds. ci is the class information for the
* class the member is added to.
*/
void
check_mem_decl(DECL_SPEC ds, TYPE t, IDENTIFIER id, CLASS_INFO ci)
{
int allow_flex_mem = (option (OPT_flex_array_member) != OPTION_DISALLOW);
unsigned tag = TAG_type (t);
IDENTIFIER mid = DEREF_id (ctype_flex_mem (crt_class));
if (!IS_NULL_id (mid)) {
/* Member after flexible array member */
ERROR err = ERR_class_mem_flex_not_last (id, mid);
report (crt_loc, err);
}
if (ds & dspec_mutable) {
/* Can't apply mutable to a const member */
CV_SPEC qual = find_cv_qual (t);
if (qual & cv_const) {
report (crt_loc, ERR_dcl_stc_mut_const (id));
if (IS_id_member (id)) {
qual &= ~cv_const;
t = qualify_type (t, qual, 0);
COPY_type (id_member_type (id), t);
}
}
}
if (tag == type_top_tag || tag == type_bottom_tag) {
/* Always report void members */
report (crt_loc, ERR_basic_fund_void_mem (id, t));
} else if (tag == type_templ_tag) {
/* Shouldn't have template type */
report (crt_loc, ERR_temp_decl_bad ());
} else if (ds & dspec_defn) {
/* Only check otherwise for defined (non-static) members */
if (tag == type_ref_tag) {
/* References don't need checking */
/* EMPTY */
} else {
ERROR err = check_complete (t);
if (!IS_NULL_err (err) && tag == type_array_tag) {
if ((ci & cinfo_struct) && allow_flex_mem) {
/* Flexible array member */
NAMESPACE ns = DEREF_nspace (ctype_member (crt_class));
MEMBER m = DEREF_member (nspace_ctype_first (ns));
m = next_data_member (m, 0);
if (!IS_NULL_member (m) && DEREF_id (member_id (m)) != id) {
/* Found another member */
COPY_id (ctype_flex_mem (crt_class), id);
destroy_error (err, 1);
err = NULL_err;
} else {
/* First member */
ERROR err2 = ERR_class_mem_flex_first ();
err = concat_error (err, err2);
}
}
}
if (!IS_NULL_err (err)) {
/* Other members should have complete type */
ERROR err2 = ERR_class_mem_incompl_mem (id);
err = concat_error (err, err2);
report (crt_loc, err);
} else {
/* Members can't have abstract type */
err = check_abstract (t);
if (!IS_NULL_err (err)) {
ERROR err2 = ERR_class_abstract_mem (id);
err = concat_error (err, err2);
report (crt_loc, err);
}
/* A struct with a flexible array member can't be a member
* of a struct */
if (ci & cinfo_struct) {
err = check_flex_mem (t);
if (!IS_NULL_err (err)) {
ERROR err2 = ERR_class_mem_flex_nested ();
err = concat_error (err, err2);
report (crt_loc, err);
}
}
}
}
}
return;
}
/*
* DECLARE A CLASS MEMBER
*
* This routine constructs the declaration of a class member with
* declaration specifiers ds, type t and name id. Note that the access
* declarations (i.e. just a qualified-id and a semicolon) can only be
* spotted at this stage. The argument sm is true if this is the first
* declarator in a list and the next token is a semicolon.
*/
IDENTIFIER
make_member_decl(DECL_SPEC ds, TYPE t, IDENTIFIER id, int sm)
{
ERROR err;
HASHID nm;
MEMBER mem;
int redef = 0;
int tokenised = 0;
IDENTIFIER old_id;
IDENTIFIER alt_id;
DECL_SPEC st, df, rs;
OFFSET off = NULL_off;
CLASS_TYPE ct = crt_class;
IDENTIFIER tok_id = NULL_id;
NAMESPACE ns = crt_namespace;
unsigned tag = TAG_type (t);
QUALIFIER cq = crt_id_qualifier;
CLASS_INFO ci = DEREF_cinfo (ctype_info (ct));
/* Check for template specialisations */
if (is_templ_decl (id, NULL_type)) {
IGNORE bind_specialise (&id, t, dspec_extern, 0, 1, 0);
if (!IS_NULL_id (id)) {
report (crt_loc, ERR_temp_spec_member (id));
}
return (id);
}
/* Find previous declaration */
nm = DEREF_hashid (id_name (id));
if (IS_hashid_constr (nm) && cq == qual_none) {
DECL_SPEC sds = (dspec_static | dspec_typedef | dspec_reserve);
if (!(ds & sds)) {
/* Can use class name for non-static member */
report (crt_loc, ERR_class_mem_ctor_data (id));
id = DEREF_id (hashid_constr_tid (nm));
nm = DEREF_hashid (id_name (id));
}
}
mem = search_member (ns, nm, 1);
old_id = DEREF_id (member_id (mem));
alt_id = DEREF_id (member_alt (mem));
if (!IS_NULL_id (old_id)) {
old_id = redecl_inherit (old_id, cq, 1, 2);
}
/* Check on member qualifications */
if (cq != qual_none) {
if ((ds & dspec_empty) && sm) {
/* Spot access declarations */
id = access_decl (id);
return (id);
}
ns = DEREF_nspace (id_parent (id));
if (EQ_nspace (ns, crt_namespace)) {
/* Qualifier indicates the current class */
report (crt_loc, ERR_dcl_meaning_mem (cq, id));
crt_id_qualifier = qual_none;
} else {
/* Qualifier indicates some other namespace */
id = make_object_decl (ds, t, id, 0);
return (id);
}
}
/* Check on identifier name */
err = check_id_name (id, CONTEXT_MEMBER);
if (!IS_NULL_err (err)) report (crt_loc, err);
/* Deal with inferred types */
ds = check_inferred_type (ds, &t, 1);
/* Check on storage class specifiers */
st = check_storage (ds, CONTEXT_MEMBER, old_id);
if (st == dspec_static) {
if (tag == type_bitfield_tag) {
/* Bitfield members can't be static */
report (crt_loc, ERR_class_bit_static ());
st = dspec_none;
df = dspec_defn;
} else {
if (ci & cinfo_union) {
/* Unions can't have static data members */
report (crt_loc, ERR_class_union_static (ct));
st = dspec_none;
df = dspec_defn;
} else {
/* Static members are only declared */
df = dspec_none;
}
}
} else {
/* Other members are defined */
df = dspec_defn;
}
/* Deal with type definitions */
if (ds & dspec_typedef) {
LIST (IDENTIFIER) ft = DEREF_list (ctype_nest (ct));
if (tag == type_bitfield_tag) {
report (crt_loc, ERR_class_bit_typedef ());
t = find_bitfield_type (t);
}
id = make_type_decl (ns, ds, t, mem, id);
CONS_id (id, ft, ft);
COPY_list (ctype_nest (ct), ft);
return (id);
}
/* Check on function specifiers */
IGNORE check_func_spec (ds, CONTEXT_MEMBER);
/* Record class properties */
rs = (ds & dspec_other);
if (rs & dspec_token) tokenised = 1;
if (st == dspec_static) {
ds = (df | rs | crt_access);
} else {
ds = (st | df | rs | crt_access);
}
/* Check for redeclarations */
t = lvalue_type (t);
if (!IS_NULL_id (old_id)) {
if (st != dspec_static && IS_id_member (old_id)) {
/* Allow for token definitions */
if (tokenised) {
/* Token with previous definition */
off = DEREF_off (id_member_off (old_id));
t = DEREF_type (id_member_type (old_id));
tokenised = 3;
} else {
tok_id = find_token (old_id);
if (IS_id_token (tok_id)) {
/* Member was previously tokenised */
DECL_SPEC tds = DEREF_dspec (id_storage (tok_id));
if (!(tds & (dspec_pure | dspec_auto))) {
/* Token can't be defined */
tokenised = 2;
}
}
}
}
if (tokenised == 0) {
/* Redeclare if neither is a token */
old_id = redecl_id (ds, t, old_id, 0, 0);
if (IS_NULL_id (old_id)) redef = 1;
}
} else if (!IS_NULL_id (alt_id)) {
redef = 1;
}
/* Create the declaration */
ds = adjust_linkage (ds, 1);
if (!really_in_function_defn) ds |= dspec_extern;
if (st == dspec_static) {
object_type (t, id_stat_member_tag);
MAKE_id_stat_member (nm, ds, ns, decl_loc, t, id);
} else {
object_type (t, id_member_tag);
MAKE_id_member (nm, ds, ns, decl_loc, t, id);
if (tokenised == 0 || tokenised == 2) {
/* Construct member offset if not tokenised */
MAKE_off_member (id, off);
}
COPY_off (id_member_off (id), off);
if (redef) mem = update_member (ns, mem);
}
/* Set the namespace member */
if (tokenised >= 2) {
/* Create dummy member */
MEMBER mem_old = DEREF_member (nspace_last (ns));
if (tokenised == 2) {
/* Define existing token */
IGNORE define_mem_token (tok_id, off, t, 1);
} else {
/* Token is defined later ... */
/* EMPTY */
}
MAKE_member_large (NULL_member, NULL_member, mem);
COPY_id (member_id (mem), id);
COPY_member (nspace_last (ns), mem);
COPY_member (member_next (mem_old), mem);
}
set_member (mem, id);
check_mem_decl (ds, t, id, ci);
is_redeclared = 0;
/* Adjust class information */
if (st == dspec_static) {
if (really_in_function_defn) {
/* Can't have static members in local classes */
report (crt_loc, ERR_class_local_static (id));
}
ci |= cinfo_static;
} else {
/* Check member types */
if (crt_access != dspec_public) ci |= cinfo_private;
ci = check_member_type (ct, ci, t, 0);
ci &= ~cinfo_empty;
}
COPY_cinfo (ctype_info (ct), ci);
return (id);
}
/*
* MAINTAIN SPECIAL MEMBER FUNCTIONS FOR A CLASS
*
* This routine updates the special member function information for
* the class ct using the function id. tag gives the identifier name tag.
*/
void
special_func_mem(CLASS_TYPE ct, IDENTIFIER id, unsigned tag, IDENTIFIER prev)
{
switch (tag) {
case hashid_constr_tag : {
/* Set constructor */
COPY_id (ctype_constr (ct), id);
break;
}
case hashid_destr_tag : {
/* Set destructor */
COPY_id (ctype_destr (ct), id);
break;
}
case hashid_conv_tag : {
/* Maintain list of conversion functions */
HASHID nm = DEREF_hashid (id_name (id));
LIST (IDENTIFIER) conv = DEREF_list (ctype_conv (ct));
LIST (IDENTIFIER) p = conv;
while (!IS_NULL_list (p)) {
IDENTIFIER pid = DEREF_id (HEAD_list (p));
HASHID pnm = DEREF_hashid (id_name (pid));
if (EQ_hashid (nm, pnm)) {
/* Already member of list */
IDENTIFIER over;
over = DEREF_id (id_function_etc_over (id));
if (!EQ_id (pid, over) && !EQ_id (pid, prev)) {
/* Template function has produced duplicate */
PTR (LOCATION) ploc = id_loc (pid);
report (crt_loc, ERR_class_mem_redecl (id, ploc));
}
COPY_id (HEAD_list (p), id);
return;
}
p = TAIL_list (p);
}
CONS_id (id, conv, conv);
COPY_list (ctype_conv (ct), conv);
break;
}
}
return;
}
/*
* DECLARE A CLASS FUNCTION MEMBER
*
* This routine constructs a function declaration for a class member
* function with declaration specifiers ds, type t and name id. The
* argument def is true to distinguish function definitions from function
* declarations.
*/
#if LANGUAGE_CPP
IDENTIFIER
make_func_mem_decl(DECL_SPEC ds, TYPE t, IDENTIFIER id, int def)
{
ERROR err;
MEMBER mem;
int ok = 1;
int chk = 1;
unsigned it;
unsigned itag;
int allocator = 0;
IDENTIFIER old_id;
LIST (VIRTUAL) vt;
DECL_SPEC st, df, fn, rs;
IDENTIFIER over_id = NULL_id;
IDENTIFIER hide_id = NULL_id;
NAMESPACE ns = crt_namespace;
QUALIFIER cq = crt_id_qualifier;
HASHID nm = DEREF_hashid (id_name (id));
CLASS_TYPE ct = crt_class;
CLASS_INFO ci = DEREF_cinfo (ctype_info (ct));
/* Check for template specialisations */
if (is_templ_decl (id, t) || is_templ_spec (t)) {
st = (dspec_extern | (ds & dspec_inline));
IGNORE bind_specialise (&id, t, st, 0, 1, def);
if (!IS_NULL_id (id)) {
report (crt_loc, ERR_temp_spec_member (id));
}
return (id);
}
/* Find previous declaration */
mem = search_member (ns, nm, 1);
old_id = DEREF_id (member_id (mem));
if (!IS_NULL_id (old_id)) {
old_id = redecl_inherit (old_id, cq, 1, 1);
}
/* Check on member qualifications */
if (cq != qual_none) {
ns = DEREF_nspace (id_parent (id));
if (EQ_nspace (ns, crt_namespace)) {
/* Qualifier indicates the current class */
report (crt_loc, ERR_dcl_meaning_mem (cq, id));
crt_id_qualifier = qual_none;
} else {
/* Qualifier indicates some other namespace */
id = make_func_decl (ds, t, id, def);
return (id);
}
}
/* Check on identifier name */
err = check_id_name (id, CONTEXT_FUNC_MEMBER);
if (!IS_NULL_err (err)) {
report (crt_loc, err);
ok = 0;
}
/* Allow for special functions */
it = TAG_hashid (nm);
switch (it) {
case hashid_constr_tag : {
t = check_constr (t, id, ns);
ds &= ~dspec_empty;
ds |= dspec_main;
break;
}
case hashid_destr_tag : {
t = check_destr (t, id, ns);
ds &= ~dspec_empty;
ds |= dspec_main;
break;
}
case hashid_op_tag : {
t = check_operator (t, id, 1, &allocator);
if (!allocator) ds |= dspec_ignore;
break;
}
case hashid_conv_tag : {
t = check_conv (t, id);
ds &= ~dspec_empty;
chk = 0;
break;
}
}
/* Deal with inferred types */
ds = check_func_type (ds, t, def, chk, 1);
/* Handle function definitions */
if (def) {
/* Functions defined in a class are inline */
df = dspec_defn;
ds |= dspec_inline;
} else {
df = dspec_none;
}
/* Check on storage class specifiers */
st = check_storage (ds, CONTEXT_FUNC_MEMBER, old_id);
/* Deal with type definitions */
if (ds & dspec_typedef) {
/* Can only apply typedef to declarations, not definitions */
if (!def) {
if (ok) {
/* Recheck identifier name */
err = check_id_name (id, CONTEXT_MEMBER);
if (!IS_NULL_err (err)) report (crt_loc, err);
}
id = make_type_decl (ns, ds, t, mem, id);
return (id);
}
report (crt_loc, ERR_dcl_typedef_func ());
}
/* Check special functions */
if (allocator) {
/* Allocator functions are implicitly static */
if (st != dspec_static) {
st = dspec_static;
allocator |= 0x4;
}
} else if (st == dspec_static) {
switch (it) {
case hashid_constr_tag : {
/* Constructors can't be static */
report (crt_loc, ERR_class_ctor_static ());
st = dspec_none;
break;
}
case hashid_destr_tag : {
/* Destructors can't be static */
report (crt_loc, ERR_class_dtor_static ());
st = dspec_none;
break;
}
case hashid_op_tag : {
/* Overloaded operators can't be static */
report (crt_loc, ERR_over_oper_static ());
st = dspec_none;
break;
}
case hashid_conv_tag : {
/* Conversion functions can't be static */
report (crt_loc, ERR_class_conv_fct_mem ());
st = dspec_none;
break;
}
}
}
/* Check on function specifiers */
fn = check_func_spec (ds, CONTEXT_FUNC_MEMBER);
vt = overrides_virtual (ct, nm, t, &hide_id);
if (!IS_NULL_list (vt)) {
/* Check for overriding of virtual functions */
if (!(fn & dspec_virtual)) {
if (!(ds & dspec_implicit)) {
err = ERR_class_virtual_override (nm);
if (!IS_NULL_err (err)) report (crt_loc, err);
}
fn |= dspec_virtual;
}
} else if (!IS_NULL_id (hide_id)) {
err = ERR_class_virtual_hide (nm, hide_id);
if (!IS_NULL_err (err)) report (crt_loc, err);
}
if (fn & dspec_virtual) {
if (st == dspec_static) {
/* Static members can't be virtual */
err = ERR_class_static_mfct_virt ();
if (allocator & 0x4) {
/* Allocator functions are implicitly static */
err = concat_error (ERR_class_free_static (nm), err);
}
report (crt_loc, err);
fn &= ~dspec_virtual;
} else if (it == hashid_constr_tag) {
/* Constructors can't be virtual */
report (crt_loc, ERR_class_ctor_virtual ());
fn &= ~dspec_virtual;
} else {
if (ci & cinfo_union) {
/* Unions can't have virtual functions */
report (crt_loc, ERR_class_union_virtual (ct));
fn &= ~dspec_virtual;
} else if (ds & dspec_template) {
/* Can't have a virtual template function */
report (crt_loc, ERR_temp_mem_virtual ());
fn &= ~dspec_virtual;
} else {
/* The current class is polymorphic */
ci |= cinfo_polymorphic;
ci &= ~cinfo_empty;
}
}
}
if (fn & dspec_explicit) {
/* Only constructors can be explicit */
if (it == hashid_constr_tag) {
have_constr_expl = 1;
} else {
if (it == hashid_conv_tag) {
report (crt_loc, ERR_dcl_fct_spec_expl_conv ());
have_conv_expl = 1;
} else {
report (crt_loc, ERR_dcl_fct_spec_expl_constr ());
fn &= ~dspec_explicit;
}
}
}
if (ds & dspec_template) {
/* Check for template function members */
if (it == hashid_destr_tag) {
report (crt_loc, ERR_temp_mem_destr ());
}
}
/* Record class properties */
rs = (ds & dspec_other);
if (st == dspec_static) {
itag = id_stat_mem_func_tag;
ds = (df | fn | rs | crt_access);
} else {
itag = id_mem_func_tag;
ds = (st | df | fn | rs | crt_access);
}
if (!(ds & dspec_implicit)) ci |= cinfo_function;
COPY_cinfo (ctype_info (ct), ci);
/* Create the function declaration */
t = lvalue_type (t);
if (!IS_NULL_id (old_id)) {
/* Check for redeclarations */
IGNORE redecl_func (ds, t, old_id, itag, &over_id, def);
}
object_type (t, itag);
ds = adjust_linkage (ds, 1);
if (!really_in_function_defn) ds |= dspec_extern;
MAKE_id_function_etc (itag, nm, ds, ns, decl_loc, t, over_id, id);
if (!IS_NULL_id (over_id)) {
id = hide_functions (id, over_id, 1);
}
set_member (mem, id);
decl_func_type (id, t, def);
is_redeclared = 0;
ds = DEREF_dspec (id_storage (id));
/* Maintain lists of functions */
special_func_mem (ct, id, it, old_id);
if (def) {
LIST (IDENTIFIER) ft = DEREF_list (ctype_nest (ct));
CONS_id (id, ft, ft);
COPY_list (ctype_nest (ct), ft);
}
if (ds & dspec_virtual) {
add_virtual (ct, id, vt);
}
if (allocator) {
allocator &= 0x3;
recheck_allocator (id, allocator);
}
/* Allow for discarded functions */
if (!(rs & dspec_ignore) && option (OPT_discard_func)) {
ds &= ~dspec_ignore;
COPY_dspec (id_storage (id), ds);
}
return (id);
}
#endif
/*
* DECLARE A FRIEND FUNCTION
*
* This routine is used to handle the declaration of a friend function
* within a class. The parameters are identical to those in the previous
* routine.
*/
#if LANGUAGE_CPP
IDENTIFIER
make_friend_decl(DECL_SPEC ds, TYPE t, IDENTIFIER id, int def, int chum)
{
ERROR err;
MEMBER mem;
int chk = 1;
unsigned it;
int allocator = 0;
int main_func = 0;
IDENTIFIER old_id;
NAMESPACE ns, ens;
DECL_SPEC st, df, fn, rs;
IDENTIFIER over_id = NULL_id;
unsigned itag = id_function_tag;
QUALIFIER cq = crt_id_qualifier;
int td = crt_templ_qualifier;
HASHID nm = DEREF_hashid (id_name (id));
/* Check for template specialisations */
if (in_template_decl && cq == qual_none && td == 0 && chum) {
TYPE s = injected_type (t, 1);
if (!EQ_type (s, t)) {
/* Friend declaration is implicitly a template */
int eq = 0;
IDENTIFIER tid = make_friend_decl (ds, s, id, 0, 0);
if (do_dump) dump_declare (tid, &decl_loc, 0);
id = deduce_func (tid, t, &eq);
if (IS_NULL_id (id)) return (tid);
crt_templ_qualifier = 1;
}
}
if (is_templ_decl (id, t) || is_templ_spec (t)) {
t = bind_specialise (&id, t, ds, 0, 1, def);
if (IS_NULL_id (id)) {
/* Invalid specialisation */
crt_id_qualifier = qual_none;
crt_templ_qualifier = 0;
id = DEREF_id (hashid_id (nm));
while (IS_type_templ (t)) {
t = DEREF_type (type_templ_defn (t));
}
id = make_friend_decl (ds, t, id, def, chum);
return (id);
}
ns = DEREF_nspace (id_parent (id));
ens = ns;
mem = NULL_member;
old_id = id;
if (def) {
/* Check enclosing namespace scope */
check_decl_nspace (id, ns, def, nonclass_namespace);
}
} else {
/* Deal with typedef immediately */
if ((ds & dspec_typedef) && !def) {
id = make_func_mem_decl (ds, t, id, def);
return (id);
}
/* Check on identifier name */
err = check_id_name (id, CONTEXT_FUNCTION);
if (!IS_NULL_err (err)) report (crt_loc, err);
/* Check on member qualification */
if (cq == qual_none) {
/* Declaration of simple identifier */
ns = nonclass_namespace;
ens = nonblock_namespace;
mem = search_member (ns, nm, 1);
old_id = DEREF_id (member_id (mem));
if (!IS_NULL_id (old_id)) {
old_id = redecl_inherit (old_id, cq, 0, 1);
}
if (in_template_decl && is_templ_nspace (crt_namespace)) {
/* Friend of template class */
NAMESPACE tns = templ_namespace;
mem = search_member (tns, nm, 1);
}
} else {
/* Redeclaration of class or namespace member */
ns = DEREF_nspace (id_parent (id));
ens = ns;
/* QUERY: Any other restrictions? */
if (IS_id_undef (id)) {
report (crt_loc, ERR_lookup_qual_undef (nm, ns));
if (IS_nspace_ctype (ns)) itag = id_mem_func_tag;
mem = search_member (ns, nm, 1);
old_id = NULL_id;
} else {
mem = NULL_member;
old_id = redecl_inherit (id, cq, 0, 1);
t = bind_specialise (&old_id, t, ds, 0, 0, def);
if (!IS_NULL_id (old_id)) {
id = old_id;
itag = TAG_id (id);
ns = DEREF_nspace (id_parent (id));
ens = ns;
if (def) {
/* Check enclosing namespace scope */
check_decl_nspace (id, ns, def, nonclass_namespace);
}
}
}
}
}
/* Can't define function in local class */
if (def && really_in_function_defn) {
report (crt_loc, ERR_class_friend_local ());
}
/* Allow for special functions */
if (EQ_KEYWORD (nm, lex_main) && IS_nspace_global (ens)) {
/* Declare main as a friend - it could happen */
t = check_main (t, nm);
main_func = 1;
}
it = TAG_hashid (nm);
switch (it) {
case hashid_constr_tag : {
t = check_constr (t, id, ns);
ds |= dspec_main;
break;
}
case hashid_destr_tag : {
t = check_destr (t, id, ns);
ds |= dspec_main;
break;
}
case hashid_op_tag : {
int cl = IS_nspace_ctype (ns);
t = check_operator (t, id, cl, &allocator);
if (!allocator) ds |= dspec_ignore;
break;
}
case hashid_conv_tag : {
t = check_conv (t, id);
chk = 0;
break;
}
}
/* Deal with inferred types */
ds = check_func_type (ds, t, def, chk, 1);
if (main_func) ds |= dspec_main;
/* Check on storage class specifiers */
st = check_storage (ds, CONTEXT_FUNCTION, old_id);
if (st != dspec_none) {
/* Can't have storage class with friend */
report (crt_loc, ERR_class_friend_storage (st));
}
if (def) {
/* Functions defined in a class are inline */
df = dspec_defn;
ds |= dspec_inline;
st = find_storage (old_id, inline_linkage, t);
} else if (ds & dspec_inline) {
df = dspec_none;
st = find_storage (old_id, inline_linkage, t);
} else {
df = dspec_none;
st = find_storage (old_id, dspec_extern, t);
}
if ((ds & dspec_inline) && main_func) {
report (crt_loc, ERR_basic_start_main_link (nm, dspec_inline));
ds &= ~dspec_inline;
st = dspec_extern;
}
/* Check on function specifiers */
fn = check_func_spec (ds, CONTEXT_FUNCTION);
/* Create the declaration */
t = lvalue_type (t);
rs = (ds & dspec_other);
ds = (st | df | fn | rs);
if (!IS_NULL_id (old_id)) {
/* Check redeclarations */
old_id = redecl_func (ds, t, old_id, itag, &over_id, def);
if (IS_NULL_id (old_id) && IS_NULL_member (mem)) {
/* Bad redeclaration of class or namespace member */
nm = lookup_anon ();
mem = search_member (ns, nm, 1);
if (IS_nspace_ctype (ns)) itag = id_mem_func_tag;
over_id = NULL_id;
}
}
object_type (t, itag);
if (IS_NULL_id (old_id)) {
/* Declare the function */
ds = adjust_linkage (ds, 0);
MAKE_id_function_etc (itag, nm, ds, ns, decl_loc, t, over_id, id);
id = unify_subsequent (id, t, def);
set_member (mem, id);
decl_func_type (id, t, def);
report (decl_loc, ERR_class_friend_pre (id));
/* Check for conversion functions */
if (it == hashid_conv_tag && itag != id_mem_func_tag) {
report (crt_loc, ERR_class_conv_fct_mem ());
}
if (itag == id_function_tag && option (OPT_decl_hide)) {
check_hiding (id);
}
is_redeclared = 0;
} else {
/* Redeclare the function */
id = old_id;
is_redeclared = 1;
}
ds = DEREF_dspec (id_storage (id));
#if LANGUAGE_CPP
if (ds & dspec_c) c_linkage (id, def);
#endif
/* Maintain list of inline functions */
if (def) {
CLASS_TYPE ct = crt_class;
LIST (IDENTIFIER) ft = DEREF_list (ctype_nest (ct));
CONS_id (id, ft, ft);
COPY_list (ctype_nest (ct), ft);
}
/* Allow for discarded functions */
if (!(rs & dspec_ignore) && option (OPT_discard_func)) {
ds &= ~dspec_ignore;
COPY_dspec (id_storage (id), ds);
}
/* Make the function a friend */
if (chum) friend_function (crt_class, id, 1);
if (main_func) recheck_main (id);
if (allocator) recheck_allocator (id, allocator);
return (id);
}
#endif
/*
* CHECK ON AN ANONYMOUS UNION
*
* An anonymous union cannot have private or protected members or member
* functions (in addition, no union can have static data members). This
* information is readily accessible from the class information field.
* The routine class_info is not used in this case because it includes
* the name of ct in its errors, which is not particularly useful when
* ct is anonymous.
*/
#if LANGUAGE_CPP
static void
check_anon_union(CLASS_TYPE ct)
{
CLASS_INFO ci = DEREF_cinfo (ctype_info (ct));
if (ci & cinfo_private) {
report (crt_loc, ERR_class_union_anon_private ());
}
if (ci & cinfo_function) {
report (crt_loc, ERR_class_union_anon_func ());
}
return;
}
#endif
/*
* CHECK AN EMPTY OBJECT DECLARATION
*
* This routine handles a declaration with no declarators, comprising the
* declaration specifiers ds, the template type qualifiers q and the type
* specifiers t (which may be an elaborate pre-type). Whether this is
* legal depends on the value of the flag have_type_specifier described
* in predict.c, which indicates whether t contained a type declaration
* or definition. Note that anonymous unions come under this heading
* (I hate anonymous unions).
*/
static IDENTIFIER
empty_object_decl(DECL_SPEC ds, TYPE q, TYPE t)
{
CV_SPEC qual;
DECL_SPEC st;
IDENTIFIER tid;
BASE_TYPE key = btype_class;
int td = have_type_declaration;
/* Check for type declarations */
if (IS_type_pre (t)) {
QUALIFIER it;
DESTROY_type_pre (destroy, qual, tid, key, it, t);
if (it == qual_none) {
/* Unqualified identifier */
DECL_SPEC mode = (dspec_defn | dspec_auto);
td = TYPE_DECL_ELABORATE;
tid = find_elaborate_type (tid, key, q, mode);
} else {
/* Qualified identifier */
DECL_SPEC mode = (dspec_defn | dspec_alias);
td = TYPE_DECL_OVER_ELAB;
if (!IS_NULL_type (q)) td = TYPE_DECL_ELABORATE;
tid = find_elaborate_type (tid, key, q, mode);
}
#if LANGUAGE_CPP
t = DEREF_type (id_class_name_etc_defn (tid));
t = qualify_type (t, qual, 0);
#endif
} else {
tid = DEREF_id (type_name (t));
qual = DEREF_cv (type_qual (t));
}
qual &= cv_qual;
#if LANGUAGE_CPP
/* Check for anonymous unions */
if (td == TYPE_DECL_ANON && !(ds & dspec_typedef)) {
TYPE s = t;
while (IS_type_templ (s)) {
s = DEREF_type (type_templ_defn (s));
}
if (IS_type_compound (s)) {
CLASS_TYPE ct = DEREF_ctype (type_compound_defn (s));
CLASS_INFO ci = DEREF_cinfo (ctype_info (ct));
if (ci & cinfo_union) {
/* Anonymous union found */
int def;
HASHID nm;
IDENTIFIER id;
check_anon_union (ct);
/* Create the union object */
nm = lookup_anon ();
id = DEREF_id (hashid_id (nm));
crt_id_qualifier = qual_none;
crt_templ_qualifier = 0;
id = make_object_decl (ds, t, id, 0);
ds = DEREF_dspec (id_storage (id));
if (ds & dspec_extern) {
/* Anonymous unions can't have external linkage */
report (crt_loc, ERR_class_union_anon_static ());
}
def = init_object (id, NULL_exp);
if (do_dump) dump_declare (id, &crt_loc, def);
if (output_diag && !in_function_defn) {
compile_variable (id, 1);
}
/* Bring fields into scope */
if (!redecl_anon_union (ct, ds, id)) {
report (crt_loc, ERR_dcl_dcl_empty ());
}
return (id);
}
}
}
#endif
/* Check declaration specifiers */
if (ds != dspec_none) {
st = check_storage (ds, CONTEXT_OBJECT, NULL_id);
if (st != dspec_none) {
/* Can't have a storage class specifier */
report (crt_loc, ERR_dcl_stc_bad (st));
}
if (ds & dspec_typedef) {
report (crt_loc, ERR_dcl_typedef_dcl ());
}
IGNORE check_func_spec (ds, CONTEXT_OBJECT);
}
/* Check type qualifiers */
if (qual != cv_none && td != TYPE_DECL_NONE) {
report (crt_loc, ERR_dcl_type_cv_unused (qual));
}
/* Check for type definitions */
switch (td) {
case TYPE_DECL_NONE : {
report (crt_loc, ERR_dcl_dcl_empty ());
tid = NULL_id;
break;
}
case TYPE_DECL_ANON : {
report (crt_loc, ERR_dcl_dcl_anon ());
break;
}
case TYPE_DECL_OVER_ELAB : {
report (crt_loc, ERR_dcl_type_elab_qual (key));
break;
}
}
return (tid);
}
/*
* CHECK AN EMPTY MEMBER DECLARATION
*
* This routine handles a class member declaration with no declarators,
* comprising the declaration specifiers ds, the template type qualifiers
* q and the type specifiers t. This is similar to empty_object_decl,
* except that provision needs to be made for friend declarations of the
* form 'friend class C'.
*/
static IDENTIFIER
empty_member_decl(DECL_SPEC ds, TYPE q, TYPE t)
{
CV_SPEC qual;
IDENTIFIER tid;
BASE_TYPE key = btype_class;
int td = have_type_declaration;
/* Check for type declarations */
if (IS_type_pre (t)) {
QUALIFIER it;
DESTROY_type_pre (destroy, qual, tid, key, it, t);
if (it == qual_none) {
/* Unqualified identifier */
DECL_SPEC mode = dspec_defn;
if (option (OPT_class_scope)) mode |= dspec_auto;
if (ds & dspec_friend) {
mode = (dspec_defn | dspec_used | dspec_friend);
if (in_template_decl && is_templ_nspace (crt_namespace)) {
/* Friend of template class */
mode |= dspec_template;
}
mode |= dspec_register;
}
tid = find_elaborate_type (tid, key, q, mode);
if (mode & dspec_template) {
NAMESPACE ns = DEREF_nspace (id_parent (tid));
if (EQ_nspace (ns, templ_namespace)) {
ns = nonclass_namespace;
COPY_nspace (id_parent (tid), ns);
}
}
td = TYPE_DECL_ELABORATE;
} else {
/* Qualified identifier */
DECL_SPEC mode = (dspec_defn | dspec_alias);
if (ds & dspec_friend) mode |= dspec_friend;
tid = find_elaborate_type (tid, key, q, mode);
td = TYPE_DECL_OVER_ELAB;
}
t = DEREF_type (id_class_name_etc_defn (tid));
t = qualify_type (t, qual, 0);
} else {
tid = DEREF_id (type_name (t));
qual = DEREF_cv (type_qual (t));
}
qual &= cv_qual;
#if LANGUAGE_CPP
/* Check for anonymous unions */
if (td == TYPE_DECL_ANON && !(ds & dspec_typedef)) {
TYPE s = t;
while (IS_type_templ (s)) {
s = DEREF_type (type_templ_defn (s));
}
if (IS_type_compound (s)) {
CLASS_TYPE ct = DEREF_ctype (type_compound_defn (s));
CLASS_INFO ci = DEREF_cinfo (ctype_info (ct));
if (ci & cinfo_union) {
/* Anonymous union found */
int def;
HASHID nm;
IDENTIFIER id;
check_anon_union (ct);
/* Create union member */
nm = lookup_anon ();
id = DEREF_id (hashid_id (nm));
crt_id_qualifier = qual_none;
crt_templ_qualifier = 0;
if (ds & dspec_static) {
/* Can't be a static data member */
report (crt_loc, ERR_class_union_anon_mem ());
ds &= ~dspec_static;
}
id = make_member_decl (ds, t, id, 0);
def = init_member (id, NULL_exp);
if (do_dump) dump_declare (id, &crt_loc, def);
/* Bring fields into scope */
if (!redecl_anon_union (ct, dspec_none, id)) {
report (crt_loc, ERR_class_mem_empty ());
}
return (id);
}
}
}
#endif
/* Check declaration specifiers */
if (ds != dspec_none) {
CLASS_TYPE cs = crt_class;
CLASS_TYPE ct = NULL_ctype;
DECL_SPEC st = check_storage (ds, CONTEXT_MEMBER, NULL_id);
if (st != dspec_none) {
/* Can't have a storage class specifier */
report (crt_loc, ERR_dcl_stc_bad (st));
}
if (ds & dspec_typedef) {
report (crt_loc, ERR_dcl_typedef_dcl ());
}
if (ds & dspec_friend) {
/* Allow for friend declarations */
while (IS_type_templ (t)) {
t = DEREF_type (type_templ_defn (t));
}
if (IS_type_compound (t)) {
ct = DEREF_ctype (type_compound_defn (t));
} else if (is_templ_type (t)) {
IDENTIFIER cid = DEREF_id (type_token_tok (t));
ct = find_class (cid);
}
if (!IS_NULL_ctype (ct)) {
switch (td) {
case TYPE_DECL_ELABORATE :
case TYPE_DECL_OVER_ELAB : {
/* Have 'friend class A;' */
break;
}
case TYPE_DECL_NORMAL : {
/* Have 'friend class A { ... };' */
report (crt_loc, ERR_class_friend_def ());
break;
}
case TYPE_DECL_ANON : {
/* Have 'friend class { ... };' */
report (crt_loc, ERR_class_mem_anon ());
report (crt_loc, ERR_class_friend_def ());
break;
}
default : {
/* Have 'friend A;' */
report (crt_loc, ERR_class_friend_elab ());
break;
}
}
ds &= ~dspec_friend;
}
}
IGNORE check_func_spec (ds, CONTEXT_MEMBER);
if (!IS_NULL_ctype (ct)) {
/* Declare a class as a friend */
tid = DEREF_id (ctype_name (ct));
if (qual != cv_none) {
report (crt_loc, ERR_dcl_type_cv_unused (qual));
}
friend_class (cs, tid, 1);
return (tid);
}
}
/* Check type qualifiers */
if (qual != cv_none && td != TYPE_DECL_NONE) {
report (crt_loc, ERR_dcl_type_cv_unused (qual));
}
/* Check for type definitions */
switch (td) {
case TYPE_DECL_NONE : {
report (crt_loc, ERR_class_mem_empty ());
tid = NULL_id;
break;
}
case TYPE_DECL_ANON : {
report (crt_loc, ERR_class_mem_anon ());
break;
}
case TYPE_DECL_OVER_ELAB : {
report (crt_loc, ERR_dcl_type_elab_qual (key));
break;
}
}
return (tid);
}
/*
* CHECK AN EMPTY DECLARATION
*
* This routine checks the empty declaration declared with the declaration
* specifiers ds, the template type qualifiers q, the pre-type bt and t
* and the cv-qualifiers cv (not all of which will be empty). The tok
* parameter is used to pass in the number of the last lexical token read,
* this is used for a backwards compatibility hack involving types such
* as wchar_t which used to be defined by typedefs, but are now keywords.
* mem is true for member declarations.
*/
IDENTIFIER
empty_decl(DECL_SPEC ds, TYPE q, BASE_TYPE bt, TYPE t, CV_SPEC cv, int tok,
int mem)
{
/* Check for empty declarations */
IDENTIFIER id;
int have_specifier = 1;
decl_loc = crt_loc;
if (ds == dspec_none && bt == btype_none && cv == cv_none) {
if (IS_NULL_type (q)) {
if (IS_NULL_type (t)) {
/* Only semicolon in declaration */
if (mem) {
if (tok != lex_func_Hop) {
/* Allowed after function definition */
report (crt_loc, ERR_class_mem_semicolon ());
}
} else {
report (crt_loc, ERR_dcl_dcl_semicolon ());
}
return (NULL_id);
}
if (have_type_declaration == TYPE_DECL_NONE) {
/* No type specifier in declaration */
have_specifier = 0;
}
}
}
#if LANGUAGE_C
/* Check 'for' statements */
if (in_for_decl) {
report (crt_loc, ERR_stmt_for_no_object ());
return (NULL_id);
}
#endif
/* Check for definitions of built-in types */
if ((ds & dspec_typedef) && !mem) {
BASE_TYPE bs = key_type (tok);
if (bs) {
/* Type is now a keyword */
HASHID nm = KEYWORD (tok);
id = DEREF_id (hashid_id (nm));
bt &= ~bs;
t = complete_pre_type (bt, t, cv, 0);
report (crt_loc, ERR_lex_key_word (tok));
crt_id_qualifier = qual_none;
crt_templ_qualifier = 0;
id = make_object_decl (ds, t, id, 0);
return (id);
}
}
/* Check for type access declarations */
if (!have_specifier && mem) {
if (!IS_NULL_type (t) && IS_type_pre (t)) {
BASE_TYPE key = DEREF_btype (type_pre_rep (t));
if (key == btype_alias) {
IDENTIFIER tid = DEREF_id (type_name (t));
QUALIFIER qual = DEREF_qual (type_pre_nqual (t));
if (qual && !IS_NULL_id (tid)) {
id = access_decl (tid);
return (id);
}
}
}
}
/* Perform the declaration */
t = empty_complete_pre_type (bt, t, cv, 0);
if (mem) {
report (crt_loc, ERR_class_mem_declarator ());
id = empty_member_decl (ds, q, t);
} else {
id = empty_object_decl (ds, q, t);
}
if (in_weak_param) {
/* Shouldn't happen in parameter declaration lists */
report (crt_loc, ERR_dcl_fct_par_none ());
}
return (id);
}
/*
* UPDATE EXTERNAL DECLARATION INFORMATION
*
* This routine is called after each external declaration. d gives
* the number of objects declared and e gives the associated expression
* for an asm definition. Note that this is the point of instantiation
* for any pending templates.
*/
void
external_declaration(EXP e, int d)
{
if (crt_file_type == 0) {
no_declarations += (unsigned long) d;
}
if (!IS_NULL_exp (e)) {
/* Compile any asm definitions */
compile_asm (e);
}
if (!in_preproc_dir) {
if (crt_access_list.pending) {
/* Clear any outstanding access checks */
IGNORE report_access (NULL_id);
}
if (!IS_NULL_list (pending_templates)) {
/* Instantiate any pending templates */
clear_templates (0);
}
}
compile_weak_pending ();
return;
}
syntax highlighted by Code2HTML, v. 0.9.1