Subversion Repositories tendra.SVN

Rev

Rev 81 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (c) 2002-2006 The TenDRA Project <http://www.tendra.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,
 *    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.
 * 3. Neither the name of The TenDRA Project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``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 COPYRIGHT HOLDERS OR
 * CONTRIBUTORS 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.
 *
 * $Id$
 */
/*
                 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.
*/


#include "config.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "etype_ops.h"
#include "exp_ops.h"
#include "ftype_ops.h"
#include "graph_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "member_ops.h"
#include "nat_ops.h"
#include "nspace_ops.h"
#include "str_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "tdf.h"
#include "access.h"
#include "assign.h"
#include "basetype.h"
#include "buffer.h"
#include "cast.h"
#include "check.h"
#include "chktype.h"
#include "class.h"
#include "compile.h"
#include "constant.h"
#include "construct.h"
#include "convert.h"
#include "declare.h"
#include "derive.h"
#include "destroy.h"
#include "dump.h"
#include "exception.h"
#include "expression.h"
#include "file.h"
#include "function.h"
#include "hash.h"
#include "identifier.h"
#include "initialise.h"
#include "instance.h"
#include "inttype.h"
#include "literal.h"
#include "namespace.h"
#include "overload.h"
#include "parse.h"
#include "predict.h"
#include "print.h"
#include "statement.h"
#include "syntax.h"
#include "template.h"
#include "tok.h"
#include "tokdef.h"
#include "token.h"
#include "ustring.h"


/*
    INLINE MEMBER DEFINITIONS

    A static member can be defined inline in its class.  This is only a
    provisional definition for use in constant expressions etc.  The real
    definition (which cannot contain an initialiser) must be provided
    elsewhere.  This value is used to indicate such members.
*/

#define dspec_stat_inline       dspec_explicit
#define dspec_ignore_mem        (dspec_alias | dspec_inherit | dspec_token)


/*
    MEMBER NUMBER

    This variable is used to keep track of the number of data members
    within next_data_member.  Anonymous unions, rather than their members,
    are counted.
*/

unsigned long member_no = 0;


/*
    FIND NEXT DATA MEMBER

    This routine returns the first non-static, non-function member of a
    class following mem.  Anonymous unions are included if bit 1 of bf
    is false, but their members if it is true.  Anonymous bitfields are
    included if bit 0 of bf is true.  The null member is returned if there
    are no further members.
*/

MEMBER
next_data_member(MEMBER mem, int bf)
{
        while (!IS_NULL_member(mem)) {
                IDENTIFIER id = DEREF_id(member_id(mem));
                if (!IS_NULL_id(id) && IS_id_member(id)) {
                        DECL_SPEC ds = DEREF_dspec(id_storage(id));
                        if (!(ds & dspec_ignore_mem)) {
                                int ok = 1;
                                HASHID nm = DEREF_hashid(id_name(id));
                                if (ds & dspec_reserve) {
                                        /* Anonymous union members */
                                        if (!(bf & 2)) {
                                                ok = 0;
                                        }
                                } else {
                                        member_no++;
                                }
                                if (IS_hashid_anon(nm)) {
                                        TYPE t = DEREF_type(id_member_type(id));
                                        unsigned tag = TAG_type(t);
                                        if (tag == type_bitfield_tag) {
                                                /* Anonymous bitfield */
                                                if (!(bf & 1)) {
                                                        ok = 0;
                                                }
                                        } else if (tag == type_compound_tag) {
                                                /* Anonymous union */
                                                if (bf & 2) {
                                                        ok = 0;
                                                }
                                        }
                                }
                                if (ok) {
                                        return (mem);
                                }
                        }
                }
                mem = DEREF_member(member_next(mem));
        }
        return (NULL_member);
}


/*
    CONSTRUCT A DYNAMIC INITIALISER

    This routine checks whether the expression e is a non-constant
    initialiser for id (or a component of id if off is not null).  If so
    it is embedded in a dynamic initialiser expression and an error is
    reported.
*/

EXP
dynamic_init(IDENTIFIER id, string off, EXP e)
{
        int fs = 0;
        int c = -1;
        if (!IS_NULL_id(id)) {
                switch (TAG_id(id)) {
                case id_variable_tag: {
                        DECL_SPEC ds = DEREF_dspec(id_storage(id));
                        if (ds & dspec_auto) {
                                if (off == NULL) {
                                        return (e);
                                }
                        } else {
                                if (!(ds & dspec_linkage)) {
                                        fs = 1;
                                }
                        }
                        break;
                }
                case id_stat_member_tag:
                        /* Check static members */
                        break;
                default:
                        /* Ignore other identifiers */
                        return (e);
                }
        }
        if (option(OPT_init_dynamic)) {
                /* Dynamic initialisation not allowed */
                c = 1;
        }
        if (!is_const_exp(e, c)) {
                /* Non-constant initialiser */
                TYPE t = DEREF_type(exp_type(e));
                if (!is_templ_type(t)) {
                        ERROR err;
                        if (off) {
                                err = ERR_dcl_init_aggr_dynamic();
                        } else {
                                err = ERR_dcl_init_dynamic();
                        }
                        if (!IS_NULL_err(err)) {
                                ERROR err2 = ERR_dcl_init_decl(id, off);
                                err = concat_error(err2, err);
                                report(crt_loc, err);
                        }
                        if (fs) {
                                /* Check function statics */
                                e = check_return_exp(e, lex_static);
                        }
                        MAKE_exp_dynamic(t, e, e);
                }
        }
        return (e);
}


/*
    CHECK A VARIABLE INITIALISER

    This routine is called to check the initialiser for a variable or static
    data member.  Its primary purpose is to mark those temporaries which
    are bound to variables.
*/

EXP
check_init(EXP e)
{
        if (!IS_NULL_exp(e)) {
                switch (TAG_exp(e)) {
                case exp_identifier_tag: {
                        IDENTIFIER id = DEREF_id(exp_identifier_id(e));
                        DECL_SPEC ds = DEREF_dspec(id_storage(id));
                        if (ds & dspec_temp) {
                                ds &= ~dspec_register;
                                COPY_dspec(id_storage(id), ds);
                        }
                        break;
                }
                case exp_init_tag: {
                        IDENTIFIER id = DEREF_id(exp_init_id(e));
                        DECL_SPEC ds = DEREF_dspec(id_storage(id));
                        if (ds & dspec_temp) {
                                ds &= ~dspec_register;
                                COPY_dspec(id_storage(id), ds);
                        }
                        break;
                }
                case exp_indir_tag: {
                        EXP a = DEREF_exp(exp_indir_ptr(e));
                        a = check_init(a);
                        COPY_exp(exp_indir_ptr(e), a);
                        break;
                }
                case exp_address_tag: {
                        EXP a = DEREF_exp(exp_address_arg(e));
                        a = check_init(a);
                        COPY_exp(exp_address_arg(e), a);
                        break;
                }
                case exp_base_cast_tag: {
                        EXP a = DEREF_exp(exp_base_cast_arg(e));
                        a = check_init(a);
                        COPY_exp(exp_base_cast_arg(e), a);
                        break;
                }
                case exp_add_ptr_tag: {
                        EXP a = DEREF_exp(exp_add_ptr_ptr(e));
                        a = check_init(a);
                        COPY_exp(exp_add_ptr_ptr(e), a);
                        break;
                }
                case exp_dynamic_tag: {
                        EXP a = DEREF_exp(exp_dynamic_arg(e));
                        a = check_init(a);
                        COPY_exp(exp_dynamic_arg(e), a);
                        break;
                }
                case exp_aggregate_tag: {
                        LIST(EXP) p = DEREF_list(exp_aggregate_args(e));
                        while (!IS_NULL_list(p)) {
                                EXP a = DEREF_exp(HEAD_list(p));
                                a = check_init(a);
                                COPY_exp(HEAD_list(p), a);
                                p = TAIL_list(p);
                        }
                        break;
                }
                case exp_nof_tag: {
                        EXP a = DEREF_exp(exp_nof_start(e));
                        EXP b = DEREF_exp(exp_nof_pad(e));
                        EXP c = DEREF_exp(exp_nof_end(e));
                        a = check_init(a);
                        b = check_init(b);
                        c = check_init(c);
                        COPY_exp(exp_nof_start(e), a);
                        COPY_exp(exp_nof_pad(e), b);
                        COPY_exp(exp_nof_end(e), c);
                        break;
                }
                }
        }
        return (e);
}


/*
    TEMPORARY VARIABLE FLAG

    The variable made_temporary is set to the temporary variable whenever
    a temporary variable is created.  temp_storage is used to determine
    the storage class for a local variable.
*/

IDENTIFIER made_temporary = NULL_id;
int keep_temporary = 0;
static DECL_SPEC temp_storage = dspec_auto;


/*
    DECLARE A TEMPORARY VARIABLE

    This routine declares a temporary variable of type t, initial value
    e, and destructor d (the implicit destructor will be created if this
    is the null expression).  It returns an expression giving the value of
    this temporary.  Note the use of an assignment expression to ensure
    that the temporary gets initialised in the right place.
*/

