Subversion Repositories tendra.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
                 Crown Copyright (c) 1997
    
    This TenDRA(r) Computer Program is subject to Copyright
    owned by the United Kingdom Secretary of State for Defence
    acting through the Defence Evaluation and Research Agency
    (DERA).  It is made available to Recipients with a
    royalty-free licence for its use, reproduction, transfer
    to other parties and amendment for any purpose not excluding
    product development provided that any such use et cetera
    shall be deemed to be acceptance of the following conditions:-
    
        (1) Its Recipients shall ensure that this Notice is
        reproduced upon any copies or amended versions of it;
    
        (2) Any amended version of it shall be clearly marked to
        show both the nature of and the organisation responsible
        for the relevant amendment or amendments;
    
        (3) Its onward transfer from a recipient to another
        party shall be deemed to be that party's acceptance of
        these conditions;
    
        (4) DERA gives no warranty or assurance as to its
        quality or suitability for any purpose and DERA accepts
        no liability whatsoever in relation to any use to which
        it may be put.
*/


#include "config.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "exp_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "inst_ops.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "off_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "basetype.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 "dump.h"
#include "file.h"
#include "function.h"
#include "hash.h"
#include "identifier.h"
#include "initialise.h"
#include "instance.h"
#include "literal.h"
#include "namespace.h"
#include "option.h"
#include "preproc.h"
#include "save.h"
#include "statement.h"
#include "stmt.h"
#include "syntax.h"
#include "tok.h"
#include "variable.h"


/*
    VARIABLE ANALYSIS FLAGS

    The flag suppress_variable may be set to 1 to suppress variable
    analysis on function parameters and to 2 to suppress all variable
    analysis.  The flag anon_c_linkage may be set to true to indicate
    that objects with C linkage in anonymous namespaces are anonymous.
*/

int suppress_variable = 0 ;
int anon_c_linkage = 0 ;


/*
    CHECK A VARIABLE OR FUNCTION USAGE

    This routine checks the usage of the identifier id.  The flag anon
    is true if the complete reach of the identifier is being analysed.  For
    example, the reach of a local variable is its scope, that of an internal
    variable is its translation unit, and that of an external variable is
    its entire program.
*/

static void check_usage
    PROTO_N ( ( id, blk, anon ) )
    PROTO_T ( IDENTIFIER id X EXP blk X int anon )
{
    int opt ;
    ERROR err ;
    LOCATION loc ;
    HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
    if ( !IS_hashid_anon ( nm ) ) {
        /* Ignore anonymous identifiers */
        DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
        if ( anon == ANON_NAMESPACE && ( ds & dspec_c ) ) {
            if ( !anon_c_linkage ) anon = ANON_NONE ;
        }
        if ( ds & dspec_static ) {
            if ( !IS_NULL_exp ( blk ) || suppress_variable == 2 ) {
                /* Global statics are external to a block etc. */
                ds |= dspec_extern ;
            }
        }
        if ( anon || !( ds & dspec_extern ) ) {
            if ( ds & dspec_implicit ) {
                /* Ignore implicitly declared constructors etc. */
                /* EMPTY */
            } else if ( ds & ( dspec_inherit | dspec_alias ) ) {
                /* Don't deal with inherited members here */
                /* EMPTY */
            } else if ( ds & dspec_token ) {
                /* Ignore tokenised identifiers */
                /* EMPTY */
            } else if ( ds & dspec_defn ) {
                if ( ds & dspec_used ) {
                    /* Defined and used */
                    /* EMPTY */
                } else {
                    /* Defined but not used */
                    if ( ds & ( dspec_inline | dspec_virtual ) ) {
                        /* Ignore inline and virtual functions */
                        /* EMPTY */
                    } else {
                        if ( ds & dspec_auto ) {
                            err = ERR_stmt_dcl_unused ( id ) ;
                        } else {
                            if ( ds & dspec_static ) {
                                opt = OPT_discard_static ;
                            } else {
                                opt = OPT_variable ;
                            }
                            err = ERR_basic_odr_unused ( id ) ;
                            err = set_severity ( err, opt, 0 ) ;
                        }
                        if ( !IS_NULL_err ( err ) ) {
                            DEREF_loc ( id_loc ( id ), loc ) ;
                            report ( loc, err ) ;
                        }
                    }
                }
            } else {
                if ( ds & dspec_used ) {
                    /* Used but not defined */
                    if ( IS_id_class_name_etc ( id ) ) {
                        /* Alright for type names */
                        /* EMPTY */
                    } else {
                        if ( ds & dspec_inline ) {
                            err = ERR_basic_odr_inline ( id ) ;
                        } else {
                            err = ERR_basic_odr_undef ( id ) ;
                        }
                        if ( !IS_NULL_err ( err ) ) {
                            DEREF_loc ( id_loc ( id ), loc ) ;
                            report ( loc, err ) ;
                        }
                        ds |= dspec_defn ;
                        COPY_dspec ( id_storage ( id ), ds ) ;
                    }
                } else if ( ds & dspec_virtual ) {
                    /* Undefined virtual function */
                    if ( ds & dspec_pure ) {
                        /* Allow pure virtual functions */
                        /* EMPTY */
                    } else {
                        err = ERR_basic_odr_undef ( id ) ;
                        if ( !IS_NULL_err ( err ) ) {
                            DEREF_loc ( id_loc ( id ), loc ) ;
                            report ( loc, err ) ;
                        }
                    }
                } else if ( ds & dspec_inline ) {
                    /* Ignore inline functions */
                    /* EMPTY */
                } else {
                    /* Not used or defined */
                    if ( ds & dspec_static ) {
                        opt = OPT_discard_static ;
                    } else {
                        opt = OPT_variable ;
                    }
                    err = ERR_basic_odr_redundant ( id ) ;
                    err = set_severity ( err, opt, 0 ) ;
                    if ( !IS_NULL_err ( err ) ) {
                        DEREF_loc ( id_loc ( id ), loc ) ;
                        report ( loc, err ) ;
                    }
                }
            }
        }
    }
    return ;
}


/*
    CHECK A TEMPLATE IDENTIFIER

    This routine calls check_identifier for each instance of the template
    id.  t gives the template type, otherwise the parameters are as in
    check_identifier.
*/

static void check_template_id
    PROTO_N ( ( t, id, ns, blk, anon, chk ) )
    PROTO_T ( TYPE t X IDENTIFIER id X NAMESPACE ns X EXP blk X
              int anon X int chk )
{
    DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
    if ( !( ds & dspec_done ) ) {
        /* Scan through template applications */
        TOKEN sort = DEREF_tok ( type_templ_sort ( t ) ) ;
        INSTANCE apps = DEREF_inst ( tok_templ_apps ( sort ) ) ;
        COPY_dspec ( id_storage ( id ), ( ds | dspec_done ) ) ;
        while ( !IS_NULL_inst ( apps ) ) {
            DECL_SPEC acc = DEREF_dspec ( inst_templ_access ( apps ) ) ;
            if ( !( acc & ( dspec_main | dspec_alias ) ) ) {
                if ( ( acc & dspec_instance ) && !( acc & dspec_mutable ) ) {
                    IDENTIFIER aid = DEREF_id ( inst_templ_id ( apps ) ) ;
                    IGNORE check_identifier ( aid, ns, blk, anon, chk ) ;
                }
            }
            apps = DEREF_inst ( inst_next ( apps ) ) ;
        }
        if ( spec_unit ) spec_unit = save_end ( spec_unit, NULL_nspace ) ;
        COPY_dspec ( id_storage ( id ), ds ) ;
    }
    return ;
}


/*
    CHECK A CLASS USAGE

    This routine checks the class usage information cu for the type ct.
*/

static void check_class_usage
    PROTO_N ( ( ct, cu ) )
    PROTO_T ( CLASS_TYPE ct X CLASS_USAGE cu )
{
    LOCATION loc ;
    if ( cu & cusage_destr ) {
        IDENTIFIER id = DEREF_id ( ctype_destr ( ct ) ) ;
        if ( !IS_NULL_id ( id ) && IS_id_mem_func ( id ) ) {
            DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
            if ( !( ds & dspec_trivial ) ) {
                DEREF_loc ( id_loc ( id ), loc ) ;
                report ( loc, ERR_expr_delete_post ( id ) ) ;
            }
        }
    }
    if ( cu & cusage_delete ) {
        IDENTIFIER id = find_operator ( ct, lex_delete ) ;
        if ( !IS_NULL_id ( id ) ) {
            DEREF_loc ( id_loc ( id ), loc ) ;
            report ( loc, ERR_expr_delete_post ( id ) ) ;
        }
    }
    if ( cu & cusage_delete_array ) {
        IDENTIFIER id = find_operator ( ct, lex_delete_Harray ) ;
        if ( !IS_NULL_id ( id ) ) {
            DEREF_loc ( id_loc ( id ), loc ) ;
            report ( loc, ERR_expr_delete_post ( id ) ) ;
        }
    }
    if ( cu & cusage_address ) {
        IDENTIFIER id = find_operator ( ct, lex_and_H1 ) ;
        while ( !IS_NULL_id ( id ) && IS_id_function_etc ( id ) ) {
            TYPE t = DEREF_type ( id_function_etc_type ( id ) ) ;
            if ( min_no_args ( t ) == 1 ) {
                DEREF_loc ( id_loc ( id ), loc ) ;
                report ( loc, ERR_expr_unary_op_ref_post ( id ) ) ;
                break ;
            }
            id = DEREF_id ( id_function_etc_over ( id ) ) ;
        }
    }
    return ;
}


/*
    COMPLETE A TENTATIVE DEFINITION

    This routine completes a C-style tentative definition of the
    variable id.  That is, if id has not been defined elsewhere, then
    it is defined to be zero.
*/

static int define_tentative
    PROTO_N ( ( id, anon ) )
    PROTO_T ( IDENTIFIER id X int anon )
{
    int def = 0 ;
    EXP e = DEREF_exp ( id_variable_etc_init ( id ) ) ;
    if ( !IS_NULL_exp ( e ) && IS_exp_zero ( e ) ) {
        LOCATION loc ;
        DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
        TYPE t = DEREF_type ( id_variable_etc_type ( id ) ) ;
        bad_crt_loc++ ;
        loc = crt_loc ;
        DEREF_loc ( id_loc ( id ), crt_loc ) ;
        if ( anon || !( ds & dspec_extern ) ) {
            /* Types for internal objects should be complete */
            ERROR err = check_complete ( t ) ;
            if ( !IS_NULL_err ( err ) ) {
                ERROR err2 = ERR_basic_types_tent_incompl ( id ) ;
                err = concat_error ( err, err2 ) ;
                report ( crt_loc, err ) ;
            }
        }
        if ( IS_type_array ( t ) ) {
            /* Complete array 'A []' to 'A [1]' */
            NAT n = DEREF_nat ( type_array_size ( t ) ) ;
            if ( IS_NULL_nat ( n ) ) {
                CV_SPEC qual = DEREF_cv ( type_qual ( t ) ) ;
                TYPE s = DEREF_type ( type_array_sub ( t ) ) ;
                n = small_nat [1] ;
                MAKE_type_array ( qual, s, n, t ) ;
                COPY_type ( id_variable_etc_type ( id ), t ) ;
            }
        }
        e = init_general ( t, NULL_exp, id, 0 ) ;
        COPY_exp ( id_variable_etc_init ( id ), e ) ;
        define_id ( id ) ;
        crt_loc = loc ;
        bad_crt_loc-- ;
        def = 1 ;
    }
    return ( def ) ;
}


