/*
* Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Crown Copyright (c) 1997
*
* This TenDRA(r) Computer Program is subject to Copyright
* owned by the United Kingdom Secretary of State for Defence
* acting through the Defence Evaluation and Research Agency
* (DERA). It is made available to Recipients with a
* royalty-free licence for its use, reproduction, transfer
* to other parties and amendment for any purpose not excluding
* product development provided that any such use et cetera
* shall be deemed to be acceptance of the following conditions:-
*
* (1) Its Recipients shall ensure that this Notice is
* reproduced upon any copies or amended versions of it;
*
* (2) Any amended version of it shall be clearly marked to
* show both the nature of and the organisation responsible
* for the relevant amendment or amendments;
*
* (3) Its onward transfer from a recipient to another
* party shall be deemed to be that party's acceptance of
* these conditions;
*
* (4) DERA gives no warranty or assurance as to its
* quality or suitability for any purpose and DERA accepts
* no liability whatsoever in relation to any use to which
* it may be put.
*
* $TenDRA: tendra/src/producers/common/construct/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;
}
syntax highlighted by Code2HTML, v. 0.9.1