EXP
make_temporary(TYPE t, EXP e, EXP d, int ref, ERROR *err)
{
        LOCATION loc;
        DECL_SPEC ds;
        EXP c = NULL_exp;
        HASHID nm = lookup_anon();
        NAMESPACE ns = crt_namespace;
        QUALIFIER cq = crt_id_qualifier;
        int tq = crt_templ_qualifier;
        int fn = in_function_defn;
        IDENTIFIER id = DEREF_id(hashid_id(nm));
        loc = decl_loc;
        decl_loc = crt_loc;
        crt_id_qualifier = qual_none;
        crt_templ_qualifier = 0;

        /* Declare the temporary object */
        if (IS_type_ref(t)) {
                t = DEREF_type(type_ref_sub(t));
        }
        if (in_default_arg) {
                /* NOT YET IMPLEMENTED */
                crt_namespace = global_namespace;
                in_function_defn = 0;
        }
        t = qualify_type(t, cv_none, 0);
        if (IS_type_compound(t)) {
                add_error(err, ERR_dcl_init_ref_tmp(t));
        }
        id = make_object_decl(dspec_temp, t, id, 0);
        ds = DEREF_dspec(id_storage(id));
        if (temp_storage != dspec_auto) {
                /* Set storage class */
                ds &= ~dspec_storage;
                ds |= temp_storage;
        }
        if (ds & dspec_auto) {
                ds |= dspec_register;
        }
        COPY_dspec(id_storage(id), ds);
        made_temporary = id;

        /* Check initialiser */
        if (!is_const_exp(e, -1)) {
                if (ds & dspec_auto) {
                        c = e;
                        e = NULL_exp;
                } else if (ref) {
                        TYPE s = DEREF_type(exp_type(e));
                        MAKE_exp_dynamic(s, e, e);
                        add_error(err, ERR_dcl_init_dynamic());
                } else {
                        TYPE s = DEREF_type(exp_type(e));
                        c = e;
                        e = make_null_exp(s);
                }
        }
        if (IS_NULL_exp(d)) {
                /* Create destructor */
                int du = do_usage;
                do_usage = 0;
                d = init_default(t, &d, DEFAULT_DESTR, EXTRA_DESTR, err);
                do_usage = du;
        }
        COPY_exp(id_variable_init(id), e);
        COPY_exp(id_variable_term(id), d);
        define_id(id);

        /* Construct the result expression */
        if (!IS_NULL_exp(c)) {
                /* Assign initial value */
                t = DEREF_type(id_variable_type(id));
                MAKE_exp_init(t, id, c, e);
                ds = DEREF_dspec(id_storage(id));
                ds |= dspec_explicit;
                COPY_dspec(id_storage(id), ds);
        } else {
                e = make_id_exp(id);
        }

        /* Define variable */
        if (!(ds & dspec_auto)) {
                if (!really_in_function_defn && !in_template_decl) {
                        /* Compile variable definition */
                        compile_variable(id, 0);
                }
        }
        if (do_dump) {
                dump_implicit = 1;
                dump_declare(id, &decl_loc, 1);
        }
        crt_templ_qualifier = tq;
        crt_id_qualifier = cq;
        in_function_defn = fn;
        crt_namespace = ns;
        decl_loc = loc;
        return (e);
}


/*
    IS A VARIABLE ALIASED IN AN EXPRESSION

    This routine checks whether the variable expression d is aliased in
    the expression e.
*/

static int
involves_alias(EXP e, EXP d)
{
        if (!IS_NULL_exp(d)) {
                /* NOT YET IMPLEMENTED */
                UNUSED(e);
                return (1);
        }
        return (0);
}


/*
    ELIMINATE A TEMPORARY VARIABLE

    This routine is called to eliminate any temporary variables from the
    value e which is to be assigned to a variable given by d.  d is the
    null expression in a variable initialisation, otherwise care needs
    to be taken if d is aliased in e.  Note that creating a temporary and
    then removing it ensures that the accesses for the copy constructor
    and destructor are checked correctly.
*/

EXP
remove_temporary(EXP e, EXP d)
{
        if (!IS_NULL_exp(e)) {
                EXP a = NULL_exp;
                unsigned tag = TAG_exp(e);
                if (tag == exp_constr_tag) {
                        LIST(EXP)p;
                        int info = DEREF_int(exp_constr_info(e));
                        if (info != DEFAULT_COPY) {
                                return (e);
                        }
                        a = DEREF_exp(exp_constr_call(e));
                        p = DEREF_list(exp_func_id_args(a));
                        if (IS_NULL_list(p)) {
                                return (e);
                        }
                        p = TAIL_list(p);
                        if (IS_NULL_list(p)) {
                                return (e);
                        }
                        a = DEREF_exp(HEAD_list(p));
                        if (!IS_exp_address(a)) {
                                return (e);
                        }
                        a = DEREF_exp(exp_address_arg(a));
                } else if (tag == exp_contents_tag) {
                        a = DEREF_exp(exp_contents_ptr(e));
                }
                if (!IS_NULL_exp(a) && IS_exp_init(a)) {
                        /* Check for temporary variable */
                        IDENTIFIER id = DEREF_id(exp_init_id(a));
                        if (IS_id_variable(id)) {
                                DECL_SPEC ds = DEREF_dspec(id_storage(id));
                                if ((ds & dspec_temp) && !keep_temporary) {
                                        /* Eliminate temporary variable */
                                        EXP b = DEREF_exp(exp_init_arg(a));
                                        if (IS_NULL_exp(b)) {
                                                b = DEREF_exp(id_variable_init(id));
                                        }
                                        if (!involves_alias(b, d)) {
                                                ds |= dspec_ignore;
                                                COPY_dspec(id_storage(id), ds);
                                                return (b);
                                        }
                                }
                        }
                }
        }
        return (e);
}


/*
    FORCE A REFERENCE INITIALISATION

    This flag forces a reference to be initialised by a less cv-qualified
    version of the same type.  Values of greater than 1 can arise in
    copy constructors.
*/

int init_ref_force = 1;


/*
    INITIALISE A REFERENCE WITH AN LVALUE

    This routine checks whether e is a suitable lvalue initialiser for a
    reference to type t.  If so a suitably converted version of e is
    returned.  The null expression is returned otherwise.
*/

EXP
init_ref_lvalue(TYPE t, EXP e, ERROR *err)
{
        EXP a = NULL_exp;
        if (!IS_NULL_exp(e)) {
                TYPE s = DEREF_type(exp_type(e));
                CV_SPEC qual = DEREF_cv(type_qual(s));
                if (qual & cv_lvalue) {
                        /* Check whether t is reference compatible with s */
                        unsigned nt = TAG_type(t);
                        unsigned ns = TAG_type(s);
                        CV_SPEC cv = cv_compare(t, s);
                        if (nt == ns) {
                                if (nt == type_compound_tag) {
                                        /* Check for base class conversions */
                                        if (cv == cv_none || init_ref_force) {
                                                a = cast_class_class(t, e, err, CAST_IMPLICIT, 1);
                                        }
                                } else if (nt == type_func_tag) {
                                        /* Allow for overloading */
                                        LIST(IDENTIFIER) pids =
                                            NULL_list(IDENTIFIER);
                                        e = resolve_cast(t, e, err, 1, 0, pids);
                                        if (!IS_exp_member(e)) {
                                                s = DEREF_type(exp_type(e));
                                                if (eq_type_unqual(t, s)) {
                                                        if (eq_except(t, s) != 2) {
                                                                add_error(err, ERR_except_spec_init());
                                                        }
                                                        a = e;
                                                }
                                        }
                                } else {
                                        /* Otherwise check for equal types */
                                        if (eq_type_unqual(t, s)) {
                                                a = e;
                                                if (cv != cv_none) {
                                                        add_error(err, ERR_dcl_init_ref_qual(cv));
                                                }
                                        }
                                }
                        }
                }
                if (is_templ_type(s)) {
                        /* Allow for template parameters */
                        a = cast_templ_type(t, e, CAST_IMPLICIT);
                }
        }
        return (a);
}


/*
    INITIALISE A REFERENCE WITH AN RVALUE

    This routine checks whether e is a suitable rvalue initialiser for a
    reference to type t.  It is firstly checked that t is a const reference
    and not a reference to function.  If t is reference compatible with the
    type of s then a temporary is created to hold the value of e and the
    contents of this temporary are returned.  Otherwise the null expression
    is returned.
*/

static EXP
init_ref_rvalue(TYPE t, EXP e, ERROR *err)
{
        /* Check for reference to functions */
        CV_SPEC qual;
        unsigned nt = TAG_type(t);
        if (nt == type_func_tag) {
                add_error(err, ERR_dcl_init_ref_func());
                e = make_null_exp(t);
                return (e);
        }

        /* Check for const references */
        qual = find_cv_qual(t);
        qual &= cv_qual;
        if (qual != cv_const) {
                int ok = 0;
                if (!IS_NULL_exp(e)) {
                        TYPE s = DEREF_type(exp_type(e));
                        if (IS_type_error(s)) {
                                ok = 1;
                        }
                }
                if (!ok) {
                        add_error(err, ERR_dcl_init_ref_const());
                }
        }

        /* Check the initialiser */
        if (!IS_NULL_exp(e)) {
                /* Check whether t is reference compatible with s */
                int force = init_ref_force;
                TYPE s = DEREF_type(exp_type(e));
                unsigned ns = TAG_type(s);
                CV_SPEC cv = cv_compare(t, s);
                if (nt == ns && (cv == cv_none || force)) {
                        TYPE r = t;
                        if (nt == type_compound_tag) {
                                /* t must be a base class of s */
                                CLASS_TYPE ct =
                                    DEREF_ctype(type_compound_defn(t));
                                CLASS_TYPE cs =
                                    DEREF_ctype(type_compound_defn(s));
                                GRAPH gr = find_base_class(cs, ct, 1);
                                if (IS_NULL_graph(gr)) {
                                        return (NULL_exp);
                                }
                                r = qualify_type(s, qual, 0);
                                /* NOT YET IMPLEMENTED: copy e */
                        } else {
                                /* Otherwise check for equal types */
                                if (!eq_type_unqual(t, s)) {
                                        return (NULL_exp);
                                }
                        }
                        if (cv != cv_none) {
                                /* Binding from more qualified type */
                                add_error(err, ERR_dcl_init_ref_qual(cv));
                        }
                        e = make_temporary(r, e, NULL_exp, 1, err);
                        if (nt == type_compound_tag) {
                                /* Bind temporary to reference */
                                e = cast_class_class(t, e, err, CAST_IMPLICIT, 1);
                        }
                        return (e);
                }
        }
        return (NULL_exp);
}


