Rev 7 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (c) 2002-2006 The TenDRA Project <http://www.tendra.org/>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of The TenDRA Project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific, prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id$
*/
/*
Crown Copyright (c) 1997
This TenDRA(r) Computer Program is subject to Copyright
owned by the United Kingdom Secretary of State for Defence
acting through the Defence Evaluation and Research Agency
(DERA). It is made available to Recipients with a
royalty-free licence for its use, reproduction, transfer
to other parties and amendment for any purpose not excluding
product development provided that any such use et cetera
shall be deemed to be acceptance of the following conditions:-
(1) Its Recipients shall ensure that this Notice is
reproduced upon any copies or amended versions of it;
(2) Any amended version of it shall be clearly marked to
show both the nature of and the organisation responsible
for the relevant amendment or amendments;
(3) Its onward transfer from a recipient to another
party shall be deemed to be that party's acceptance of
these conditions;
(4) DERA gives no warranty or assurance as to its
quality or suitability for any purpose and DERA accepts
no liability whatsoever in relation to any use to which
it may be put.
*/
#include "config.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "exp_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "inst_ops.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "off_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "basetype.h"
#include "check.h"
#include "chktype.h"
#include "class.h"
#include "compile.h"
#include "constant.h"
#include "construct.h"
#include "convert.h"
#include "declare.h"
#include "dump.h"
#include "file.h"
#include "function.h"
#include "hash.h"
#include "identifier.h"
#include "initialise.h"
#include "instance.h"
#include "literal.h"
#include "namespace.h"
#include "option.h"
#include "preproc.h"
#include "save.h"
#include "statement.h"
#include "stmt.h"
#include "syntax.h"
#include "tok.h"
#include "variable.h"
/*
VARIABLE ANALYSIS FLAGS
The flag suppress_variable may be set to 1 to suppress variable
analysis on function parameters and to 2 to suppress all variable
analysis. The flag anon_c_linkage may be set to true to indicate
that objects with C linkage in anonymous namespaces are anonymous.
*/
int suppress_variable = 0;
int anon_c_linkage = 0;
/*
CHECK A VARIABLE OR FUNCTION USAGE
This routine checks the usage of the identifier id. The flag anon
is true if the complete reach of the identifier is being analysed. For
example, the reach of a local variable is its scope, that of an internal
variable is its translation unit, and that of an external variable is
its entire program.
*/
static void
check_usage(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 (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 anon)
{
int def = 0;
EXP e = DEREF_exp(id_variable_etc_init(id));
if (!IS_NULL_exp(e) && IS_exp_zero(e)) {
LOCATION loc;
DECL_SPEC ds = DEREF_dspec(id_storage(id));
TYPE t = DEREF_type(id_variable_etc_type(id));
bad_crt_loc++;
loc = crt_loc;
DEREF_loc(id_loc(id), crt_loc);
if (anon || !(ds & dspec_extern)) {
/* Types for internal objects should be complete */
ERROR err = check_complete(t);
if (!IS_NULL_err(err)) {
ERROR err2 = ERR_basic_types_tent_incompl(id);
err = concat_error(err, err2);
report(crt_loc, err);
}
}
if (IS_type_array(t)) {
/* Complete array 'A []' to 'A [1]' */
NAT n = DEREF_nat(type_array_size(t));
if (IS_NULL_nat(n)) {
CV_SPEC qual = DEREF_cv(type_qual(t));
TYPE s = DEREF_type(type_array_sub(t));
n = small_nat[1];
MAKE_type_array(qual, s, n, t);
COPY_type(id_variable_etc_type(id), t);
}
}
e = init_general(t, NULL_exp, id, 0);
COPY_exp(id_variable_etc_init(id), e);
define_id(id);
crt_loc = loc;
bad_crt_loc--;
def = 1;
}
return (def);
}
/*
CHECK A GLOBAL IDENTIFIER
This routine applies the global program checks to the identifier id
from the namespace ns. If id is a class or namespace name then the
checks are also applied recursively to the members of id. The flag
anon is true if the parent namespace of id is local to the current
translation unit. The routine returns the number of identifiers in
id (allowing for overloaded functions).
*/
unsigned long
check_identifier(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, anon);
}
compile_variable(id, 0);
} else {
/* End of local block */
if (ds & dspec_auto) {
EXP term;
ds &= ~dspec_access;
COPY_dspec(id_storage(id), ds);
term = DEREF_exp(id_variable_etc_term(id));
if (!IS_NULL_exp(term)) {
if (do_usage) {
dump_destr(id,
&stmt_loc);
}
}
}
}
}
break;
case id_parameter_tag: {
/* Check parameter usage */
if (chk && suppress_variable == 0) {
check_usage(id, blk, anon);
}
break;
}
case id_stat_member_tag:
/* Check static data member usage */
if (anon == ANON_CLASS) {
LOCATION loc;
DEREF_loc(id_loc(id), loc);
report(loc, ERR_class_static_data_anon(id));
}
if (chk) {
check_usage(id, blk, anon);
if (IS_NULL_exp(blk)) {
DECL_SPEC ds = DEREF_dspec(id_storage(id));
if (ds & dspec_defn) {
IGNORE define_tentative(id, anon);
}
compile_variable(id, 0);
}
}
break;
case id_function_tag:
case id_mem_func_tag:
case id_stat_mem_func_tag: {
/* Check member function usage */
TYPE t = DEREF_type(id_function_etc_type(id));
IDENTIFIER over = DEREF_id(id_function_etc_over(id));
if (!IS_NULL_id(over)) {
n += check_identifier(over, ns, blk, anon, chk);
}
if (IS_type_func(t)) {
/* Simple functions */
if (chk) {
DECL_SPEC ds = DEREF_dspec(id_storage(id));
if ((ds & dspec_static) && preserve_all) {
compile_preserve(id);
}
if (ds & dspec_inline) {
if (!anon) {
anon = ANON_INLINE;
}
}
check_usage(id, blk, anon);
if (IS_NULL_exp(blk)) {
compile_function(id, 0);
}
}
} else {
/* Template functions */
check_template_id(t, id, ns, blk, anon, chk);
}
break;
}
case id_enumerator_tag:
/* Check enumerator value */
if (chk) {
EXP e = DEREF_exp(id_enumerator_value(id));
if (overflow_exp(e)) {
compile_variable(id, 0);
}
}
break;
case id_nspace_alias_tag:
case id_member_tag:
case id_weak_param_tag:
/* Don't check these (yet) */
break;
}
return (n);
}
/*
CHECK A NAMESPACE
This routine scans through all the members of the namespace ns, calling
check_identifier for each member (including hidden members) and
compiling any functions or non-local variables. It returns the number
of identifiers declared in ns. ns may be a block scope associated
with the statement blk. The flag anon is true if the parent namespace
of ns is local to the current translation unit.
*/
unsigned long
check_namespace(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;
}