/*
    CHECK A GLOBAL IDENTIFIER

    This routine applies the global program checks to the identifier id
    from the namespace ns.  If id is a class or namespace name then the
    checks are also applied recursively to the members of id.  The flag
    anon is true if the parent namespace of id is local to the current
    translation unit.  The routine returns the number of identifiers in
    id (allowing for overloaded functions).
*/

unsigned long check_identifier
    PROTO_N ( ( id, ns, blk, anon, chk ) )
    PROTO_T ( IDENTIFIER id X NAMESPACE ns X EXP blk X int anon X int chk )
{
    unsigned long n = 1 ;
    if ( spec_unit ) spec_unit = save_id ( spec_unit, id, ns ) ;
    switch ( TAG_id ( id ) ) {

        case id_class_name_tag : {
            /* Check the members of a class */
            DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
            if ( !( ds & dspec_implicit ) ) {
                int templ = 0 ;
                CLASS_TYPE ct ;
                CLASS_INFO ci ;
                NAMESPACE cns ;
                int tchk = chk ;
                TYPE t = DEREF_type ( id_class_name_defn ( id ) ) ;
                TYPE s = t ;
                while ( IS_type_templ ( t ) ) {
                    /* Template classes */
                    templ = 1 ;
                    tchk = 0 ;
                    t = DEREF_type ( type_templ_defn ( t ) ) ;
                }
                ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
                cns = DEREF_nspace ( ctype_member ( ct ) ) ;
                ci = DEREF_cinfo ( ctype_info ( ct ) ) ;
                if ( ci & cinfo_complete ) {
                    /* Check class member namespace */
                    CLASS_USAGE cu = DEREF_cusage ( ctype_usage ( ct ) ) ;
                    IDENTIFIER cid = DEREF_id ( ctype_name ( ct ) ) ;
                    HASHID cnm = DEREF_hashid ( id_name ( cid ) ) ;
                    if ( IS_hashid_anon ( cnm ) ) anon = ANON_CLASS ;
#if LANGUAGE_CPP
                    if ( ( ci & cinfo_polymorphic ) && tchk ) {
                        /* Compile virtual function table */
                        compile_virtual ( ct, anon ) ;
                    }
#endif
                    if ( cu != cusage_none ) {
                        /* Check class usage */
                        check_class_usage ( ct, cu ) ;
                    }
                } else {
                    if ( ( ci & cinfo_incomplete ) && tchk ) {
                        /* Report incomplete types */
                        complete_class ( ct, 0 ) ;
                        ci = DEREF_cinfo ( ctype_info ( ct ) ) ;
                        if ( !( ci & cinfo_complete ) ) {
                            ERROR err = ERR_basic_types_completed ( t ) ;
                            if ( !IS_NULL_err ( err ) ) {
                                LOCATION loc ;
                                DEREF_loc ( id_loc ( id ), loc ) ;
                                report ( loc, err ) ;
                            }
                        }
                    }
                    cns = NULL_nspace ;
                }
                IGNORE check_namespace ( cns, NULL_exp, anon, tchk ) ;
                if ( templ ) {
                    /* Template classes */
                    check_template_id ( s, id, ns, blk, anon, chk ) ;
                }
            }
            goto type_label ;
        }

        case id_enum_name_tag :
        case id_class_alias_tag :
        case id_enum_alias_tag :
        case id_type_alias_tag :
        type_label : {
            /* Compile type names */
            if ( chk ) {
                if ( anon ) check_usage ( id, blk, anon ) ;
                if ( IS_NULL_exp ( blk ) ) compile_type ( id ) ;
            }
            break ;
        }

        case id_nspace_name_tag : {
            /* Check the members of a namespace */
            NAMESPACE cns = DEREF_nspace ( id_nspace_name_defn ( id ) ) ;
            n += check_namespace ( cns, NULL_exp, anon, chk ) ;
            break ;
        }

        case id_variable_tag : {
            /* Check variable usage */
            if ( chk ) {
                DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
                if ( ( ds & dspec_static ) && preserve_all ) {
                    compile_preserve ( id ) ;
                }
                check_usage ( id, blk, anon ) ;
                if ( IS_NULL_exp ( blk ) ) {
                    if ( ds & dspec_defn ) {
                        IGNORE define_tentative ( id, anon ) ;
                    }
                    compile_variable ( id, 0 ) ;
                } else {
                    /* End of local block */
                    if ( ds & dspec_auto ) {
                        EXP term ;
                        ds &= ~dspec_access ;
                        COPY_dspec ( id_storage ( id ), ds ) ;
                        term = DEREF_exp ( id_variable_etc_term ( id ) ) ;
                        if ( !IS_NULL_exp ( term ) ) {
                            if ( do_usage ) dump_destr ( id, &stmt_loc ) ;
                        }
                    }
                }
            }
            break ;
        }

        case id_parameter_tag : {
            /* Check parameter usage */
            if ( chk && suppress_variable == 0 ) {
                check_usage ( id, blk, anon ) ;
            }
            break ;
        }

        case id_stat_member_tag : {
            /* Check static data member usage */
            if ( anon == ANON_CLASS ) {
                LOCATION loc ;
                DEREF_loc ( id_loc ( id ), loc ) ;
                report ( loc, ERR_class_static_data_anon ( id ) ) ;
            }
            if ( chk ) {
                check_usage ( id, blk, anon ) ;
                if ( IS_NULL_exp ( blk ) ) {
                    DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
                    if ( ds & dspec_defn ) {
                        IGNORE define_tentative ( id, anon ) ;
                    }
                    compile_variable ( id, 0 ) ;
                }
            }
            break ;
        }

        case id_function_tag :
        case id_mem_func_tag :
        case id_stat_mem_func_tag : {
            /* Check member function usage */
            TYPE t = DEREF_type ( id_function_etc_type ( id ) ) ;
            IDENTIFIER over = DEREF_id ( id_function_etc_over ( id ) ) ;
            if ( !IS_NULL_id ( over ) ) {
                n += check_identifier ( over, ns, blk, anon, chk ) ;
            }
            if ( IS_type_func ( t ) ) {
                /* Simple functions */
                if ( chk ) {
                    DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
                    if ( ( ds & dspec_static ) && preserve_all ) {
                        compile_preserve ( id ) ;
                    }
                    if ( ds & dspec_inline ) {
                        if ( !anon ) anon = ANON_INLINE ;
                    }
                    check_usage ( id, blk, anon ) ;
                    if ( IS_NULL_exp ( blk ) ) compile_function ( id, 0 ) ;
                }
            } else {
                /* Template functions */
                check_template_id ( t, id, ns, blk, anon, chk ) ;
            }
            break ;
        }

        case id_enumerator_tag : {
            /* Check enumerator value */
            if ( chk ) {
                EXP e = DEREF_exp ( id_enumerator_value ( id ) ) ;
                if ( overflow_exp ( e ) ) compile_variable ( id, 0 ) ;
            }
            break ;
        }

        case id_nspace_alias_tag :
        case id_member_tag :
        case id_weak_param_tag : {
            /* Don't check these (yet) */
            break ;
        }
    }
    return ( n ) ;
}


/*
    CHECK A NAMESPACE

    This routine scans through all the members of the namespace ns, calling
    check_identifier for each member (including hidden members) and
    compiling any functions or non-local variables.  It returns the number
    of identifiers declared in ns.  ns may be a block scope associated
    with the statement blk.  The flag anon is true if the parent namespace
    of ns is local to the current translation unit.
*/

unsigned long check_namespace
    PROTO_N ( ( ns, blk, anon, chk ) )
    PROTO_T ( NAMESPACE ns X EXP blk X int anon X int chk )
{
    unsigned long n = 0 ;
    if ( !IS_NULL_nspace ( ns ) ) {
        MEMBER mem ;
        LIST ( IDENTIFIER ) extra ;

        /* Find namespace components */
        switch ( TAG_nspace ( ns ) ) {
            case nspace_named_tag :
            case nspace_global_tag :
            case nspace_ctype_tag : {
                mem = DEREF_member ( nspace_named_etc_first ( ns ) ) ;
                extra = DEREF_list ( nspace_named_etc_extra ( ns ) ) ;
                break ;
            }
            case nspace_unnamed_tag : {
                mem = DEREF_member ( nspace_unnamed_first ( ns ) ) ;
                extra = DEREF_list ( nspace_unnamed_extra ( ns ) ) ;
                if ( anon != ANON_GLOBAL && suppress_variable != 2 ) {
                    anon = ANON_NAMESPACE ;
                }
                break ;
            }
            default : {
                mem = DEREF_member ( nspace_last ( ns ) ) ;
                extra = NULL_list ( IDENTIFIER ) ;
                anon = ANON_NONE ;
                break ;
            }
        }

        /* Scan through namespace members */
        while ( !IS_NULL_member ( mem ) ) {
            IDENTIFIER id = DEREF_id ( member_id ( mem ) ) ;
            IDENTIFIER alt = DEREF_id ( member_alt ( mem ) ) ;
            if ( !IS_NULL_id ( id ) ) {
                n += check_identifier ( id, ns, blk, anon, chk ) ;
            }
            if ( !IS_NULL_id ( alt ) && !EQ_id ( id, alt ) ) {
                n += check_identifier ( alt, ns, blk, anon, chk ) ;
            }
            mem = DEREF_member ( member_next ( mem ) ) ;
        }

        /* Scan through extra namespace members */
        while ( !IS_NULL_list ( extra ) ) {
            IDENTIFIER id = DEREF_id ( HEAD_list ( extra ) ) ;
            n += check_identifier ( id, ns, blk, anon, chk ) ;
            extra = TAIL_list ( extra ) ;
        }
    }
    if ( spec_unit ) spec_unit = save_end ( spec_unit, ns ) ;
    return ( n ) ;
}


/*
    CHECK THE TOKEN NAMESPACE

    This routine applies the usage checks to the tokens declared in the
    translation unit.
*/

static void check_token
    PROTO_Z ()
{
    NAMESPACE ns = token_namespace ;
    MEMBER mem = DEREF_member ( nspace_global_first ( ns ) ) ;
    while ( !IS_NULL_member ( mem ) ) {
        IDENTIFIER id = DEREF_id ( member_id ( mem ) ) ;
        if ( !IS_NULL_id ( id ) ) {
            DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
            if ( !( ds & dspec_done ) ) {
                if ( ds & dspec_defn ) {
                    /* Defined token */
                    compile_token ( id, 1 ) ;
                } else if ( !( ds & dspec_pure ) ) {
                    /* Undefined token */
                    LOCATION loc ;
                    TOKEN tok = DEREF_tok ( id_token_sort ( id ) ) ;
                    bad_crt_loc++ ;
                    loc = crt_loc ;
                    DEREF_loc ( id_loc ( id ), crt_loc ) ;
                    if ( IS_tok_func ( tok ) ) {
                        /* Implicitly define 'FUNC' tokens */
                        IDENTIFIER fn = DEREF_id ( id_token_alt ( id ) ) ;
                        COPY_id ( tok_func_defn ( tok ), fn ) ;
                        ds |= dspec_defn ;
                        COPY_dspec ( id_storage ( id ), ds ) ;
                        if ( !( ds & dspec_explicit ) ) {
                            /* Not explicitly redeclared */
                            report ( crt_loc, ERR_token_def_implicit ( id ) ) ;
                        }
                        use_func_id ( fn, 0, 0 ) ;
                        compile_function ( fn, 0 ) ;
                        compile_token ( id, 1 ) ;
                    } else if ( ds & dspec_static ) {
                        /* Check for built-in token definitions */
                        if ( builtin_token ( id ) != -1 ) {
                            ds = DEREF_dspec ( id_storage ( id ) ) ;
                            if ( !( ds & dspec_defn ) ) {
                                compile_token ( id, 0 ) ;
                            }
                        } else if ( suppress_variable != 2 ) {
                            compile_token ( id, 0 ) ;
                        }
                    }
                    crt_loc = loc ;
                    bad_crt_loc-- ;
                }
            }
        }
        mem = DEREF_member ( member_next ( mem ) ) ;
    }
    return ;
}