/*
    CREATE A REFERENCE INITIALISER

    This routine creates a reference initialiser of type t out of the
    expression e.
*/

EXP
make_ref_init(TYPE t, EXP e)
{
        if (!IS_NULL_exp(e)) {
                if (IS_exp_op(e)) {
                        /* Allow for template parameters */
                        COPY_type(exp_type(e), t);
                } else {
                        TYPE s = t;
                        unsigned tag = TAG_type(s);
                        if (tag == type_ref_tag) {
                                s = DEREF_type(type_ref_sub(s));
                                tag = TAG_type(s);
                        }
                        if (tag == type_token_tag && is_templ_type(s)) {
                                /* Check again later */
                                /* EMPTY */
                        } else {
                                MAKE_exp_address(t, e, e);
                        }
                }
        }
        return (e);
}


/*
    CREATE A NULL EXPRESSION

    This routine creates a null expression (i.e. all zeros) for the type t.
    This is the default value for a non-explicitly initialised variable with
    internal or external linkage.
*/

EXP
make_null_exp(TYPE t)
{
        EXP e;
        switch (TAG_type(t)) {
        case type_integer_tag:
        case type_enumerate_tag: {
                NAT n = small_nat[0];
                MAKE_exp_int_lit(t, n, exp_int_lit_tag, e);
                break;
        }
        case type_floating_tag: {
                FLOAT f = get_float(t, 0);
                MAKE_exp_float_lit(t, f, e);
                break;
        }
        case type_bitfield_tag: {
                TYPE s = find_bitfield_type(t);
                e = make_null_exp(s);
                MAKE_exp_cast(t, (CONV_BITFIELD | CONV_REVERSE), e, e);
                break;
        }
        default:
                MAKE_exp_null(t, e);
                break;
        }
        return (e);
}


/*
    CREATE A UNIT EXPRESSION

    This routine creates a unit expression (i.e. one) for the type t.
*/

EXP
make_unit_exp(TYPE t)
{
        EXP e;
        switch (TAG_type(t)) {
        case type_integer_tag:
        case type_enumerate_tag: {
                NAT n = small_nat[1];
                MAKE_exp_int_lit(t, n, exp_int_lit_tag, e);
                break;
        }
        case type_floating_tag: {
                FLOAT f = get_float(t, 1);
                MAKE_exp_float_lit(t, f, e);
                break;
        }
        case type_bitfield_tag: {
                TYPE s = find_bitfield_type(t);
                e = make_unit_exp(s);
                MAKE_exp_cast(t,(CONV_BITFIELD | CONV_REVERSE), e, e);
                break;
        }
        default:
                FAIL(Invalid unit type);
                MAKE_exp_null(t, e);
                break;
        }
        return (e);
}


/*
    IS AN EXPRESSION NULL?

    This routine checks whether the expression e is a null expression.
*/

int
is_null_exp(EXP e)
{
        if (IS_NULL_exp(e)) {
                return (1);
        }
        switch (TAG_exp(e)) {
        case exp_int_lit_tag: {
                NAT n = DEREF_nat(exp_int_lit_nat(e));
                return (is_zero_nat(n));
        }
        case exp_float_lit_tag: {
                FLOAT f = DEREF_flt(exp_float_lit_flt(e));
                return (is_zero_float(f));
        }
        case exp_null_tag:
        case exp_zero_tag:
                return (1);
        case exp_aggregate_tag: {
                LIST(EXP)p = DEREF_list(exp_aggregate_args(e));
                while (!IS_NULL_list(p)) {
                        EXP a = DEREF_exp(HEAD_list(p));
                        if (!is_null_exp(a)) {
                                return (0);
                        }
                        p = TAIL_list(p);
                }
                return (1);
        }
        case exp_nof_tag: {
                EXP a = DEREF_exp(exp_nof_start(e));
                EXP b = DEREF_exp(exp_nof_pad(e));
                EXP c = DEREF_exp(exp_nof_end(e));
                if (!is_null_exp(a)) {
                        return (0);
                }
                if (!is_null_exp(b)) {
                        return (0);
                }
                return (is_null_exp(c));
        }
        }
        return (0);
}


/*
    CREATE AN EMPTY INITIALISER

    This routine creates an empty initialiser for the type t.  Basically
    this is the same as make_null_exp except that it also checks for
    uninitialised references and const objects.  Also if force is false
    then a value is only created if absolutely necessary.
*/

EXP
init_empty(TYPE t, CV_SPEC cv, int force, ERROR *err)
{
        EXP e = NULL_exp;
        switch (TAG_type(t)) {
        case type_array_tag: {
                NAT n = DEREF_nat(type_array_size(t));
                TYPE s = DEREF_type(type_array_sub(t));
                e = init_empty(s, cv, force, err);
                if (!IS_NULL_exp(e)) {
                        MAKE_exp_nof(t, NULL_exp, n, e, NULL_exp, e);
                }
                break;
        }
        case type_ref_tag: {
                /* References must be initialised */
                TYPE s = DEREF_type(type_ref_sub(t));
                add_error(err, ERR_dcl_init_ref_none());
                if (IS_type_func(s)) {
                        e = make_null_exp(s);
                } else {
                        e = init_empty(s, cv_none, 1, err);
                        e = make_temporary(s, e, NULL_exp, 1, err);
                        e = make_ref_init(t, e);
                }
                break;
        }
        case type_compound_tag:
                /* Call default constructor for classes */
                e = init_default(t, &e, DEFAULT_CONSTR, EXTRA_CONSTR, err);
                if (IS_NULL_exp(e)) {
                        goto default_lab;
                }
                break;
        case type_enumerate_tag:
                if (force) {
                        /* Check for zero enumerator */
                        ENUM_TYPE et = DEREF_etype(type_enumerate_defn(t));
                        CLASS_INFO ei = DEREF_cinfo(etype_info(et));
                        if (!(ei & cinfo_usr_constr)) {
                                add_error(err, ERR_dcl_enum_zero(t));
                        }
                }
                goto default_lab;
        case type_token_tag:
                if (is_templ_type(t)) {
                        /* Allow for template parameters */
                        if (force) {
                                MAKE_exp_op(t, lex_cast, NULL_exp, NULL_exp, e);
                        }
                        break;
                }
                goto default_lab;
        default :
default_lab: {
                     CV_SPEC qual = find_cv_qual(t);
                     qual |= cv;
                     if (qual & cv_const) {
                             /* Const objects must be initialised */
                             add_error(err, ERR_dcl_init_const());
                     }
                     if (force) {
                             e = make_null_exp(t);
                     }
                     break;
             }
        }
        return (e);
}


/*
    LAST ARRAY INITIALISER SIZE

    This variable is used to hold the number of elements in the last array
    initialiser processed.  It is subsequently used to calculate the bound
    for an unbounded, but initialised, array type.
*/

static NAT last_array_size = NULL_nat;


/*
    IS A TYPE A CHARACTER ARRAY?

    This routine checks whether the type t is an array of 'char', 'signed
    char', 'unsigned char' 'wchar_t', and so may be initialised by a single
    literal.  It returns 1 for character arrays, 2 for wide character
    arrays, and 3 for types compatible with wide character arrays.
*/

static int
is_char_array(TYPE t)
{
        if (IS_type_array(t)) {
                TYPE s = DEREF_type(type_array_sub(t));
                if (check_int_type(s, btype_char)) {
                        return (1);
                }
                if (check_int_type(s, btype_wchar_t)) {
                        return (2);
                }
                if (!basetype_info[ntype_wchar_t].key) {
                        s = type_composite(s, type_wchar_t, 1, 0, KILL_err, 0);
                        if (!IS_NULL_type(s)) {
                                return (3);
                        }
                }
        }
        return (0);

}


/*
    PAD AN ARRAY INITIALISER

    This routine pads the array initialiser e, which contains m elements,
    with zeros until it matches the type t.  n gives the bound size of t.
*/

static EXP
pad_array(EXP e, NAT m, TYPE t, NAT n, int pad, ERROR *err)
{
        EXP a;
        int eq;
        unsigned long c;
        ERROR err2 = NULL_err;
        TYPE s = DEREF_type(type_array_sub(t));

        /* Check for equality */
        eq = compare_nat(n, m);
        if (eq == 0) {
                return (e);
        } else if (eq == 1) {
                if (!pad) {
                        return (NULL_exp);
                }
        } else if (eq == -1) {
                /* Too many initialisers */
                if (!pad) {
                        return (NULL_exp);
                }
                add_error(err, ERR_dcl_init_aggr_excess(t));
                return (e);
        } else {
                /* Allow for token definitions */
                force_tokdef++;
                eq = eq_nat(n, m);
                force_tokdef--;
                if (eq) {
                        return (e);
                }
                if (!pad) {
                        return (NULL_exp);
                }
        }

        /* Find number of uninitialised elements */
        c = get_nat_value(m);
        if (c != 0) {
                EXP en = calc_nat_value(n, type_size_t);
                EXP em = calc_nat_value(m, type_size_t);
                en = make_minus_exp(en, em);
                if (IS_exp_int_lit(en)) {
                        n = DEREF_nat(exp_int_lit_nat(en));
                        if (pad && c > 1 && is_calc_nat(n)) {
                                /* Warn about potentially dubious
                                 * initialisers */
                                err2 = ERR_dcl_init_aggr_array_ti(m, t);
                        }
                        c = get_nat_value(n);
                        if (c == 0) {
                                return (e);
                        }
                }
        }

        /* Form initialiser */
        if (IS_NULL_err(err2)) {
                err2 = ERR_dcl_init_aggr_pad(n, t);
        }
        if (!IS_NULL_err(err2)) {
                add_error(err, err2);
        }
        a = init_empty(s, cv_none, 1, err);
        if (!IS_NULL_exp(e) && IS_exp_aggregate(e)) {
                if (c <= ARRAY_PADDING) {
                        /* Explicitly pad small arrays */
                        LIST(EXP)p = DEREF_list(exp_aggregate_args(e));
                        LIST(EXP)q = NULL_list(EXP);
                        while (c) {
                                CONS_exp(a, q, q);
                                c--;
                        }
                        p = APPEND_list(p, q);
                        COPY_list(exp_aggregate_args(e), p);
                        COPY_type(exp_type(e), t);
                        return (e);
                }
        }
        MAKE_exp_nof(t, e, n, a, NULL_exp, e);
        return (e);
}


