/* * Copyright (c) 2002, The Tendra Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * Crown Copyright (c) 1997 * * This TenDRA(r) Computer Program is subject to Copyright * owned by the United Kingdom Secretary of State for Defence * acting through the Defence Evaluation and Research Agency * (DERA). It is made available to Recipients with a * royalty-free licence for its use, reproduction, transfer * to other parties and amendment for any purpose not excluding * product development provided that any such use et cetera * shall be deemed to be acceptance of the following conditions:- * * (1) Its Recipients shall ensure that this Notice is * reproduced upon any copies or amended versions of it; * * (2) Any amended version of it shall be clearly marked to * show both the nature of and the organisation responsible * for the relevant amendment or amendments; * * (3) Its onward transfer from a recipient to another * party shall be deemed to be that party's acceptance of * these conditions; * * (4) DERA gives no warranty or assurance as to its * quality or suitability for any purpose and DERA accepts * no liability whatsoever in relation to any use to which * it may be put. * * $TenDRA: tendra/src/producers/common/construct/variable.c,v 1.12 2005/11/07 18:42:37 stefanf Exp $ */ #include "config.h" #include "producer.h" #include "msgcat.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(IDENTIFIER id, EXP blk, 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 (nm == KEYWORD (lex_func_Hid)) { /* Ignore unused __func__ variable */ /* 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(TYPE t, IDENTIFIER id, NAMESPACE ns, EXP blk, int anon, 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(CLASS_TYPE ct, 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(IDENTIFIER id) { int def = 0; EXP e = DEREF_exp (id_variable_etc_init (id)); if (!IS_NULL_exp (e) && IS_exp_zero (e)) { LOCATION loc; TYPE t = DEREF_type (id_variable_etc_type (id)); bad_crt_loc++; loc = crt_loc; DEREF_loc (id_loc (id), crt_loc); 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); } } else { 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); } } 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); } /* * ADDITIONAL CHECKS FOR INLINE FUNCTIONS * * This routine performs additional constraint checks on the inline * function id as demanded by ISO C99. */ static void check_inline_func(IDENTIFIER id) { #if LANGUAGE_C DECL_SPEC ds = DEREF_dspec (id_storage (id)); ERROR err = NULL_err; LOCATION loc; DEREF_loc (id_loc (id), loc); /* Functions with external linkage must be defined. */ if (!(ds & dspec_static) && IS_NULL_exp (DEREF_id (id_function_etc_defn (id)))) { err = ERR_dcl_fct_spec_inline_undefined (id); if (!IS_NULL_err (err)) { err = concat_error (ERR_dcl_fct_spec_inline_extern (id), err); } } /* More checks for inline definitions. */ if (DEREF_int (id_function_etc_inline_def (id))) { /* Must not have a definition of a modifiable static object. */ IDENTIFIER sid = DEREF_id (id_function_etc_static_def (id)); if (!IS_NULL_id (sid)) { err = ERR_dcl_fct_spec_inline_static_def (sid); } /* Must not have a reference to an object with internal linkage. */ sid = DEREF_id (id_function_etc_static_ref (id)); if (!IS_NULL_id (sid)) { err = concat_error (err, ERR_dcl_fct_spec_inline_static_ref (sid)); } if (!IS_NULL_err (err)) { err = concat_error (ERR_dcl_fct_spec_inline_def_extern (id), err); } } if (!IS_NULL_err (err)) report (loc, err); #else UNUSED (id); #endif } /* * 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(IDENTIFIER id, NAMESPACE ns, EXP blk, int anon, 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); } 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); } 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) { check_inline_func (id); 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(NAMESPACE ns, EXP blk, int anon, 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(void) { 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(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(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(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(IDENTIFIER id, 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(EXP, VAR_INFO); static VAR_INFO flow_alias_exp(EXP, VAR_INFO); static VAR_INFO flow_stmt(EXP, VAR_INFO, int); static VAR_INFO flow_offset(OFFSET, VAR_INFO, int); static void set_variable(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(IDENTIFIER id, DECL_SPEC ds, 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(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(IDENTIFIER id, VAR_INFO ds, 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(int flow, 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(IDENTIFIER id, 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(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(VAR_INFO dp, VAR_INFO dq, 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(VAR_LIST p, VAR_LIST q, 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(VAR_LIST va, 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(VAR_LIST va, 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(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(VAR_LIST va, 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(IDENTIFIER lab, VAR_INFO use, 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(IDENTIFIER lab, VAR_INFO use, 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(void) { 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(VAR_INFO use, 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(EXP c, EXP a, EXP b, VAR_INFO use, 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(EXP e, VAR_INFO use, 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(LIST (TOKEN) p, 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(OFFSET off, VAR_INFO use, 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(LIST (EXP) p, VAR_INFO use, 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(EXP e, 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(EXP e, 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(EXP a, 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(EXP e, VAR_INFO use, 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(EXP e, VAR_INFO use, 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(EXP e, LIST (IDENTIFIER) p, VAR_INFO use, 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(EXP e, VAR_INFO use, 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(EXP e, VAR_INFO use, 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(EXP e, VAR_INFO use, 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(EXP e, VAR_INFO use, 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(IDENTIFIER id, EXP e, 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; }