/*
    CHECK THE GLOBAL NAMESPACE

    This routine applies check_namespace to the global namespace,
    including checking the main function if complete is true.  It also
    calls check_token.
*/

unsigned long check_global
    PROTO_N ( ( complete ) )
    PROTO_T ( int complete )
{
    /* Check main function if necessary */
    unsigned long n ;
    int anon = ANON_NONE ;
    NAMESPACE ns = global_namespace ;
    if ( complete && suppress_variable != 2 ) {
        IDENTIFIER id = main_function ;
        HASHID nm = KEYWORD ( lex_main ) ;
        if ( IS_NULL_id ( id ) ) {
            /* Look up if not defined */
            MEMBER mem = search_member ( ns, nm, 0 ) ;
            if ( !IS_NULL_member ( mem ) ) {
                id = DEREF_id ( member_id ( mem ) ) ;
                if ( !IS_NULL_id ( id ) && IS_id_function ( id ) ) {
                    main_function = id ;
                } else {
                    id = NULL_id ;
                }
            }
        }
        if ( IS_NULL_id ( id ) ) {
            id = DEREF_id ( hashid_id ( nm ) ) ;
            id = underlying_id ( id ) ;
            report ( builtin_loc, ERR_basic_odr_undef ( id ) ) ;
        } else {
            /* Mark as being used */
            DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
            ds |= ( dspec_used | dspec_called ) ;
            COPY_dspec ( id_storage ( id ), ds ) ;
        }
        anon = ANON_GLOBAL ;
    }

    /* Apply checks to namespaces */
    begin_spec () ;
    n = check_namespace ( ns, NULL_exp, anon, 1 ) ;
    end_spec () ;
    check_token () ;
    return ( n ) ;
}


/*
    CONSTRUCT A SET EXPRESSION

    This routine constructs the expression 'set ( a )'.
*/

EXP make_set_exp
    PROTO_N ( ( a ) )
    PROTO_T ( EXP a )
{
    EXP e ;
    a = convert_reference ( a, REF_NORMAL ) ;
    a = convert_lvalue ( a ) ;
    MAKE_exp_set ( type_void, a, e ) ;
    return ( e ) ;
}


/*
    CONSTRUCT AN UNUSED EXPRESSION

    This routine constructs the expression 'unused ( a )'.
*/

EXP make_unused_exp
    PROTO_N ( ( a ) )
    PROTO_T ( EXP a )
{
    EXP e ;
    a = convert_reference ( a, REF_NORMAL ) ;
    a = convert_lvalue ( a ) ;
    MAKE_exp_unused ( type_void, a, e ) ;
    return ( e ) ;
}


/*
    PRESERVE ALL STATIC IDENTIFIERS

    This flag is set to true to indicate that all static identifiers
    should be preserved.
*/

int preserve_all = 0 ;


/*
    PRESERVE AN IDENTIFIER

    This routine preserves or suspends the static identifier id, as
    indicated by the action act.
*/

void preserve_id
    PROTO_N ( ( id, act ) )
    PROTO_T ( IDENTIFIER id X int act )
{
    switch ( TAG_id ( id ) ) {
        case id_function_tag : {
            IDENTIFIER over = DEREF_id ( id_function_over ( id ) ) ;
            if ( !IS_NULL_id ( over ) ) preserve_id ( over, act ) ;
            goto action_lab ;
        }
        case id_variable_tag :
        action_lab : {
            DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
            if ( ds & dspec_static ) {
                if ( act == lex_preserve ) {
                    compile_preserve ( id ) ;
                } else if ( act == lex_suspend ) {
                    ds |= dspec_used ;
                    COPY_dspec ( id_storage ( id ), ds ) ;
                }
            }
            break ;
        }
        default : {
            ERROR err = ERR_pragma_preserve_undecl ( act, id ) ;
            report ( preproc_loc, err ) ;
            break ;
        }
    }
    return ;
}


/*
    FLOW ANALYSIS FLAGS

    These values are used to mark the various usage states in the flow
    analysis routines.  The fact that va_variable equals dspec_auto and
    va_done equals dspec_done is important, but the other values are
    merely convenient.
*/

typedef DECL_SPEC VAR_INFO ;

#define va_none                 dspec_none
#define va_used_this            dspec_used
#define va_set                  dspec_defn
#define va_maybe_set            dspec_inherit

#define va_filter               ( va_used_this | va_set | va_maybe_set )

#define va_any_use              dspec_static
#define va_any_set              dspec_extern

#define va_done                 dspec_done
#define va_variable             dspec_auto
#define va_ignore               dspec_ignore
#define va_error                dspec_implicit

#define va_address              dspec_register
#define va_assign               dspec_mutable
#define va_alias_used           dspec_inline
#define va_alias_set            dspec_virtual
#define va_partial              dspec_explicit
#define va_member               dspec_friend
#define va_this                 dspec_typedef
#define va_dummy                dspec_pure

#define va_alias                ( va_alias_used | va_alias_set )
#define va_becomes              ( va_assign | va_dummy )
#define va_other                ( va_address | va_becomes | va_partial )
#define va_mask                 ( va_alias | va_other )

#define va_unreached            dspec_main


/*
    FORWARD DECLARATIONS

    The following declarations are required for the flow analysis
    routines.
*/

static VAR_INFO flow_exp PROTO_S ( ( EXP, VAR_INFO ) ) ;
static VAR_INFO flow_alias_exp PROTO_S ( ( EXP, VAR_INFO ) ) ;
static VAR_INFO flow_stmt PROTO_S ( ( EXP, VAR_INFO, int ) ) ;
static VAR_INFO flow_offset PROTO_S ( ( OFFSET, VAR_INFO, int ) ) ;
static void set_variable PROTO_S ( ( IDENTIFIER, VAR_INFO ) ) ;


/*
    LIST OF CURRENTLY ACTIVE VARIABLES

    The variable crt_flow_vars is used to hold a list of all the local
    variables which are in scope at any point in the variable analysis
    routines.
*/

typedef LIST ( VARIABLE ) VAR_LIST ;
static VAR_LIST crt_flow_vars = NULL_list ( VARIABLE ) ;
static VAR_LIST crt_flow_mems = NULL_list ( VARIABLE ) ;
static LIST ( IDENTIFIER ) crt_flow_assign = NULL_list ( IDENTIFIER ) ;


/*
    START VARIABLE ANALYSIS

    This routine is called at the start of the analysis of the local
    variable id.
*/

static void start_variable
    PROTO_N ( ( id, ds, use ) )
    PROTO_T ( IDENTIFIER id X DECL_SPEC ds X VAR_INFO use )
{
    VARIABLE var ;
    use |= ( va_variable | va_done | va_used_this ) ;
    if ( !( ds & dspec_used ) ) use |= va_ignore ;
    var.id = id ;
    var.info = DEREF_dspec ( id_storage ( id ) ) ;
    COPY_dspec ( id_storage ( id ), use ) ;
    CONS_var ( var, crt_flow_vars, crt_flow_vars ) ;
    DEREF_loc ( id_loc ( id ), stmt_loc ) ;
    return ;
}


/*
    SHOULD VARIABLE ANALYSIS BE IGNORED?

    This routine checks whether any variable analysis errors involving
    the local variable id should be ignored.  This is done if id is an
    anonymous identifier or is a const variable all of whose uses have
    been replaced by its definition (see convert_lvalue).
*/

static int ignore_variable
    PROTO_N ( ( id ) )
    PROTO_T ( IDENTIFIER id )
{
    HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
    if ( IS_hashid_anon ( nm ) ) return ( 1 ) ;
    if ( IS_id_variable ( id ) ) {
        TYPE t = DEREF_type ( id_variable_type ( id ) ) ;
        CV_SPEC cv = find_cv_qual ( t ) ;
        if ( cv & cv_volatile ) {
            /* Ignore volatile variables */
            return ( 1 ) ;
        }
        if ( cv == ( cv_const | cv_lvalue ) ) {
            /* Check for const variables */
            EXP e = DEREF_exp ( id_variable_init ( id ) ) ;
            if ( !IS_NULL_exp ( e ) ) {
                if ( IS_exp_int_lit ( e ) ) return ( 1 ) ;
                if ( IS_exp_null ( e ) ) return ( 1 ) ;
            }
        }
    } else if ( IS_id_member ( id ) ) {
        TYPE t = DEREF_type ( id_member_type ( id ) ) ;
        CV_SPEC cv = find_cv_qual ( t ) ;
        if ( cv & cv_volatile ) {
            /* Ignore volatile members */
            return ( 1 ) ;
        }
    }
    return ( 0 ) ;
}


/*
    END A VARIABLE SETTING

    This routine marks the end of the variable setting ds.  ret is true if
    this end of scope is due to a return statement.  In particular it
    checks whether the variable has been assigned to and not used.
*/

static VAR_INFO end_usage
    PROTO_N ( ( id, ds, ret ) )
    PROTO_T ( IDENTIFIER id X VAR_INFO ds X int ret )
{
    if ( ret && ( ds & va_member ) ) {
        /* Class members are used after return */
        if ( ret == 2 ) {
            set_variable ( id, va_member ) ;
            ds = DEREF_dspec ( id_storage ( id ) ) ;
        } else {
            ds |= va_used_this ;
        }
    } else if ( !( ds & va_used_this ) && ( ds & va_set ) ) {
        if ( !( ds & va_ignore ) && !ignore_variable ( id ) ) {
            report ( stmt_loc, ERR_stmt_dcl_reset ( id ) ) ;
            ds |= va_error ;
        }
        ds |= va_used_this ;
    }
    return ( ds ) ;
}


/*
    END VARIABLE ANALYSIS

    This routine is called at the end of the analysis of a local
    variable.  Note that unused variable have already been dealt with.
*/

static void end_variable
    PROTO_N ( ( flow, ret ) )
    PROTO_T ( int flow X int ret )
{
    VARIABLE var ;
    DESTROY_CONS_var ( destroy, var, crt_flow_vars, crt_flow_vars ) ;
    if ( flow ) {
        VAR_INFO use = DEREF_dspec ( id_storage ( var.id ) ) ;
        use = end_usage ( var.id, use, ret ) ;
        if ( !( use & ( va_member | va_ignore ) ) ) {
            if ( !( use & va_any_use ) ) {
                /* Not used in reached code */
                if ( !ignore_variable ( var.id ) ) {
                    LOCATION loc ;
                    DEREF_loc ( id_loc ( var.id ), loc ) ;
                    report ( loc, ERR_stmt_dcl_unused ( var.id ) ) ;
                }
            } else if ( !( use & va_any_set ) ) {
                /* Not set in reached code */
                if ( !( use & va_error ) && !ignore_variable ( var.id ) ) {
                    LOCATION loc ;
                    DEREF_loc ( id_loc ( var.id ), loc ) ;
                    report ( loc, ERR_stmt_dcl_unset ( var.id ) ) ;
                }
            }
        }
    }
    COPY_dspec ( id_storage ( var.id ), var.info ) ;
    return ;
}


/*
    SET A VARIABLE

    This routine adjusts the variable analysis information for the local
    variable use according to the analysis state use.
*/