/*
    CHECK AN ASSIGNMENT STYLE ARRAY INITIALISER

    This routine checks the assignment style initialiser 't id = e ;' where
    t is an array type.  If arr is true then e is allowed to be another
    array expression of compatible type.  The routine returns the
    initialising expression for id.
*/

EXP
init_array(TYPE t, CV_SPEC cv, EXP e, int arr, ERROR *err)
{
        TYPE r = DEREF_type(exp_type(e));
        NAT n = DEREF_nat(type_array_size(t));
        TYPE s = DEREF_type(type_array_sub(t));
        if (IS_type_array(r)) {
                unsigned tag = TAG_exp(e);
                NAT m = DEREF_nat(type_array_size(r));
                TYPE u = DEREF_type(type_array_sub(r));
                if (IS_NULL_nat(n)) {
                        n = m;
                }
                last_array_size = m;

                /* Check for templates */
                if (in_template_decl) {
                        if (is_templ_type(s) || is_templ_type(u)) {
                                e = cast_templ_type(t, e, CAST_IMPLICIT);
                                return (e);
                        }
                }

                /* Initialisation by string literal */
                if (tag == exp_string_lit_tag) {
                        unsigned long na, ma;
                        int ca = is_char_array(t);
                        STRING str = DEREF_str(exp_string_lit_str(e));
                        unsigned kind = DEREF_unsigned(str_simple_kind(str));
                        if (kind & STRING_WIDE) {
                                /* Wide string literals */
                                if (ca == 2) {
                                        u = s;
                                } else if (ca == 3) {
                                        if (IS_type_enumerate(s)) {
                                                /* It could happen ... */
                                                EXP a;
                                                ENUM_TYPE es;
                                                MAKE_exp_value(u, a);
                                                a = cast_int_int(s, a, err, CAST_IMPLICIT, -1);
                                                es = DEREF_etype(type_enumerate_defn(s));
                                                s = DEREF_type(etype_rep(es));
                                                free_exp(a, 1);
                                        }
                                } else {
                                        add_error(err,
                                                  ERR_dcl_init_string_wchar());
                                }
                        } else {
                                /* Normal string literals */
                                if (ca == 1) {
                                        u = s;
                                } else {
                                        add_error(err,
                                                  ERR_dcl_init_string_char());
                                }
                        }
                        if (!EQ_type(u, s)) {
                                /* Deal with invalid cases */
                                if (IS_type_integer(s)) {
                                        /* Cast string to appropriate type */
                                        MAKE_type_array(cv_none, s, m, r);
                                        MAKE_exp_string_lit(r, str, e);
                                } else {
                                        /* Don't take any initialisers from the string */
                                        e = NULL_exp;
                                }
                        }

                        /* Check array bound */
                        na = get_nat_value(n);
                        ma = get_nat_value(m);
                        if (na != EXTENDED_MAX) {
                                /* Known array bounds */
                                if (ma > na) {
                                        /* Too many initialisers - trim string */
                                        if (ma == na + 1) {
                                                add_error(err, ERR_dcl_init_string_zero(t));
                                        } else {
                                                add_error(err, ERR_dcl_init_string_excess(t));
                                        }
                                        MAKE_exp_string_lit(t, str, e);
                                } else if (ma < na) {
                                        /* Not enough initialisers */
                                        NAT d = make_nat_value(na - ma);
                                        add_error(err,
                                                  ERR_dcl_init_aggr_pad(d, t));
                                        MAKE_exp_string_lit(t, str, e);
                                }
                        } else {
                                /* Unknown array bounds */
                                e = pad_array(e, m, t, n, 1, err);
                        }
                        return (e);
                }

                /* Check array initialisers */
                if (tag == exp_token_tag) {
                        /* Allow rvalue array tokens */
                        CV_SPEC qual = DEREF_cv(type_qual(r));
                        if (!(qual & cv_lvalue)) {
                                arr = 2;
                        }
                }
                e = convert_reference(e, REF_ASSIGN);
                if (arr == 0) {
                        /* Invalid array initialiser */
                        report(crt_loc, ERR_dcl_init_aggr_array_bad());
                        if (tag == exp_paren_tag) {
                                /* Parenthesised initialiser */
                                e = init_array(t, cv, e, arr, err);
                                return (e);
                        }
                        arr = 1;
                }
                if (eq_type_unqual(s, u)) {
                        if (arr != 2) {
                                EXP d;
                                d = init_default(r, &e, DEFAULT_COPY,
                                                 EXTRA_CONSTR, err);
                                if (IS_NULL_exp(d)) {
                                        MAKE_exp_contents(r, e, e);
                                } else {
                                        MAKE_exp_preinc(r, e, d, lex_array, e);
                                }
                        }
                        e = pad_array(e, m, t, n, arr - 1, err);
                        if (!IS_NULL_exp(e)) {
                                return (e);
                        }
                }
                add_error(err, ERR_basic_link_incompat(t, r));
        } else {
                /* Other array initialisations are not allowed */
                report(crt_loc, ERR_dcl_init_aggr_array_bad());
                last_array_size = NULL_nat;
        }
        e = init_empty(t, cv, 1, err);
        return (e);
}


/*
    REPORT AN INITIALISATION ERROR

    In C there is no distinction between conversion by initialisation and
    conversion by assignment.  This routine adds a suitable error message
    to err which says that the conversion cannot be done by initialisation.
*/

ERROR
init_error(ERROR err, int init)
{
        ERROR ferr;
#if LANGUAGE_CPP
        ferr = ERR_dcl_init_conv();
        UNUSED(init);
#else
        ferr = ERR_expr_ass_conv();
        if (init) {
                ferr = concat_error(ferr, ERR_dcl_init_assign());
        }
#endif
        err = concat_warning(err, ferr);
        return (err);
}


/*
    CHECK AN ASSIGNMENT STYLE INITIALISER

    This routine checks the assignment style initialiser 'cv t id = e ;'.
    It returns a suitably converted version of e.
*/

EXP
init_assign(TYPE t, CV_SPEC cv, EXP e, ERROR *err)
{
    switch (TAG_type(t)) {
        case type_array_tag: {
            /* Array initialisers */
            e = init_array(t, cv, e, 0, err);
            break;
        }
        case type_ref_tag: {
            /* Reference initialisers */
            EXP a;
            TYPE s = DEREF_type(type_ref_sub(t));
            TYPE r = DEREF_type(exp_type(e));
            if (IS_type_compound(r)) {
                if (IS_type_compound(s)) {
                    /* Check base class conversions first */
                    a = init_ref_lvalue(s, e, err);
                    if (!IS_NULL_exp(a)) {
                        e = make_ref_init(t, a);
                        break;
                    }
                }
                a = convert_conv_aux(t, e, err, CAST_IMPLICIT);
                if (!IS_NULL_exp(a)) {
                    e = a;
                    r = DEREF_type(exp_type(e));
                    if (eq_type(r, t)) {
                            break;
                    }
                    e = convert_reference(e, REF_ASSIGN);
                }
            }
            a = init_ref_lvalue(s, e, err);
            if (IS_NULL_exp(a)) {
                a = init_ref_rvalue(s, e, err);
                if (IS_NULL_exp(a)) {
                    e = init_assign(s, cv_none, e, err);
                    if (!IS_exp_null(e)) {
                        e = make_temporary(s, e, NULL_exp, 1, err);
                    }
                } else {
                    e = a;
                }
            } else {
                e = a;
            }
            e = make_ref_init(t, e);
            break;
        }
        case type_compound_tag: {
            /* Class initialisers */
            TYPE s = DEREF_type(exp_type(e));
            if (IS_type_compound(s)) {
                /* Check for base class initialisers */
                CLASS_TYPE cs = DEREF_ctype(type_compound_defn(s));
                CLASS_TYPE ct = DEREF_ctype(type_compound_defn(t));
                GRAPH gr = find_base_class(cs, ct, 1);
                if (!IS_NULL_graph(gr)) {
                    e = init_direct(t, e, err);
                    break;
                }
            }
            e = convert_conv(t, e, err, CAST_IMPLICIT);
            if (!IS_exp_null(e)) {
                e = make_temporary(t, e, NULL_exp, 0, err);
                e = init_direct(t, e, err);
                e = remove_temporary(e, NULL_exp);
            }
            break;
        }
        default:
                /* Do conversion by initialisation */
                e = convert_assign(t, e, err);
                break;
    }
    return (e);
}


