/* * Copyright (c) 2003, The Tendra Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * Crown Copyright (c) 1997, 1998 * * This TenDRA(r) Computer Program is subject to Copyright * owned by the United Kingdom Secretary of State for Defence * acting through the Defence Evaluation and Research Agency * (DERA). It is made available to Recipients with a * royalty-free licence for its use, reproduction, transfer * to other parties and amendment for any purpose not excluding * product development provided that any such use et cetera * shall be deemed to be acceptance of the following conditions:- * * (1) Its Recipients shall ensure that this Notice is * reproduced upon any copies or amended versions of it; * * (2) Any amended version of it shall be clearly marked to * show both the nature of and the organisation responsible * for the relevant amendment or amendments; * * (3) Its onward transfer from a recipient to another * party shall be deemed to be that party's acceptance of * these conditions; * * (4) DERA gives no warranty or assurance as to its * quality or suitability for any purpose and DERA accepts * no liability whatsoever in relation to any use to which * it may be put. * * $TenDRA: tendra/src/producers/common/construct/statement.c,v 1.14 2005/10/24 14:13:41 stefanf Exp $ */ #include "config.h" #include "producer.h" #include "c_types.h" #include "etype_ops.h" #include "exp_ops.h" #include "hashid_ops.h" #include "id_ops.h" #include "loc_ext.h" #include "member_ops.h" #include "nspace_ops.h" #include "str_ops.h" #include "type_ops.h" #include "error.h" #include "catalog.h" #include "option.h" #include "assign.h" #include "basetype.h" #include "cast.h" #include "check.h" #include "chktype.h" #include "class.h" #include "constant.h" #include "construct.h" #include "convert.h" #include "declare.h" #include "destroy.h" #include "dump.h" #include "exception.h" #include "expression.h" #include "file.h" #include "function.h" #include "hash.h" #include "identifier.h" #include "initialise.h" #include "label.h" #include "lex.h" #include "literal.h" #include "member.h" #include "namespace.h" #include "overload.h" #include "parse.h" #include "predict.h" #include "quality.h" #include "redeclare.h" #include "statement.h" #include "symbols.h" #include "syntax.h" #include "template.h" #include "ustring.h" #include "variable.h" /* * UNREACHED CODE ANALYSIS * * The detection of unreachable code is primarily by means of the simple * flag, which is true for unreported statements. The flag unreached_last * is set to equal unreached_code whenever a reachability check is applied. * This is to ensure that only the first statement in an unreached block * is reported. At the end of a conditional, or other complex statement, * unreached_prev is set to the value of unreached_code at the start of * that statement. The flag unreached_fall is set to false immediately * following a 'case' or 'default' label, but set to true after a non- * trivial statement (this is included in the grammar). */ int unreached_code = 0; int unreached_last = 0; int unreached_prev = 0; int unreached_fall = 0; int suppress_fall = 0; /* * DOES AN EXPRESSION NOT RETURN? * * This routine tests whether the expression e has type bottom, i.e. does * not return a value. */ static int is_bottom(EXP e) { TYPE t; if (IS_NULL_exp (e)) return (0); t = DEREF_type (exp_type (e)); return (IS_type_bottom (t)); } /* * FIND THE PARENT OF A STATEMENT * * This routine returns a pointer to the parent of the statement e. It * returns the null pointer if e is a simple expression. */ static PTR (EXP) parent_stmt(EXP e) { PTR (EXP) ptr = NULL_ptr (EXP); if (!IS_NULL_exp (e)) { switch (TAG_exp (e)) { case exp_reach_tag : { ptr = exp_reach_parent (e); break; } case exp_unreach_tag : { ptr = exp_unreach_parent (e); break; } case exp_sequence_tag : { ptr = exp_sequence_parent (e); break; } case exp_solve_stmt_tag : { ptr = exp_solve_stmt_parent (e); break; } case exp_decl_stmt_tag : { ptr = exp_decl_stmt_parent (e); break; } case exp_if_stmt_tag : { ptr = exp_if_stmt_parent (e); break; } case exp_while_stmt_tag : { ptr = exp_while_stmt_parent (e); break; } case exp_do_stmt_tag : { ptr = exp_do_stmt_parent (e); break; } case exp_switch_stmt_tag : { ptr = exp_switch_stmt_parent (e); break; } case exp_hash_if_tag : { ptr = exp_hash_if_parent (e); break; } case exp_return_stmt_tag : { ptr = exp_return_stmt_parent (e); break; } case exp_goto_stmt_tag : { ptr = exp_goto_stmt_parent (e); break; } case exp_label_stmt_tag : { ptr = exp_label_stmt_parent (e); break; } case exp_try_block_tag : { ptr = exp_try_block_parent (e); break; } case exp_handler_tag : { ptr = exp_handler_parent (e); break; } case exp_token_tag : { ptr = exp_token_parent (e); break; } case exp_location_tag : { EXP a = DEREF_exp (exp_location_arg (e)); ptr = parent_stmt (a); break; } case exp_paren_tag : { EXP a = DEREF_exp (exp_paren_arg (e)); ptr = parent_stmt (a); break; } } } return (ptr); } /* * GET THE PARENT OF A STATEMENT * * This routine returns the parent statement of e. */ EXP get_parent_stmt(EXP e) { EXP p = NULL_exp; if (!IS_NULL_exp (e)) { PTR (EXP) ptr = parent_stmt (e); if (!IS_NULL_ptr (ptr)) p = DEREF_exp (ptr); } return (p); } /* * SET THE PARENT OF A STATEMENT * * This routine sets the parent of the statement e to be p. */ void set_parent_stmt(EXP e, EXP p) { if (!IS_NULL_exp (e)) { PTR (EXP) ptr = parent_stmt (e); if (!IS_NULL_ptr (ptr)) COPY_exp (ptr, p); } return; } /* * STATEMENT LOCATION FLAG * * The flag record_location may be set to true to force extra expressions * giving the location of each statement to be inserted into the output. * The variable stmt_loc records the last such location. */ int record_location = 0; LOCATION stmt_loc = NULL_loc; static int adjusted_line = 0; /* * ADJUST COLUMN NUMBER * * This routine sets stmt_loc to point to the start of the preprocessing * token p. */ static void adjust_column(PPTOKEN *p) { if (p) { int t = p->tok; if (t >= FIRST_SYMBOL && t <= LAST_SYMBOL) { /* Adjust to start of symbol */ ulong len = (ulong) ustrlen (token_name (t)); stmt_loc = crt_loc; stmt_loc.column -= (len - 1); return; } if (t >= FIRST_KEYWORD && t <= LAST_KEYWORD) { /* Map keywords to underlying identifier */ t = lex_identifier; } if (t == lex_identifier) { IDENTIFIER id = p->pp_data.id.use; id = underlying_id (id); DEREF_loc (id_loc (id), stmt_loc); return; } } stmt_loc = crt_loc; return; } /* * ADJUST LINE NUMBER * * This routine is called whenever the location of a statement is * recorded. It is intended to ensure that the current location points * to the start of the next statement rather than the end of the current * statement. */ static void adjust_line(int next) { if (!adjusted_line) { adjusted_line = 1; if (next) { switch (crt_lex_token) { case lex_open_Hbrace_H1 : case lex_colon : case lex_semicolon : case lex_close_Hround : case lex_else : case lex_exhaustive : { /* Scan to next token */ PPTOKEN *p = crt_token->next; if (p) { /* Set location from next token */ p = read_loc_tokens (p); if (crt_state_depth == 0) { adjust_column (p); return; } } else { if (crt_state_depth == 0) { /* Skip white space from input file */ unsigned long sp = skip_white (1); update_column (); stmt_loc = crt_loc; stmt_loc.column++; if (sp) patch_white (sp); return; } } break; } default : { /* Use current token */ if (crt_state_depth == 0) { adjust_column (crt_token); return; } break; } } } stmt_loc = crt_loc; } return; } /* * BLOCK NAMESPACE * * This variable can be set to make begin_compound_stmt use an existing * namespace rather than creating a new one. */ NAMESPACE block_namespace = NULL_nspace; /* * BEGIN A COMPOUND STATEMENT * * These routine begins the construction of a compound statement. If * scope is true then this compound statement also establishes a scope. * A compound statement consists of a list of statements (the first of * which is always a dummy null expression) plus a pointer indicating * where the next statement to be added to the compound statement is to * go. For simple statement lists this is the last element of the list, * but if labels or declarations are introduced then any statements added * subsequently will go at the end of the dummy block introduced by * begin_label_stmt or make_decl_stmt. The parent field of the main * statement is used within the construction to point to the innermost * block of this kind. It is only set to its correct value at the end * of the compound statement. */ EXP begin_compound_stmt(int scope) { TYPE t; NAMESPACE ns; EXP e = NULL_exp; LIST (EXP) stmts; NAMESPACE cns = NULL_nspace; /* Create block namespace */ if (scope > 0) { ns = block_namespace; if (IS_NULL_nspace (ns)) { /* Create new namespace */ unsigned tag = nspace_block_tag; if (scope == 2) tag = nspace_dummy_tag; cns = crt_namespace; ns = make_namespace (crt_func_id, tag, 0); push_namespace (ns); } else { /* Use existing namespace */ MEMBER mem = DEREF_member (nspace_last (ns)); COPY_member (nspace_prev (ns), mem); cns = ns; block_namespace = NULL_nspace; } IGNORE incr_value (OPT_VAL_statement_depth); } else { ns = NULL_nspace; } /* Create compound statement */ t = (unreached_code ? type_bottom : type_void); if (record_location && scope >= 0) { /* Record start of block */ adjust_line (scope); adjusted_line = 0; MAKE_exp_location (t, stmt_loc, e, e); if (do_scope) dump_begin_scope (NULL_id, ns, cns, &crt_loc); } CONS_exp (e, NULL_list (EXP), stmts); MAKE_exp_sequence (t, stmts, stmts, ns, 0, e); COPY_exp (exp_sequence_parent (e), e); return (e); } /* * MARK THE START OF A COMPOUND STATEMENT * * On occasions a compound statement may be created before the open brace * which marks its start. In this case this routine is called when the * open brace is encountered to record the actual position of the start * of the block. */ void mark_compound_stmt(EXP prev) { if (record_location) { LIST (EXP) stmts = DEREF_list (exp_sequence_first (prev)); EXP stmt = DEREF_exp (HEAD_list (stmts)); if (!IS_NULL_exp (stmt) && IS_exp_location (stmt)) { adjust_line (1); adjusted_line = 0; COPY_loc (exp_location_end (stmt), stmt_loc); } } return; } /* * EXTEND A COMPOUND STATEMENT * * This routine adds the statement stmt to the end of the compound * statement prev. Note that this routine also decides where the * statement after this one is to go on the basis of the rules above. */ static EXP extend_compound_stmt(EXP prev, EXP stmt, int loc) { EXP body; EXP parent; LIST (EXP) elem; LIST (EXP) elem0; LIST (EXP) stmts; /* Allow for null statements */ if (IS_NULL_exp (stmt)) return (prev); /* Add statement to list */ stmts = DEREF_list (exp_sequence_last (prev)); CONS_exp (stmt, NULL_list (EXP), elem); COPY_list (PTR_TAIL_list (stmts), elem); elem0 = elem; /* Set the parent of stmt */ parent = DEREF_exp (exp_sequence_parent (prev)); set_parent_stmt (stmt, parent); /* Find location of next statement */ switch (TAG_exp (stmt)) { case exp_decl_stmt_tag : { /* Transfer to the body of a declaration */ unsigned tag; body = stmt; do { body = DEREF_exp (exp_decl_stmt_body (body)); tag = TAG_exp (body); } while (tag == exp_decl_stmt_tag); if (tag == exp_sequence_tag) { elem = DEREF_list (exp_sequence_last (body)); COPY_exp (exp_sequence_parent (prev), body); } loc = 0; break; } case exp_label_stmt_tag : { /* Transfer to the body of a labelled statement */ body = DEREF_exp (exp_label_stmt_body (stmt)); if (IS_exp_sequence (body)) { elem = DEREF_list (exp_sequence_last (body)); COPY_exp (exp_sequence_parent (prev), body); } loc = 0; break; } case exp_location_tag : case exp_thrown_tag : { /* Don't record location in these cases */ loc = 0; break; } } COPY_list (exp_sequence_last (prev), elem); /* Record statement location */ if (record_location) { adjust_line (1); if (loc) { TYPE t = DEREF_type (exp_type (stmt)); MAKE_exp_location (t, stmt_loc, stmt, stmt); COPY_exp (HEAD_list (elem0), stmt); } } /* Unreached code analysis */ if (is_bottom (stmt)) { COPY_type (exp_type (prev), type_bottom); } return (prev); } /* * ADD A STATEMENT TO A COMPOUND STATEMENT * * This routine adds the statement stmt to the compound statement prev, * returning the resulting compound statement. It differs from the * previous routine in taking any declarations in stmt into account. * This is done using the last field of the current namespace which keeps * track of the last variable in the block for which a declaration * statement has been introduced. The treatment of labelled statements * is tricky - the declarations need to be inserted between the label * and its body. */ EXP add_compound_stmt(EXP prev, EXP stmt) { EXP parent = NULL_exp; LIST (EXP) last = NULL_list (EXP); NAMESPACE ns = DEREF_nspace (exp_sequence_decl (prev)); if (!IS_NULL_nspace (ns)) { MEMBER p = DEREF_member (nspace_last (ns)); MEMBER q = DEREF_member (nspace_prev (ns)); if (!EQ_member (p, q)) { /* Create declaration statement */ int vars = 0; EXP decl = make_decl_stmt (p, q, &vars); if (!IS_NULL_exp (stmt) && IS_exp_label_stmt (stmt)) { /* Labels come before declarations */ EXP body = DEREF_exp (exp_label_stmt_body (stmt)); if (IS_exp_sequence (body)) { last = DEREF_list (exp_sequence_last (body)); body = DEREF_exp (HEAD_list (last)); if (!IS_NULL_exp (body)) { EXP b = body; if (IS_exp_location (b)) { b = DEREF_exp (exp_location_arg (b)); } if (!IS_NULL_exp (b)) { COPY_exp (HEAD_list (last), NULL_exp); } } last = NULL_list (EXP); prev = extend_compound_stmt (prev, stmt, 1); stmt = body; } } if (!vars) { last = DEREF_list (exp_sequence_last (prev)); parent = DEREF_exp (exp_sequence_parent (prev)); } prev = extend_compound_stmt (prev, decl, 0); COPY_member (nspace_prev (ns), p); } } if (!IS_NULL_exp (stmt)) { /* Add the new statement */ prev = extend_compound_stmt (prev, stmt, 1); } if (!IS_NULL_list (last)) { /* Restrict scope of temporaries to stmt */ last = END_list (last); COPY_list (exp_sequence_last (prev), last); COPY_exp (exp_sequence_parent (prev), parent); } adjusted_line = 0; return (prev); } /* * END A COMPOUND STATEMENT * * This routine ends the compound statement prev. */ EXP end_compound_stmt(EXP prev) { /* Take local declarations out of scope */ int blk = DEREF_int (exp_sequence_block (prev)); NAMESPACE ns = DEREF_nspace (exp_sequence_decl (prev)); if (!IS_NULL_nspace (ns)) { if (check_namespace (ns, prev, ANON_NONE, 1)) { if (blk == 0) { COPY_int (exp_sequence_block (prev), 1); } } if (do_scope) dump_end_scope (NULL_id, ns, &stmt_loc); decr_value (OPT_VAL_statement_depth); IGNORE pop_namespace (); } COPY_exp (exp_sequence_parent (prev), NULL_exp); return (prev); } /* * CONSTRUCT A TEMPORARY DECLARATION STATEMENT * * This routine binds any temporary variable declarations given by the * namespace members p to q to the expression e. This is part of the * action of make_decl_stmt. */ EXP make_temp_decl(MEMBER p, MEMBER q, EXP e) { MEMBER r = p; while (!EQ_member (r, q)) { IDENTIFIER id = DEREF_id (member_id (r)); if (!IS_NULL_id (id) && IS_id_variable (id)) { /* Construct declaration statement */ DECL_SPEC ds = DEREF_dspec (id_storage (id)); if (ds & dspec_temp) { /* Temporary variables */ if (!(ds & dspec_ignore)) { EXP d; TYPE t = DEREF_type (id_variable_type (id)); EXP term = DEREF_exp (id_variable_term (id)); if (!IS_NULL_exp (term)) have_destructor = 1; MAKE_exp_decl_stmt (t, id, e, d); set_parent_stmt (e, d); e = d; } } } r = DEREF_member (member_next (r)); } return (e); } /* * CONSTRUCT A DECLARATION STATEMENT * * This routine constructs a series of nested declaration statements * corresponding to the given list of namespace members. p gives the * last member of the current block namespace defined, while q gives * the last member defined before this declaration. The body of a * declaration statement consists of the rest of the statements in the * enclosing block. That is: * * { * stmt1; * .... * decl1; * decl2; * .... * body1; * .... * } * * is transformed into: * * { * stmt1; * .... * decl1; { * decl2; { * .... { * body1; * .... * } * } * } * } * * except that the introduced blocks do not establish scopes. Any * temporary variables introduced are declared before the normal * variables regardless of their actual order of declaration. */ EXP make_decl_stmt(MEMBER p, MEMBER q, int *vars) { MEMBER r = p; unsigned temps = 0; IDENTIFIER init = NULL_id; LIST (IDENTIFIER) destr = NULL_list (IDENTIFIER); EXP e = begin_compound_stmt (-1); /* Scan through members */ while (!EQ_member (r, q)) { IDENTIFIER id = DEREF_id (member_id (r)); if (!IS_NULL_id (id) && IS_id_variable (id)) { /* Construct declaration statement */ DECL_SPEC ds = DEREF_dspec (id_storage (id)); if (ds & dspec_temp) { /* Temporary variables */ if (!(ds & dspec_ignore)) { if (ds & dspec_register) { EXP term = DEREF_exp (id_variable_term (id)); if (!IS_NULL_exp (term)) { CONS_id (id, destr, destr); } } temps++; } } else if (ds & dspec_linkage) { /* External variables */ /* EMPTY */ } else if ((ds & dspec_reserve) && !is_anon_member (id)) { /* Injected variables */ /* EMPTY */ } else { EXP d; TYPE t = DEREF_type (id_variable_type (id)); EXP def = DEREF_exp (id_variable_init (id)); EXP term = DEREF_exp (id_variable_term (id)); if (!IS_NULL_exp (term)) have_destructor = 1; if (!IS_NULL_exp (def) && !IS_exp_null (def)) { /* Identifier is initialised */ init = id; } MAKE_exp_decl_stmt (t, id, e, d); set_parent_stmt (e, d); *vars = 1; e = d; } } r = DEREF_member (member_next (r)); } /* Scan through again for temporary variables */ if (temps) { e = make_temp_decl (p, q, e); if (!IS_NULL_list (destr)) { /* NOT YET IMPLEMENTED */ DESTROY_list (destr, SIZE_id); } } /* Only report unreached declarations if they are initialised */ if (!IS_NULL_id (init) && unreached_code) { if (!unreached_last) { LOCATION loc; DEREF_loc (id_loc (init), loc); report (loc, ERR_stmt_stmt_unreach ()); unreached_last = 1; } } return (e); } /* * BIND TEMPORARY VARIABLES TO AN EXPRESSION * * This routine binds any temporary variables declared in e to the * expression. Reference conversions are also applied, but any * parentheses are preserved for the benefit of the assignment in * boolean check. */ EXP bind_temporary(EXP e) { if (!IS_NULL_exp (e)) { NAMESPACE ns = crt_namespace; unsigned tag = TAG_exp (e); e = convert_reference (e, REF_NORMAL); if (!IS_NULL_nspace (ns) && IS_nspace_block_etc (ns)) { MEMBER p = DEREF_member (nspace_last (ns)); MEMBER q = DEREF_member (nspace_prev (ns)); if (!EQ_member (p, q)) { e = make_temp_decl (p, q, e); COPY_member (nspace_prev (ns), p); } } if (tag == exp_paren_tag && option (OPT_bool_assign)) { e = make_paren_exp (e); } } return (e); } /* * DISCARD AN EXPRESSION * * This routine discards the expression e. If e is an lvalue then the * lvalue conversions are checked but not performed. */ EXP make_discard_exp(EXP e) { if (!IS_NULL_exp (e)) { unsigned tag = TAG_exp (e); TYPE t = DEREF_type (exp_type (e)); switch (TAG_type (t)) { case type_top_tag : case type_bottom_tag : { /* Void types */ break; } case type_bitfield_tag : { /* Remove bitfield components */ if (tag == exp_contents_tag) { e = DEREF_exp (exp_contents_ptr (e)); tag = TAG_exp (e); } if (tag == exp_indir_tag) { e = DEREF_exp (exp_indir_ptr (e)); tag = TAG_exp (e); if (tag == exp_add_ptr_tag) { e = DEREF_exp (exp_add_ptr_ptr (e)); tag = TAG_exp (e); if (tag == exp_address_tag) { e = DEREF_exp (exp_address_arg (e)); tag = TAG_exp (e); } } } break; } case type_array_tag : case type_func_tag : case type_templ_tag : { /* Check for overloaded functions */ e = convert_lvalue (e); tag = TAG_exp (e); break; } case type_token_tag : { if (is_templ_type (t)) { /* Mark template parameters */ MAKE_exp_op (t, lex_void, e, NULL_exp, e); tag = exp_op_tag; break; } goto default_lab; } default : default_lab : { /* Check lvalue conversions */ ERROR err; CV_SPEC qual = DEREF_cv (type_qual (t)); if (!(qual & cv_lvalue)) break; if (qual == (cv_lvalue | cv_const)) { if (tag == exp_identifier_tag) { e = convert_const (e); tag = TAG_exp (e); break; } } err = check_incomplete (t); if (!IS_NULL_err (err)) { err = concat_error (err, ERR_stmt_expr_incompl ()); report (crt_loc, err); } break; } } switch (tag) { case exp_postinc_tag : { /* Discard postincrement expressions */ COPY_exp (exp_postinc_value (e), NULL_exp); break; } case exp_address_tag : case exp_address_mem_tag : { /* Check for overloaded functions */ e = convert_lvalue (e); break; } case exp_constr_tag : { /* Introduce temporary for constructor */ e = convert_none (e); break; } } } return (e); } /* * CHECK A DISCARDED EXPRESSION * * This routine checks whether discarding the expression e should be * warned about. */ static int check_discard_exp(EXP e) { if (!IS_NULL_exp (e)) { switch (TAG_exp (e)) { case exp_indir_tag : { /* Ignore indirection expressions */ EXP a = DEREF_exp (exp_indir_ptr (e)); return (check_discard_exp (a)); } case exp_contents_tag : { /* Ignore contents expressions */ EXP a = DEREF_exp (exp_contents_ptr (e)); return (check_discard_exp (a)); } case exp_comma_tag : { /* Examine final component of comma expression */ LIST (EXP) p = DEREF_list (exp_comma_args (e)); EXP a = DEREF_exp (HEAD_list (END_list (p))); return (check_discard_exp (a)); } case exp_postinc_tag : { /* Discard postincrement expressions */ COPY_exp (exp_postinc_value (e), NULL_exp); goto assign_lab; } case exp_assign_tag : case exp_init_tag : case exp_preinc_tag : case exp_decl_stmt_tag : assign_lab : { /* Assignments and declarations are allowed */ if (!IS_NULL_id (made_temporary)) { report (crt_loc, ERR_stmt_expr_discard_val ()); } break; } case exp_func_tag : { /* Discarded function return */ report (crt_loc, ERR_stmt_expr_discard_func ()); break; } case exp_func_id_tag : { /* Discarded function return */ DECL_SPEC ds; IDENTIFIER id = DEREF_id (exp_func_id_id (e)); if (IS_id_token (id)) { id = DEREF_id (id_token_alt (id)); } ds = DEREF_dspec (id_storage (id)); if (!(ds & dspec_ignore)) { ERROR err = ERR_expr_call_func (id); ERROR err2 = ERR_stmt_expr_discard_func (); err = concat_error (err, err2); report (crt_loc, err); } break; } default : { /* Discarded value */ report (crt_loc, ERR_stmt_expr_discard_val ()); return (1); } } } return (0); } /* * CONSTRUCT AN EXPRESSION STATEMENT * * This routine constructs an expression statement from the expression e. * This is basically an identity operation, however various reachability * and discarded value checks (which need to take account of certain * special forms) are applied. */ EXP make_exp_stmt(EXP e) { TYPE t; /* Perform operand conversion */ if (IS_NULL_exp (e)) return (e); e = convert_reference (e, REF_NORMAL); made_temporary = NULL_id; e = make_discard_exp (e); /* Check the type of e */ t = DEREF_type (exp_type (e)); switch (TAG_type (t)) { case type_bottom_tag : { /* Unreached code */ unreached_code = 1; return (e); } case type_top_tag : case type_error_tag : { /* No effect */ return (e); } case type_token_tag : { /* Check for template types */ if (is_templ_type (t)) { MAKE_exp_op (t, lex_discard, e, NULL_exp, e); return (e); } break; } } /* Check discarded value */ if (check_discard_exp (e)) { MAKE_exp_cast (type_void, CONV_ELLIPSIS, e, e); } return (e); } /* * CURRENT CONDITION * * This value is used to hold an indicating of the value of the condition * in the current conditional or iteration statement. There are four * possible values corresponding to true, false and indeterminate constant * conditions, plus non-constant conditions. */ unsigned crt_condition = BOOL_INVALID; /* * CHECK A CONDITION * * This routine converts the condition expression cond to a boolean, * including setting the value of crt_condition. Note that the condition * can be a condition declaration or the null expression (indicating an * absent condition in a for-statement). In the former case the * declaration is returned via pd. op denotes the context for the * condition (an if-statement etc.). */ EXP check_cond(EXP cond, EXP *pd, int op) { EXP c; TYPE tc; unsigned ca; unsigned cc; unsigned tag; EXP a = NULL_exp; ERROR err = NULL_err; /* Check for null expressions */ if (IS_NULL_exp (cond)) { c = make_bool_exp (BOOL_TRUE, exp_int_lit_tag); crt_condition = BOOL_TRUE; if (record_location && op != lex_cond_Hop) { /* Mark position of condition */ TYPE t = DEREF_type (exp_type (c)); MAKE_exp_location (t, crt_loc, c, c); } return (c); } /* Check for condition declarations */ tag = TAG_exp (cond); if (tag == exp_paren_tag) { TYPE t; DESTROY_exp_paren (destroy, t, cond, cond); UNUSED (t); } if (IS_exp_decl_stmt (cond)) { DECL_SPEC ds; IDENTIFIER id; *pd = cond; do { /* Step over temporary variables */ id = DEREF_id (exp_decl_stmt_id (cond)); cond = DEREF_exp (exp_decl_stmt_body (cond)); } while (IS_exp_decl_stmt (cond)); ds = DEREF_dspec (id_storage (id)); if (IS_id_variable (id) && !(ds & dspec_temp)) { /* This doesn't count as a use of id */ EXP b = DEREF_exp (id_variable_init (id)); TYPE t = DEREF_type (id_variable_type (id)); if (!IS_NULL_exp (b)) { if (!IS_type_compound (t)) { /* Condition value is used in constant checks */ a = convert_boolean (b, exp_paren_tag, KILL_err); } if (op == lex_while || op == lex_for) { /* Move initialisation into loop condition */ MAKE_exp_init (t, id, b, cond); COPY_exp (id_variable_init (id), NULL_exp); ds |= dspec_explicit; COPY_dspec (id_storage (id), ds); } else { MAKE_exp_identifier (t, id, qual_none, cond); } } else { MAKE_exp_identifier (t, id, qual_none, cond); } } } /* Convert the condition to a boolean */ c = convert_reference (cond, REF_NORMAL); tc = DEREF_type (exp_type (c)); ca = type_category (&tc); if (IS_TYPE_CLASS (ca)) { /* Allow for conversion functions */ c = convert_conv (type_bool, c, &err, CAST_IMPLICIT); } else if (IS_TYPE_TEMPL (ca)) { /* Allow for template parameters */ MAKE_exp_op (type_bool, op, c, NULL_exp, c); } else { /* Simple conversions */ c = convert_lvalue (c); c = convert_boolean (c, tag, &err); } if (!IS_NULL_err (err)) { ERROR err2; switch (op) { case lex_if : err2 = ERR_stmt_if_cond (); break; case lex_do : err2 = ERR_stmt_do_cond (); break; case lex_for : err2 = ERR_stmt_for_cond (); break; case lex_while : err2 = ERR_stmt_while_cond (); break; case lex_cond_Hop : err2 = ERR_expr_cond_bool (); break; default : err2 = NULL_err; break; } err = concat_error (err, err2); report (crt_loc, err); } /* Check for constant conditions */ if (IS_NULL_exp (a)) a = c; cc = eval_const_cond (a); if (cc != BOOL_INVALID) { if (op == lex_if) { /* Report determinate constants in if statements */ if (cc != BOOL_UNKNOWN) { report (crt_loc, ERR_stmt_if_const ()); } } else if (op == lex_cond_Hop) { /* Similarly for conditional expressions */ if (cc != BOOL_UNKNOWN) { report (crt_loc, ERR_expr_cond_const ()); } } else { /* Report non-literal constants in iteration statements */ if (!is_literal (a)) { ERROR err2; switch (op) { case lex_do : err2 = ERR_stmt_do_const (); break; case lex_for : err2 = ERR_stmt_for_const (); break; case lex_while : err2 = ERR_stmt_while_const (); break; default : err2 = NULL_err; break; } report (crt_loc, err2); } } } crt_condition = cc; if (record_location && op != lex_cond_Hop) { /* Mark position of condition */ TYPE t = DEREF_type (exp_type (c)); MAKE_exp_location (t, crt_loc, c, c); } return (c); } /* * CHECK A CONDITIONAL BODY * * This routine is called following a conditional or loop statement * (given by op) to check for suspicious empty bodies which may be * due to a misplaced semicolon. */ void check_empty_stmt(int op) { if (!suppress_quality) { int t = crt_lex_token; if (t == lex_semicolon) { PPTOKEN *p = crt_token; NAMESPACE ns = crt_lookup; t = next_token (); if (t == lex_open_Hbrace_H1) { /* Have 'op; {', probably mean 'op {' */ report (crt_loc, ERR_stmt_stmt_empty (op)); } crt_lookup = ns; crt_token = p; } } return; } /* * CREATE A CONDITION DECLARATION * * This routine combines the condition declaration d with its associated * condition e. If tmp is true a temporary variable is introduced for * the value of e. */ static EXP make_cond_decl(EXP d, EXP e, int tmp) { EXP b; EXP c = d; if (tmp) { /* Introduce temporary variable */ ERROR err = NULL_err; TYPE t = DEREF_type (exp_type (e)); e = make_temporary (t, e, NULL_exp, 0, &err); if (!IS_NULL_err (err)) report (crt_loc, err); } for (;;) { b = DEREF_exp (exp_decl_stmt_body (c)); if (!IS_exp_decl_stmt (b)) break; c = b; } if (IS_exp_sequence (b)) free_exp (b, 0); COPY_exp (exp_decl_stmt_body (c), e); set_parent_stmt (e, c); if (tmp) { /* Result is contents of temporary variable */ e = make_id_exp (made_temporary); e = convert_reference (e, REF_NORMAL); e = convert_lvalue (e); d = join_exp (d, e); } return (d); } /* * FIND A CONDITIONAL STATEMENT * * This routine finds the conditional statement associated with e. * e may contain an enclosing condition declaration. */ static EXP find_cond_stmt(EXP e) { unsigned tag = TAG_exp (e); while (tag == exp_decl_stmt_tag) { e = DEREF_exp (exp_decl_stmt_body (e)); tag = TAG_exp (e); } while (tag == exp_sequence_tag) { LIST (EXP) p = DEREF_list (exp_sequence_first (e)); p = TAIL_list (p); e = DEREF_exp (HEAD_list (p)); tag = TAG_exp (e); if (tag == exp_location_tag) { e = DEREF_exp (exp_location_arg (e)); tag = TAG_exp (e); } } return (e); } /* * BEGIN AN IF STATEMENT * * This routine begins the construction of an if statement with condition * cond. In this and all routines involving a condition, a constant true * condition is replaced by the null expression. */ EXP begin_if_stmt(EXP cond) { EXP e; EXP d = NULL_exp; EXP b = begin_label_stmt (NULL_id, lex_if); IDENTIFIER lab = DEREF_id (exp_label_stmt_label (b)); /* Check the condition */ cond = check_cond (cond, &d, lex_if); if (crt_condition == BOOL_FALSE) unreached_code = 1; /* Construct the result */ MAKE_exp_if_stmt (type_void, cond, NULL_exp, NULL_exp, lab, e); check_empty_stmt (lex_if); set_parent_stmt (b, e); if (!IS_NULL_exp (d)) e = make_cond_decl (d, e, 0); return (e); } /* * CONTINUE AN IF STATEMENT * * This routine continues the construction of an if statement by adding * the statement, right, which is evaluated if the condition is true. * This is called just before any else clause is processed. */ EXP cont_if_stmt(EXP prev, EXP right) { /* Do unreached code analysis */ EXP e; if (crt_condition == BOOL_TRUE) { unreached_code = 1; } else { unreached_code = unreached_prev; } /* Copy the right code into the conditional */ e = find_cond_stmt (prev); COPY_exp (exp_if_stmt_true_code (e), right); set_parent_stmt (right, e); return (prev); } /* * COMPLETE AN IF STATEMENT * * This routine completes the construction of an if statement by adding * the statement, wrong, which is evaluated if the condition is false. */ EXP end_if_stmt(EXP prev, EXP wrong) { /* Do unreached code analysis */ EXP e = find_cond_stmt (prev); EXP right = DEREF_exp (exp_if_stmt_true_code (e)); if (is_bottom (wrong)) { if (is_bottom (right)) { /* Don't reach the end of either branch */ COPY_type (exp_type (prev), type_bottom); COPY_type (exp_type (e), type_bottom); unreached_code = 1; } else if (crt_condition == BOOL_FALSE) { /* Don't reach the end of the taken branch */ COPY_type (exp_type (e), type_bottom); unreached_code = 1; } else { /* Reach the end of one branch */ unreached_code = unreached_prev; } } else if (is_bottom (right)) { if (crt_condition == BOOL_TRUE) { /* Don't reach the end of the taken branch */ COPY_type (exp_type (prev), type_bottom); COPY_type (exp_type (e), type_bottom); unreached_code = 1; } else { /* Reach the end of one branch */ unreached_code = unreached_prev; } } else { /* Reach the end of both branches */ unreached_code = unreached_prev; } /* Copy the wrong code into the conditional */ COPY_exp (exp_if_stmt_false_code (e), wrong); set_parent_stmt (wrong, e); return (prev); } /* * STACK OF CURRENTLY ACTIVE ITERATION AND SWITCH STATEMENTS * * This stack is used to record a nested list of iteration and switch * statements in order to determine which statement a break, continue, * case or default refers to. */ STACK(EXP) crt_loop_stack = NULL_stack (EXP); /* * BEGIN A DO STATEMENT * * This routine begins the construction of a do statement. */ EXP begin_do_stmt(void) { EXP e; /* Construct the break and continue destinations */ EXP bk = begin_label_stmt (NULL_id, lex_break); EXP cn = begin_label_stmt (NULL_id, lex_continue); EXP lp = begin_label_stmt (NULL_id, lex_do); IDENTIFIER bk_lab = DEREF_id (exp_label_stmt_label (bk)); IDENTIFIER cn_lab = DEREF_id (exp_label_stmt_label (cn)); IDENTIFIER lp_lab = DEREF_id (exp_label_stmt_label (lp)); /* Construct the do statement */ MAKE_exp_do_stmt (type_void, NULL_exp, bk_lab, cn_lab, lp_lab, e); set_parent_stmt (bk, e); set_parent_stmt (cn, e); set_parent_stmt (lp, e); /* Add statement to loop stack */ PUSH_exp (e, crt_loop_stack); return (e); } /* * COMPLETE A DO STATEMENT * * This routine completes the construction of the do statement prev * using the statement body and the condition cond. */ EXP end_do_stmt(EXP prev, EXP body, EXP cond) { int uc; IDENTIFIER bk; EXP d = NULL_exp; /* Remove statement from loop stack */ EXP e; POP_exp (e, crt_loop_stack); UNUSED (e); /* Convert the condition to a boolean */ cond = check_cond (cond, &d, lex_do); if (!IS_NULL_exp (d)) cond = make_cond_decl (d, cond, 1); /* Find whether the condition is reached */ uc = unreached_code; if (uc) { IDENTIFIER cn = DEREF_id (exp_do_stmt_cont_lab (prev)); if (used_label (cn) == 1) { /* Reached using a continue */ uc = unreached_prev; } else { /* Report unreached code */ if (!unreached_last) { report (crt_loc, ERR_stmt_stmt_unreach ()); unreached_last = 1; } } } /* Find whether the following statement is reached */ bk = DEREF_id (exp_do_stmt_break_lab (prev)); COPY_loc (id_loc (bk), crt_loc); if (crt_condition == BOOL_TRUE) uc = 1; if (uc) { if (used_label (bk) == 1) { /* Can reach next statement using break */ uc = unreached_prev; } else { COPY_type (exp_type (prev), type_bottom); } } unreached_code = uc; /* Fill in the gaps in the do statement */ COPY_exp (exp_do_stmt_cond (prev), cond); COPY_exp (exp_do_stmt_body (prev), body); set_parent_stmt (body, prev); return (prev); } /* * BEGIN A FOR STATEMENT * * The construction of a for statement is in four stages, given by the * following four routines. A statement of the form: * * for (init; cond; step) body; * * is translated to: * * { * init; * while (cond) { * body; * cn : step; * } * } * * where an absent cond is replaced by 'true', except that 'continue' is * mapped to 'goto cn' (for the pre-ISO scoping rules the outer braces * do not form a scope - or rather they don't until after the init). * This routine, begin_for_stmt, creates the initial compound statement. * The next, init_for_stmt, adds init to this compound. The third, * cond_for_stmt, creates the while loop from cond and body. Finally * end_for_stmt fills in the body and completes the construction. */ EXP begin_for_stmt(void) { EXP e; int scope = 0; if (option (OPT_for_scope) == OPTION_ON) scope = 1; e = begin_compound_stmt (scope); return (e); } /* * ADD AN INITIAL STATEMENT TO A FOR STATEMENT * * This routine adds the initial statement pointed to by init to the for * statement prev (see above). If the initial statement is a declaration * then the declaration statement is only created during this routine * (init is the null expression). In this case init is set to point to * the declaration statement created. */ EXP init_for_stmt(EXP prev, EXP *init) { EXP stmt = *init; NAMESPACE ns = DEREF_nspace (exp_sequence_decl (prev)); if (!IS_NULL_nspace (ns)) { /* Add any declarations to the block */ MEMBER p = DEREF_member (nspace_last (ns)); MEMBER q = DEREF_member (nspace_prev (ns)); if (!EQ_member (p, q)) { /* Create declaration statement */ int vars = 0; EXP decl = make_decl_stmt (p, q, &vars); prev = extend_compound_stmt (prev, decl, 0); COPY_member (nspace_prev (ns), p); *init = decl; } } else { /* Defer declarations to enclosing block */ NAMESPACE cns = crt_namespace; ns = make_namespace (crt_func_id, nspace_block_tag, 0); push_namespace (ns); if (do_scope) dump_begin_scope (NULL_id, ns, cns, &crt_loc); COPY_nspace (exp_sequence_decl (prev), ns); IGNORE incr_value (OPT_VAL_statement_depth); } if (!IS_NULL_exp (stmt)) { /* Add the new statement */ prev = extend_compound_stmt (prev, stmt, 1); } adjusted_line = 0; return (prev); } /* * ADD A CONDITION TO A FOR STATEMENT * * This routine adds the condition and step statements, cond and step, to * the for statement prev (see above). Note that cond is wrapped in a * location expression. */ EXP cond_for_stmt(EXP prev, EXP cond, EXP step) { EXP e; TYPE t; LOCATION loc; EXP d = NULL_exp; /* Construct the break and continue destinations */ EXP bk = begin_label_stmt (NULL_id, lex_break); EXP cn = begin_label_stmt (NULL_id, lex_continue); EXP lp = begin_label_stmt (NULL_id, lex_for); IDENTIFIER bk_lab = DEREF_id (exp_label_stmt_label (bk)); IDENTIFIER cn_lab = DEREF_id (exp_label_stmt_label (cn)); IDENTIFIER lp_lab = DEREF_id (exp_label_stmt_label (lp)); /* Convert condition to a boolean */ bad_crt_loc++; loc = crt_loc; DESTROY_exp_location (destroy, t, crt_loc, cond, cond); cond = check_cond (cond, &d, lex_for); if (crt_condition == BOOL_FALSE) unreached_code = 1; crt_loc = loc; bad_crt_loc--; UNUSED (t); /* Deal with the step statement */ if (!IS_NULL_exp (step)) { if (record_location) { t = DEREF_type (exp_type (step)); MAKE_exp_location (t, stmt_loc, step, step); } IGNORE end_label_stmt (cn, step); } /* Construct the while statement */ MAKE_exp_while_stmt (type_void, cond, bk_lab, cn_lab, lp_lab, e); set_parent_stmt (bk, e); set_parent_stmt (cn, e); set_parent_stmt (lp, e); check_empty_stmt (lex_for); /* Add statement to loop stack */ PUSH_exp (e, crt_loop_stack); /* Add to for statement */ if (!IS_NULL_exp (d)) { EXP c = d; LIST (IDENTIFIER) cids = NULL_list (IDENTIFIER); while (!IS_NULL_exp (c) && IS_exp_decl_stmt (c)) { IDENTIFIER cid = DEREF_id (exp_decl_stmt_id (c)); CONS_id (cid, cids, cids); c = DEREF_exp (exp_decl_stmt_body (c)); } COPY_list (exp_while_stmt_cond_id (e), cids); d = make_cond_decl (d, e, 0); e = d; } e = add_compound_stmt (prev, e); return (e); } /* * END A FOR STATEMENT * * This routine completes the construction of the for statement prev by * adding the body statement, body (see above). */ EXP end_for_stmt(EXP prev, EXP body) { TYPE t; LIST (EXP) stmts = DEREF_list (exp_sequence_last (prev)); EXP stmt = DEREF_exp (HEAD_list (stmts)); if (IS_exp_location (stmt)) { EXP e = DEREF_exp (exp_location_arg (stmt)); e = end_while_stmt (e, body); t = DEREF_type (exp_type (e)); COPY_exp (exp_location_arg (stmt), e); } else { EXP e = end_while_stmt (stmt, body); t = DEREF_type (exp_type (e)); COPY_exp (HEAD_list (stmts), e); } prev = end_compound_stmt (prev); COPY_type (exp_type (prev), t); /* Mark for-init variables as private */ if (option (OPT_for_scope) == OPTION_WARN) { NAMESPACE ns = crt_namespace; MEMBER p = DEREF_member (nspace_last (ns)); MEMBER q = DEREF_member (nspace_prev (ns)); while (!EQ_member (p, q)) { IDENTIFIER id = DEREF_id (member_id (p)); if (!IS_NULL_id (id) && IS_id_variable (id)) { DECL_SPEC ds = DEREF_dspec (id_storage (id)); if (ds & dspec_auto) { ds |= dspec_private; COPY_dspec (id_storage (id), ds); } } p = DEREF_member (member_next (p)); } } return (prev); } /* * BEGIN A WHILE STATEMENT * * This routine begins the construction of a while statement with * condition cond. */ EXP begin_while_stmt(EXP cond) { EXP e; EXP d = NULL_exp; /* Construct the break and continue destinations */ EXP bk = begin_label_stmt (NULL_id, lex_break); EXP cn = begin_label_stmt (NULL_id, lex_continue); EXP lp = begin_label_stmt (NULL_id, lex_while); IDENTIFIER bk_lab = DEREF_id (exp_label_stmt_label (bk)); IDENTIFIER cn_lab = DEREF_id (exp_label_stmt_label (cn)); IDENTIFIER lp_lab = DEREF_id (exp_label_stmt_label (lp)); /* Convert the condition to a boolean */ cond = check_cond (cond, &d, lex_while); if (crt_condition == BOOL_FALSE) unreached_code = 1; /* Construct the while statement */ MAKE_exp_while_stmt (type_void, cond, bk_lab, cn_lab, lp_lab, e); set_parent_stmt (bk, e); set_parent_stmt (cn, e); set_parent_stmt (lp, e); check_empty_stmt (lex_while); /* Add statement to loop stack */ PUSH_exp (e, crt_loop_stack); /* Allow for condition declarations */ if (!IS_NULL_exp (d)) { EXP c = d; LIST (IDENTIFIER) cids = NULL_list (IDENTIFIER); while (!IS_NULL_exp (c) && IS_exp_decl_stmt (c)) { IDENTIFIER cid = DEREF_id (exp_decl_stmt_id (c)); CONS_id (cid, cids, cids); c = DEREF_exp (exp_decl_stmt_body (c)); } COPY_list (exp_while_stmt_cond_id (e), cids); d = make_cond_decl (d, e, 0); e = d; } return (e); } /* * COMPLETE A WHILE STATEMENT * * This routine completes the construction of the while statement prev * using the statement body. */ EXP end_while_stmt(EXP prev, EXP body) { /* Remove statement from loop stack */ EXP e; IDENTIFIER bk; POP_exp (e, crt_loop_stack); UNUSED (e); /* Find whether the end of the body is reached */ e = find_cond_stmt (prev); if (unreached_code) { IDENTIFIER cn = DEREF_id (exp_while_stmt_cont_lab (e)); if (used_label (cn) == 1) { /* Reached using a continue */ unreached_code = unreached_prev; } else { /* Check for unreached continuation statement */ LIST (EXP) step; EXP cont = DEREF_exp (id_label_stmt (cn)); cont = DEREF_exp (exp_label_stmt_body (cont)); step = DEREF_list (exp_sequence_first (cont)); step = TAIL_list (step); if (!IS_NULL_list (step)) { LOCATION loc; DEREF_loc (id_loc (cn), loc); report (loc, ERR_stmt_stmt_unreach ()); } } } /* Find whether the following statement is reached */ bk = DEREF_id (exp_while_stmt_break_lab (e)); COPY_loc (id_loc (bk), crt_loc); unreached_code = unreached_prev; if (crt_condition == BOOL_TRUE) { if (used_label (bk) != 1) { /* Infinite loop and no breaks */ COPY_type (exp_type (prev), type_bottom); COPY_type (exp_type (e), type_bottom); unreached_code = 1; } } /* Copy the body into the result */ COPY_exp (exp_while_stmt_body (e), body); set_parent_stmt (body, e); return (prev); } /* * CONSTRUCT A BREAK STATEMENT * * This routine constructs a break statement. Note that this must appear * inside an iteration or a switch statement. It is implemented as a jump * to the break label. */ EXP make_break_stmt(void) { LIST (EXP) st = LIST_stack (crt_loop_stack); if (!IS_NULL_list (st)) { IDENTIFIER lab; EXP stmt = DEREF_exp (HEAD_list (st)); unsigned tag = TAG_exp (stmt); if (tag == exp_while_stmt_tag) { lab = DEREF_id (exp_while_stmt_break_lab (stmt)); } else if (tag == exp_do_stmt_tag) { lab = DEREF_id (exp_do_stmt_break_lab (stmt)); } else { lab = DEREF_id (exp_switch_stmt_break_lab (stmt)); } return (make_jump_stmt (lab, stmt)); } report (crt_loc, ERR_stmt_break_bad ()); return (NULL_exp); } /* * CONSTRUCT A CONTINUE STATEMENT * * This routine constructs a continue statement. Note that this must * appear inside an iteration statement. It is implemented as a jump to * the continue label. */ EXP make_continue_stmt(void) { LIST (EXP) st = LIST_stack (crt_loop_stack); while (!IS_NULL_list (st)) { EXP stmt = DEREF_exp (HEAD_list (st)); unsigned tag = TAG_exp (stmt); if (tag == exp_switch_stmt_tag) { /* Switch statements don't count for continues */ /* EMPTY */ } else { /* Find continue destination label */ IDENTIFIER lab; if (tag == exp_while_stmt_tag) { lab = DEREF_id (exp_while_stmt_cont_lab (stmt)); } else { lab = DEREF_id (exp_do_stmt_cont_lab (stmt)); } return (make_jump_stmt (lab, stmt)); } st = TAIL_list (st); } report (crt_loc, ERR_stmt_cont_bad ()); return (NULL_exp); } /* * CHECK A RETURN EXPRESSION * * This routine checks the return expression a. In particular returning * a reference to a local variable is detected. It is also used to check * a throw expression (as indicated by op). */ EXP check_return_exp(EXP a, int op) { if (option (OPT_ptr_operator)) { EXP b = NULL_exp; DECL_SPEC ds = find_exp_linkage (a, &b, 1); if (ds & dspec_auto) { if (IS_exp_identifier (b)) { IDENTIFIER id = DEREF_id (exp_identifier_id (b)); if (IS_id_variable (id)) { TYPE t = DEREF_type (id_variable_type (id)); if (!IS_type_ref (t)) { report (crt_loc, ERR_stmt_return_auto (id, op)); } } } } } return (a); } /* * CONSTRUCT A RETURN EXPRESSION * * This routine constructs the value for the statement 'return a'. a can * be the null expression to indicate a plain 'return'. op is lex_return * to indicate explicit returns rather than the implicit return at the * end of the function. If the return is via a jump then the label is * assigned to lab. */ EXP find_return_exp(EXP a, IDENTIFIER *lab, int op) { TYPE r = crt_func_return; if (!IS_NULL_exp (a)) { /* Apply reference conversion */ a = convert_reference (a, REF_NORMAL); } if (IS_NULL_type (r)) { /* Not in a function definition */ IDENTIFIER id = crt_func_id; report (crt_loc, ERR_token_stmt_ret (id)); if (IS_NULL_exp (a)) { r = type_void; } else { a = convert_lvalue (a); r = DEREF_type (exp_type (a)); } } switch (TAG_type (r)) { case type_top_tag : { /* Function returns no value */ if (!IS_NULL_exp (a)) { IDENTIFIER id = crt_func_id; HASHID nm = DEREF_hashid (id_name (id)); unsigned tag = TAG_hashid (nm); if (tag == hashid_constr_tag) { report (crt_loc, ERR_class_ctor_result (id)); } else if (tag == hashid_destr_tag) { report (crt_loc, ERR_class_dtor_result (id)); *lab = find_postlude_label (); } else { TYPE ta = DEREF_type (exp_type (a)); /* Special case for void because C++ allows return (void)0 * and the like. */ if (eq_type_qual (ta, type_void, 1)) report (crt_loc, ERR_stmt_return_void_expr (id)); else report (crt_loc, ERR_stmt_return_none (id, r)); } } if (in_func_handler == 2) { /* In function-try-block handler */ IDENTIFIER id = crt_func_id; report (crt_loc, ERR_except_handle_return (id)); } break; } case type_bottom_tag : { /* Function should not return */ IDENTIFIER id = crt_func_id; if (IS_NULL_exp (a)) { report (crt_loc, ERR_stmt_return_bottom (id, r)); } else { report (crt_loc, ERR_stmt_return_none (id, r)); } break; } case type_token_tag : { /* Check for template types */ if (is_templ_type (r)) { MAKE_exp_op (r, op, a, NULL_exp, a); break; } goto default_lab; } default : default_lab : { /* Function returns a value */ if (IS_NULL_exp (a)) { IDENTIFIER id = crt_func_id; if (op == lex_return) { /* Explicit return statement */ report (crt_loc, ERR_stmt_return_void (id, r)); MAKE_exp_value (r, a); } else { /* Implicit fall out of function */ HASHID nm = DEREF_hashid (id_name (id)); DECL_SPEC ds = DEREF_dspec (id_storage (id)); if ((ds & dspec_main) && IS_hashid_name (nm)) { /* Implicit 'return 0' in main */ ERROR err = ERR_basic_start_main_fall (id, r); report (crt_loc, err); MAKE_exp_null (r, a); } else { report (crt_loc, ERR_stmt_return_fall (id, r)); MAKE_exp_value (r, a); } } } else { /* Convert to result type by initialisation */ ERROR err = NULL_err; a = init_assign (r, cv_none, a, &err); if (!IS_NULL_err (err)) { /* Bad return type */ err = init_error (err, 0); err = concat_error (err, ERR_stmt_return_conv ()); report (crt_loc, err); } if (crt_func_complex) { a = remove_temporary (a, NULL_exp); } a = check_return_exp (a, lex_return); } } } return (a); } /* * CONSTRUCT A RETURN STATEMENT * * This routine constructs the return statement 'return a'. op is as in * find_return_exp. */ EXP make_return_stmt(EXP a, int op) { IDENTIFIER lab = NULL_id; EXP e = find_return_exp (a, &lab, op); if (IS_NULL_id (lab)) { /* Construct return statement */ MAKE_exp_return_stmt (type_bottom, e, e); } else { /* Jump to postlude label */ e = make_jump_stmt (lab, NULL_exp); } unreached_code = 1; unreached_last = 0; return (e); } /* * FALL OUT OF A FUNCTION DEFINITION * * This routine is called at the end of a function definition to check * the implicit 'return' statement caused by falling out of the function. */ EXP fall_return_stmt(void) { EXP e = NULL_exp; if (!unreached_code) { TYPE ret = crt_func_return; if (!IS_NULL_type (ret) && !IS_type_top (ret)) { e = make_return_stmt (NULL_exp, lex_fall); } } return (e); } /* * CHECK A SWITCH CONTROL EXPRESSION * * This routine checks the switch control expression cont. pd is as in * check_cond and pb is used to return an enumeration or boolean control * expression. */ EXP check_control(EXP cont, EXP *pd, EXP *pb) { TYPE t; int ok = 1; unsigned c; EXP a = NULL_exp; /* Check for condition declarations */ if (IS_exp_decl_stmt (cont)) { IDENTIFIER id = DEREF_id (exp_decl_stmt_id (cont)); if (IS_id_variable (id)) { *pd = cont; t = DEREF_type (id_variable_type (id)); a = DEREF_exp (id_variable_init (id)); /* This doesn't count as a use of id */ MAKE_exp_identifier (t, id, qual_none, cont); } } /* Apply reference conversions to control */ cont = convert_reference (cont, REF_NORMAL); t = DEREF_type (exp_type (cont)); c = type_category (&t); if (IS_TYPE_INT (c)) { /* Integral types are allowed */ /* EMPTY */ } else if (IS_TYPE_TEMPL (c)) { /* Allow for template parameters */ MAKE_exp_op (t, lex_switch, cont, NULL_exp, cont); ok = 0; } else { /* Allow for overloading */ ok = 0; if (IS_TYPE_CLASS (c)) { ERROR err = NULL_err; a = convert_gen (CTYPE_INT, cont, &err); if (!IS_NULL_exp (a)) { if (!IS_NULL_err (err)) { err = concat_error (err, ERR_stmt_switch_conv ()); report (crt_loc, err); } cont = a; t = DEREF_type (exp_type (cont)); c = type_category (&t); ok = 1; } } if (!ok && !IS_TYPE_ERROR (c)) { report (crt_loc, ERR_stmt_switch_control (t)); } } /* Promote control construct */ if (ok) { if (IS_TYPE_ADDRESS (c)) { cont = convert_lvalue (cont); t = DEREF_type (exp_type (cont)); } if (IS_NULL_exp (a)) a = cont; if (is_const_exp (a, -1)) { /* Report constant control expressions */ report (crt_loc, ERR_stmt_switch_const ()); } if (IS_type_enumerate (t) || check_int_type (t, btype_bool)) { /* Store enumeration control expression */ *pb = cont; } t = promote_type (t); cont = convert_promote (t, cont); if (record_location) { /* Mark position of control expression */ MAKE_exp_location (t, crt_loc, cont, cont); } } return (cont); } /* * BEGIN A SWITCH STATEMENT * * This routine begins the construction of a switch statement with * controlling expression cont. During construction an enumeration or * boolean control expression is held in the body field. */ EXP begin_switch_stmt(EXP cont) { EXP e; EXP d = NULL_exp; EXP body = NULL_exp; EXP bk = begin_label_stmt (NULL_id, lex_break); IDENTIFIER bk_lab = DEREF_id (exp_label_stmt_label (bk)); cont = check_control (cont, &d, &body); MAKE_exp_switch_stmt (type_void, cont, body, 0, bk_lab, e); check_empty_stmt (lex_switch); /* Add statement to loop stack */ PUSH_exp (e, crt_loop_stack); /* Allow for condition declarations */ if (!IS_NULL_exp (d)) e = make_cond_decl (d, e, 0); /* The following statement is never reached */ unreached_code = 1; unreached_last = 0; unreached_fall = 1; return (e); } /* * DOES A VALUE APPEAR IN A CASE LIST? * * This routine checks whether the integer constant n appears in the list * of cases p. If so it returns the label corresponding to the case * statement drawn from the list of labels q. */ IDENTIFIER find_case(LIST (NAT) p, LIST (IDENTIFIER) q, NAT n) { while (!IS_NULL_list (p)) { NAT m = DEREF_nat (HEAD_list (p)); if (EQ_nat (n, m) || eq_nat (n, m)) { IDENTIFIER lab = DEREF_id (HEAD_list (q)); return (lab); } q = TAIL_list (q); p = TAIL_list (p); } return (NULL_id); } /* * COMPLETE A SWITCH STATEMENT * * This routine completes the construction of the switch statement prev * using the statement body. exhaust is true if the switch statement * is declared to be exhaustive. */ EXP end_switch_stmt(EXP prev, EXP body, int exhaust) { EXP bk; EXP cont; EXP stmt; unsigned ncases; IDENTIFIER bk_lab; LIST (NAT) cases; IDENTIFIER default_lab; LIST (IDENTIFIER) case_labs; /* Remove statement from loop stack */ EXP e; POP_exp (e, crt_loop_stack); UNUSED (e); /* Copy the body into the result */ stmt = find_cond_stmt (prev); cont = DEREF_exp (exp_switch_stmt_body (stmt)); bk_lab = DEREF_id (exp_switch_stmt_break_lab (stmt)); bk = DEREF_exp (id_label_stmt (bk_lab)); if (!unreached_code) { /* Add break statement to end if necessary */ if (!IS_NULL_exp (body) && IS_exp_sequence (body)) { EXP b = make_jump_stmt (bk_lab, stmt); body = add_compound_stmt (body, b); } } MAKE_exp_solve_stmt (type_void, body, e); CONS_exp (e, all_solve_stmts, all_solve_stmts); COPY_exp (exp_solve_stmt_parent (e), stmt); COPY_exp (exp_switch_stmt_body (stmt), e); set_parent_stmt (body, e); set_parent_stmt (bk, e); /* Check lists of cases */ cases = DEREF_list (exp_switch_stmt_cases (stmt)); cases = REVERSE_list (cases); ncases = LENGTH_list (cases); if (ncases == 0) { ERROR err; if (exhaust) { err = ERR_stmt_switch_exhaust_none (); } else { err = ERR_stmt_switch_case_none (); } report (crt_loc, err); } IGNORE check_value (OPT_VAL_switch_cases, (ulong) ncases); COPY_list (exp_switch_stmt_cases (stmt), cases); case_labs = DEREF_list (exp_switch_stmt_case_labs (stmt)); case_labs = REVERSE_list (case_labs); COPY_list (exp_switch_stmt_case_labs (stmt), case_labs); /* Check default label */ default_lab = DEREF_id (exp_switch_stmt_default_lab (stmt)); if (IS_NULL_id (default_lab)) { report (crt_loc, ERR_stmt_switch_no_default ()); } else { exhaust = 1; } /* Check switch jumps */ stmt = solve_switch (stmt); /* Check switches on enumerations and booleans */ if (!IS_NULL_exp (cont) && !exhaust) { TYPE t = DEREF_type (exp_type (cont)); if (IS_type_enumerate (t)) { ENUM_TYPE et = DEREF_etype (type_enumerate_defn (t)); LIST (IDENTIFIER) evals = DEREF_list (etype_values (et)); exhaust = 1; while (!IS_NULL_list (evals)) { IDENTIFIER eid = DEREF_id (HEAD_list (evals)); EXP en = DEREF_exp (id_enumerator_value (eid)); NAT n = DEREF_nat (exp_int_lit_nat (en)); IDENTIFIER lid = find_case (cases, case_labs, n); if (IS_NULL_id (lid)) { report (crt_loc, ERR_stmt_switch_case_enum (eid)); exhaust = 0; } evals = TAIL_list (evals); } } else { NAT n = small_nat [BOOL_FALSE]; IDENTIFIER lid = find_case (cases, case_labs, n); if (!IS_NULL_id (lid)) { n = small_nat [BOOL_TRUE]; lid = find_case (cases, case_labs, n); if (!IS_NULL_id (lid)) exhaust = 1; } } } /* Unreached code analysis */ if (unreached_code) { if (used_label (bk_lab) == 1) { /* Can reach the end using break */ unreached_code = unreached_prev; } else if (exhaust) { /* All cases covered by switch */ COPY_type (exp_type (stmt), type_bottom); COPY_type (exp_type (prev), type_bottom); } else { /* Not all cases covered by switch */ unreached_code = unreached_prev; } } COPY_int (exp_switch_stmt_exhaust (stmt), exhaust); return (prev); } /* * BEGIN A CASE STATEMENT * * This routine begins the construction of a case statement (or a jump * to a case statement if jump is true) with labelling value val. Note * that a case statement must appear inside a switch statement. */ EXP begin_case_stmt(EXP val, int jump) { /* Search for enclosing switch statement */ LIST (EXP) st = LIST_stack (crt_loop_stack); while (!IS_NULL_list (st)) { EXP stmt = DEREF_exp (HEAD_list (st)); if (IS_exp_switch_stmt (stmt)) { /* Switch statement found */ NAT n; EXP e; IDENTIFIER lab; IDENTIFIER old_lab; LIST (NAT) cases; ERROR err = NULL_err; int uc = unreached_code; LIST (IDENTIFIER) lbls; unsigned etag = null_tag; unsigned tag = TAG_exp (val); TYPE ta = DEREF_type (exp_type (val)); EXP cont = DEREF_exp (exp_switch_stmt_control (stmt)); TYPE tc = DEREF_type (exp_type (cont)); /* Cast val to control type */ while (tag == exp_paren_tag) { val = DEREF_exp (exp_paren_arg (val)); tag = TAG_exp (val); } if (tag == exp_int_lit_tag) { TYPE tb = ta; if (IS_type_enumerate (ta)) tb = promote_type (ta); etag = DEREF_unsigned (exp_int_lit_etag (val)); if (!eq_type (tb, tc)) { val = make_cast_nat (tc, val, &err, CAST_IMPLICIT); } } /* Check that val is a constant */ n = make_nat_exp (val, &err); if (!IS_NULL_err (err)) { err = concat_error (err, ERR_stmt_switch_case_const ()); report (crt_loc, err); } /* Check whether this value has been used previously */ lbls = DEREF_list (exp_switch_stmt_case_labs (stmt)); cases = DEREF_list (exp_switch_stmt_cases (stmt)); old_lab = find_case (cases, lbls, n); if (!IS_NULL_id (old_lab) && !jump) { DECL_SPEC info = DEREF_dspec (id_storage (old_lab)); if ((info & dspec_defn) && !is_error_nat (n)) { /* Duplicate case */ PTR (LOCATION) loc = id_loc (old_lab); report (crt_loc, ERR_stmt_switch_case_dup (n, loc)); old_lab = NULL_id; } } /* Construct the case statement */ if (jump) { e = make_goto_stmt (old_lab); lab = DEREF_id (exp_goto_stmt_label (e)); COPY_int (id_label_op (lab), lex_case); } else { e = begin_label_stmt (old_lab, lex_case); lab = DEREF_id (exp_label_stmt_label (e)); } COPY_exp (id_label_gotos (lab), stmt); if (IS_NULL_id (old_lab)) { CONS_id (lab, lbls, lbls); CONS_nat (n, cases, cases); COPY_list (exp_switch_stmt_case_labs (stmt), lbls); COPY_list (exp_switch_stmt_cases (stmt), cases); } if (jump) return (e); /* Check enumeration switches */ cont = DEREF_exp (exp_switch_stmt_body (stmt)); if (!IS_NULL_exp (cont)) { int ok = 0; TYPE t = DEREF_type (exp_type (cont)); if (IS_type_enumerate (t)) { /* Enumeration switch */ if (etag == exp_identifier_tag && eq_type (t, ta)) { ok = 1; } else { ENUM_TYPE et; IDENTIFIER eid; et = DEREF_etype (type_enumerate_defn (t)); eid = find_enumerator (et, n); if (!IS_NULL_id (eid)) ok = 1; } } else { /* Boolean switch */ unsigned long v = get_nat_value (n); if (v == 0 || v == 1) ok = 1; } if (!ok && !is_error_nat (n)) { err = ERR_stmt_switch_case_extra (n, t); report (crt_loc, err); } } /* Check for falling into a case */ if (!uc && unreached_fall && !suppress_fall) { report (crt_loc, ERR_stmt_label_fall (lex_case)); } suppress_fall = 0; return (e); } /* Iteration statements are ignored for cases */ st = TAIL_list (st); } report (crt_loc, ERR_stmt_label_case ()); return (NULL_exp); } /* * COMPLETE A CASE STATEMENT * * This routine completes the construction of the case statement prev * using the statement body. This is just a call to end_label_stmt. */ EXP end_case_stmt(EXP prev, EXP body) { return (end_label_stmt (prev, body)); } /* * BEGIN A DEFAULT STATEMENT * * This routine begins the construction of a default statement (or a jump * to a default statement if jump is true). Note that a default statement * must appear inside a switch statement. */ EXP begin_default_stmt(int jump) { /* Search for enclosing switch statement */ LIST (EXP) st = LIST_stack (crt_loop_stack); while (!IS_NULL_list (st)) { EXP stmt = DEREF_exp (HEAD_list (st)); if (IS_exp_switch_stmt (stmt)) { /* Switch statement found */ EXP e; IDENTIFIER lab; int uc = unreached_code; /* Check for previous default statements */ lab = DEREF_id (exp_switch_stmt_default_lab (stmt)); if (!IS_NULL_id (lab) && !jump) { DECL_SPEC info = DEREF_dspec (id_storage (lab)); if (info & dspec_defn) { /* Duplicate default statement */ PTR (LOCATION) loc = id_loc (lab); report (crt_loc, ERR_stmt_switch_default_dup (loc)); lab = NULL_id; } } /* Construct the default statement */ if (jump) { e = make_goto_stmt (lab); lab = DEREF_id (exp_goto_stmt_label (e)); COPY_int (id_label_op (lab), lex_default); } else { int exhaust; exhaust = DEREF_int (exp_switch_stmt_exhaust (stmt)); if (exhaust) { report (crt_loc, ERR_stmt_switch_exhaust_default ()); } e = begin_label_stmt (lab, lex_default); lab = DEREF_id (exp_label_stmt_label (e)); } COPY_exp (id_label_gotos (lab), stmt); COPY_id (exp_switch_stmt_default_lab (stmt), lab); /* Check for falling into default */ if (!jump) { if (!uc && unreached_fall && !suppress_fall) { report (crt_loc, ERR_stmt_label_fall (lex_default)); } suppress_fall = 0; } return (e); } /* Iteration statements are ignored for defaults */ st = TAIL_list (st); } report (crt_loc, ERR_stmt_label_default ()); return (NULL_exp); } /* * COMPLETE A DEFAULT STATEMENT * * This routine completes the construction of the default statement prev * using the statement body. This is just a call to end_label_stmt. */ EXP end_default_stmt(EXP prev, EXP body) { return (end_label_stmt (prev, body)); } /* * CURRENT #IF CONDITION * * This variable is used to keep track of the cumulative target dependent * condition at any point in the compilation. It takes the form * 'c1 && c2 && ... && cn' where c1, c2, ..., cn are the target dependent * conditions currently in scope. The case n = 0 gives the null expression. */ EXP crt_hash_cond = NULL_exp; /* * ADD A CONDITION TO A LIST OF CONDITIONS * * This routine adds the condition a to the list of conditions b by * forming 'b && a'. */ EXP make_if_cond(EXP a, EXP b) { if (!IS_NULL_exp (b)) { MAKE_exp_log_and (type_bool, b, a, a); } return (a); } /* * NEGATE THE LAST CONDITION IN A LIST * * This routine negates the last condition in the list of conditions a, * that is, it maps 'a1 && a2 && ... && an' to 'a1 && a2 && ... && !an'. */ EXP make_else_cond(EXP a) { if (!IS_NULL_exp (a)) { if (IS_exp_log_and (a)) { EXP b = DEREF_exp (exp_log_and_arg1 (a)); EXP c = DEREF_exp (exp_log_and_arg2 (a)); MAKE_exp_not (type_bool, c, c); MAKE_exp_log_and (type_bool, b, c, a); } else { MAKE_exp_not (type_bool, a, a); } } return (a); } /* * BEGIN A #IF STATEMENT * * This routine begins the construction of a #if statement. cond gives * the condition, which is already a boolean value. The case where cond * is a target independent constant have already been handled by the * preprocessor. right gives the code which is evaluated if this condition * is true. */ EXP begin_hash_if_stmt(EXP cond, EXP right) { /* Construct the result */ EXP e; MAKE_exp_hash_if (type_void, cond, right, NULL_exp, e); COPY_exp (exp_hash_if_last (e), e); set_parent_stmt (right, e); /* Unreached code analysis */ if (is_bottom (right)) { COPY_type (exp_type (e), type_bottom); } /* Next branch is reached */ unreached_code = unreached_prev; return (e); } /* * CONTINUE A #IF STATEMENT * * This routine continues the construction of a #if statement, prev, by * adding a #elif statement to the condition. cond gives the condition, * which is already a boolean value. The case where cond is a target * independent constant have already been handled by the preprocessor. * right gives the code which is evaluated if this condition, and none of * the previous conditions in prev, is true. */ EXP cont_hash_if_stmt(EXP prev, EXP cond, EXP right) { /* Map '#elif' to '#else #if' */ EXP e; EXP last = DEREF_exp (exp_hash_if_last (prev)); MAKE_exp_hash_if (type_void, cond, right, NULL_exp, e); COPY_exp (exp_hash_if_parent (e), last); COPY_exp (exp_hash_if_false_code (last), e); COPY_exp (exp_hash_if_last (prev), e); set_parent_stmt (right, e); /* Unreached code analysis */ if (!is_bottom (right)) { /* Reaching the end of a condition */ COPY_type (exp_type (prev), type_void); } /* Next branch is reached */ unreached_code = unreached_prev; return (prev); } /* * COMPLETE A #IF STATEMENT * * This routine completes the construction of a #if statement, prev. * wrong gives the code which is evaluated if none of the conditions in * prev is true. */ EXP end_hash_if_stmt(EXP prev, EXP wrong) { /* Copy wrong expression into result */ EXP last = DEREF_exp (exp_hash_if_last (prev)); COPY_exp (exp_hash_if_false_code (last), wrong); set_parent_stmt (wrong, last); /* Unreached code analysis */ if (is_bottom (wrong)) { /* Reachability is determined by the previous branches */ if (is_bottom (prev)) { unreached_code = 1; } else { unreached_code = unreached_prev; } } else { /* End of statement is reached */ COPY_type (exp_type (prev), type_void); unreached_code = unreached_prev; } return (prev); } /* * CREATE A FLOW CONTROL STATEMENT * * This routine creates a flow control statement indicating that the * statement body is either reached or unreached, depending on the * value of reach. */ EXP make_reach_stmt(EXP body, int reach) { EXP e; TYPE t; if (IS_NULL_exp (body)) { t = type_void; } else { unsigned tag = TAG_exp (body); if (tag == exp_decl_stmt_tag || tag == exp_label_stmt_tag) { /* Don't bother in these cases */ return (body); } t = DEREF_type (exp_type (body)); } if (reach) { MAKE_exp_reach (t, body, e); } else { MAKE_exp_unreach (t, body, e); } set_parent_stmt (body, e); return (e); } /* * CHECK A CONDITION DECLARATION TYPE * * This routine checks the type t of a condition declaration. */ TYPE make_cond_type(TYPE t) { int td = have_type_declaration; if (td != TYPE_DECL_NONE) { /* Check for type declarations */ if (td == TYPE_DECL_ELABORATE && found_elaborate_type) { /* This is allowed */ /* EMPTY */ } else { report (crt_loc, ERR_stmt_select_typedef ()); } } switch (TAG_type (t)) { case type_func_tag : { member_func_type (NULL_ctype, id_variable_tag, t); check_weak_func (t, 0); report (crt_loc, ERR_stmt_select_type (t)); MAKE_type_ptr (cv_none, t, t); break; } case type_array_tag : { report (crt_loc, ERR_stmt_select_type (t)); t = DEREF_type (type_array_sub (t)); MAKE_type_ptr (cv_none, t, t); break; } } return (t); } /* * BEGIN A CONDITION DECLARATION * * Condition declarations are declared in their own local scope and * brought into the outermost scope of the statements they control * using inject_cond. This routine begins the construction of such a * local scope. */ void begin_cond(void) { NAMESPACE ns = make_namespace (crt_func_id, nspace_dummy_tag, 0); push_namespace (ns); return; } /* * END A CONDITION DECLARATION * * This routine ends the local scope for a condition declaration, returning * a corresponding declaration statement. */ EXP end_cond(void) { int vars = 0; NAMESPACE ns = pop_namespace (); MEMBER p = DEREF_member (nspace_last (ns)); EXP cond = make_decl_stmt (p, NULL_member, &vars); return (cond); } /* * INJECT A CONDITION DECLARATION INTO THE CURRENT SCOPE * * This routine injects the condition declaration cond into the current * scope, prev, returning the result. It is also used to deal with * declarations in for-init statements. */ EXP inject_cond(EXP prev, EXP cond) { if (!IS_NULL_exp (cond)) { NAMESPACE ns = crt_namespace; while (IS_exp_decl_stmt (cond)) { IDENTIFIER id = DEREF_id (exp_decl_stmt_id (cond)); IGNORE redeclare_id (ns, id); cond = DEREF_exp (exp_decl_stmt_body (cond)); } } return (prev); } /* * CONSTRUCT AN ASM STATEMENT * * This routine constructs an asm statement from the string literal * expression e. Note that the semantics of asm are totally implementation * dependent, so anything goes. */ EXP make_asm(EXP e, LIST (EXP) args) { STRING s = DEREF_str (exp_string_lit_str (e)); if (!IS_NULL_list (args)) { report (crt_loc, ERR_dcl_asm_args ()); args = convert_args (args); } MAKE_exp_assembler (type_void, s, args, e); report (crt_loc, ERR_dcl_asm_ti ()); return (e); }