static void set_variable
    PROTO_N ( ( id, use ) )
    PROTO_T ( IDENTIFIER id X VAR_INFO use )
{
    if ( !( use & va_unreached ) ) {
        VAR_INFO ds = DEREF_dspec ( id_storage ( id ) ) ;
        if ( use & va_assign ) {
            /* Assignment */
            if ( use & va_dummy ) {
                set_variable ( id, va_none ) ;
                ds = DEREF_dspec ( id_storage ( id ) ) ;
            }
            if ( use & va_partial ) {
                /* Partial assignment */
                ds |= ( va_maybe_set | va_any_set ) ;
                ds |= va_used_this ;
            } else {
                /* Complete assignment */
                ds = end_usage ( id, ds, 0 ) ;
                if ( ds & va_assign ) {
                    if ( !( ds & va_ignore ) && !ignore_variable ( id ) ) {
                        /* Multiple assignments */
                        report ( stmt_loc, ERR_expr_ass_twice ( id ) ) ;
                        ds |= va_error ;
                    }
                }
                CONS_id ( id, crt_flow_assign, crt_flow_assign ) ;
                ds |= ( va_set | va_maybe_set | va_any_set | va_assign ) ;
                ds &= ~va_used_this ;
            }

        } else if ( use & va_alias_set ) {
            /* Read-write alias */
            ds |= ( va_maybe_set | va_any_set ) ;
            ds |= ( va_used_this | va_any_use ) ;

        } else if ( use & va_alias_used ) {
            /* Read-only alias */
            ds |= ( va_used_this | va_any_use ) ;

        } else if ( use & va_address ) {
            /* Address */
            ds |= ( va_used_this | va_any_use ) ;

        } else {
            /* Use */
            if ( !( ds & ( va_set | va_maybe_set ) ) ) {
                /* Used without being set */
                if ( !( ds & va_ignore ) && !ignore_variable ( id ) ) {
                    ERROR err ;
                    if ( use & va_member ) {
                        err = ERR_class_base_init_none ( id ) ;
                    } else {
                        err = ERR_stmt_dcl_unset ( id ) ;
                    }
                    report ( stmt_loc, err ) ;
                    ds |= va_error ;
                }
                ds |= ( va_maybe_set | va_any_set ) ;
            }
            ds |= ( va_used_this | va_any_use ) ;
        }
        COPY_dspec ( id_storage ( id ), ds ) ;
    }
    return ;
}


/*
    SET ALL CLASS MEMBERS

    This routine calls set_variable for each of the class members in the
    current variable list.
*/

static void set_members
    PROTO_N ( ( use ) )
    PROTO_T ( VAR_INFO use )
{
    if ( !( use & va_unreached ) ) {
        VAR_LIST va = crt_flow_mems ;
        while ( !IS_NULL_list ( va ) ) {
            IDENTIFIER id = DEREF_id ( var_id ( HEAD_list ( va ) ) ) ;
            set_variable ( id, use ) ;
            va = TAIL_list ( va ) ;
        }
    }
    return ;
}


/*
    MERGE A VARIABLE SETTING

    This routine merges the variable analysis settings given by dp and dq.
    u indicates whether the variable set values are to be taken from dp,
    from dq, from both, or from neither.
*/

static VAR_INFO merge_usage
    PROTO_N ( ( dp, dq, u ) )
    PROTO_T ( VAR_INFO dp X VAR_INFO dq X int u )
{
    VAR_INFO ds = ( dp | dq ) ;
    VAR_INFO dt = dspec_none ;
    switch ( u ) {
        case 0 : dt = ( dp & dq ) ; break ;
        case 1 : dt = dp ; break ;
        case 2 : dt = dq ; break ;
    }
    ds = ( ( ds & ~va_filter ) | ( dt & va_filter ) ) ;
    return ( ds ) ;
}


/*
    SEARCH A LIST OF VARIABLES

    This routine searches the list of variables p for one corresponding to
    id.  q is a member of p which corresponds to id is the simplest case.
    It returns a pointer to the variable if it exists and a null pointer
    otherwise.
*/

static PTR ( VARIABLE ) search_vars
    PROTO_N ( ( p, q, id ) )
    PROTO_T ( VAR_LIST p X VAR_LIST q X IDENTIFIER id )
{
    if ( !IS_NULL_list ( q ) ) {
        PTR ( VARIABLE ) ptr = HEAD_list ( q ) ;
        IDENTIFIER pid = DEREF_id ( var_id ( ptr ) ) ;
        if ( EQ_id ( pid, id ) ) return ( ptr ) ;
    }
    while ( !IS_NULL_list ( p ) ) {
        PTR ( VARIABLE ) ptr = HEAD_list ( p ) ;
        IDENTIFIER pid = DEREF_id ( var_id ( ptr ) ) ;
        if ( EQ_id ( pid, id ) ) return ( ptr ) ;
        p = TAIL_list ( p ) ;
    }
    return ( NULL_ptr ( VARIABLE ) ) ;
}


/*
    SAVE CURRENT VARIABLE SETTINGS

    This routine saves the current variable settings by merging them with
    those stored in the list va.
*/

static VAR_LIST save_vars
    PROTO_N ( ( va, cond ) )
    PROTO_T ( VAR_LIST va X int cond )
{
    VAR_LIST vb = crt_flow_vars ;
    VAR_LIST vc = va ;
    VAR_LIST vd = va ;
    while ( !IS_NULL_list ( vb ) ) {
        VARIABLE var ;
        PTR ( VARIABLE ) pvar ;
        var.id = DEREF_id ( var_id ( HEAD_list ( vb ) ) ) ;
        var.info = DEREF_dspec ( id_storage ( var.id ) ) ;
        if ( cond ) {
            var.info |= va_used_this ;
            COPY_dspec ( id_storage ( var.id ), var.info ) ;
        }
        pvar = search_vars ( vc, vd, var.id ) ;
        if ( IS_NULL_ptr ( pvar ) ) {
            /* Add new identifier to list */
            CONS_var ( var, va, va ) ;
        } else {
            /* Merge with existing information */
            VAR_INFO acc = DEREF_dspec ( var_info ( pvar ) ) ;
            acc = merge_usage ( acc, var.info, 0 ) ;
            COPY_dspec ( var_info ( pvar ), acc ) ;
        }
        if ( !IS_NULL_list ( vd ) ) vd = TAIL_list ( vd ) ;
        vb = TAIL_list ( vb ) ;
    }
    if ( IS_NULL_list ( vc ) ) va = REVERSE_list ( va ) ;
    return ( va ) ;
}


/*
    LOAD CURRENT VARIABLE SETTINGS

    This routine loads the current variable settings by merging them with
    those stored in the list va.
*/

static void load_vars
    PROTO_N ( ( va, u ) )
    PROTO_T ( VAR_LIST va X int u )
{
    VAR_LIST vb = crt_flow_vars ;
    VAR_LIST vc = va ;
    while ( !IS_NULL_list ( vb ) ) {
        IDENTIFIER id = DEREF_id ( var_id ( HEAD_list ( vb ) ) ) ;
        VAR_INFO acc = DEREF_dspec ( id_storage ( id ) ) ;
        PTR ( VARIABLE ) pvar = search_vars ( va, vc, id ) ;
        if ( !IS_NULL_ptr ( pvar ) ) {
            VAR_INFO pacc = DEREF_dspec ( var_info ( pvar ) ) ;
            acc = merge_usage ( acc, pacc, u ) ;
        }
        COPY_dspec ( id_storage ( id ), acc ) ;
        if ( !IS_NULL_list ( vc ) ) vc = TAIL_list ( vc ) ;
        vb = TAIL_list ( vb ) ;
    }
    return ;
}


/*
    SWAP CURRENT VARIABLE SETTINGS

    This routine swaps the current variable settings with those stored in
    the list va.
*/

static void swap_vars
    PROTO_N ( ( va ) )
    PROTO_T ( VAR_LIST va )
{
    while ( !IS_NULL_list ( va ) ) {
        VARIABLE var ;
        VAR_INFO acc ;
        DEREF_var ( HEAD_list ( va ), var ) ;
        acc = DEREF_dspec ( id_storage ( var.id ) ) ;
        COPY_dspec ( var_info ( HEAD_list ( va ) ), acc ) ;
        COPY_dspec ( id_storage ( var.id ), var.info ) ;
        va = TAIL_list ( va ) ;
    }
    return ;
}


/*
    MARK VARIABLE SETTINGS

    This routine marks all the variables in the list va with use.
*/

static void mark_vars
    PROTO_N ( ( va, use ) )
    PROTO_T ( VAR_LIST va X VAR_INFO use )
{
    while ( !IS_NULL_list ( va ) ) {
        IDENTIFIER id = DEREF_id ( var_id ( HEAD_list ( va ) ) ) ;
        VAR_INFO acc = DEREF_dspec ( id_storage ( id ) ) ;
        acc |= use ;
        COPY_dspec ( id_storage ( id ), acc ) ;
        va = TAIL_list ( va ) ;
    }
    return ;
}


/*
    PERFORM FLOW ANALYSIS ON A GOTO STATEMENT

    This routine performs flow analysis on a jump to the label lab.  cond
    is true to indicate a conditional jump.
*/