/*
    CHECK A CONSTRUCTOR STYLE INITIALISER

    This routine checks the constructor style initialiser 't id ( args ) ;'.
    It returns an expression representing the result of converting args to
    type t.
*/

EXP
init_constr(TYPE t, LIST(EXP)args, ERROR *err)
{
        EXP e;
        unsigned tag = TAG_type(t);
        switch (tag) {
        case type_ref_tag: {
                /* Reference initialisers */
                EXP a;
                TYPE s = DEREF_type(type_ref_sub(t));
                if (LENGTH_list(args) == 1) {
                        a = DEREF_exp(HEAD_list(args));
                        a = init_ref_lvalue(s, a, err);
                } else {
                        a = NULL_exp;
                }
                if (IS_NULL_exp(a)) {
                        a = init_ref_rvalue(s, a, err);
                        if (IS_NULL_exp(a)) {
                                e = init_constr(s, args, err);
                                if (!IS_exp_null(e)) {
                                        e = make_temporary(s, e, NULL_exp, 1,
                                                           err);
                                }
                        } else {
                                e = a;
                        }
                } else {
                        e = a;
                }
                e = make_ref_init(t, e);
                break;
        }
        case type_compound_tag: {
                /* Class constructor initialisers */
                e = convert_constr(t, args, err, CAST_STATIC);
                e = remove_temporary(e, NULL_exp);
                break;
        }
        case type_token_tag: {
                /* Check for template parameters */
                if (is_templ_type(t)) {
                        LIST(OFFSET) offs = NULL_list(OFFSET);
                        MAKE_exp_initialiser(t, args, offs, 0, 0, 0, e);
                        e = cast_templ_type(t, e, CAST_IMPLICIT);
                        break;
                }
                goto default_lab;
        }
        default:
default_lab: {
                /* Should have at most one argument otherwise */
                unsigned nargs = LENGTH_list(args);
                if (nargs == 0) {
                        e = init_empty(t, cv_none, 1, err);
                } else {
                        EXP a = DEREF_exp(HEAD_list(args));
                        DESTROY_list(args, SIZE_exp);
                        if (nargs > 1) {
                                /* Can't have more than one initialiser */
                                add_error(err, ERR_dcl_init_ctor(t));
                        }
                        if (tag == type_array_tag) {
                                e = init_array(t, cv_none, a, 0, err);
                        } else {
                                TYPE s = DEREF_type(exp_type(a));
                                if (IS_type_compound(s)) {
                                        e = convert_conv(t, a, err,
                                                         CAST_STATIC);
                                } else {
                                        e = convert_assign(t, a, err);
                                }
                        }
                }
                break;
             }
        }
        return (e);
}


/*
    CHECK A DIRECT INITIALISER

    This routine checks the direct initialiser 't id ( a ) ;'.  It is
    a special case of init_constr in which there is only one initialiser.
*/

EXP
init_direct(TYPE t, EXP a, ERROR *err)
{
        LIST(EXP) args;
        CONS_exp(a, NULL_list(EXP), args);
        a = init_constr(t, args, err);
        return (a);
}


/*
    FIELD NAME BUFFER

    This buffer is used to build up field names for use in error reporting.
*/

BUFFER field_buff = NULL_buff;


/*
    SET LOCATION FROM AN AGGREGATE INITIALISER

    Because aggregate initialisers may be spread over several lines each
    component is embedded in a location expression.  This routine gets
    the first element of the aggregate list p, setting the current location
    as appropriate.  It returns the tag of e (ignoring parentheses) via
    ptag.
*/

static EXP
get_aggr_elem(LIST(EXP) p, unsigned *ptag)
{
        EXP a = DEREF_exp(HEAD_list(p));
        if (!IS_NULL_exp(a)) {
                if (IS_exp_location(a)) {
                        TYPE t;
                        DESTROY_exp_location(destroy, t, crt_loc, a, a);
                        UNUSED(t);
                        COPY_exp(HEAD_list(p), a);
                }
                if (!IS_NULL_exp(a)) {
                        EXP b = a;
                        unsigned tag = TAG_exp(b);
                        while (tag == exp_paren_tag) {
                                b = DEREF_exp(exp_paren_arg(b));
                                tag = TAG_exp(b);
                        }
                        *ptag = tag;
                }
        }
        return (a);
}


/*
    CHECK AN AGGREGATE INITIALISER

    This routine checks the aggregate initialiser expression list pointed
    to by r against the type t.  The argument start is 1 to indicate the
    presence of a open brace immediately preceding r and 2 to indicate
    the top-level aggregate.  The result is a structured aggregate
    initialiser expression for compound types t or a suitably converted
    initialiser expression.
*/

