aboutsummaryrefslogtreecommitdiff
path: root/gcc/java/check-init.c
diff options
context:
space:
mode:
authorPer Bothner <bothner@gcc.gnu.org>1999-01-06 09:49:36 -0800
committerPer Bothner <bothner@gcc.gnu.org>1999-01-06 09:49:36 -0800
commit4ba9a1aa641fb88c0ede517de0cf4e86cd305b58 (patch)
tree93b2e15837f07366e2cd099a9483028a58ce79a3 /gcc/java/check-init.c
parentdb84cc462232ae771283b4a81ef151e0008fa4d1 (diff)
downloadgcc-4ba9a1aa641fb88c0ede517de0cf4e86cd305b58.zip
gcc-4ba9a1aa641fb88c0ede517de0cf4e86cd305b58.tar.gz
gcc-4ba9a1aa641fb88c0ede517de0cf4e86cd305b58.tar.bz2
check-init.c: New file.
* check-init.c: New file. Checks for definite assignment. Various minor other changes; see ChangeLog. From-SVN: r24522
Diffstat (limited to 'gcc/java/check-init.c')
-rw-r--r--gcc/java/check-init.c699
1 files changed, 699 insertions, 0 deletions
diff --git a/gcc/java/check-init.c b/gcc/java/check-init.c
new file mode 100644
index 0000000..e1630d4
--- /dev/null
+++ b/gcc/java/check-init.c
@@ -0,0 +1,699 @@
+/* Code to test for "definitive assignment".
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+Java and all Java-based marks are trademarks or registered trademarks
+of Sun Microsystems, Inc. in the United States and other countries.
+The Free Software Foundation is independent of Sun Microsystems, Inc. */
+
+/* Written by Per Bothner <bothner@cygnus.com>, January 1999. */
+
+#include "config.h"
+#include "system.h"
+#include "tree.h"
+#include "java-tree.h"
+#include "toplev.h" /* Needed for fatal. */
+
+/* The basic idea is that we assign each local variable declaration
+ an index, and then we pass around bitstrings, where the i'th bit
+ is set if decl whose DECL_BIT_INDEX is i is definitely assigned. */
+
+/* One segment of a bitstring. */
+typedef unsigned int word;
+
+/* Pointer to a bitstring. */
+typedef word *words;
+
+/* For a local VAR_DECL, holds the index into a words bitstring that
+ specifies if this decl is definitively assigned.
+ A DECL_BIT_INDEX of -1 means we no longer care. */
+#define DECL_BIT_INDEX(DECL) DECL_FIELD_SIZE(DECL)
+
+/* Number of locals variables currently active. */
+int num_current_locals = 0;
+
+/* The index of the first local variable in the current block.
+
+ The variables whose DECL_BIT_INDEX are in the range from
+ start_current_locals (inclusive) up to num_current_locals (exclusive)
+ are declared in the "current" block. If there is a loop or branch
+ form, we set start_current_locals to num_current_locals to indicate
+ there is no current block.
+
+ The point is that if a variable in the current block is set,
+ there are no other control paths that we have to worry about.
+ Hence, we can remove it from the set of variables we are
+ checking, making its bit index available for some other variable.
+ For simplicity, we only do that if the variable's bit index
+ is (num_current_locals-1); freeing up its bit index is then
+ just a simple matter of decrementing num_current_locals.
+ The reason this is worth doing is that it is simple, and
+ allows us to use short (usually one-word) bit-strings,
+ even for methods with thousands of local variables, as
+ long as most of them are initialized immediately after or in
+ their declaration. */
+int start_current_locals = 0;
+
+int num_current_words = 1;
+
+static tree wfl;
+
+#define COPYN(DST, SRC, NWORDS) memcpy (DST, SRC, NWORDS * sizeof(word))
+#define COPY(DST, SRC) COPYN (DST, SRC, num_current_words)
+
+#define SET_ALL(DST) memset (DST, ~0, num_current_words * sizeof(word))
+#define CLEAR_ALL(DST) memset (DST, 0, num_current_words * sizeof(word))
+
+#define INTERSECTN(DST, SRC1, SRC2, N) \
+ do { int n = N; \
+ while (--n >= 0) DST[n] = SRC1[n] & SRC2[n]; \
+ } while (0)
+
+#define UNION(DST, SRC1, SRC2) \
+ UNIONN (DST, SRC1, SRC2, num_current_words)
+
+#define UNIONN(DST, SRC1, SRC2, N) \
+ do { int n = N; \
+ while (--n >= 0) DST[n] = SRC1[n] | SRC2[n]; \
+ } while (0)
+
+#define INTERSECT(DST, SRC1, SRC2) \
+ INTERSECTN (DST, SRC1, SRC2, num_current_words)
+
+#define WORD_SIZE ((unsigned int)(sizeof(word) * 8))
+
+static void check_bool_init PROTO ((tree, words, words, words));
+static void check_init PROTO ((tree, words));
+
+#if 0
+#define ALLOC_WORDS(NUM) ((word*) xmalloc ((NUM) * sizeof (word)))
+#define FREE_WORDS(PTR) (free (PTR))
+#else
+#define ALLOC_WORDS(NUM) ((word*)alloca ((NUM) * sizeof (word)))
+#define FREE_WORDS(PTR) ((void)0)
+#endif
+
+#define SET_P(WORDS, BIT) \
+ (WORDS[BIT / WORD_SIZE] & (1 << (BIT % WORD_SIZE)))
+
+#define CLEAR_BIT(WORDS, BIT) \
+ (WORDS[BIT / WORD_SIZE] &= ~ (1 << (BIT % WORD_SIZE)))
+
+#define SET_BIT(WORDS, BIT) \
+ (WORDS[BIT / WORD_SIZE] |= (1 << (BIT % WORD_SIZE)))
+
+#define WORDS_NEEDED(BITS) (((BITS)+(WORD_SIZE-1))/(WORD_SIZE))
+
+/* Check a conditional form (TEST_EXP ? THEN_EXP : ELSE_EXP) for
+ definite assignment.
+ BEFORE, WHEN_FALSE, and WHEN_TRUE are as in check_bool_init. */
+
+void
+check_cond_init (test_exp, then_exp, else_exp,
+ before, when_false, when_true)
+ tree test_exp, then_exp, else_exp;
+ words before, when_false, when_true;
+{
+ words tmp = ALLOC_WORDS (6 * num_current_words);
+ words test_false = tmp;
+ words test_true = tmp + num_current_words;
+ words then_false = tmp + 2 * num_current_words;
+ words then_true = tmp + 3 * num_current_words;
+ words else_false = tmp + 4 * num_current_words;
+ words else_true = tmp + 5 * num_current_words;
+ check_bool_init (test_exp, before, test_false, test_true);
+ check_bool_init (then_exp, test_true, then_false, then_true);
+ check_bool_init (else_exp, test_false, else_false, else_true);
+ INTERSECT (when_false, then_false, else_false);
+ INTERSECT (when_true, then_true, else_true);
+ FREE_WORDS (tmp);
+}
+
+/* Check a boolean binary form CODE (EXP0, EXP1),
+ where CODE is one of EQ_EXPR, BIT_AND_EXPR, or BIT_OR_EXPR.
+ BEFORE, WHEN_FALSE, and WHEN_TRUE are as in check_bool_init. */
+
+static void
+check_bool2_init (code, exp0, exp1, before, when_false, when_true)
+ enumtree_code code; tree exp0, exp1;
+ words before, when_false, when_true;
+{
+ word buf[4];
+ words tmp = num_current_words <= 1 ? buf
+ : ALLOC_WORDS (4 * num_current_words);
+ words when_false_0 = tmp;
+ words when_false_1 = tmp+num_current_words;
+ words when_true_0 = tmp+2*num_current_words;
+ words when_true_1 = tmp+3*num_current_words;
+ check_bool_init (exp0, before, when_false_0, when_true_0);
+ INTERSECT (before, when_false_0, when_true_0);
+ check_bool_init (exp1, before, when_false_1, when_true_1);
+
+ INTERSECT (before, when_false_1, when_true_1);
+
+ if (code == EQ_EXPR)
+ {
+ /* Now set:
+ * when_true = (when_false_1 INTERSECTION when_true_1)
+ * UNION (when_true_0 INTERSECTION when_false_1)
+ * UNION (when_false_0 INTERSECTION when_true_1);
+ * using when_false and before as temporary working areas. */
+ INTERSECT (when_true, when_true_0, when_false_1);
+ INTERSECT (when_false, when_true_0, when_false_1);
+ UNION (when_true, when_true, when_false);
+ UNION (when_true, when_true, before);
+
+ /* Now set:
+ * when_false = (when_false_1 INTERSECTION when_true_1)
+ * UNION (when_true_0 INTERSECTION when_true_1)
+ * UNION (when_false_0 INTERSECTION when_false_1);
+ * using before as a temporary working area. */
+ INTERSECT (when_false, when_true_0, when_true_1);
+ UNION (when_false, when_false, before);
+ INTERSECT (before, when_false_0, when_false_1);
+ UNION (when_false, when_false, before);
+ }
+ else if (code == BIT_AND_EXPR)
+ {
+ UNION (when_true, when_true_0, when_true_1);
+ INTERSECT (when_false, when_false_0, when_false_1);
+ UNION (when_false, before);
+ }
+ else /* if (code == BIT_OR_EXPR) */
+ {
+ UNION (when_false, when_false_0, when_false_1);
+ INTERSECT (when_true, when_true_0, when_true_1);
+ UNION (when_true, before);
+ }
+
+ if (tmp != buf)
+ FREE_WORDS (tmp);
+}
+
+/* Check a boolean expression EXP for definite assignment.
+ BEFORE is the set of variables definitely assigned before the conditional.
+ (This bitstring may be modified arbitrarily in this function.)
+ On output, WHEN_FALSE is the set of variables definitely assigned after
+ the conditional when the conditional is false.
+ On output, WHEN_TRUE is the set of variables definitely assigned after
+ the conditional when the conditional is true.
+ (WHEN_FALSE and WHEN_TRUE are overwriten with initial values ignored.)
+ (None of BEFORE, WHEN_FALSE, or WHEN_TRUE can overlap, as they may
+ be used as temporary working areas. */
+*/
+
+static void
+check_bool_init (exp, before, when_false, when_true)
+ tree exp;
+ words before, when_false, when_true;
+{
+ switch (TREE_CODE (exp))
+ {
+ case COND_EXPR:
+ check_cond_init (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
+ TREE_OPERAND (exp, 2),
+ before, when_false, when_true);
+ return;
+
+ case TRUTH_ANDIF_EXPR:
+ check_cond_init (TREE_OPERAND (exp, 0),
+ TREE_OPERAND (exp, 1), boolean_false_node,
+ before, when_false, when_true);
+ return;
+ case TRUTH_ORIF_EXPR:
+ check_cond_init (TREE_OPERAND (exp, 0),
+ boolean_true_node, TREE_OPERAND (exp, 1),
+ before, when_false, when_true);
+ return;
+ case TRUTH_NOT_EXPR:
+ check_bool_init (TREE_OPERAND (exp, 0), before, when_true, when_false);
+ return;
+ case MODIFY_EXPR:
+ {
+ tree tmp = TREE_OPERAND (exp, 0);
+ if (TREE_CODE (tmp) == VAR_DECL && ! FIELD_STATIC (tmp))
+ {
+ int index;
+ check_bool_init (TREE_OPERAND (exp, 1), before,
+ when_false, when_true);
+ index = DECL_BIT_INDEX (tmp);
+ if (index >= 0)
+ {
+ SET_BIT (when_false, index);
+ SET_BIT (when_true, index);
+ }
+ break;
+ }
+ }
+ goto do_default;
+
+ case BIT_AND_EXPR:
+ case BIT_OR_EXPR:
+ case EQ_EXPR:
+ check_bool2_init (TREE_CODE (exp),
+ TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
+ before, when_false, when_true);
+ return;
+
+ case BIT_XOR_EXPR:
+ case NE_EXPR:
+ /* Just like EQ_EXPR, but switch when_true and when_false. */
+ check_bool2_init (EQ_EXPR, TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
+ before, when_true, when_false);
+
+ return;
+
+ case INTEGER_CST:
+ if (integer_zerop (exp))
+ {
+ SET_ALL (when_true);
+ COPY (when_false, before);
+ }
+ else
+ {
+ SET_ALL (when_false);
+ COPY (when_true, before);
+ }
+ break;
+ default:
+ do_default:
+ check_init (exp, before);
+ COPY (when_false, before);
+ COPY (when_true, before);
+ }
+}
+
+/* Used to keep track of control flow branches. */
+
+struct alternatives
+{
+ struct alternatives *outer;
+
+ /* The value of num_current_locals at the start of this compound. */
+ int num_locals;
+
+ /* The value of the "before" set at the start of the control stucture.
+ Used for SWITCH_EXPR but not set for LABELED_BLOCK_EXPR. */
+ words saved;
+
+ int save_start_current_locals;
+
+ /* If num_current_words==1, combined==&one_word, for efficiency. */
+ word one_word;
+
+ /* The intersection of the "after" sets from previous branches. */
+ words combined;
+
+ tree block;
+};
+
+struct alternatives * alternatives = NULL;
+
+#define BEGIN_ALTERNATIVES(before, current) \
+{ \
+ current.saved = NULL; \
+ current.num_locals = num_current_locals; \
+ current.combined = num_current_words <= 1 ? &current.one_word \
+ : ALLOC_WORDS (num_current_words); \
+ SET_ALL (current.combined); \
+ current.outer = alternatives; \
+ alternatives = &current; \
+ current.save_start_current_locals = start_current_locals; \
+ start_current_locals = num_current_locals; \
+}
+
+static void
+done_alternative (after, current)
+ words after;
+ struct alternatives *current;
+{
+ INTERSECTN (current->combined, current->combined, after,
+ WORDS_NEEDED (current->num_locals));
+}
+
+#define END_ALTERNATIVES(after, current) \
+{ \
+ alternatives = current.outer; \
+ COPY (after, current.combined); \
+ if (current.combined != &current.one_word) \
+ FREE_WORDS (current.combined); \
+ start_current_locals = current.save_start_current_locals; \
+}
+
+/* Check for (un)initialized local variables in EXP.
+*/
+
+static void
+check_init (exp, before)
+ tree exp;
+ words before;
+{
+ tree tmp;
+ again:
+ switch (TREE_CODE (exp))
+ {
+ case VAR_DECL:
+ if (! FIELD_STATIC (exp) && DECL_NAME (exp) != NULL_TREE)
+ {
+ int index = DECL_BIT_INDEX (exp);
+ if (index >= 0 && ! SET_P (before, index))
+ {
+#if 1
+ parse_error_context (wfl,
+ "Variable `%s' may not have been initialized"
+ , IDENTIFIER_POINTER (DECL_NAME (exp)));
+#else
+ error_with_decl (exp, "variable may be used uninitialized");
+#endif
+ /* Suppress further errors. */
+ DECL_BIT_INDEX (exp) = -1;
+ }
+ }
+ break;
+ case MODIFY_EXPR:
+ tmp = TREE_OPERAND (exp, 0);
+ if (TREE_CODE (tmp) == VAR_DECL && ! FIELD_STATIC (tmp))
+ {
+ int index;
+ check_init (TREE_OPERAND (exp, 1), before);
+ index = DECL_BIT_INDEX (tmp);
+ if (index >= 0)
+ SET_BIT (before, index);
+ /* Minor optimization. See comment for start_current_locals. */
+ if (index >= start_current_locals
+ && index == num_current_locals - 1)
+ {
+ num_current_locals--;
+ DECL_BIT_INDEX (tmp) = -1;
+ }
+ break;
+ }
+ else
+ goto binop;
+ case BLOCK:
+ if (BLOCK_EXPR_BODY (exp))
+ {
+ tree decl = BLOCK_EXPR_DECLS (exp);
+ int words_needed;
+ word* tmp;
+ int i;
+ int save_start_current_locals = start_current_locals;
+ int save_num_current_words = num_current_words;
+ start_current_locals = num_current_locals;
+ for (; decl != NULL_TREE; decl = TREE_CHAIN (decl))
+ {
+ DECL_BIT_INDEX (decl) = num_current_locals++;
+ }
+ words_needed = WORDS_NEEDED (num_current_locals);
+ if (words_needed > num_current_words)
+ {
+ tmp = ALLOC_WORDS (words_needed);
+ COPY (tmp, before);
+ num_current_words = words_needed;
+ }
+ else
+ tmp = before;
+ for (i = start_current_locals; i < num_current_locals; i++)
+ CLEAR_BIT (tmp, i);
+ check_init (BLOCK_EXPR_BODY (exp), tmp);
+ num_current_locals = start_current_locals;
+ start_current_locals = save_start_current_locals;
+ if (tmp != before)
+ {
+ num_current_words = save_num_current_words;
+ COPY (before, tmp);
+ FREE_WORDS (tmp);
+ }
+ }
+ break;
+ case LOOP_EXPR:
+ {
+ struct alternatives alt;
+ BEGIN_ALTERNATIVES (before, alt);
+ alt.block = exp;
+ check_init (TREE_OPERAND (exp, 0), before);
+ done_alternative (before, &alt);
+ END_ALTERNATIVES (before, alt);
+ return;
+ }
+ case EXIT_EXPR:
+ {
+ struct alternatives *alt = alternatives;
+ words tmp = ALLOC_WORDS (2 * num_current_words);
+ words when_true = tmp;
+ words when_false = tmp + num_current_words;
+#ifdef ENABLE_CHECKING
+ if (TREE_CODE (alt->block) != LOOP_EXPR)
+ fatal ("internal error in check-init: EXIT_EXPR not in LOOP_EXPR");
+#endif
+ check_bool_init (TREE_OPERAND (exp, 0), before, when_false, when_true);
+ done_alternative (when_true, alt);
+ COPY (before, when_false);
+ FREE_WORDS (tmp);
+ return;
+ }
+ case LABELED_BLOCK_EXPR:
+ {
+ struct alternatives alt;
+ BEGIN_ALTERNATIVES (before, alt);
+ alt.block = exp;
+ check_init (LABELED_BLOCK_BODY (exp), before);
+ done_alternative (before, &alt);
+ END_ALTERNATIVES (before, alt);
+ return;
+ }
+ case EXIT_BLOCK_EXPR:
+ {
+ tree block = TREE_OPERAND (exp, 0);
+ struct alternatives *alt = alternatives;
+ while (alt->block != block)
+ alt = alt->outer;
+ done_alternative (before, alt);
+ SET_ALL (before);
+ return;
+ }
+ case SWITCH_EXPR:
+ {
+ struct alternatives alt;
+ check_init (TREE_OPERAND (exp, 0), before);
+ BEGIN_ALTERNATIVES (before, alt);
+ alt.saved = ALLOC_WORDS (num_current_words);
+ COPY (alt.saved, before);
+ alt.block = exp;
+ check_init (TREE_OPERAND (exp, 1), before);
+ done_alternative (before, &alt);
+ FREE_WORDS (alt.saved);
+ END_ALTERNATIVES (before, alt);
+ return;
+ }
+ case CASE_EXPR:
+ case DEFAULT_EXPR:
+ {
+ int i;
+ struct alternatives *alt = alternatives;
+ while (TREE_CODE (alt->block) != SWITCH_EXPR)
+ alt = alt->outer;
+ COPYN (before, alt->saved, alt->num_locals);
+ for (i = alt->num_locals; i < num_current_locals; i++)
+ CLEAR_BIT (before, i);
+ break;
+ }
+
+ case CLEANUP_POINT_EXPR:
+ {
+ struct alternatives alt;
+ BEGIN_ALTERNATIVES (before, alt);
+ CLEAR_ALL (alt.combined);
+ check_init (TREE_OPERAND (exp, 0), before);
+ UNION (alt.combined, alt.combined, before);
+ END_ALTERNATIVES (before, alt);
+ }
+ return;
+ case WITH_CLEANUP_EXPR:
+ {
+ struct alternatives *alt = alternatives;
+#ifdef ENABLE_CHECKING
+ if (TREE_CODE (alt->block) != CLEANUP_POIN_EXPR)
+ fatal ("internal error in check-init: WITH_CLEANUP_EXPR not in CLEANUP_POINT_EXPR");
+#endif
+ check_init (TREE_OPERAND (exp, 0), before);
+ UNION (alt->combined, alt->combined, before);
+ check_init (TREE_OPERAND (exp, 2), alt->combined);
+ return;
+ }
+
+ case TRY_EXPR:
+ {
+ tree try_clause = TREE_OPERAND (exp, 0);
+ tree clause = TREE_OPERAND (exp, 1);
+ tree finally = TREE_OPERAND (exp, 2);
+ words save = ALLOC_WORDS (num_current_words);
+ words tmp = ALLOC_WORDS (num_current_words);
+ struct alternatives alt;
+ BEGIN_ALTERNATIVES (before, alt);
+ COPY (save, before);
+ COPY (tmp, save);
+ check_init (try_clause, tmp);
+ done_alternative (tmp, &alt);
+ for ( ; clause != NULL_TREE; clause = TREE_CHAIN (clause))
+ {
+ tree catch_clause = TREE_OPERAND (clause, 0);
+ COPY (tmp, save);
+ check_init (catch_clause, tmp);
+ done_alternative (tmp, &alt);
+ }
+ if (finally != NULL_TREE)
+ {
+ check_init (finally, save);
+ UNION (alt.combined, alt.combined, save);
+ }
+ FREE_WORDS (tmp);
+ FREE_WORDS (save);
+ END_ALTERNATIVES (before, alt);
+ }
+ return;
+
+ case RETURN_EXPR:
+ case THROW_EXPR:
+ if (TREE_OPERAND (exp, 0))
+ check_init (TREE_OPERAND (exp, 0), before);
+ SET_ALL (before);
+ return;
+
+ case ERROR_MARK:
+ SET_ALL (before);
+ break;
+
+ case COND_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ {
+ words tmp = ALLOC_WORDS (2 * num_current_words);
+ words when_true = tmp;
+ words when_false = tmp + num_current_words;
+ check_bool_init (exp, before, when_false, when_true);
+ INTERSECT (before, when_false, when_true);
+ FREE_WORDS (tmp);
+ }
+ break;
+ case UNARY_PLUS_EXPR:
+ case NEGATE_EXPR:
+ case TRUTH_NOT_EXPR:
+ case BIT_NOT_EXPR:
+ case CONVERT_EXPR:
+ case COMPONENT_REF:
+ case NOP_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case INDIRECT_REF:
+ case ADDR_EXPR:
+ case SAVE_EXPR:
+ case PREDECREMENT_EXPR:
+ case PREINCREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ case NON_LVALUE_EXPR:
+ case INSTANCEOF_EXPR:
+ /* Avoid needless recursion. */
+ exp = TREE_OPERAND (exp, 0);
+ goto again;
+
+ case COMPOUND_EXPR:
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ case MULT_EXPR:
+ case TRUNC_DIV_EXPR:
+ case TRUNC_MOD_EXPR:
+ case RDIV_EXPR:
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ case URSHIFT_EXPR:
+ case BIT_AND_EXPR:
+ case BIT_XOR_EXPR:
+ case BIT_IOR_EXPR:
+ case EQ_EXPR:
+ case NE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case LT_EXPR:
+ case LE_EXPR:
+ case ARRAY_REF:
+ binop:
+ check_init (TREE_OPERAND (exp, 0), before);
+ /* Avoid needless recursion, especially for COMPOUND_EXPR. */
+ exp = TREE_OPERAND (exp, 1);
+ goto again;
+
+ case PARM_DECL:
+ case RESULT_DECL:
+ case FUNCTION_DECL:
+ case INTEGER_CST:
+ case REAL_CST:
+ case STRING_CST:
+ break;
+
+ case NEW_CLASS_EXPR:
+ case CALL_EXPR:
+ {
+ tree x = TREE_OPERAND (exp, 1);
+ check_init (TREE_OPERAND (exp, 0), before);
+
+ for ( ; x != NULL_TREE; x = TREE_CHAIN (x))
+ check_init (TREE_VALUE (x), before);
+ }
+ break;
+
+ case NEW_ARRAY_INIT:
+ {
+ tree x = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0));
+ for ( ; x != NULL_TREE; x = TREE_CHAIN (x))
+ check_init (TREE_VALUE (x), before);
+ }
+ break;
+
+ case EXPR_WITH_FILE_LOCATION:
+ {
+ char *saved_input_filename = input_filename;
+ tree saved_wfl = wfl;
+ tree body = EXPR_WFL_NODE (exp);
+ int saved_lineno = lineno;
+ if (body == empty_stmt_node)
+ break;
+ wfl = exp;
+ input_filename = EXPR_WFL_FILENAME (exp);
+ lineno = EXPR_WFL_LINENO (exp);
+ check_init (body, before);
+ input_filename = saved_input_filename;
+ lineno = saved_lineno;
+ wfl = saved_wfl;
+ }
+ break;
+
+ default:
+ fatal ("internal error in check-init: tree code not implemented: %s",
+ tree_code_name [(int) TREE_CODE (exp)]);
+ }
+}
+
+void
+check_for_initialization (body)
+ tree body;
+{
+ word before = 0;
+ check_init (body, &before);
+}