static VAR_INFO flow_goto_stmt
    PROTO_N ( ( lab, use, cond ) )
    PROTO_T ( IDENTIFIER lab X VAR_INFO use X int cond )
{
    if ( !( use & va_unreached ) ) {
        VAR_INFO ds = DEREF_dspec ( id_storage ( lab ) ) ;
        if ( ds & dspec_reserve ) {
            /* Has backward jump */
            mark_vars ( crt_flow_vars, va_used_this ) ;
        } else {
            /* Only forward jumps */
            VAR_LIST va = DEREF_list ( id_label_vars ( lab ) ) ;
            va = save_vars ( va, cond ) ;
            COPY_list ( id_label_vars ( lab ), va ) ;
        }
        ds |= dspec_temp ;
        COPY_dspec ( id_storage ( lab ), ds ) ;
        use |= va_unreached ;
    }
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON A LABELLED STATEMENT

    This routine performs flow analysis on the statement labelled by the
    label lab.
*/

static VAR_INFO flow_label_stmt
    PROTO_N ( ( lab, use, flow ) )
    PROTO_T ( IDENTIFIER lab X VAR_INFO use X int flow )
{
    EXP e = DEREF_exp ( id_label_stmt ( lab ) ) ;
    VAR_INFO ds = DEREF_dspec ( id_storage ( lab ) ) ;
    if ( ds & dspec_reserve ) {
        /* Has backward jump */
        mark_vars ( crt_flow_vars, ( va_maybe_set | va_used_this ) ) ;
        use &= ~va_unreached ;
    } else {
        /* Only forward jumps */
        int unreached = 0 ;
        VAR_LIST va = DEREF_list ( id_label_vars ( lab ) ) ;
        if ( use & va_unreached ) unreached = 2 ;
        load_vars ( va, unreached ) ;
        DESTROY_list ( va, SIZE_var ) ;
        COPY_list ( id_label_vars ( lab ), NULL_list ( VARIABLE ) ) ;
        if ( ds & dspec_temp ) use &= ~va_unreached ;
    }
    if ( !IS_NULL_exp ( e ) ) {
        e = DEREF_exp ( exp_label_stmt_body ( e ) ) ;
        use = flow_stmt ( e, use, flow ) ;
    }
    return ( use ) ;
}


/*
    CHECK A SEQUENCE POINT

    This routine is called at each sequence point in the flow analysis
    routines.  It clears the list of identifiers assigned since the last
    sequence point.
*/

static void flow_sequence_point
    PROTO_Z ()
{
    LIST ( IDENTIFIER ) p = crt_flow_assign ;
    while ( !IS_NULL_list ( p ) ) {
        VAR_INFO ds ;
        IDENTIFIER id ;
        DESTROY_CONS_id ( destroy, id, p, p ) ;
        ds = DEREF_dspec ( id_storage ( id ) ) ;
        ds &= ~va_assign ;
        COPY_dspec ( id_storage ( id ), ds ) ;
    }
    crt_flow_assign = NULL_list ( IDENTIFIER ) ;
    return ;
}


/*
    PERFORM FLOW ANALYSIS ON AN UNREACHABLE STATEMENT

    This routine performs flow analysis following a return statement (in
    which case ret is true) or other expression which causes execution to
    cease.
*/

static VAR_INFO flow_terminate
    PROTO_N ( ( use, ret ) )
    PROTO_T ( VAR_INFO use X int ret )
{
    if ( !( use & va_unreached ) ) {
        VAR_LIST va = crt_flow_vars ;
        while ( !IS_NULL_list ( va ) ) {
            IDENTIFIER id = DEREF_id ( var_id ( HEAD_list ( va ) ) ) ;
            VAR_INFO ds = DEREF_dspec ( id_storage ( id ) ) ;
            ds = end_usage ( id, ds, ret ) ;
            COPY_dspec ( id_storage ( id ), ds ) ;
            va = TAIL_list ( va ) ;
        }
    }
    UNUSED ( ret ) ;
    use |= va_unreached ;
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON AN IF EXPRESSION

    This routine preforms flow analysis on the if expression with condition
    c and branches a and b.
*/

static VAR_INFO flow_if_exp
    PROTO_N ( ( c, a, b, use, flow ) )
    PROTO_T ( EXP c X EXP a X EXP b X VAR_INFO use X int flow )
{
    VAR_LIST va ;
    VAR_INFO ua, ub ;
    int unreached = 0 ;
    unsigned cv = eval_const_cond ( c ) ;
    if ( flow ) {
        /* Condition */
        use = flow_exp ( c, use ) ;
        flow_sequence_point () ;
    }
    ua = use ;
    ub = use ;
    if ( cv == BOOL_FALSE ) ua |= va_unreached ;
    if ( cv == BOOL_TRUE ) ub |= va_unreached ;
    va = save_vars ( NULL_list ( VARIABLE ), 1 ) ;
    ua = flow_stmt ( a, ua, flow ) ;
    swap_vars ( va ) ;
    ub = flow_stmt ( b, ub, flow ) ;
    if ( ua & va_unreached ) unreached |= 1 ;
    if ( ub & va_unreached ) unreached |= 2 ;
    load_vars ( va, unreached ) ;
    DESTROY_list ( va, SIZE_var ) ;
    use = ( ua & ub ) ;
    return ( use ) ;
}


/*
    PROCESS A FLOW ANALYSIS DIRECTIVE

    This routine processes the flow analysis directive indicated by act
    to the expression e.
*/

static VAR_INFO flow_set
    PROTO_N ( ( e, use, act ) )
    PROTO_T ( EXP e X VAR_INFO use X VAR_INFO act )
{
    if ( !( use & va_unreached ) && !IS_NULL_exp ( e ) ) {
        EXP a = NULL_exp ;
        if ( IS_exp_contents ( e ) ) {
            /* Check for simple lvalues */
            a = DEREF_exp ( exp_contents_ptr ( e ) ) ;
        } else if ( IS_exp_address ( e ) ) {
            /* Check for array to pointer conversions */
            EXP b = DEREF_exp ( exp_address_arg ( e ) ) ;
            TYPE s = DEREF_type ( exp_type ( b ) ) ;
            if ( IS_type_array ( s ) ) a = b ;
        }
        if ( !IS_NULL_exp ( a ) && IS_exp_identifier ( a ) ) {
            IDENTIFIER id = DEREF_id ( exp_identifier_id ( a ) ) ;
            if ( IS_id_variable_etc ( id ) ) {
                DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
                if ( ds & dspec_auto ) {
                    if ( act & va_set ) {
                        /* Set variable */
                        ds |= ( va_set | va_maybe_set | va_any_set ) ;
                        ds &= ~va_used_this ;
                    } else {
                        /* Unused variable */
                        set_variable ( id, act ) ;
                        ds = DEREF_dspec ( id_storage ( id ) ) ;
                        ds &= ~( va_set | va_maybe_set ) ;
                    }
                    COPY_dspec ( id_storage ( id ), ds ) ;
                }
            }
        }
    }
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON A LIST OF TOKEN ARGUMENTS

    This routine performs flow analysis on the list of token arguments p.
*/

static VAR_INFO flow_token_list
    PROTO_N ( ( p, use ) )
    PROTO_T ( LIST ( TOKEN ) p X VAR_INFO use )
{
    while ( !IS_NULL_list ( p ) ) {
        TOKEN tok = DEREF_tok ( HEAD_list ( p ) ) ;
        if ( !IS_NULL_tok ( tok ) ) {
            switch ( TAG_tok ( tok ) ) {
                case tok_exp_tag : {
                    /* Expression tokens */
                    EXP e = DEREF_exp ( tok_exp_value ( tok ) ) ;
                    use = flow_alias_exp ( e, use ) ;
                    break ;
                }
                case tok_stmt_tag : {
                    /* Statement tokens */
                    EXP e = DEREF_exp ( tok_stmt_value ( tok ) ) ;
                    use = flow_stmt ( e, use, 1 ) ;
                    break ;
                }
                case tok_member_tag : {
                    /* Member tokens */
                    OFFSET off = DEREF_off ( tok_member_value ( tok ) ) ;
                    use = flow_offset ( off, use, 0 ) ;
                    break ;
                }
            }
        }
        p = TAIL_list ( p ) ;
    }
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON AN OFFSET

    This routine performs flow analysis on the offset off.  mem is true
    in member functions when the offset is being added to 'this'.
*/

static VAR_INFO flow_offset
    PROTO_N ( ( off, use, mem ) )
    PROTO_T ( OFFSET off X VAR_INFO use X int mem )
{
    DECL_SPEC ua = ( use & ~va_mask ) ;
    if ( IS_NULL_off ( off ) ) return ( ua ) ;
    ASSERT ( ORDER_off == 13 ) ;
    switch ( TAG_off ( off ) ) {

        case off_member_tag : {
            /* Member offsets */
            if ( mem ) {
                IDENTIFIER id = DEREF_id ( off_member_id ( off ) ) ;
                VAR_INFO ds = DEREF_dspec ( id_storage ( id ) ) ;
                if ( ds & va_variable ) set_variable ( id, use ) ;
            }
            break ;
        }

        case off_ptr_mem_tag : {
            /* Pointer to member offsets */
            EXP a = DEREF_exp ( off_ptr_mem_arg ( off ) ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case off_negate_tag : {
            /* Negated offsets */
            OFFSET off1 = DEREF_off ( off_negate_arg ( off ) ) ;
            ua = flow_offset ( off1, ua, 0 ) ;
            break ;
        }

        case off_plus_tag : {
            /* Offset addition */
            OFFSET off1 = DEREF_off ( off_plus_arg1 ( off ) ) ;
            OFFSET off2 = DEREF_off ( off_plus_arg2 ( off ) ) ;
            ua = flow_offset ( off1, ua, 0 ) ;
            ua = flow_offset ( off2, ua, 0 ) ;
            break ;
        }

        case off_mult_tag : {
            /* Offset multiplication */
            OFFSET off1 = DEREF_off ( off_mult_arg1 ( off ) ) ;
            EXP a = DEREF_exp ( off_mult_arg2 ( off ) ) ;
            ua = flow_offset ( off1, ua, 0 ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case off_ptr_diff_tag : {
            /* Difference of two pointers */
            EXP a = DEREF_exp ( off_ptr_diff_ptr1 ( off ) ) ;
            EXP b = DEREF_exp ( off_ptr_diff_ptr2 ( off ) ) ;
            ua = flow_exp ( a, ua ) ;
            ua = flow_exp ( b, ua ) ;
            break ;
        }

        case off_token_tag : {
            /* Tokenised offsets */
            LIST ( TOKEN ) args = DEREF_list ( off_token_args ( off ) ) ;
            ua = flow_token_list ( args, ua ) ;
            break ;
        }
    }
    ua &= ~va_mask ;
    return ( ua ) ;
}


/*
    PERFORM FLOW ANALYSIS ON A LIST OF EXPRESSIONS

    This routine performs flow analysis on the list of expressions p.
*/

static VAR_INFO flow_exp_list
    PROTO_N ( ( p, use, fn ) )
    PROTO_T ( LIST ( EXP ) p X VAR_INFO use X int fn )
{
    while ( !IS_NULL_list ( p ) ) {
        EXP a = DEREF_exp ( HEAD_list ( p ) ) ;
        if ( !IS_NULL_exp ( a ) ) {
            VAR_INFO ua ;
            if ( fn ) {
                ua = flow_alias_exp ( a, use ) ;
            } else {
                ua = flow_exp ( a, use ) ;
            }
            if ( ua & va_unreached ) use |= va_unreached ;
        }
        p = TAIL_list ( p ) ;
    }
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON AN ALIASED EXPRESSION

    This routine performs flow analysis on the expression e which may be
    aliased by being on the right hand side of an assignment.  Note that
    there are read-only and read-write aliases, depending on whether the
    access is through a pointer or reference to const or not.
*/

static VAR_INFO flow_alias_exp
    PROTO_N ( ( e, use ) )
    PROTO_T ( EXP e X VAR_INFO use )
{
    VAR_INFO ua = ( use | va_alias_used ) ;
    if ( !IS_NULL_exp ( e ) ) {
        TYPE t = DEREF_type ( exp_type ( e ) ) ;
        CV_SPEC cv = find_cv_qual ( t ) ;
        if ( cv & cv_lvalue ) {
            /* Aliased via lvalue */
            if ( !( cv & cv_const ) ) ua |= va_alias_set ;
        }
        if ( IS_type_ptr_etc ( t ) ) {
            /* Aliased via pointer or reference */
            TYPE s = DEREF_type ( type_ptr_etc_sub ( t ) ) ;
            cv = find_cv_qual ( s ) ;
            if ( !( cv & cv_const ) ) ua |= va_alias_set ;
        }
    }
    ua = flow_exp ( e, ua ) ;
    return ( ua ) ;
}


/*
    PERFORM FLOW ANALYSIS ON AN EXPRESSION

    This routine performs flow analysis on the expression e.
*/

static VAR_INFO flow_exp
    PROTO_N ( ( e, use ) )
    PROTO_T ( EXP e X VAR_INFO use )
{
    VAR_INFO ua = ( use & ~va_mask ) ;
    if ( IS_NULL_exp ( e ) ) return ( ua ) ;
    ASSERT ( ORDER_exp == 88 ) ;
    switch ( TAG_exp ( e ) ) {

        case exp_identifier_tag : {
            /* Identifier expressions */
            IDENTIFIER id = DEREF_id ( exp_identifier_id ( e ) ) ;
            if ( IS_id_variable_etc ( id ) ) {
                DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
                if ( ds & dspec_auto ) {
                    set_variable ( id, use ) ;
                    if ( ( ds & va_this ) && ( use & va_alias ) ) {
                        /* Allow for aliasing using 'this' */
                        set_members ( use ) ;
                    }
                }
            }
            break ;
        }

        case exp_assign_tag : {
            /* Assignment expressions */
            EXP a = DEREF_exp ( exp_assign_ref ( e ) ) ;
            EXP b = DEREF_exp ( exp_assign_arg ( e ) ) ;
            ua = flow_alias_exp ( b, ua ) ;
            if ( ua & va_unreached ) use |= va_unreached ;
            ua = flow_exp ( a, ( use | va_assign ) ) ;
            break ;
        }

        case exp_init_tag : {
            /* Initialisation expressions */
            EXP a = DEREF_exp ( exp_init_arg ( e ) ) ;
            ua = flow_alias_exp ( a, ua ) ;
            if ( ua & va_unreached ) use |= va_unreached ;
            if ( !IS_NULL_exp ( a ) ) {
                IDENTIFIER id = DEREF_id ( exp_init_id ( e ) ) ;
                if ( IS_id_variable ( id ) ) {
                    DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
                    if ( ds & dspec_auto ) {
                        /* Initialiser expression */
                        set_variable ( id, ( use | va_assign ) ) ;
                        break ;
                    }
                }
            }
            break ;
        }

        case exp_preinc_tag : {
            /* Pre-increment expressions */
            EXP a = DEREF_exp ( exp_preinc_ref ( e ) ) ;
            EXP b = DEREF_exp ( exp_preinc_op ( e ) ) ;
            EXP c = DEREF_exp ( exp_dummy_value ( a ) ) ;
            int op = DEREF_int ( exp_preinc_becomes ( e ) ) ;
            if ( op == lex_assign ) {
                /* Bitfield assignment */
                ua = flow_alias_exp ( b, ua ) ;
                if ( ua & va_unreached ) use |= va_unreached ;
                if ( !IS_NULL_list ( crt_flow_mems ) && is_this_exp ( c ) ) {
                    /* Have 'this->off' */
                    OFFSET off = DEREF_off ( exp_dummy_off ( a ) ) ;
                    ua = flow_offset ( off, ( use | va_assign ), 1 ) ;
                    break ;
                }
                use |= va_partial ;
            } else {
                COPY_exp ( exp_dummy_value ( a ), NULL_exp ) ;
                ua = flow_exp ( b, ua ) ;
                COPY_exp ( exp_dummy_value ( a ), c ) ;
                if ( ua & va_unreached ) use |= va_unreached ;
                use |= va_becomes ;
            }
            ua = flow_exp ( c, ( use | va_assign ) ) ;
            break ;
        }

        case exp_postinc_tag : {
            /* Post-increment expressions */
            EXP a = DEREF_exp ( exp_postinc_ref ( e ) ) ;
            EXP b = DEREF_exp ( exp_postinc_op ( e ) ) ;
            EXP c = DEREF_exp ( exp_dummy_value ( a ) ) ;
            COPY_exp ( exp_dummy_value ( a ), NULL_exp ) ;
            ua = flow_exp ( b, ua ) ;
            COPY_exp ( exp_dummy_value ( a ), c ) ;
            if ( ua & va_unreached ) use |= va_unreached ;
            ua = flow_exp ( a, ( use | va_becomes ) ) ;
            break ;
        }

        case exp_indir_tag : {
            /* Indirection expressions */
            EXP a = DEREF_exp ( exp_indir_ptr ( e ) ) ;
            if ( !IS_NULL_list ( crt_flow_mems ) && is_this_exp ( e ) ) {
                /* Have '*this' */
                set_members ( use ) ;
                break ;
            }
            if ( IS_exp_add_ptr ( a ) ) {
                EXP b = DEREF_exp ( exp_add_ptr_ptr ( a ) ) ;
                OFFSET off = DEREF_off ( exp_add_ptr_off ( a ) ) ;
                if ( !IS_NULL_list ( crt_flow_mems ) && is_this_exp ( b ) ) {
                    /* Have 'this->off' */
                    ua = flow_offset ( off, use, 1 ) ;
                    break ;
                }
                if ( IS_exp_address ( b ) ) {
                    /* Have 'c.off' */
                    EXP c = DEREF_exp ( exp_address_arg ( b ) ) ;
                    ua = flow_offset ( off, use, 0 ) ;
                    if ( ua & va_unreached ) use |= va_unreached ;
                    ua = flow_exp ( c, ( use | va_partial ) ) ;
                    break ;
                }
            } else if ( IS_exp_address ( a ) ) {
                /* Have '*&b' */
                EXP b = DEREF_exp ( exp_address_arg ( a ) ) ;
                ua = flow_exp ( b, use ) ;
                break ;
            }
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_contents_tag : {
            /* Contents expressions */
            EXP a = DEREF_exp ( exp_contents_ptr ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_address_tag : {
            /* Address expressions */
            EXP a = DEREF_exp ( exp_address_arg ( e ) ) ;
            if ( IS_exp_indir ( a ) && !is_this_exp ( a ) ) {
                EXP b = DEREF_exp ( exp_indir_ptr ( a ) ) ;
                TYPE t = DEREF_type ( exp_type ( b ) ) ;
                if ( !IS_type_ref ( t ) ) {
                    ua = flow_exp ( b, use ) ;
                    break ;
                }
            }
            ua = ( use & ~va_other ) ;
            ua = flow_exp ( a, ( ua | va_address ) ) ;
            break ;
        }

        case exp_address_mem_tag : {
            /* Member address expressions */
            EXP a = DEREF_exp ( exp_address_mem_arg ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_func_tag : {
            /* Function expressions */
            TYPE t = DEREF_type ( exp_type ( e ) ) ;
            EXP a = DEREF_exp ( exp_func_fn ( e ) ) ;
            LIST ( EXP ) args = DEREF_list ( exp_func_args ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            ua = flow_exp_list ( args, ua, 1 ) ;
            if ( IS_type_bottom ( t ) ) {
                /* Deal with functions like exit */
                ua = flow_terminate ( ua, 1 ) ;
            }
            break ;
        }

        case exp_func_id_tag : {
            /* Function expressions */
            TYPE t = DEREF_type ( exp_type ( e ) ) ;
            LIST ( EXP ) args = DEREF_list ( exp_func_id_args ( e ) ) ;
            ua = flow_exp_list ( args, ua, 1 ) ;
            if ( IS_type_bottom ( t ) ) {
                /* Deal with functions like exit */
                ua = flow_terminate ( ua, 1 ) ;
            }
            break ;
        }

        case exp_call_tag : {
            /* Pointer to member function expressions */
            EXP a = DEREF_exp ( exp_call_ptr ( e ) ) ;
            EXP b = DEREF_exp ( exp_call_arg ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            ua = flow_exp ( b, ua ) ;
            break ;
        }

        case exp_negate_tag :
        case exp_compl_tag :
        case exp_not_tag :
        case exp_abs_tag : {
            /* Unary expressions */
            EXP a = DEREF_exp ( exp_negate_etc_arg ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_plus_tag :
        case exp_minus_tag :
        case exp_mult_tag :
        case exp_div_tag :
        case exp_rem_tag :
        case exp_and_tag :
        case exp_or_tag :
        case exp_xor_tag :
        case exp_lshift_tag :
        case exp_rshift_tag :
        case exp_max_tag :
        case exp_min_tag : {
            /* Binary expressions */
            EXP a = DEREF_exp ( exp_plus_etc_arg1 ( e ) ) ;
            EXP b = DEREF_exp ( exp_plus_etc_arg2 ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            ua = flow_exp ( b, ua ) ;
            break ;
        }

        case exp_log_and_tag :
        case exp_log_or_tag : {
            /* Logical expressions */
            EXP a = DEREF_exp ( exp_plus_etc_arg1 ( e ) ) ;
            EXP b = DEREF_exp ( exp_plus_etc_arg2 ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            flow_sequence_point () ;
            ua = flow_exp ( b, ua ) ;
            break ;
        }

        case exp_test_tag : {
            /* Test expressions */
            EXP a = DEREF_exp ( exp_test_arg ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_compare_tag : {
            /* Comparison expressions */
            EXP a = DEREF_exp ( exp_compare_arg1 ( e ) ) ;
            EXP b = DEREF_exp ( exp_compare_arg2 ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            ua = flow_exp ( b, ua ) ;
            break ;
        }

        case exp_cast_tag : {
            /* Cast expressions */
            TYPE t = DEREF_type ( exp_type ( e ) ) ;
            EXP a = DEREF_exp ( exp_cast_arg ( e ) ) ;
            ua = ( use & ~va_other ) ;
            ua = flow_exp ( a, ua ) ;
            if ( IS_type_bottom ( t ) ) {
                /* Allow for casting to bottom */
                ua = flow_terminate ( ua, 1 ) ;
            }
            break ;
        }

        case exp_base_cast_tag : {
            /* Base cast expressions */
            EXP a = DEREF_exp ( exp_base_cast_arg ( e ) ) ;
            ua = ( use & ~va_other ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_dyn_cast_tag : {
            /* Dynamic cast expressions */
            EXP a = DEREF_exp ( exp_dyn_cast_arg ( e ) ) ;
            ua = ( use & ~va_other ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_add_ptr_tag : {
            /* Pointer addition expressions */
            EXP a = DEREF_exp ( exp_add_ptr_ptr ( e ) ) ;
            OFFSET off = DEREF_off ( exp_add_ptr_off ( e ) ) ;
            if ( !IS_NULL_list ( crt_flow_mems ) && is_this_exp ( a ) ) {
                /* Have 'this->off' */
                use &= ~va_other ;
                ua = flow_offset ( off, use, 1 ) ;
                break ;
            }
            ua = flow_offset ( off, ua, 0 ) ;
            if ( ua & va_unreached ) use |= va_unreached ;
            use &= ~va_other ;
            ua = flow_exp ( a, use ) ;
            break ;
        }

        case exp_offset_size_tag : {
            /* Offset expressions */
            OFFSET off = DEREF_off ( exp_offset_size_off ( e ) ) ;
            ua = flow_offset ( off, ua, 0 ) ;
            break ;
        }

        case exp_constr_tag : {
            /* Constructor calls */
            EXP a = DEREF_exp ( exp_constr_call ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_destr_tag : {
            /* Destructor calls */
            EXP a = DEREF_exp ( exp_destr_call ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_alloc_tag : {
            /* Allocation expressions */
            EXP a = DEREF_exp ( exp_alloc_call ( e ) ) ;
            EXP b = DEREF_exp ( exp_alloc_init ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            ua = flow_exp ( b, ua ) ;
            break ;
        }

        case exp_dealloc_tag : {
            /* Deallocation expressions */
            EXP a = DEREF_exp ( exp_dealloc_term ( e ) ) ;
            EXP b = DEREF_exp ( exp_dealloc_call ( e ) ) ;
            EXP c = DEREF_exp ( exp_dealloc_arg ( e ) ) ;
            EXP d = DEREF_exp ( exp_dummy_value ( c ) ) ;
            ua = flow_exp ( a, ua ) ;
            ua = flow_exp ( b, ua ) ;
            ua = flow_set ( d, ua, va_none ) ;
            break ;
        }

        case exp_rtti_tag : {
            /* Run-time type information expressions */
            EXP a = DEREF_exp ( exp_rtti_arg ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_dynamic_tag : {
            /* Dynamic initialiser expressions */
            EXP a = DEREF_exp ( exp_dynamic_arg ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            break ;
        }

        case exp_aggregate_tag : {
            /* Aggregate initialiser expressions */
            LIST ( EXP ) args ;
            args = DEREF_list ( exp_aggregate_args ( e ) ) ;
            ua = flow_exp_list ( args, ua, 0 ) ;
            break ;
        }

        case exp_initialiser_tag : {
            /* Constructor initialiser expressions */
            int kind = DEREF_int ( exp_initialiser_kind ( e ) ) ;
            LIST ( EXP ) p = DEREF_list ( exp_initialiser_args ( e ) ) ;
            LIST ( OFFSET ) q = DEREF_list ( exp_initialiser_offs ( e ) ) ;
            if ( kind == 0 ) q = NULL_list ( OFFSET ) ;
            while ( !IS_NULL_list ( p ) ) {
                EXP a = DEREF_exp ( HEAD_list ( p ) ) ;
                ua = flow_exp ( a, ua ) ;
                if ( !IS_NULL_list ( q ) ) {
                    OFFSET off = DEREF_off ( HEAD_list ( q ) ) ;
                    if ( kind != DEFAULT_DESTR && !IS_NULL_exp ( a ) ) {
                        ua |= va_assign ;
                    }
                    ua = flow_offset ( off, ua, 1 ) ;
                    flow_sequence_point () ;
                    q = TAIL_list ( q ) ;
                }
                p = TAIL_list ( p ) ;
            }
            break ;
        }

        case exp_nof_tag : {
            /* Array initialiser expressions */
            EXP a = DEREF_exp ( exp_nof_start ( e ) ) ;
            EXP b = DEREF_exp ( exp_nof_pad ( e ) ) ;
            EXP c = DEREF_exp ( exp_nof_end ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            ua = flow_exp ( b, ua ) ;
            ua = flow_exp ( c, ua ) ;
            break ;
        }

        case exp_comma_tag : {
            /* Comma expressions */
            LIST ( EXP ) p = DEREF_list ( exp_comma_args ( e ) ) ;
            if ( !IS_NULL_list ( p ) ) {
                EXP a ;
                for ( ; ; ) {
                    a = DEREF_exp ( HEAD_list ( p ) ) ;
                    p = TAIL_list ( p ) ;
                    if ( IS_NULL_list ( p ) ) break ;
                    ua = flow_exp ( a, ua ) ;
                    flow_sequence_point () ;
                    if ( ua & va_unreached ) use |= va_unreached ;
                }
                ua = flow_exp ( a, use ) ;
            }
            break ;
        }

        case exp_if_stmt_tag : {
            /* Conditional expressions */
            EXP c = DEREF_exp ( exp_if_stmt_cond ( e ) ) ;
            EXP a = DEREF_exp ( exp_if_stmt_true_code ( e ) ) ;
            EXP b = DEREF_exp ( exp_if_stmt_false_code ( e ) ) ;
            ua = flow_if_exp ( c, a, b, ua, 1 ) ;
            break ;
        }

        case exp_exception_tag : {
            /* Exception expressions */
            EXP a = DEREF_exp ( exp_exception_arg ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            ua = flow_terminate ( ua, 1 ) ;
            break ;
        }

        case exp_set_tag : {
            /* Set expressions */
            EXP a = DEREF_exp ( exp_set_arg ( e ) ) ;
            ua = flow_set ( a, ua, va_set ) ;
            break ;
        }

        case exp_unused_tag : {
            /* Unused expressions */
            EXP a = DEREF_exp ( exp_unused_arg ( e ) ) ;
            ua = flow_set ( a, ua, va_none ) ;
            break ;
        }

        case exp_op_tag : {
            /* Undetermined expressions */
            EXP a = DEREF_exp ( exp_op_arg1 ( e ) ) ;
            EXP b = DEREF_exp ( exp_op_arg2 ( e ) ) ;
            ua = flow_exp ( a, ua ) ;
            ua = flow_exp ( b, ua ) ;
            break ;
        }

        case exp_opn_tag : {
            /* Undetermined expressions */
            LIST ( EXP ) args = DEREF_list ( exp_opn_args ( e ) ) ;
            ua = flow_exp_list ( args, ua, 0 ) ;
            break ;
        }

        case exp_token_tag : {
            /* Tokenised expressions */
            LIST ( TOKEN ) args = DEREF_list ( exp_token_args ( e ) ) ;
            ua = flow_token_list ( args, ua ) ;
            break ;
        }

        case exp_dummy_tag : {
            /* Dummy expressions */
            EXP a = DEREF_exp ( exp_dummy_value ( e ) ) ;
            ua = flow_exp ( a, use ) ;
            break ;
        }

        case exp_paren_tag :
        case exp_copy_tag : {
            /* Parenthesised expressions */
            EXP a = DEREF_exp ( exp_paren_etc_arg ( e ) ) ;
            ua = flow_exp ( a, use ) ;
            break ;
        }

        case exp_location_tag : {
            /* Location expressions */
            EXP a = DEREF_exp ( exp_location_arg ( e ) ) ;
            ua = flow_exp ( a, use ) ;
            DEREF_loc ( exp_location_end ( e ), stmt_loc ) ;
            break ;
        }

        case exp_sequence_tag :
        case exp_solve_stmt_tag :
        case exp_decl_stmt_tag :
        case exp_while_stmt_tag :
        case exp_do_stmt_tag :
        case exp_switch_stmt_tag :
        case exp_hash_if_tag :
        case exp_return_stmt_tag :
        case exp_goto_stmt_tag :
        case exp_label_stmt_tag :
        case exp_try_block_tag :
        case exp_handler_tag :
        case exp_reach_tag :
        case exp_unreach_tag : {
            /* Statements */
            ua = flow_stmt ( e, ua, 1 ) ;
            break ;
        }
    }
    ua &= ~va_mask ;
    return ( ua ) ;
}


/*
    PERFORM FLOW ANALYSIS FOR DESTRUCTORS

    This routine performs flow analysis for any destructors called by
    the goto or return statement a.  b gives smallest statement enclosing
    both the jump and its destination.
*/

static void flow_jump
    PROTO_N ( ( a, b ) )
    PROTO_T ( EXP a X EXP b )
{
    unsigned long d = no_destructors ;
    while ( !IS_NULL_exp ( a ) && !EQ_exp ( a, b ) && d ) {
        /* Scan up to enclosing statement */
        if ( IS_exp_decl_stmt ( a ) ) {
            IDENTIFIER id = DEREF_id ( exp_decl_stmt_id ( a ) ) ;
            if ( IS_id_variable ( id ) ) {
                DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
                if ( ds & dspec_auto ) {
                    /* Local variable */
                    EXP term = DEREF_exp ( id_variable_term ( id ) ) ;
                    if ( !IS_NULL_exp ( term ) ) {
                        if ( do_usage ) dump_destr ( id, &stmt_loc ) ;
                        d-- ;
                    }
                }
            }
        }
        a = get_parent_stmt ( a ) ;
    }
    return ;
}


/*
    PERFORM FLOW ANALYSIS ON A WHILE STATEMENT

    This routine performs flow analysis on the while statement e.
*/

static VAR_INFO flow_while_stmt
    PROTO_N ( ( e, use, flow ) )
    PROTO_T ( EXP e X VAR_INFO use X int flow )
{
    VAR_INFO ua ;
    EXP c = DEREF_exp ( exp_while_stmt_cond ( e ) ) ;
    EXP a = DEREF_exp ( exp_while_stmt_body ( e ) ) ;
    IDENTIFIER blab = DEREF_id ( exp_while_stmt_break_lab ( e ) ) ;
    IDENTIFIER clab = DEREF_id ( exp_while_stmt_cont_lab ( e ) ) ;
    unsigned cv = eval_const_cond ( c ) ;
    if ( flow ) {
        use = flow_exp ( c, use ) ;
        flow_sequence_point () ;
    }
    ua = use ;
    if ( cv != BOOL_TRUE ) {
        IGNORE flow_goto_stmt ( blab, use, 1 ) ;
        if ( cv == BOOL_FALSE ) ua |= va_unreached ;
    }
    ua = flow_stmt ( a, ua, flow ) ;
    ua = flow_label_stmt ( clab, ua, flow ) ;
    if ( flow ) {
        IGNORE flow_exp ( c, ua ) ;
        flow_sequence_point () ;
    }
    mark_vars ( crt_flow_vars, va_used_this ) ;
    use = flow_label_stmt ( blab, ( use | va_unreached ), flow ) ;
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON A DO STATEMENT

    This routine performs flow analysis on the do statement e.
*/

static VAR_INFO flow_do_stmt
    PROTO_N ( ( e, use, flow ) )
    PROTO_T ( EXP e X VAR_INFO use X int flow )
{
    EXP c = DEREF_exp ( exp_do_stmt_cond ( e ) ) ;
    EXP a = DEREF_exp ( exp_do_stmt_body ( e ) ) ;
    IDENTIFIER blab = DEREF_id ( exp_do_stmt_break_lab ( e ) ) ;
    IDENTIFIER clab = DEREF_id ( exp_do_stmt_cont_lab ( e ) ) ;
    unsigned cv = eval_const_cond ( c ) ;
    use = flow_stmt ( a, use, flow ) ;
    use = flow_label_stmt ( clab, use, flow ) ;
    if ( flow ) {
        use = flow_exp ( c, use ) ;
        flow_sequence_point () ;
    }
    mark_vars ( crt_flow_vars, va_used_this ) ;
    if ( cv == BOOL_TRUE ) use |= va_unreached ;
    use = flow_label_stmt ( blab, use, flow ) ;
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON A SOLVE STATEMENT

    This routine performs flow analysis on the solve statement e with
    associated variables p.
*/

static VAR_INFO flow_solve_stmt
    PROTO_N ( ( e, p, use, flow ) )
    PROTO_T ( EXP e X LIST ( IDENTIFIER ) p X VAR_INFO use X int flow )
{
    if ( !IS_NULL_list ( p ) ) {
        /* Scan through variables */
        IDENTIFIER id = DEREF_id ( HEAD_list ( p ) ) ;
        DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
        if ( ( ds & dspec_auto ) && !( ds & dspec_done ) ) {
            start_variable ( id, ds, va_none ) ;
            use = flow_solve_stmt ( e, TAIL_list ( p ), use, flow ) ;
            end_variable ( flow, 0 ) ;
            return ( use ) ;
        }
        use = flow_solve_stmt ( e, TAIL_list ( p ), use, flow ) ;
        return ( use ) ;
    }
    use = flow_stmt ( e, use, flow ) ;
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON A SWITCH STATEMENT

    This routine performs flow analysis on the switch statement e.
*/

static VAR_INFO flow_switch_stmt
    PROTO_N ( ( e, use, flow ) )
    PROTO_T ( EXP e X VAR_INFO use X int flow )
{
    EXP a = DEREF_exp ( exp_switch_stmt_body ( e ) ) ;
    int exhaust = DEREF_int ( exp_switch_stmt_exhaust ( e ) ) ;
    LIST ( IDENTIFIER ) p = DEREF_list ( exp_switch_stmt_case_labs ( e ) ) ;
    IDENTIFIER dlab = DEREF_id ( exp_switch_stmt_default_lab ( e ) ) ;
    IDENTIFIER blab = DEREF_id ( exp_switch_stmt_break_lab ( e ) ) ;
    if ( flow ) {
        /* Deal with control statement */
        EXP c = DEREF_exp ( exp_switch_stmt_control ( e ) ) ;
        use = flow_exp ( c, use ) ;
        flow_sequence_point () ;
    }
    if ( !IS_NULL_id ( dlab ) ) {
        /* Mark jumps to default statement */
        dlab = DEREF_id ( id_alias ( dlab ) ) ;
        IGNORE flow_goto_stmt ( dlab, use, 1 ) ;
        exhaust = 1 ;
    }
    while ( !IS_NULL_list ( p ) ) {
        /* Mark jumps to case statements */
        IDENTIFIER clab = DEREF_id ( HEAD_list ( p ) ) ;
        clab = DEREF_id ( id_alias ( clab ) ) ;
        if ( !EQ_id ( clab, dlab ) ) {
            /* Only mark each label once */
            IGNORE flow_goto_stmt ( clab, use, 1 ) ;
            dlab = clab ;
        }
        p = TAIL_list ( p ) ;
    }
    if ( !exhaust ) {
        /* Break label is reached in non-exhaustive cases */
        IGNORE flow_goto_stmt ( blab, use, 1 ) ;
    }
    use = flow_stmt ( a, ( use | va_unreached ), flow ) ;
    use = flow_label_stmt ( blab, use, flow ) ;
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON A TRY BLOCK

    This routine performs flow analysis on the try block e.
*/

static VAR_INFO flow_try_block
    PROTO_N ( ( e, use, flow ) )
    PROTO_T ( EXP e X VAR_INFO use X int flow )
{
    VAR_INFO ua = ( use | va_unreached ) ;
    EXP a = DEREF_exp ( exp_try_block_body ( e ) ) ;
    LIST ( EXP ) p = DEREF_list ( exp_try_block_handlers ( e ) ) ;
    EXP c = DEREF_exp ( exp_try_block_ellipsis ( e ) ) ;
    use = flow_stmt ( a, use, flow ) ;
    /* NOT YET IMPLEMENTED */
    while ( !IS_NULL_list ( p ) ) {
        EXP b = DEREF_exp ( HEAD_list ( p ) ) ;
        IGNORE flow_stmt ( b, ua, flow ) ;
        p = TAIL_list ( p ) ;
    }
    IGNORE flow_stmt ( c, ua, flow ) ;
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON A DECLARATION STATEMENT

    This routine performs flow analysis on the declaration statement e.
*/

static VAR_INFO flow_decl_stmt
    PROTO_N ( ( e, use, flow ) )
    PROTO_T ( EXP e X VAR_INFO use X int flow )
{
    EXP a = DEREF_exp ( exp_decl_stmt_body ( e ) ) ;
    IDENTIFIER id = DEREF_id ( exp_decl_stmt_id ( e ) ) ;
    if ( IS_id_variable ( id ) ) {
        DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
        if ( ds & dspec_auto ) {
            /* Local variable */
            EXP b = DEREF_exp ( id_variable_init ( id ) ) ;
            EXP d = DEREF_exp ( id_variable_term ( id ) ) ;
            if ( !IS_NULL_exp ( d ) ) no_destructors++ ;
            if ( !( ds & dspec_done ) ) {
                start_variable ( id, ds, va_none ) ;
            }
            if ( !IS_NULL_exp ( b ) ) {
                /* Initialiser expression */
                if ( flow ) use = flow_alias_exp ( b, use ) ;
                set_variable ( id, ( use | va_assign ) ) ;
                flow_sequence_point () ;
            }
            use = flow_stmt ( a, use, flow ) ;
            if ( flow ) {
                /* Destructor */
                use = flow_exp ( d, use ) ;
                flow_sequence_point () ;
            }
            if ( !( ds & dspec_done ) ) end_variable ( flow, 0 ) ;
            if ( !IS_NULL_exp ( d ) ) no_destructors-- ;
            return ( use ) ;
        }
    }
    use = flow_stmt ( a, use, flow ) ;
    return ( use ) ;
}


/*
    PERFORM FLOW ANALYSIS ON A STATEMENT

    This routine performs flow analysis on the statement e.
*/

static VAR_INFO flow_stmt
    PROTO_N ( ( e, use, flow ) )
    PROTO_T ( EXP e X VAR_INFO use X int flow )
{
    /* Deal with statements */
    VAR_INFO ua = ( use & ~va_mask ) ;
    if ( IS_NULL_exp ( e ) ) return ( ua ) ;
    ASSERT ( ORDER_exp == 88 ) ;
    switch ( TAG_exp ( e ) ) {

        case exp_sequence_tag : {
            /* Compound statements */
            LIST ( EXP ) p = DEREF_list ( exp_sequence_first ( e ) ) ;
            while ( !IS_NULL_list ( p ) ) {
                EXP a = DEREF_exp ( HEAD_list ( p ) ) ;
                if ( !IS_NULL_exp ( a ) ) {
                    VAR_INFO ub = flow_stmt ( a, ua, flow ) ;
                    if ( ub & va_unreached ) {
                        ua |= va_unreached ;
                    } else {
                        ua &= ~va_unreached ;
                    }
                }
                p = TAIL_list ( p ) ;
            }
            break ;
        }

        case exp_solve_stmt_tag : {
            /* Solve statements */
            LIST ( IDENTIFIER ) p ;
            EXP a = DEREF_exp ( exp_solve_stmt_body ( e ) ) ;
            p = DEREF_list ( exp_solve_stmt_vars ( e ) ) ;
            ua = flow_solve_stmt ( a, p, ua, flow ) ;
            break ;
        }

        case exp_decl_stmt_tag : {
            /* Declaration statements */
            ua = flow_decl_stmt ( e, ua, flow ) ;
            break ;
        }

        case exp_if_stmt_tag : {
            /* Conditional statements */
            EXP c = DEREF_exp ( exp_if_stmt_cond ( e ) ) ;
            EXP a = DEREF_exp ( exp_if_stmt_true_code ( e ) ) ;
            EXP b = DEREF_exp ( exp_if_stmt_false_code ( e ) ) ;
            ua = flow_if_exp ( c, a, b, ua, flow ) ;
            break ;
        }

        case exp_while_stmt_tag : {
            /* While statements */
            ua = flow_while_stmt ( e, ua, flow ) ;
            break ;
        }

        case exp_do_stmt_tag : {
            /* Do statements */
            ua = flow_do_stmt ( e, ua, flow ) ;
            break ;
        }

        case exp_switch_stmt_tag : {
            /* Switch statements */
            ua = flow_switch_stmt ( e, ua, flow ) ;
            break ;
        }

        case exp_hash_if_tag : {
            /* Target dependent conditional statements */
            EXP c = DEREF_exp ( exp_hash_if_cond ( e ) ) ;
            EXP a = DEREF_exp ( exp_hash_if_true_code ( e ) ) ;
            EXP b = DEREF_exp ( exp_hash_if_false_code ( e ) ) ;
            ua = flow_if_exp ( c, a, b, ua, flow ) ;
            break ;
        }

        case exp_return_stmt_tag : {
            /* Return statements */
            EXP a = DEREF_exp ( exp_return_stmt_value ( e ) ) ;
            if ( flow ) {
                ua = flow_exp ( a, ua ) ;
                flow_sequence_point () ;
            }
            ua = flow_terminate ( ua, 2 ) ;
            flow_jump ( e, NULL_exp ) ;
            break ;
        }

        case exp_goto_stmt_tag : {
            /* Goto statements */
            EXP join = DEREF_exp ( exp_goto_stmt_join ( e ) ) ;
            IDENTIFIER lab = DEREF_id ( exp_goto_stmt_label ( e ) ) ;
            lab = DEREF_id ( id_alias ( lab ) ) ;
            ua = flow_goto_stmt ( lab, ua, 0 ) ;
            flow_jump ( e, join ) ;
            break ;
        }

        case exp_label_stmt_tag : {
            /* Labelled statements */
            IDENTIFIER lab = DEREF_id ( exp_label_stmt_label ( e ) ) ;
            ua = flow_label_stmt ( lab, ua, flow ) ;
            break ;
        }

        case exp_try_block_tag : {
            /* Try blocks */
            ua = flow_try_block ( e, ua, flow ) ;
            break ;
        }

        case exp_handler_tag : {
            /* Exception handlers */
            EXP a = DEREF_exp ( exp_handler_body ( e ) ) ;
            ua = flow_stmt ( a, ua, flow ) ;
            break ;
        }

        case exp_reach_tag : {
            /* Reached statements */
            EXP a = DEREF_exp ( exp_reach_body ( e ) ) ;
            ua &= ~va_unreached ;
            ua = flow_stmt ( a, ua, flow ) ;
            break ;
        }

        case exp_unreach_tag : {
            /* Unreached statements */
            EXP a = DEREF_exp ( exp_unreach_body ( e ) ) ;
            ua |= va_unreached ;
            ua = flow_stmt ( a, ua, flow ) ;
            break ;
        }

        case exp_location_tag : {
            /* Location expressions */
            EXP a = DEREF_exp ( exp_location_arg ( e ) ) ;
            ua = flow_stmt ( a, use, flow ) ;
            DEREF_loc ( exp_location_end ( e ), stmt_loc ) ;
            break ;
        }

        default : {
            /* Simple expressions */
            if ( !( ua & va_unreached ) && flow ) {
                ua = flow_exp ( e, ua ) ;
                flow_sequence_point () ;
            }
            break ;
        }
    }
    ua &= ~va_mask ;
    return ( ua ) ;
}


/*
    PERFORM FLOW ANALYSIS ON A FUNCTION DEFINITION

    This routine performs the flow analysis on the compound statement e
    which defines the function id.  Full flow analysis is enabled if
    flow is true, otherwise a minimal scan sufficient to determine all
    implicit destructor calls is done.
*/

void check_flow
    PROTO_N ( ( id, e, flow ) )
    PROTO_T ( IDENTIFIER id X EXP e X int flow )
{
    VAR_INFO use ;
    LIST ( IDENTIFIER ) pids ;
    if ( IS_id_function_etc ( id ) ) {
        TYPE t = DEREF_type ( id_function_etc_type ( id ) ) ;
        while ( IS_type_templ ( t ) ) {
            t = DEREF_type ( type_templ_defn ( t ) ) ;
        }
        if ( IS_id_mem_func ( id ) ) {
            /* Allow for 'this' parameter and class members */
            IDENTIFIER pid = this_param ( id, 0 ) ;
            if ( flow ) {
                HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
                CLASS_TYPE ct = parent_class ( id ) ;
                NAMESPACE ns = DEREF_nspace ( ctype_member ( ct ) ) ;
                MEMBER mem = DEREF_member ( nspace_ctype_first ( ns ) ) ;
                mem = next_data_member ( mem, 0 ) ;
                while ( !IS_NULL_member ( mem ) ) {
                    IDENTIFIER mid = DEREF_id ( member_id ( mem ) ) ;
                    start_variable ( mid, dspec_used, va_member ) ;
                    if ( !IS_hashid_constr ( nm ) ) {
                        /* Members are set except in constructors */
                        set_variable ( mid, va_assign ) ;
                        set_variable ( mid, va_none ) ;
                    }
                    mem = DEREF_member ( member_next ( mem ) ) ;
                    mem = next_data_member ( mem, 0 ) ;
                }
                crt_flow_mems = crt_flow_vars ;
            }
            if ( !IS_NULL_id ( pid ) ) {
                start_variable ( pid, dspec_none, va_this ) ;
                set_variable ( pid, va_assign ) ;
            }
        }
        pids = DEREF_list ( type_func_pids ( t ) ) ;
        while ( !IS_NULL_list ( pids ) ) {
            /* Set up parameter scopes */
            IDENTIFIER pid = DEREF_id ( HEAD_list ( pids ) ) ;
            if ( !IS_NULL_id ( pid ) ) {
                DECL_SPEC ds = DEREF_dspec ( id_storage ( pid ) ) ;
                start_variable ( pid, ds, va_none ) ;
                set_variable ( pid, va_assign ) ;
            }
            pids = TAIL_list ( pids ) ;
        }
    }
    DEREF_loc ( id_loc ( id ), stmt_loc ) ;
    flow_sequence_point () ;
    use = flow_stmt ( e, va_none, flow ) ;
    IGNORE flow_terminate ( use, 2 ) ;
    crt_flow_mems = NULL_list ( VARIABLE ) ;
    while ( !IS_NULL_list ( crt_flow_vars ) ) {
        /* End parameter and member scopes */
        end_variable ( flow, 2 ) ;
    }
    stmt_loc = crt_loc ;
    return ;
}