static EXP
init_aggr_aux(TYPE t, CV_SPEC cv, LIST(EXP) *r, int start, IDENTIFIER id,
              ERROR *err)
{
        EXP e;
        LIST(EXP) p = *r;
        ERROR cerr = NULL_err;
        CLASS_INFO ci = cinfo_none;
        unsigned tag = TAG_type(t);
        switch (tag) {
        case type_array_tag: {
                /* Array types */
                NAT nc;
                LIST(EXP) a = NULL_list(EXP);
                TYPE s = DEREF_type(type_array_sub(t));
                int str = is_char_array(s);
                BUFFER *bf = &field_buff;
                unsigned boff = (unsigned)(bf->posn - bf->start);

                /* Find the array size */
                NAT n = DEREF_nat(type_array_size(t));
                unsigned long m = get_nat_value(n);
                unsigned long c = 0;

                /* Report partially bracketed initialisers */
                if (!start) {
                        add_error(err, ERR_dcl_init_aggr_partial());
                }

                /* Check for string literals in braces */
                if (start && !IS_NULL_list(p)) {
                        unsigned et = null_tag;
                        e = get_aggr_elem(p, &et);
                        if (et == exp_string_lit_tag && is_char_array(t)) {
                                e = init_array(t, cv, e, 0, err);
                                p = TAIL_list(p);
                                break;
                        }
                }

                /* Loop through at most m initialisers */
                while (!IS_NULL_list(p) && c != m) {
                        LIST(EXP) p0 = p;
                        ERROR serr = NULL_err;
                        unsigned et = null_tag;

                        /* Build up the field name */
                        bfprintf(bf, " [%lu]", c);

                        /* Check first element of aggregate */
                        e = get_aggr_elem(p, &et);
                        if (IS_NULL_exp(e)) {
                                /* Can occur in template initialisers */
                                e = init_empty(s, cv_none, 1, &serr);
                                COPY_exp(HEAD_list(p), e);
                        }
                        if (et == exp_string_lit_tag && str) {
                                /* Check for string literals */
                                e = init_array(s, cv, e, 0, &serr);
                                p = TAIL_list(p);
                        } else if (et == exp_aggregate_tag && start) {
                                /* Check for sub-aggregates */
                                LIST(EXP) q;
                                q = DEREF_list(exp_aggregate_args(e));
                                e = init_aggr_aux(s, cv, &q, 1, id, &serr);
                                p = TAIL_list(p);
                        } else {
                                /* Otherwise read constituents from p */
                                e = init_aggr_aux(s, cv, &p, 0, id, &serr);
                        }

                        /* Report any errors for this member */
                        if (!IS_NULL_err(serr)) {
                                ERROR ferr = ERR_dcl_init_decl(id, bf->start);
                                serr = concat_error(ferr, serr);
                                report(crt_loc, serr);
                        }

                        /* Check for dynamic initialisers */
                        e = dynamic_init(id, bf->start, e);

                        /* Restore the field name */
                        bf->posn = bf->start + boff;
                        bf->posn[0] = 0;

                        /* Check that some initialisers were used up */
                        if (EQ_list(p, p0)) {
                                break;
                        }

                        /* Build up the result (in reverse order) */
                        CONS_exp(e, a, a);
                        c++;
                }

                /* Construct the result */
                a = REVERSE_list(a);
                nc = make_nat_value(c);
                MAKE_type_array(cv_none, s, nc, s);
                MAKE_exp_aggregate(s, a, NULL_list(OFFSET), e);

                /* Check array size */
                if (!IS_NULL_nat(n)) {
                        e = pad_array(e, nc, t, n, 1, err);
                }
                last_array_size = nc;
                break;
        }
        case type_ref_tag: {
                /* Reference types */
                TYPE s = DEREF_type(type_ref_sub(t));
                e = init_ref_rvalue(s, NULL_exp, err);
                if (IS_NULL_exp(e)) {
                        e = init_aggr_aux(s, cv, r, start, id, err);
                        e = make_temporary(s, e, NULL_exp, 1, err);
                        e = make_ref_init(t, e);
                }
                return (e);
        }
        case type_compound_tag: {
                /* Compound types */
                MEMBER mem;
                NAMESPACE ns;
                unsigned long pads = 0;
                LIST(EXP) a = NULL_list(EXP);
                LIST(OFFSET)b = NULL_list(OFFSET);
                CV_SPEC cv1 = (DEREF_cv(type_qual(t)) | cv);
                CLASS_TYPE ct = DEREF_ctype(type_compound_defn(t));
                GRAPH gr = DEREF_graph(ctype_base(ct));
                LIST(GRAPH) br = DEREF_list(graph_tails(gr));
                BUFFER *bf = &field_buff;
                unsigned boff = (unsigned)(bf->posn - bf->start);

                /* Check for non-aggregate classes */
                ci = DEREF_cinfo(ctype_info(ct));
                if (!(ci & cinfo_defined)) {
                        /* Instantiate template types if necessary */
                        complete_class(ct, 1);
                        ci = DEREF_cinfo(ctype_info(ct));
                }
                if (!(ci & cinfo_complete)) {
                        /* Incomplete types can't be initialised */
                        goto incomplete_lab;
                }
                if (ci & cinfo_non_aggregate) {
                        /* Types with these properties can't be initialised */
                        cerr = class_info(ct, cinfo_non_aggregate, 1);
                        if (ci & cinfo_token) {
                                goto token_lab;
                        }
                        if (ci & cinfo_usr_constr) {
                                goto non_aggregate_lab;
                        }
                        add_error(err, cerr);
                        add_error(err, ERR_dcl_init_aggr_type(t));
                        cerr = NULL_err;
                }

                /* Check for non-aggregate initialisations */
                if (!IS_NULL_list(p)) {
                        unsigned rank;
                        CONVERSION conv;
                        unsigned et = null_tag;
                        e = get_aggr_elem(p, &et);
                        conv.from = DEREF_type(exp_type(e));
                        conv.to = t;
                        rank = std_convert_seq(&conv, e, 0, 0);
                        if (rank != CONV_NONE) {
                                goto non_aggregate_lab;
                        }
                }

                /* Report partially bracketed initialisers */
                if (!start) {
                        add_error(err, ERR_dcl_init_aggr_partial());
                }

                /* Loop through base classes */
                while (!IS_NULL_list(br)) {
                        ERROR serr = NULL_err;
                        GRAPH gs = DEREF_graph(HEAD_list(br));
                        OFFSET off = DEREF_off(graph_off(gs));
                        CLASS_TYPE cs = DEREF_ctype(graph_head(gs));
                        TYPE s = make_class_type(cs);

                        /* Build up field name */
                        IDENTIFIER sid = DEREF_id(ctype_name(cs));
                        HASHID snm = DEREF_hashid(id_name(sid));
                        if (!IS_hashid_anon(snm)) {
                                bfputc(bf, '.');
                                IGNORE print_hashid(snm, 1, 0, bf, 0);
                        }

                        /* Check next initialiser */
                        if (!IS_NULL_list(p)) {
                                unsigned et = null_tag;
                                e = get_aggr_elem(p, &et);
                                if (et == exp_aggregate_tag && start) {
                                        /* Check for sub-aggregates */
                                        LIST(EXP) q;
                                        q = DEREF_list(exp_aggregate_args(e));
                                        e = init_aggr_aux(s, cv1, &q, 1, id,
                                                          &serr);
                                        p = TAIL_list(p);
                                } else {
                                        /* Otherwise read constituents from p */
                                        e = init_aggr_aux(s, cv1, &p, 0, id,
                                                          &serr);
                                }
                        } else {
                                e = init_empty(s, cv1, 1, &serr);
                                pads++;
                        }

                        /* Report any errors for this field */
                        if (!IS_NULL_err(serr)) {
                                ERROR ferr = ERR_dcl_init_decl(id, bf->start);
                                serr = concat_error(ferr, serr);
                                report(crt_loc, serr);
                        }

                        /* Check for dynamic initialisers */
                        e = dynamic_init(id, bf->start, e);

                        /* Restore field name */
                        bf->posn = bf->start + boff;
                        bf->posn[0] = 0;

                        /* Build up the result (in reverse order) */
                        CONS_exp(e, a, a);
                        CONS_off(off, b, b);
                        br = TAIL_list(br);
                }

                /* Find list of class members */
                ns = DEREF_nspace(ctype_member(ct));
                mem = DEREF_member(nspace_ctype_first(ns));
                mem = next_data_member(mem, 0);

                /* Loop through structure members */
                while (!IS_NULL_member(mem)) {
                        ERROR serr = NULL_err;
                        CV_SPEC cv2 = cv1;
                        IDENTIFIER sid = DEREF_id(member_id(mem));
                        TYPE s = DEREF_type(id_member_type(sid));
                        DECL_SPEC ds = DEREF_dspec(id_storage(sid));
                        OFFSET off = DEREF_off(id_member_off(sid));

                        /* Build up field name */
                        HASHID snm = DEREF_hashid(id_name(sid));
                        if (!IS_hashid_anon(snm)) {
                                bfputc(bf, '.');
                                IGNORE print_hashid(snm, 1, 0, bf, 0);
                        }

                        /* Adjust cv-qualifiers */
                        if (ds & dspec_mutable)cv2 = cv_none;

                        /* Check next initialiser */
                        if (!IS_NULL_list(p)) {
                                unsigned et = null_tag;
                                e = get_aggr_elem(p, &et);
                                if (et == exp_string_lit_tag &&
                                    is_char_array(s)) {
                                        /* Check for string literals */
                                        e = init_array(s, cv2, e, 0, &serr);
                                        p = TAIL_list(p);
                                } else if (et == exp_aggregate_tag && start) {
                                        /* Check for sub-aggregates */
                                        LIST(EXP) q;
                                        q = DEREF_list(exp_aggregate_args(e));
                                        e = init_aggr_aux(s, cv2, &q, 1, id,
                                                          &serr);
                                        p = TAIL_list(p);
                                } else {
                                        /* Otherwise read constituents from p */
                                        e = init_aggr_aux(s, cv2, &p, 0, id,
                                                          &serr);
                                }
                        } else {
                                /* Pad rest of structure */
                                e = init_empty(s, cv2, 1, &serr);
                                pads++;
                        }

                        /* Report any errors for this field */
                        if (!IS_NULL_err(serr)) {
                                ERROR ferr = ERR_dcl_init_decl(id, bf->start);
                                serr = concat_error(ferr, serr);
                                report(crt_loc, serr);
                        }

                        /* Check for dynamic initialisers */
                        e = dynamic_init(id, bf->start, e);

                        /* Restore field name */
                        bf->posn = bf->start + boff;
                        bf->posn[0] = 0;

                        /* Build up the result (in reverse order) */
                        CONS_exp(e, a, a);
                        CONS_off(off, b, b);

                        /* Examine next member */
                        if (ci & cinfo_union) {
                                break;
                        }
                        mem = DEREF_member(member_next(mem));
                        mem = next_data_member(mem, 0);
                }

                /* Report padded structures */
                if (pads) {
                        NAT n = make_nat_value(pads);
                        add_error(err, ERR_dcl_init_aggr_pad(n, t));
                }

                /* Construct the result */
                a = REVERSE_list(a);
                b = REVERSE_list(b);
                MAKE_exp_aggregate(t, a, b, e);
                break;
        }
        case type_integer_tag:
        case type_floating_tag:
        case type_enumerate_tag:
        case type_ptr_tag:
        case type_ptr_mem_tag: {
                /* Scalar types */
                if (IS_NULL_list(p)) {
                        /* Can't have empty initialiser */
                        add_error(err, ERR_dcl_init_aggr_no_scalar());
                        e = init_empty(t, cv, 1, err);
                } else {
                        /* The first element must be a scalar */
                        unsigned et = null_tag;
                        e = get_aggr_elem(p, &et);
                        if (et == exp_aggregate_tag) {
                                LIST(EXP)q;
                                q = DEREF_list(exp_aggregate_args(e));
                                e = init_aggr_aux(t, cv, &q, 1, id, err);
                        } else {
                                ERROR ferr = NULL_err;
                                if (start == 1) {
                                        /* Can only have aggregate at top
                                         * level */
                                        ferr = ERR_dcl_init_aggr_nest();
                                }
                                e = convert_reference(e, REF_ASSIGN);
                                e = init_assign(t, cv, e, &ferr);
                                if (!IS_NULL_err(ferr)) {
                                        ferr = init_error(ferr, 1);
                                        add_error(err, ferr);
                                }
                        }
                        p = TAIL_list(p);
                }
                break;
        }
        case type_token_tag:
token_lab: {
                   /* Tokenised types */
                   TYPE s = expand_type(t, 0);
                   if (EQ_type(s, t)) {
                           goto non_aggregate_lab;
                   }
                   e = init_aggr_aux(s, cv, r, start, id, err);
                   return (e);
           }
        case type_top_tag:
        case type_bottom_tag:
incomplete_lab:
           /* Incomplete types */
           add_error(err, ERR_basic_types_incompl(t));
           add_error(err, ERR_dcl_init_incompl());
           e = init_empty(t, cv, 1, err);
           break;
        default:
non_aggregate_lab:
           /* Other types */
           if (start) {
                   /* Can't have aggregate initialisers */
                   ERROR ferr = ERR_dcl_init_decl(id, NULL_string);
                   ferr = concat_error(ferr, cerr);
                   ferr = concat_error(ferr, ERR_dcl_init_aggr_type(t));
                   report(crt_loc, ferr);
                   if (ci & cinfo_usr_constr) {
                           /* Map to constructor call */
                           LIST(EXP) q = p;
                           while (!IS_NULL_list(q)) {
                                   unsigned et = null_tag;
                                   IGNORE get_aggr_elem(q, &et);
                                   q = TAIL_list(q);
                           }
                           e = init_constr(t, p, err);
                           return (e);
                   }
           } else {
                   if (!IS_NULL_err(cerr)) {
                           destroy_error(cerr, 1);
                   }
           }
           if (IS_NULL_list(p)) {
                   /* Empty initialiser list */
                   e = init_empty(t, cv, 1, err);
           } else {
                   /* Get next initialiser from list */
                   unsigned et = null_tag;
                   e = get_aggr_elem(p, &et);
                   if (et == exp_aggregate_tag) {
                           LIST(EXP) q;
                           q = DEREF_list(exp_aggregate_args(e));
                           e = init_aggr_aux(t, cv, &q, 1, id, err);
                   } else {
                           e = convert_reference(e, REF_ASSIGN);
                           if (tag == type_token_tag && is_zero_exp(e)) {
                                   /* Special concession to tokenised types */
                                   e = init_empty(t, cv, 1, err);
                           } else {
                                   ERROR ferr = NULL_err;
                                   e = init_assign(t, cv, e, &ferr);
                                   if (!IS_NULL_err(ferr)) {
                                           ferr = init_error(ferr, 1);
                                           add_error(err, ferr);
                                   }
                           }
                   }
                   p = TAIL_list(p);
           }
           break;
        }

        /* Check for end of initialiser list */
        if (start && !IS_NULL_list(p)) {
                ERROR ferr = ERR_dcl_init_decl(id, NULL_string);
                ferr = concat_error(ferr, ERR_dcl_init_aggr_excess(t));
                report(crt_loc, ferr);
                p = NULL_list(EXP);
        }
        *r = p;
        return (e);
}


