/* Code to test for "definitive [un]assignment".
- Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation,
+ Inc.
-This program is free software; you can redistribute it and/or modify
+This file is part of GCC.
+
+GCC 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,
+GCC 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
+along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "tree.h"
#include "flags.h" /* Needed for optimize. */
#include "java-tree.h"
static int num_current_words;
-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 WORD_SIZE ((unsigned int)(sizeof(word) * BITS_PER_UNIT))
-static void check_bool_init PARAMS ((tree, words, words, words));
-static void check_init PARAMS ((tree, words));
-static void check_cond_init PARAMS ((tree, tree, tree, words, words, words));
-static void check_bool2_init PARAMS ((enum tree_code, tree, tree, words, words, words));
+static void check_bool_init (tree, words, words, words);
+static void check_init (tree, words);
+static void check_cond_init (tree, tree, tree, words, words, words);
+static void check_bool2_init (enum tree_code, tree, tree, words, words, words);
struct alternatives;
-static void done_alternative PARAMS ((words, struct alternatives *));
-static tree get_variable_decl PARAMS ((tree));
-static void final_assign_error PARAMS ((tree));
-static void check_final_reassigned PARAMS ((tree, words));
+static void done_alternative (words, struct alternatives *);
+static tree get_variable_decl (tree);
+static void final_assign_error (tree);
+static void check_final_reassigned (tree, words);
-#define ALLOC_WORDS(NUM) ((word*) xmalloc ((NUM) * sizeof (word)))
+#define ALLOC_WORDS(NUM) (xmalloc ((NUM) * sizeof (word)))
#define FREE_WORDS(PTR) (free (PTR))
/* DECLARE_BUFFERS is used to allocate NUMBUFFER bit sets, each of
Return the declaration or NULL_TREE if no interesting declaration. */
static tree
-get_variable_decl (exp)
- tree exp;
+get_variable_decl (tree exp)
{
+ /* A static field can be wrapped in a COMPOUND_EXPR where the first
+ argument initializes the class. */
+ if (TREE_CODE (exp) == COMPOUND_EXPR)
+ exp = extract_field_decl (exp);
+
if (TREE_CODE (exp) == VAR_DECL)
{
if (! TREE_STATIC (exp) || FIELD_FINAL (exp))
return op1;
}
}
+ else if (TREE_CODE (exp) == INDIRECT_REF)
+ {
+ /* For indirect dispatch, look for an expression of the form
+ (indirect_ref (+ (array_ref otable <N>) this)).
+ FIXME: it would probably be better to generate a JAVA_FIELD_REF
+ expression that gets converted to OTABLE access at
+ gimplification time. */
+ exp = TREE_OPERAND (exp, 0);
+ if (TREE_CODE (exp) == PLUS_EXPR)
+ {
+ tree op0 = TREE_OPERAND (exp, 0);
+ STRIP_NOPS (op0);
+ if (TREE_CODE (op0) == ARRAY_REF)
+ {
+ tree table = TREE_OPERAND (op0, 0);
+ if (TREE_CODE (table) == VAR_DECL
+ && DECL_LANG_SPECIFIC (table)
+ && DECL_OWNER (table)
+ && TYPE_OTABLE_DECL (DECL_OWNER (table)) == table)
+ {
+ HOST_WIDE_INT index
+ = TREE_INT_CST_LOW (TREE_OPERAND (op0, 1));
+ tree otable_methods
+ = TYPE_OTABLE_METHODS (DECL_OWNER (table));
+ tree element;
+ for (element = otable_methods;
+ element;
+ element = TREE_CHAIN (element))
+ {
+ if (index == 1)
+ {
+ tree purpose = TREE_PURPOSE (element);
+ if (TREE_CODE (purpose) == FIELD_DECL)
+ return purpose;
+ else
+ return NULL_TREE;
+ }
+ --index;
+ }
+ }
+ }
+ }
+ }
+
return NULL_TREE;
}
static void
-final_assign_error (name)
- tree name;
+final_assign_error (tree name)
{
- static const char format[]
- = "can't reassign a value to the final variable '%s'";
- parse_error_context (wfl, format, IDENTIFIER_POINTER (name));
+ error ("Can't reassign a value to the final variable %qs",
+ IDENTIFIER_POINTER (name));
}
static void
-check_final_reassigned (decl, before)
- tree decl;
- words before;
+check_final_reassigned (tree decl, words before)
{
int index = DECL_BIT_INDEX (decl);
/* A final local already assigned or a final parameter
assigned must be reported as errors */
if (DECL_FINAL (decl) && index != -2
&& (index < loop_current_locals /* I.e. -1, or outside current loop. */
- || ! UNASSIGNED_P (before, index)))
+ || (DECL_LOCAL_FINAL_IUD (decl) ? ASSIGNED_P (before, index)
+ : ! UNASSIGNED_P (before, index))))
{
final_assign_error (DECL_NAME (decl));
}
BEFORE, WHEN_FALSE, and WHEN_TRUE are as in check_bool_init. */
static 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;
+check_cond_init (tree test_exp, tree then_exp, tree else_exp,
+ words before, words when_false, words when_true)
{
int save_start_current_locals = start_current_locals;
DECLARE_BUFFERS(test_false, 6);
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)
- enum tree_code code; tree exp0, exp1;
- words before, when_false, when_true;
+check_bool2_init (enum tree_code code, tree exp0, tree exp1,
+ words before, words when_false, words when_true)
{
word buf[2*4];
words tmp = num_current_words <= 2 ? buf
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;
+check_bool_init (tree exp, words before, words when_false, words when_true)
{
switch (TREE_CODE (exp))
{
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 ((tmp = get_variable_decl (tmp)) != NULL_TREE)
- {
- int index;
- check_bool_init (TREE_OPERAND (exp, 1), before,
- when_false, when_true);
- check_final_reassigned (tmp, before);
- index = DECL_BIT_INDEX (tmp);
- if (index >= 0)
- {
- SET_ASSIGNED (when_false, index);
- SET_ASSIGNED (when_true, index);
- CLEAR_UNASSIGNED (when_false, index);
- CLEAR_UNASSIGNED (when_true, index);
- }
- break;
- }
- }
- goto do_default;
case BIT_AND_EXPR:
case BIT_IOR_EXPR:
COPY (when_true, before);
}
break;
+
default:
- do_default:
check_init (exp, before);
COPY (when_false, before);
COPY (when_true, before);
/* 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.
+ /* The value of the "before" set at the start of the control structure.
Used for SWITCH_EXPR but not set for LABELED_BLOCK_EXPR. */
words saved;
struct alternatives * alternatives = NULL;
-/* True if we've seen a DEFAULT_EXPR for the current SWITCH_EXPR. */
-static int current_switch_has_default;
-
/* Begin handling a control flow branch.
BEFORE is the state of [un]assigned variables on entry.
CURRENT is a struct alt to manage the branch alternatives. */
of previous alternative branches. */
static void
-done_alternative (after, current)
- words after;
- struct alternatives *current;
+done_alternative (words after, struct alternatives *current)
{
INTERSECTN (current->combined, current->combined, after,
WORDS_NEEDED (2 * current->num_locals));
/* Check for (un)initialized local variables in EXP. */
static void
-check_init (exp, before)
- tree exp;
- words before;
+check_init (tree exp, words before)
{
tree tmp;
+ location_t save_location = input_location;
again:
+ if (EXPR_HAS_LOCATION (exp))
+ input_location = EXPR_LOCATION (exp);
switch (TREE_CODE (exp))
{
case VAR_DECL:
if (! LOCAL_CLASS_INITIALIZATION_FLAG_P (exp)
&& index >= 0 && ! ASSIGNED_P (before, index))
{
- parse_error_context
- (wfl, "Variable `%s' may not have been initialized",
- IDENTIFIER_POINTER (DECL_NAME (exp)));
- /* Suppress further errors. */
+ error ("variable %qD may not have been initialized", exp);
DECL_BIT_INDEX (exp) = -2;
}
}
int index = DECL_BIT_INDEX (tmp);
if (index >= 0 && ! ASSIGNED_P (before, index))
{
- parse_error_context
- (wfl, "variable '%s' may not have been initialized",
- IDENTIFIER_POINTER (DECL_NAME (tmp)));
+ error ("variable %qD may not have been initialized", tmp);
/* Suppress further errors. */
DECL_BIT_INDEX (tmp) = -2;
}
definitely assigned when once we checked the whole
function. */
if (! STATIC_CLASS_INIT_OPT_P () /* FIXME */
+ && ! DECL_FINAL (tmp)
&& index >= start_current_locals
&& index == num_current_locals - 1)
{
final_assign_error (DECL_NAME (decl));
break;
}
- else if (TREE_CODE (tmp) == INDIRECT_REF && IS_ARRAY_LENGTH_ACCESS (tmp))
+ else if (TREE_CODE (tmp) == COMPONENT_REF && IS_ARRAY_LENGTH_ACCESS (tmp))
{
/* We can't emit a more specific message here, because when
compiling to bytecodes we don't get here. */
if (fndecl && METHOD_STATIC (fndecl)
&& (DECL_INITIAL (decl) == boolean_true_node
|| (index >= 0 && ASSIGNED_P (tmp, index))))
- hash_lookup (&DECL_FUNCTION_INITIALIZED_CLASS_TABLE (fndecl),
- DECL_FUNCTION_INIT_TEST_CLASS(decl), TRUE, NULL);
+ *(htab_find_slot
+ (DECL_FUNCTION_INITIALIZED_CLASS_TABLE (fndecl),
+ DECL_FUNCTION_INIT_TEST_CLASS (decl), INSERT)) =
+ DECL_FUNCTION_INIT_TEST_CLASS (decl);
}
DECL_BIT_INDEX (decl) = -1;
}
END_ALTERNATIVES (before, alt);
loop_current_locals = save_loop_current_locals;
start_current_locals = save_start_current_locals;
- return;
+ break;
}
case EXIT_EXPR:
{
done_alternative (when_true, alt);
COPY (before, when_false);
RELEASE_BUFFERS(when_true);
- return;
+ break;
}
case LABELED_BLOCK_EXPR:
{
check_init (LABELED_BLOCK_BODY (exp), before);
done_alternative (before, &alt);
END_ALTERNATIVES (before, alt);
- return;
+ break;
}
case EXIT_BLOCK_EXPR:
{
alt = alt->outer;
done_alternative (before, alt);
SET_ALL (before);
- return;
+ break;
}
case SWITCH_EXPR:
{
struct alternatives alt;
- int saved_current_switch_has_default = current_switch_has_default;
word buf[2];
- current_switch_has_default = 0;
check_init (TREE_OPERAND (exp, 0), before);
BEGIN_ALTERNATIVES (before, alt);
alt.saved = ALLOC_BUFFER(buf, num_current_words);
alt.block = exp;
check_init (TREE_OPERAND (exp, 1), before);
done_alternative (before, &alt);
- if (! current_switch_has_default)
- {
- done_alternative (alt.saved, &alt);
- }
+ if (! SWITCH_HAS_DEFAULT (exp))
+ done_alternative (alt.saved, &alt);
FREE_BUFFER(alt.saved, buf);
END_ALTERNATIVES (before, alt);
- current_switch_has_default = saved_current_switch_has_default;
- return;
+ break;
}
- case DEFAULT_EXPR:
- current_switch_has_default = 1;
- /* .. then fall through ... */
case CASE_EXPR:
+ case DEFAULT_EXPR:
{
int i;
struct alternatives *alt = alternatives;
}
END_ALTERNATIVES (before, alt);
}
- return;
+ break;
case TRY_FINALLY_EXPR:
{
UNION (before, before, tmp);
RELEASE_BUFFERS(tmp);
}
- return;
+ break;
case RETURN_EXPR:
case THROW_EXPR:
case ERROR_MARK:
never_continues:
SET_ALL (before);
- return;
+ break;
case COND_EXPR:
case TRUTH_ANDIF_EXPR:
break;
case NOP_EXPR:
- if (exp == empty_stmt_node)
+ if (IS_EMPTY_STMT (exp))
break;
/* ... else fall through ... */
case UNARY_PLUS_EXPR:
case FIX_FLOOR_EXPR:
case FIX_ROUND_EXPR:
case ABS_EXPR:
- case FFS_EXPR:
/* Avoid needless recursion. */
exp = TREE_OPERAND (exp, 0);
goto again;
case POSTINCREMENT_EXPR:
tmp = get_variable_decl (TREE_OPERAND (exp, 0));
if (tmp != NULL_TREE && DECL_FINAL (tmp))
- final_assign_error (DECL_NAME (tmp));
+ final_assign_error (DECL_NAME (tmp));
+ else if (TREE_CODE (tmp = TREE_OPERAND (exp, 0)) == COMPONENT_REF)
+ {
+ /* Take care of array length accesses too. */
+ tree decl = TREE_OPERAND (tmp, 1);
+ if (DECL_FINAL (decl))
+ final_assign_error (DECL_NAME (decl));
+ }
/* Avoid needless recursion. */
exp = TREE_OPERAND (exp, 0);
case SAVE_EXPR:
if (IS_INIT_CHECKED (exp))
- return;
+ break;
IS_INIT_CHECKED (exp) = 1;
exp = TREE_OPERAND (exp, 0);
goto again;
case FLOOR_MOD_EXPR:
case ROUND_MOD_EXPR:
case EXACT_DIV_EXPR:
+ case UNLT_EXPR:
+ case UNLE_EXPR:
+ case UNGT_EXPR:
+ case UNGE_EXPR:
+ case UNEQ_EXPR:
+ case LTGT_EXPR:
binop:
check_init (TREE_OPERAND (exp, 0), before);
/* Avoid needless recursion, especially for COMPOUND_EXPR. */
case INTEGER_CST:
case REAL_CST:
case STRING_CST:
+ case DECL_EXPR:
case JAVA_EXC_OBJ_EXPR:
break;
case EXPR_WITH_FILE_LOCATION:
{
- const char *saved_input_filename = input_filename;
- tree saved_wfl = wfl;
+ location_t saved_location = input_location;
tree body = EXPR_WFL_NODE (exp);
- int saved_lineno = lineno;
- if (body == empty_stmt_node)
+ if (IS_EMPTY_STMT (body))
break;
- wfl = exp;
+#ifdef USE_MAPPED_LOCATION
+ input_location = EXPR_LOCATION (exp);
+#else
input_filename = EXPR_WFL_FILENAME (exp);
- lineno = EXPR_WFL_LINENO (exp);
+ input_line = EXPR_WFL_LINENO (exp);
+#endif
check_init (body, before);
- input_filename = saved_input_filename;
- lineno = saved_lineno;
- wfl = saved_wfl;
+ input_location = saved_location;
}
break;
("internal error in check-init: tree code not implemented: %s",
tree_code_name [(int) TREE_CODE (exp)]);
}
+ input_location = save_location;
}
void
-check_for_initialization (body, mdecl)
- tree body, mdecl;
+check_for_initialization (tree body, tree mdecl)
{
tree decl;
word buf[2];
if (index >= 0 && ! ASSIGNED_P (before, index))
{
if (! is_finit_method)
- error_with_decl (decl, "final field '%s' may not have been initialized");
+ error ("%Jfinal field %qD may not have been initialized",
+ decl, decl);
}
else if (is_finit_method)
DECL_FIELD_FINAL_IUD (decl) = 1;
start_current_locals = num_current_locals = 0;
}
-
-/* Call for every element in DECL_FUNCTION_INITIALIZED_CLASS_TABLE of
- a method to consider whether the type indirectly described by ENTRY
- is definitly initialized and thus remembered as such. */
-
-bool
-attach_initialized_static_class (entry, ptr)
- struct hash_entry *entry;
- PTR ptr;
-{
- struct init_test_hash_entry *ite = (struct init_test_hash_entry *) entry;
- tree fndecl = DECL_CONTEXT (ite->init_test_decl);
- int index = DECL_BIT_INDEX (ite->init_test_decl);
-
- /* If the initializer flag has been definitly assigned (not taking
- into account its first mandatory assignment which has been
- already added but escaped analysis.) */
- if (fndecl && METHOD_STATIC (fndecl)
- && (DECL_INITIAL (ite->init_test_decl) == boolean_true_node
- || (index >= 0 && ASSIGNED_P (((word *) ptr), index))))
- hash_lookup (&DECL_FUNCTION_INITIALIZED_CLASS_TABLE (fndecl),
- entry->key, TRUE, NULL);
- return true;
-}