/*
    CHECK AN AGGREGATE INITIALISER

    This is the top-level routine for analysing the aggregate initialiser
    e for the object id of type t.  Any errors are added to err.
*/

EXP
init_aggregate(TYPE t, EXP e, IDENTIFIER id, ERROR *err)
{
        LOCATION loc;
        LIST(EXP) args = DEREF_list(exp_aggregate_args(e));
        if (IS_NULL_list(args)) {
                /* Report empty aggregate initialisers */
                add_error(err, ERR_dcl_init_aggr_empty());
        }
        bad_crt_loc++;
        loc = crt_loc;
        IGNORE clear_buffer(&field_buff, NIL(FILE));
        e = init_aggr_aux(t, cv_none, &args, 2, id, err);
        crt_loc = loc;
        bad_crt_loc--;
        return (e);
}


/*
    CHECK ANY INITIALISER

    This routine calls init_assign, init_constr or init_aggregate depending
    on the value of e.  If tentative is true then an absent initialiser
    (i.e. when e is null) is treated as a tentative definition.
*/

EXP
init_general(TYPE t, EXP e, IDENTIFIER id, int tentative)
{
        /* Check the initialiser */
        ERROR err = NULL_err;
        if (IS_NULL_exp(e)) {
                /* Empty initialisers */
                DECL_SPEC ds = DEREF_dspec(id_storage(id));
                if (ds & dspec_auto) {
                        e = init_empty(t, cv_none, 0, &err);
                } else if (tentative) {
                        MAKE_exp_zero(t, e);
                } else {
                        e = init_empty(t, cv_none, 1, &err);
                }
        } else {
                switch (TAG_exp(e)) {
                case exp_aggregate_tag: {
                        /* Aggregate initialisers */
                        if (is_templ_type(t)) {
                                e = cast_templ_type(t, e, CAST_IMPLICIT);
                        } else {
                                e = init_aggregate(t, e, id, &err);
                        }
                        break;
                }
                case exp_nof_tag: {
                        /* Padded aggregate initialisers */
                        e = DEREF_exp(exp_nof_start(e));
                        e = init_general(t, e, id, 0);
                        return (e);
                }
                case exp_initialiser_tag: {
                        /* Function style initialisers */
                        LIST(EXP)args;
                        args = DEREF_list(exp_initialiser_args(e));
                        args = convert_args(args);
                        e = init_constr(t, args, &err);
                        if (!IS_NULL_err(err)) {
                                err = init_error(err, 1);
                        }
                        break;
                }
                default: {
                        /* Simple initialisers */
                        unsigned etag = TAG_exp(e);
                        e = convert_reference(e, REF_ASSIGN);
                        if (etag == exp_paren_tag && IS_type_array(t)) {
                                e = make_paren_exp(e);
                        }
                        e = init_assign(t, cv_none, e, &err);
                        if (!IS_NULL_err(err)) {
                                err = init_error(err, 1);
                        }
                        break;
                }
                }
        }

        /* Report any errors */
        if (!IS_NULL_err(err)) {
                ERROR ferr = ERR_dcl_init_decl(id, NULL_string);
                err = concat_error(ferr, err);
                report(crt_loc, err);
        }

        /* Check for dynamic initialisers */
        if (!IS_NULL_exp(e)) {
                e = dynamic_init(id, NULL_string, e);
        }
        return (e);
}


/*
    DESTROY AN OBJECT

    This routine creates a destructor for the object id of type t,
    reporting any errors.
*/

EXP
destroy_general(TYPE t, IDENTIFIER id)
{
        EXP d = NULL_exp;
        int du = do_usage;
        ERROR err = NULL_err;
        do_usage = 0;
        d = init_default(t, &d, DEFAULT_DESTR, EXTRA_DESTR, &err);
        if (!IS_NULL_err(err)) {
                /* Report any destructor errors */
                ERROR ferr = ERR_dcl_init_decl(id, NULL_string);
                err = concat_error(ferr, err);
                report(decl_loc, err);
        }
        do_usage = du;
        return (d);
}


/*
    INITIALISE AN OBJECT

    This routine initialises the object given by id to the value e.  Note
    that e can be the null expression, indicating that no initialiser is
    given.  The routine 1 if the object is defined, 2 if it is tentatively
    defined, and 0 if it is just declared (see dump_declare).
*/

int
init_object(IDENTIFIER id, EXP e)
{
        EXP d;
        TYPE t;
        int def = 0;
        unsigned tag;
        DECL_SPEC ds;
        unsigned itag;
        int ignore = 0;

        /* Check for non-object declarations */
        if (IS_NULL_id(id)) {
                return (0);
        }
        itag = TAG_id(id);
        switch (itag) {
        case id_variable_tag:
        case id_stat_member_tag:
                /* Variables and static data members */
                break;
        case id_parameter_tag:
                /* Function parameters */
                if (!in_default_arg) {
                        if (!IS_NULL_exp(e)) {
                                report(crt_loc, ERR_dcl_fct_default_weak(id));
                        }
                        return (0);
                }
                break;
        case id_function_tag:
        case id_mem_func_tag:
        case id_stat_mem_func_tag:
                /* Check for function declarations */
                if (!IS_NULL_exp(e)) {
                        /* Can't initialise functions */
                        report(crt_loc, ERR_dcl_init_func(id));
                }

                /* Check previous definition */
                d = DEREF_exp(id_function_etc_defn(id));
                if (!IS_NULL_exp(d)) {
                        ds = DEREF_dspec(id_storage(id));
                        ds |= dspec_defn;
                        COPY_dspec(id_storage(id), ds);
                }
                return (0);
        case id_member_tag:
                /* Check for non-static members (shouldn't be reached) */
                report(crt_loc, ERR_class_mem_init_mem(id));
                return (0);
        default:
                /* The declaration could have been a typedef */
                if (!IS_NULL_exp(e)) {
                        /* Can't initialise a typedef */
                        report(crt_loc, ERR_dcl_init_typedef(id));
                }
                return (1);
        }

        /* Get declaration data */
        t = DEREF_type(id_variable_etc_type(id));
        tag = TAG_type(t);
        ds = DEREF_dspec(id_storage(id));
        temp_storage = (ds & dspec_storage);

        /* Check array initialisers */
        if (tag == type_array_tag) {
                int chk = 0;
                if (!IS_NULL_exp(e)) {
                        /* Initialisation of array types */
                        NAT n = DEREF_nat(type_array_size(t));
                        if ((ds & dspec_auto) && itag == id_variable_tag) {
                                /* Local aggregate initialiser */
                                report(crt_loc, ERR_dcl_init_aggr_auto(id));
                        }
                        last_array_size = NULL_nat;
                        e = init_general(t, e, id, 0);
                        if (IS_NULL_nat(n)) {
                                /* Complete unbounded arrays */
                                n = last_array_size;
                                if (!IS_NULL_nat(n)) {
                                        CV_SPEC qual = DEREF_cv(type_qual(t));
                                        TYPE s = DEREF_type(type_array_sub(t));
                                        n = check_array_dim(n);
                                        MAKE_type_array(qual, s, n, t);
                                        COPY_type(id_variable_etc_type(id), t);
                                }
                        }
                        ds |= dspec_defn;
                        chk = 1;
                } else {
                        /* Allow for tentative definitions */
                        if (ds & dspec_defn) {
                                chk = LANGUAGE_CPP;
                        }
                }

                /* Can only spot incomplete arrays at this stage */
                if (chk) {
                        ERROR err = check_complete(t);
                        if (!IS_NULL_err(err)) {
                                ERROR err2 = ERR_basic_types_def_incompl(id);
                                err = concat_error(err, err2);
                                report(decl_loc, err);
                        }
                }
        } else {
                /* Other initialisers */
                if (!IS_NULL_exp(e)) {
                        if (tag == type_compound_tag) {
                                if ((ds & dspec_auto) &&
                                    itag == id_variable_tag) {
                                        /* Local aggregate initialiser */
                                        report(crt_loc,
                                               ERR_dcl_init_aggr_auto(id));
                                }
                        }
                        if (ds & dspec_defn) {
                                /* Type already check for completeness */
                                /* EMPTY */
                        } else if (tag == type_ref_tag) {
                                /* Reference types don't need checking */
                                /* EMPTY */
                        } else {
                                /* Type not yet checked for completeness */
                                ERROR err = check_complete(t);
                                if (!IS_NULL_err(err)) {
                                        ERROR err2 =
                                            ERR_basic_types_def_incompl(id);
                                        err = concat_error(err, err2);
                                        report(decl_loc, err);
                                }
                                ds |= dspec_defn;
                        }

                        /* Examine initialiser */
                        e = init_general(t, e, id, 0);
                }
        }

        /* Check on definition */
        if (ds & dspec_stat_inline) {
                /* Check for definitions of inline static members */
                if (ds & dspec_defn) {
                        if (!IS_NULL_exp(e)) {
                                /* Can't give a value in the definition */
                                PTR(LOCATION) loc = id_loc(id);
                                report(decl_loc,
                                       ERR_class_static_data_def(id, loc));
                        } else {
                                /* Force definition to existing value */
                                e = DEREF_exp(id_variable_etc_init(id));
                        }
                        /* Mark as defined and no longer inline */
                        ds &= ~dspec_stat_inline;
                }
        } else {
                /* Provide default definition if necessary */
                if (ds & dspec_defn) {
                        def = 1;
                        if (IS_NULL_exp(e)) {
                                e = init_general(t, e, id, LANGUAGE_C);
                                if (!IS_NULL_exp(e) && IS_exp_zero(e)) {
                                        def = 2;
                                }
                        }
                }

                /* Check previous definition */
                d = DEREF_exp(id_variable_etc_init(id));
                if (!IS_NULL_exp(d)) {
                        if (!IS_NULL_exp(e)) {
                                /* Defined twice */
                                ERROR err;
                                PTR(LOCATION) loc = id_loc(id);
                                if (def == 2) {
                                        /* This definition is tentative */
                                        err = ERR_basic_odr_tentative(id, loc);
                                        ignore = 1;
                                } else if (IS_exp_zero(d)) {
                                        /* Previous definition was tentative */
                                        err = ERR_basic_odr_tentative(id, loc);
                                } else {
                                        /* Neither definition is tentative */
                                        err = ERR_basic_odr_def(id, loc);
                                }
                                report(decl_loc, err);
                        }
                        ds |= dspec_defn;
                }
        }

        /* Update declaration data */
        COPY_type(id_variable_etc_type(id), t);
        COPY_dspec(id_storage(id), ds);
        if (!IS_NULL_exp(e)) {
                /* Define object */
                if (!ignore) {
                        if ((ds & dspec_auto) && (ds & dspec_used)) {
                                /* Local variable initialised in terms of
                                 * itself */
                                TYPE s = DEREF_type(exp_type(e));
                                MAKE_exp_dynamic(s, e, e);
                        }
                        if (itag != id_parameter_tag) {
                                e = check_init(e);
                        }
                        COPY_exp(id_variable_etc_init(id), e);
                        COPY_loc(id_loc(id), decl_loc);
                        define_id(id);
                }
                if (ds & dspec_linkage) {
                        /* Check enclosing namespace */
                        NAMESPACE ns = DEREF_nspace(id_parent(id));
                        check_decl_nspace(id, ns, 1, crt_namespace);
                }
                if (def == 0) {
                        def = 1;
                }
        }
        if (def) {
                /* Create destructor */
                EXP d1 = DEREF_exp(id_variable_etc_term(id));
                d = destroy_general(t, id);
                if (!IS_NULL_exp(d1) && IS_exp_paren(d1)) {
                        /* Preserve parenthesised type information */
                        TYPE t1 = DEREF_type(exp_type(d1));
                        MAKE_exp_paren(t1, d, d);
                }
                COPY_exp(id_variable_etc_term(id), d);
        }
        if (!IS_NULL_id(unify_id_pending)) {
                /* Deal with any pending identifiers */
                IGNORE unify_id(unify_id_pending, id, 1);
        }
        temp_storage = dspec_auto;
        if (def && !(ds & dspec_auto)) {
                if (!really_in_function_defn && !in_template_decl) {
                        /* Compile variable definition */
                        compile_variable(id, 0);
                }
        }
        return (def);
}


/*
    INITIALISE A FUNCTION PARAMETER

    This routine initialises the function parameter id with the expression
    e (i.e. sets up a default argument value).  Note that the class
    definition case, where e is initially just skipped over, is dealt
    with here.
*/

void
init_param(IDENTIFIER id, EXP e)
{
        if (!IS_NULL_exp(e)) {
                if (!IS_NULL_id(id)) {
                        if (IS_id_token(id)) {
                                /* Template parameter */
                                init_exp_param(id, e);
                        } else {
                                /* Function parameter */
                                if (IS_exp_uncompiled(e)) {
                                        if (in_class_defn) {
                                                LIST(IDENTIFIER) ft;
                                                CLASS_TYPE ct = crt_class;
                                                ft = DEREF_list(ctype_nest(ct));
                                                CONS_id(id, ft, ft);
                                                COPY_list(ctype_nest(ct), ft);
                                        }
                                        COPY_exp(id_parameter_init(id), e);
                                } else {
                                        in_default_arg++;
                                        IGNORE init_object(id, e);
                                        in_default_arg--;
                                }
                        }
                }
        }
        return;
}


/*
    INITIALISE A CLASS MEMBER

    This routine initialises the class member id with the expression e
    (which since it is a constant-expression has already undergone the
    appropriate operand conversions).  Note that the pure specifier is
    indistinguishible syntactically from a member initialiser, and so is
    only spotted at this stage.  See above for inline definitions of static
    data members.  The routine returns 1 for a definition and 0 for a
    declaration (see dump_declare).
*/

int
init_member(IDENTIFIER id, EXP e)
{
        int def;
        unsigned tag;

        /* Check for definitions */
        if (IS_NULL_id(id)) {
                return (0);
        }
        tag = TAG_id(id);
        if (have_access_decl) {
                def = 2;
                have_access_decl = 0;
        } else {
                switch (tag) {
                case id_stat_member_tag:
                case id_mem_func_tag:
                case id_stat_mem_func_tag:
                        def = 0;
                        break;
                default:
                        def = 1;
                        break;
                }
        }

        /* Check for function declarations */
        if (IS_NULL_exp(e)) {
                if (tag == id_mem_func_tag || tag == id_stat_mem_func_tag) {
                        if (really_in_function_defn) {
                                NAMESPACE ns = DEREF_nspace(id_parent(id));
                                if (EQ_nspace(ns, crt_namespace)) {
                                        DECL_SPEC ds, mds;
                                        ds = DEREF_dspec(id_storage(id));
                                        mds = (dspec_inherit | dspec_implicit |
                                               dspec_defn);
                                        if (!(ds & mds)) {
                                                /* Local functions should be
                                                 * defined */
                                                report(decl_loc, ERR_class_local_func(id));
                                        }
                                }
                        }
                }
                return (def);
        }

        /* Check the various types of member */
        switch (tag) {
        case id_stat_member_tag: {
                /* Static data members may be initialised */
                DECL_SPEC ds;
                TYPE t = DEREF_type(id_stat_member_type(id));
                CV_SPEC cv = find_cv_qual(t);
                switch (TAG_type(t)) {
                case type_integer_tag:
                case type_enumerate_tag: {
                        ERROR err = NULL_err;
                        IGNORE make_nat_exp(e, &err);
                        if (!IS_NULL_err(err)) {
                                /* Initialiser should be a constant
                                 * expression */
                                ERROR err2 = ERR_class_mem_init_const();
                                err = concat_error(err, err2);
                                report(crt_loc, err);
                        }
                        break;
                }
                case type_token_tag: {
                        if (!is_templ_type(t)) {
                                goto default_lab;
                        }
                        break;
                }
                default:
default_lab:
                        /* Only integral types allowed */
                        report(crt_loc, ERR_class_static_data_init(id, t));
                        break;
                }
                if (cv != (cv_const | cv_lvalue)) {
                        /* Only const types allowed */
                        report(crt_loc, ERR_class_static_data_const(id, t));
                }
                e = init_general(t, e, id, 0);
                e = dynamic_init(id, NULL_string, e);
                e = check_init(e);
                COPY_exp(id_stat_member_init(id), e);

                /* Mark inline member definition */
                ds = DEREF_dspec(id_storage(id));
                ds |= dspec_stat_inline;
                COPY_dspec(id_storage(id), ds);
                break;
        }
        case id_mem_func_tag:
        case id_stat_mem_func_tag: {
                /* Check for pure specifiers */
                if (is_zero_exp(e)) {
                        DECL_SPEC ds = DEREF_dspec(id_storage(id));
                        if (ds & dspec_virtual) {
                                CLASS_TYPE ct = crt_class;
                                CLASS_INFO ci = DEREF_cinfo(ctype_info(ct));

                                /* Pure specifier should be precisely '0' */
                                int tok = last_lex_token;
                                if (tok != lex_integer_Hexp ||
                                    is_literal(e)!= 2) {
                                        report(crt_loc,
                                               ERR_class_abstract_zero());
                                }

                                /* Mark class as abstract */
                                ci |= cinfo_abstract;
                                COPY_cinfo(ctype_info(ct), ci);

                                /* Mark function as pure */
                                ds |= dspec_pure;
                                COPY_dspec(id_storage(id), ds);
                        } else {
                                /* Only virtual functions can be pure */
                                report(crt_loc, ERR_class_abstract_virt());
                        }
                } else {
                        /* Can't initialise functions otherwise */
                        report(crt_loc, ERR_dcl_init_func(id));
                }
                break;
        }
        case id_member_tag: {
                /* Can't initialise non-static members */
                report(crt_loc, ERR_class_mem_init_mem(id));
                break;
        }
        default:
                /* Can't initialise a typedef */
                report(crt_loc, ERR_dcl_init_typedef(id));
                break;
        }
        return (def);
}


/*
    ALLOW A TOKEN AS AN INITIALISER

    This routine enables the token id as an initialiser.
*/

void
allow_initialiser(IDENTIFIER id)
{
        id = find_token(id);
        if (IS_id_token(id)) {
                /* NOT YET IMPLEMENTED */
                /* EMPTY */
        } else {
                report(crt_loc, ERR_token_undecl(id));
        }
        return;
}


/*
    INITIALISE THE FIELD BUFFER

    This routine initialises the field buffer.
*/

void
init_initialise(void)
{
        field_buff.posn = extend_buffer(&field_buff, field_buff.posn);
        return;
}