check-init.c: Handle definite unassignment to finals in addition to definite assignment.
* check-init.c: Handle definite unassignment to finals in addition to definite assignment. [see ChangeLog] From-SVN: r47619
This commit is contained in:
parent
ff52499130
commit
4f88ccda2d
|
@ -1,3 +1,56 @@
|
|||
2001-12-04 Per Bothner <per@bothner.com>
|
||||
|
||||
* check-init.c: Handle definite unassignment to finals in addition
|
||||
to definite assignment.
|
||||
(loop_current_locals): New field.
|
||||
(num_current_locals, int start_current_locals, num_current_words):
|
||||
Make static.
|
||||
(SET_P, CLEAR_P, SET_BIT): Add needed but missing parentheses.
|
||||
(ASSIGNED_P, UNASSIGNED_P, SET_ASSIGNED, SET_UNASSIGNED,
|
||||
CLEAR_ASSIGNED, CLEAR_UNASSIGNED): New macros.
|
||||
(get_variable_decl, check_final_reassigned): New functions.
|
||||
(check_init, check_bool_init): Modify as needed for checking finals.
|
||||
(check_for_initialization): Take extra parameter and return void.
|
||||
Do extra start-up logic to check final fields for assignment.
|
||||
* parse.y (check_static_final_variable_assignment_flag,
|
||||
reset_static_final_variable_assignment_flag, check_final_assignment,
|
||||
check_final_variable_local_assignment_flag,
|
||||
reset_final_variable_indirect_assignment_flag,
|
||||
reset_final_variable_global_assignment_flag): Remove functions.
|
||||
(java_complete_expand_methods, outer_field_access_fix,
|
||||
patch_assignment): Remove no-longer used logic.
|
||||
* java-tree.h (DECL_FIELD_FINAL_IUD): Change usage and comments.
|
||||
* parse.y (register_fields, java_complete_tree): Update accordingly.
|
||||
|
||||
* check-init.c (ALLOC_WORDS/FREE_WORDS): Use xmalloc/free, not alloca.
|
||||
(DECLARE_BUFFERS, RELEASE_BUFFERS, ALLOC_BUFFER, FREE_BUFFER): New.
|
||||
(check_cond_init, check_bool2_init): Use DECLARE_BUFFERS.
|
||||
|
||||
* java-tree.h (STATIC_CLASS_INIT_OPT_P): Temporarily turn off.
|
||||
|
||||
* java-tree.h (DECL FINAL): New bit-field.
|
||||
(METHOD_FINAL, FIELD_FINAL, CLASS_FINAL): Define as DECL_FINAL.
|
||||
(LOCAL_FINAL_P): Use DECL_FINAL rather than old LOCAL_FINAL.
|
||||
(DECL_INIT_CALLS_THIS): New macro.
|
||||
(struct lang_decl): New bit-field init_calls_this.
|
||||
(DECL_FUNCTION_ALL_FINAL_INITIALIZED, DECL_FIELD_FINAL_LIIC,
|
||||
DECL_FIELD_FINAL_IERR, LOCAL_FINAL, TYPE_HAS_FINAL_VARIABLE
|
||||
(DECL_BIT_INDEX): Change to use pointer_alias_set since we now
|
||||
use it for both local variables and final fields.
|
||||
(struct lang_decl_var): Remove bit-fields final_liic, final_ierr,
|
||||
and local_final.
|
||||
(struct lang_type): Remove hfv bit-field.
|
||||
(check_for_initialization): Change to return void.
|
||||
|
||||
* java-tree.h (IS_ARRAY_LENGTH_ACCESS): New macros.
|
||||
* expr.c (build_java_array_length_access): Set IS_ARRAY_LENGTH_ACCESS.
|
||||
* check-init.c (final_assign_error): New helper function.
|
||||
(check_final_reassigned, check_init): Use it.
|
||||
(check_init): Also check IS_ARRAY_LENGTH_ACCESS for ARRAY.length.
|
||||
|
||||
* java-tree.h (struct lang_decl, struct lang_decl_var): Change all
|
||||
bit-fields to unsigned.
|
||||
|
||||
2001-12-03 Per Bothner <per@bothner.com>
|
||||
|
||||
* parse.y (patch_binop): Minor constant folding.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Code to test for "definitive assignment".
|
||||
/* Code to test for "definitive [un]assignment".
|
||||
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -30,8 +30,10 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */
|
|||
#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. */
|
||||
and each blank final field an index, and then we pass around
|
||||
bitstrings, where the (2*i)'th bit is set if decl whose DECL_BIT_INDEX
|
||||
is i is definitely assigned, and the the (2*i=1)'th bit is set if
|
||||
decl whose DECL_BIT_INDEX is i is definitely unassigned */
|
||||
|
||||
/* One segment of a bitstring. */
|
||||
typedef unsigned int word;
|
||||
|
@ -40,7 +42,11 @@ typedef unsigned int word;
|
|||
typedef word *words;
|
||||
|
||||
/* Number of locals variables currently active. */
|
||||
int num_current_locals = 0;
|
||||
static int num_current_locals = 0;
|
||||
|
||||
/* The value of num_current_locals when we entered the closest
|
||||
enclosing LOOP_EXPR. */
|
||||
static int loop_current_locals;
|
||||
|
||||
/* The index of the first local variable in the current block.
|
||||
|
||||
|
@ -62,9 +68,9 @@ int num_current_locals = 0;
|
|||
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;
|
||||
static int start_current_locals = 0;
|
||||
|
||||
int num_current_words = 1;
|
||||
static int num_current_words;
|
||||
|
||||
static tree wfl;
|
||||
|
||||
|
@ -98,28 +104,120 @@ 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));
|
||||
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));
|
||||
|
||||
#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
|
||||
|
||||
/* DECLARE_BUFFERS is used to allocate NUMBUFFER bit sets, each of
|
||||
which is an array of length num_current_words number of words.
|
||||
Declares a new local variable BUFFER to hold the result (or rather
|
||||
a pointer to the first of the bit sets). In almost all cases
|
||||
num_current_words will be 1 or at most 2, so we try to stack
|
||||
allocate the arrays in that case, using a stack array
|
||||
named BUFFER##_short. Each DECLARE_BUFFERS must be matched by
|
||||
a corresponding RELEASE_BUFFERS to avoid memory leaks. */
|
||||
|
||||
#define DECLARE_BUFFERS(BUFFER, NUMBUFFERS) \
|
||||
word BUFFER##_short[2 * NUMBUFFERS]; \
|
||||
words BUFFER = ALLOC_BUFFER(BUFFER##_short, NUMBUFFERS * num_current_words)
|
||||
|
||||
#define RELEASE_BUFFERS(BUFFER) \
|
||||
FREE_BUFFER(BUFFER, BUFFER##_short)
|
||||
|
||||
#define ALLOC_BUFFER(SHORTBUFFER, NUMWORDS) \
|
||||
((NUMWORDS) * sizeof(word) <= sizeof(SHORTBUFFER) ? SHORTBUFFER \
|
||||
: ALLOC_WORDS(NUMWORDS))
|
||||
|
||||
#define FREE_BUFFER(BUFFER, SHORTBUFFER) \
|
||||
if (BUFFER != SHORTBUFFER) FREE_WORDS(BUFFER)
|
||||
|
||||
#define SET_P(WORDS, BIT) \
|
||||
(WORDS[BIT / WORD_SIZE] & (1 << (BIT % WORD_SIZE)))
|
||||
(WORDS[(BIT) / WORD_SIZE] & (1 << ((BIT) % WORD_SIZE)))
|
||||
|
||||
#define CLEAR_BIT(WORDS, BIT) \
|
||||
(WORDS[BIT / WORD_SIZE] &= ~ (1 << (BIT % WORD_SIZE)))
|
||||
(WORDS[(BIT) / WORD_SIZE] &= ~ (1 << ((BIT) % WORD_SIZE)))
|
||||
|
||||
#define SET_BIT(WORDS, BIT) \
|
||||
(WORDS[BIT / WORD_SIZE] |= (1 << (BIT % WORD_SIZE)))
|
||||
(WORDS[(BIT) / WORD_SIZE] |= (1 << ((BIT) % WORD_SIZE)))
|
||||
|
||||
#define WORDS_NEEDED(BITS) (((BITS)+(WORD_SIZE-1))/(WORD_SIZE))
|
||||
|
||||
#define ASSIGNED_P(WORDS, BIT) SET_P(WORDS, 2 * (BIT))
|
||||
#define UNASSIGNED_P(WORDS, BIT) SET_P(WORDS, 2 * (BIT) + 1)
|
||||
|
||||
#define SET_ASSIGNED(WORDS, INDEX) SET_BIT (WORDS, 2 * (INDEX))
|
||||
#define SET_UNASSIGNED(WORDS, INDEX) SET_BIT (WORDS, 2 * (INDEX) + 1)
|
||||
|
||||
#define CLEAR_ASSIGNED(WORDS, INDEX) CLEAR_BIT (WORDS, 2 * (INDEX))
|
||||
#define CLEAR_UNASSIGNED(WORDS, INDEX) CLEAR_BIT (WORDS, 2 * (INDEX) + 1)
|
||||
|
||||
/* Get the "interesting" declaration from a MODIFY_EXPR or COMPONENT_REF.
|
||||
Return the declaration or NULL_TREE if no interesting declaration. */
|
||||
|
||||
static tree
|
||||
get_variable_decl (exp)
|
||||
tree exp;
|
||||
{
|
||||
if (TREE_CODE (exp) == VAR_DECL)
|
||||
{
|
||||
if (! TREE_STATIC (exp) || FIELD_FINAL (exp))
|
||||
return exp;
|
||||
}
|
||||
/* We only care about final parameters. */
|
||||
else if (TREE_CODE (exp) == PARM_DECL)
|
||||
{
|
||||
if (DECL_FINAL (exp))
|
||||
return exp;
|
||||
}
|
||||
/* See if exp is this.field. */
|
||||
else if (TREE_CODE (exp) == COMPONENT_REF)
|
||||
{
|
||||
tree op0 = TREE_OPERAND (exp, 0);
|
||||
tree op1 = TREE_OPERAND (exp, 1);
|
||||
tree mdecl = current_function_decl;
|
||||
if (TREE_CODE (op0) == INDIRECT_REF
|
||||
&& TREE_CODE (op1) == FIELD_DECL
|
||||
&& ! METHOD_STATIC (mdecl)
|
||||
&& FIELD_FINAL (op1))
|
||||
{
|
||||
op0 = TREE_OPERAND (op0, 0);
|
||||
if (op0 == BLOCK_EXPR_DECLS (DECL_FUNCTION_BODY (mdecl)))
|
||||
return op1;
|
||||
}
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
static void
|
||||
final_assign_error (name)
|
||||
tree name;
|
||||
{
|
||||
static const char format[]
|
||||
= "can't re-assign here a value to the final variable '%s'";
|
||||
parse_error_context (wfl, format, IDENTIFIER_POINTER (name));
|
||||
}
|
||||
|
||||
static void
|
||||
check_final_reassigned (decl, before)
|
||||
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)))
|
||||
{
|
||||
final_assign_error (DECL_NAME (decl));
|
||||
}
|
||||
}
|
||||
|
||||
/* Check a conditional form (TEST_EXP ? THEN_EXP : ELSE_EXP) for
|
||||
definite assignment.
|
||||
definite [un]assignment.
|
||||
BEFORE, WHEN_FALSE, and WHEN_TRUE are as in check_bool_init. */
|
||||
|
||||
static void
|
||||
|
@ -128,19 +226,22 @@ check_cond_init (test_exp, then_exp, else_exp,
|
|||
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;
|
||||
int save_start_current_locals = start_current_locals;
|
||||
DECLARE_BUFFERS(test_false, 6);
|
||||
words test_true = test_false + num_current_words;
|
||||
words then_false = test_true + num_current_words;
|
||||
words then_true = then_false + num_current_words;
|
||||
words else_false = then_true + num_current_words;
|
||||
words else_true = else_false + num_current_words;
|
||||
start_current_locals = num_current_locals;
|
||||
|
||||
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);
|
||||
RELEASE_BUFFERS(test_false);
|
||||
start_current_locals = save_start_current_locals;
|
||||
}
|
||||
|
||||
/* Check a boolean binary form CODE (EXP0, EXP1),
|
||||
|
@ -152,8 +253,8 @@ check_bool2_init (code, exp0, exp1, before, when_false, when_true)
|
|||
enum tree_code code; tree exp0, exp1;
|
||||
words before, when_false, when_true;
|
||||
{
|
||||
word buf[4];
|
||||
words tmp = num_current_words <= 1 ? buf
|
||||
word buf[2*4];
|
||||
words tmp = num_current_words <= 2 ? buf
|
||||
: ALLOC_WORDS (4 * num_current_words);
|
||||
words when_false_0 = tmp;
|
||||
words when_false_1 = tmp+num_current_words;
|
||||
|
@ -204,12 +305,12 @@ check_bool2_init (code, exp0, exp1, before, when_false, when_true)
|
|||
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
|
||||
/* Check a boolean expression EXP for definite [un]assignment.
|
||||
BEFORE is the set of variables definitely [un]assigned before the
|
||||
conditional. (This bitstring may be modified arbitrarily in this function.)
|
||||
On output, WHEN_FALSE is the set of variables [un]definitely assigned after
|
||||
the conditional when the conditional is false.
|
||||
On output, WHEN_TRUE is the set of variables definitely assigned after
|
||||
On output, WHEN_TRUE is the set of variables definitely [un]assigned after
|
||||
the conditional when the conditional is true.
|
||||
(WHEN_FALSE and WHEN_TRUE are overwritten with initial values ignored.)
|
||||
(None of BEFORE, WHEN_FALSE, or WHEN_TRUE can overlap, as they may
|
||||
|
@ -244,16 +345,19 @@ check_bool_init (exp, before, when_false, when_true)
|
|||
case MODIFY_EXPR:
|
||||
{
|
||||
tree tmp = TREE_OPERAND (exp, 0);
|
||||
if (TREE_CODE (tmp) == VAR_DECL && ! FIELD_STATIC (tmp))
|
||||
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_BIT (when_false, index);
|
||||
SET_BIT (when_true, index);
|
||||
SET_ASSIGNED (when_false, index);
|
||||
SET_ASSIGNED (when_true, index);
|
||||
CLEAR_UNASSIGNED (when_false, index);
|
||||
CLEAR_UNASSIGNED (when_true, index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -325,6 +429,10 @@ struct alternatives
|
|||
|
||||
struct alternatives * alternatives = NULL;
|
||||
|
||||
/* 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. */
|
||||
|
||||
#define BEGIN_ALTERNATIVES(before, current) \
|
||||
{ \
|
||||
current.saved = NULL; \
|
||||
|
@ -338,15 +446,23 @@ struct alternatives * alternatives = NULL;
|
|||
start_current_locals = num_current_locals; \
|
||||
}
|
||||
|
||||
/* We have finished with one branch of branching control flow.
|
||||
Store the [un]assigned state, merging (intersecting) it with the state
|
||||
of previous alternative branches. */
|
||||
|
||||
static void
|
||||
done_alternative (after, current)
|
||||
words after;
|
||||
struct alternatives *current;
|
||||
{
|
||||
INTERSECTN (current->combined, current->combined, after,
|
||||
WORDS_NEEDED (current->num_locals));
|
||||
WORDS_NEEDED (2 * current->num_locals));
|
||||
}
|
||||
|
||||
/* Used when we done with a control flow branch and are all merged again.
|
||||
* AFTER is the merged state of [un]assigned variables,
|
||||
CURRENT is a struct alt that was passed to BEGIN_ALTERNATIVES. */
|
||||
|
||||
#define END_ALTERNATIVES(after, current) \
|
||||
{ \
|
||||
alternatives = current.outer; \
|
||||
|
@ -368,48 +484,62 @@ check_init (exp, before)
|
|||
switch (TREE_CODE (exp))
|
||||
{
|
||||
case VAR_DECL:
|
||||
if (! FIELD_STATIC (exp) && DECL_NAME (exp) != NULL_TREE)
|
||||
case PARM_DECL:
|
||||
if (! FIELD_STATIC (exp) && DECL_NAME (exp) != NULL_TREE
|
||||
&& DECL_NAME (exp) != this_identifier_node)
|
||||
{
|
||||
int index = DECL_BIT_INDEX (exp);
|
||||
/* We don't want to report and mark as non initialized flags
|
||||
the are, they will be marked initialized later on when
|
||||
assigned to `true.' */
|
||||
if ((STATIC_CLASS_INIT_OPT_P ()
|
||||
&& ! LOCAL_CLASS_INITIALIZATION_FLAG_P (exp))
|
||||
&& index >= 0 && ! SET_P (before, index))
|
||||
/* We don't want to report and mark as non initialized class
|
||||
initialization flags. */
|
||||
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. */
|
||||
DECL_BIT_INDEX (exp) = -1;
|
||||
DECL_BIT_INDEX (exp) = -2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case COMPONENT_REF:
|
||||
check_init (TREE_OPERAND (exp, 0), before);
|
||||
if ((tmp = get_variable_decl (exp)) != NULL_TREE)
|
||||
{
|
||||
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)));
|
||||
/* Suppress further errors. */
|
||||
DECL_BIT_INDEX (tmp) = -2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MODIFY_EXPR:
|
||||
tmp = TREE_OPERAND (exp, 0);
|
||||
/* We're interested in variable declaration and parameter
|
||||
declaration when they're declared with the `final' modifier. */
|
||||
if ((TREE_CODE (tmp) == VAR_DECL && ! FIELD_STATIC (tmp))
|
||||
|| (TREE_CODE (tmp) == PARM_DECL && LOCAL_FINAL_P (tmp)))
|
||||
if ((tmp = get_variable_decl (tmp)) != NULL_TREE)
|
||||
{
|
||||
int index;
|
||||
check_init (TREE_OPERAND (exp, 1), before);
|
||||
check_final_reassigned (tmp, before);
|
||||
index = DECL_BIT_INDEX (tmp);
|
||||
/* A final local already assigned or a final parameter
|
||||
assigned must be reported as errors */
|
||||
if (LOCAL_FINAL_P (tmp)
|
||||
&& (index == -1 || TREE_CODE (tmp) == PARM_DECL))
|
||||
parse_error_context (wfl, "Can't assign here a value to the `final' variable `%s'", IDENTIFIER_POINTER (DECL_NAME (tmp)));
|
||||
|
||||
if (index >= 0)
|
||||
SET_BIT (before, index);
|
||||
{
|
||||
SET_ASSIGNED (before, index);
|
||||
CLEAR_UNASSIGNED (before, index);
|
||||
}
|
||||
/* Minor optimization. See comment for start_current_locals.
|
||||
If we're optimizing for class initialization, we keep
|
||||
this information to check whether the variable is
|
||||
definitely assigned when once we checked the whole
|
||||
function. */
|
||||
if (! STATIC_CLASS_INIT_OPT_P ()
|
||||
if (! STATIC_CLASS_INIT_OPT_P () /* FIXME */
|
||||
&& index >= start_current_locals
|
||||
&& index == num_current_locals - 1)
|
||||
{
|
||||
|
@ -418,6 +548,22 @@ check_init (exp, before)
|
|||
}
|
||||
break;
|
||||
}
|
||||
else if (TREE_CODE (tmp = TREE_OPERAND (exp, 0)) == COMPONENT_REF)
|
||||
{
|
||||
tree decl;
|
||||
check_init (tmp, before);
|
||||
check_init (TREE_OPERAND (exp, 1), before);
|
||||
decl = TREE_OPERAND (tmp, 1);
|
||||
if (DECL_FINAL (decl))
|
||||
final_assign_error (DECL_NAME (decl));
|
||||
break;
|
||||
}
|
||||
else if (TREE_CODE (tmp) == INDIRECT_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. */
|
||||
final_assign_error (length_identifier_node);
|
||||
}
|
||||
else
|
||||
goto binop;
|
||||
case BLOCK:
|
||||
|
@ -434,7 +580,7 @@ check_init (exp, before)
|
|||
{
|
||||
DECL_BIT_INDEX (decl) = num_current_locals++;
|
||||
}
|
||||
words_needed = WORDS_NEEDED (num_current_locals);
|
||||
words_needed = WORDS_NEEDED (2 * num_current_locals);
|
||||
if (words_needed > num_current_words)
|
||||
{
|
||||
tmp = ALLOC_WORDS (words_needed);
|
||||
|
@ -444,7 +590,10 @@ check_init (exp, before)
|
|||
else
|
||||
tmp = before;
|
||||
for (i = start_current_locals; i < num_current_locals; i++)
|
||||
CLEAR_BIT (tmp, i);
|
||||
{
|
||||
CLEAR_ASSIGNED (tmp, i);
|
||||
SET_UNASSIGNED (tmp, i);
|
||||
}
|
||||
check_init (BLOCK_EXPR_BODY (exp), tmp);
|
||||
num_current_locals = start_current_locals;
|
||||
start_current_locals = save_start_current_locals;
|
||||
|
@ -454,23 +603,44 @@ check_init (exp, before)
|
|||
COPY (before, tmp);
|
||||
FREE_WORDS (tmp);
|
||||
}
|
||||
|
||||
/* Re-set DECL_BIT_INDEX since it is also DECL_POINTER_ALIAS_SET. */
|
||||
for (decl = BLOCK_EXPR_DECLS (exp);
|
||||
decl != NULL_TREE; decl = TREE_CHAIN (decl))
|
||||
{
|
||||
DECL_BIT_INDEX (decl) = -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LOOP_EXPR:
|
||||
{
|
||||
/* The JLS 2nd edition discusses a complication determining
|
||||
definite unassignment of loop statements. They define a
|
||||
"hypothetical" analysis model. We do something much
|
||||
simpler: We just disallow assignments inside loops to final
|
||||
variables declared outside the loop. This means we may
|
||||
disallow some contrived assignments that the JLS, but I
|
||||
can't see how anything except a very contrived testcase (a
|
||||
do-while whose condition is false?) would care. */
|
||||
|
||||
struct alternatives alt;
|
||||
int save_loop_current_locals = loop_current_locals;
|
||||
int save_start_current_locals = start_current_locals;
|
||||
loop_current_locals = num_current_locals;
|
||||
start_current_locals = num_current_locals;
|
||||
BEGIN_ALTERNATIVES (before, alt);
|
||||
alt.block = exp;
|
||||
check_init (TREE_OPERAND (exp, 0), before);
|
||||
END_ALTERNATIVES (before, alt);
|
||||
loop_current_locals = save_loop_current_locals;
|
||||
start_current_locals = save_start_current_locals;
|
||||
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;
|
||||
DECLARE_BUFFERS(when_true, 2);
|
||||
words when_false = when_true + num_current_words;
|
||||
#ifdef ENABLE_JC1_CHECKING
|
||||
if (TREE_CODE (alt->block) != LOOP_EXPR)
|
||||
abort ();
|
||||
|
@ -478,7 +648,7 @@ check_init (exp, before)
|
|||
check_bool_init (TREE_OPERAND (exp, 0), before, when_false, when_true);
|
||||
done_alternative (when_true, alt);
|
||||
COPY (before, when_false);
|
||||
FREE_WORDS (tmp);
|
||||
RELEASE_BUFFERS(when_true);
|
||||
return;
|
||||
}
|
||||
case LABELED_BLOCK_EXPR:
|
||||
|
@ -505,14 +675,17 @@ check_init (exp, before)
|
|||
case SWITCH_EXPR:
|
||||
{
|
||||
struct alternatives alt;
|
||||
word buf[2];
|
||||
check_init (TREE_OPERAND (exp, 0), before);
|
||||
BEGIN_ALTERNATIVES (before, alt);
|
||||
alt.saved = ALLOC_WORDS (num_current_words);
|
||||
alt.saved = ALLOC_BUFFER(buf, 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);
|
||||
FREE_BUFFER(alt.saved, buf);
|
||||
if (alt.saved != buf)
|
||||
FREE_WORDS (alt.saved);
|
||||
END_ALTERNATIVES (before, alt);
|
||||
return;
|
||||
}
|
||||
|
@ -523,9 +696,9 @@ check_init (exp, before)
|
|||
struct alternatives *alt = alternatives;
|
||||
while (TREE_CODE (alt->block) != SWITCH_EXPR)
|
||||
alt = alt->outer;
|
||||
COPYN (before, alt->saved, WORDS_NEEDED (alt->num_locals));
|
||||
COPYN (before, alt->saved, WORDS_NEEDED (2 * alt->num_locals));
|
||||
for (i = alt->num_locals; i < num_current_locals; i++)
|
||||
CLEAR_BIT (before, i);
|
||||
CLEAR_ASSIGNED (before, i);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -533,8 +706,10 @@ check_init (exp, before)
|
|||
{
|
||||
tree try_clause = TREE_OPERAND (exp, 0);
|
||||
tree clause = TREE_OPERAND (exp, 1);
|
||||
words save = ALLOC_WORDS (num_current_words);
|
||||
words tmp = ALLOC_WORDS (num_current_words);
|
||||
word buf[2*2];
|
||||
words tmp = (num_current_words <= 2 ? buf
|
||||
: ALLOC_WORDS (2 * num_current_words));
|
||||
words save = tmp + num_current_words;
|
||||
struct alternatives alt;
|
||||
BEGIN_ALTERNATIVES (before, alt);
|
||||
COPY (save, before);
|
||||
|
@ -548,20 +723,22 @@ check_init (exp, before)
|
|||
check_init (catch_clause, tmp);
|
||||
done_alternative (tmp, &alt);
|
||||
}
|
||||
FREE_WORDS (tmp);
|
||||
FREE_WORDS (save);
|
||||
if (tmp != buf)
|
||||
{
|
||||
FREE_WORDS (tmp);
|
||||
}
|
||||
END_ALTERNATIVES (before, alt);
|
||||
}
|
||||
return;
|
||||
|
||||
case TRY_FINALLY_EXPR:
|
||||
{
|
||||
words tmp = ALLOC_WORDS (num_current_words);
|
||||
DECLARE_BUFFERS(tmp, 1);
|
||||
COPY (tmp, before);
|
||||
check_init (TREE_OPERAND (exp, 0), before);
|
||||
check_init (TREE_OPERAND (exp, 1), tmp);
|
||||
UNION (before, before, tmp);
|
||||
FREE_WORDS (tmp);
|
||||
RELEASE_BUFFERS(tmp);
|
||||
}
|
||||
return;
|
||||
|
||||
|
@ -580,14 +757,18 @@ check_init (exp, before)
|
|||
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;
|
||||
DECLARE_BUFFERS(when_true, 2);
|
||||
words when_false = when_true + num_current_words;
|
||||
check_bool_init (exp, before, when_false, when_true);
|
||||
INTERSECT (before, when_false, when_true);
|
||||
FREE_WORDS (tmp);
|
||||
RELEASE_BUFFERS(when_true);
|
||||
}
|
||||
break;
|
||||
|
||||
case NOP_EXPR:
|
||||
if (exp == empty_stmt_node)
|
||||
break;
|
||||
/* ... else fall through ... */
|
||||
case UNARY_PLUS_EXPR:
|
||||
case NEGATE_EXPR:
|
||||
case TRUTH_AND_EXPR:
|
||||
|
@ -596,9 +777,7 @@ check_init (exp, before)
|
|||
case TRUTH_NOT_EXPR:
|
||||
case BIT_NOT_EXPR:
|
||||
case CONVERT_EXPR:
|
||||
case COMPONENT_REF:
|
||||
case BIT_FIELD_REF:
|
||||
case NOP_EXPR:
|
||||
case FLOAT_EXPR:
|
||||
case FIX_TRUNC_EXPR:
|
||||
case INDIRECT_REF:
|
||||
|
@ -662,7 +841,6 @@ check_init (exp, before)
|
|||
exp = TREE_OPERAND (exp, 1);
|
||||
goto again;
|
||||
|
||||
case PARM_DECL:
|
||||
case RESULT_DECL:
|
||||
case FUNCTION_DECL:
|
||||
case INTEGER_CST:
|
||||
|
@ -720,13 +898,87 @@ check_init (exp, before)
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int
|
||||
check_for_initialization (body)
|
||||
tree body;
|
||||
void
|
||||
check_for_initialization (body, mdecl)
|
||||
tree body, mdecl;
|
||||
{
|
||||
word before = 0;
|
||||
check_init (body, &before);
|
||||
return before;
|
||||
tree decl;
|
||||
word buf[2];
|
||||
words before = buf;
|
||||
tree owner = DECL_CONTEXT (mdecl);
|
||||
int is_static_method = METHOD_STATIC (mdecl);
|
||||
/* We don't need to check final fields of <init> it it calls this(). */
|
||||
int is_finit_method = DECL_FINIT_P (mdecl) || DECL_INSTINIT_P (mdecl);
|
||||
int is_init_method
|
||||
= (is_finit_method || DECL_CLINIT_P (mdecl)
|
||||
|| (DECL_INIT_P (mdecl) && ! DECL_INIT_CALLS_THIS (mdecl)));
|
||||
|
||||
start_current_locals = num_current_locals = 0;
|
||||
num_current_words = 2;
|
||||
|
||||
if (is_init_method)
|
||||
{
|
||||
int words_needed, i;
|
||||
for (decl = TYPE_FIELDS (owner);
|
||||
decl != NULL_TREE; decl = TREE_CHAIN (decl))
|
||||
{
|
||||
if (DECL_FINAL (decl) && FIELD_STATIC (decl) == is_static_method)
|
||||
{
|
||||
if (DECL_FIELD_FINAL_IUD (decl))
|
||||
DECL_BIT_INDEX (decl) = -1;
|
||||
else
|
||||
DECL_BIT_INDEX (decl) = num_current_locals++;
|
||||
}
|
||||
}
|
||||
words_needed = WORDS_NEEDED (2 * num_current_locals);
|
||||
if (words_needed > 2)
|
||||
{
|
||||
num_current_words = words_needed;
|
||||
before = ALLOC_WORDS(words_needed);
|
||||
}
|
||||
i = 0;
|
||||
for (decl = TYPE_FIELDS (owner);
|
||||
decl != NULL_TREE; decl = TREE_CHAIN (decl))
|
||||
{
|
||||
if (FIELD_FINAL (decl) && FIELD_STATIC (decl) == is_static_method)
|
||||
{
|
||||
if (! DECL_FIELD_FINAL_IUD (decl))
|
||||
{
|
||||
CLEAR_ASSIGNED (before, i);
|
||||
SET_UNASSIGNED (before, i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
check_init (body, before);
|
||||
|
||||
if (is_init_method)
|
||||
{
|
||||
for (decl = TYPE_FIELDS (owner);
|
||||
decl != NULL_TREE; decl = TREE_CHAIN (decl))
|
||||
{
|
||||
if (FIELD_FINAL (decl) && FIELD_STATIC (decl) == is_static_method)
|
||||
{
|
||||
int index = DECL_BIT_INDEX (decl);
|
||||
if (index >= 0 && ! ASSIGNED_P (before, index))
|
||||
{
|
||||
if (! is_finit_method)
|
||||
error_with_decl (decl, "final field '%s' may not have been initialized");
|
||||
}
|
||||
else if (is_finit_method)
|
||||
DECL_FIELD_FINAL_IUD (decl) = 1;
|
||||
|
||||
/* Re-set to initial state, since we later may use the
|
||||
same bit for DECL_POINTER_ALIAS_SET. */
|
||||
DECL_BIT_INDEX (decl) = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_current_locals = num_current_locals = 0;
|
||||
}
|
||||
|
||||
/* Call for every element in DECL_FUNCTION_INITIALIZED_CLASS_TABLE of
|
||||
|
@ -747,7 +999,7 @@ attach_initialized_static_class (entry, ptr)
|
|||
already added but escaped analysis.) */
|
||||
if (fndecl && METHOD_STATIC (fndecl)
|
||||
&& (DECL_INITIAL (ite->init_test_decl) == boolean_true_node
|
||||
|| (index >= 0 && SET_P (((word *) ptr), index))))
|
||||
|| (index >= 0 && ASSIGNED_P (((word *) ptr), index))))
|
||||
hash_lookup (&DECL_FUNCTION_INITIALIZED_CLASS_TABLE (fndecl),
|
||||
entry->key, TRUE, NULL);
|
||||
return true;
|
||||
|
|
|
@ -706,10 +706,12 @@ build_java_array_length_access (node)
|
|||
length = java_array_type_length (type);
|
||||
if (length >= 0)
|
||||
return build_int_2 (length, 0);
|
||||
return fold (build1 (INDIRECT_REF, int_type_node,
|
||||
fold (build (PLUS_EXPR, ptr_type_node,
|
||||
java_check_reference (node, 1),
|
||||
JAVA_ARRAY_LENGTH_OFFSET(node)))));
|
||||
node = build1 (INDIRECT_REF, int_type_node,
|
||||
fold (build (PLUS_EXPR, ptr_type_node,
|
||||
java_check_reference (node, 1),
|
||||
JAVA_ARRAY_LENGTH_OFFSET(node))));
|
||||
IS_ARRAY_LENGTH_ACCESS (node) = 1;
|
||||
return fold (node);
|
||||
}
|
||||
|
||||
/* Optionally checks a reference against the NULL pointer. ARG1: the
|
||||
|
|
|
@ -63,6 +63,7 @@ struct JCF;
|
|||
4: IS_A_COMMAND_LINE_FILENAME_P (in IDENTIFIER_NODE)
|
||||
RESOLVE_TYPE_NAME_P (in EXPR_WITH_FILE_LOCATION)
|
||||
CALL_USING_SUPER (in CALL_EXPR)
|
||||
IS_ARRAY_LENGTH_ACCESS (in INDIRECT_REF)
|
||||
5: HAS_BEEN_ALREADY_PARSED_P (in IDENTIFIER_NODE)
|
||||
IS_BREAK_STMT_P (in EXPR_WITH_FILE_LOCATION)
|
||||
IS_CRAFTED_STRING_BUFFER_P (in CALL_EXPR)
|
||||
|
@ -92,6 +93,7 @@ struct JCF;
|
|||
3: METHOD_FINAL (in FUNCTION_DECL)
|
||||
FIELD_FINAL (in FIELD_DECL)
|
||||
CLASS_FINAL (in TYPE_DECL)
|
||||
DECL_FINAL (in any decl)
|
||||
4: METHOD_SYNCHRONIZED (in FUNCTION_DECL).
|
||||
LABEL_IN_SUBR (in LABEL_DECL)
|
||||
CLASS_INTERFACE (in TYPE_DECL)
|
||||
|
@ -476,6 +478,7 @@ extern tree java_global_trees[JTI_MAX];
|
|||
java_global_trees[JTI_FINIT_IDENTIFIER_NODE] /* "finit$" */
|
||||
#define finit_leg_identifier_node \
|
||||
java_global_trees[JTI_FINIT_LEG_IDENTIFIER_NODE] /* "$finit$" */
|
||||
/* FIXME "instinit$" and "finit$" should be merged */
|
||||
#define instinit_identifier_node \
|
||||
java_global_trees[JTI_INSTINIT_IDENTIFIER_NODE] /* "instinit$" */
|
||||
#define void_signature_node \
|
||||
|
@ -740,11 +743,11 @@ struct lang_identifier
|
|||
/* True if DECL is a synthetic ctor. */
|
||||
#define DECL_FUNCTION_SYNTHETIC_CTOR(DECL) \
|
||||
(DECL_LANG_SPECIFIC(DECL)->synthetic_ctor)
|
||||
/* True if DECL initializes all its finals */
|
||||
#define DECL_FUNCTION_ALL_FINAL_INITIALIZED(DECL) \
|
||||
(DECL_LANG_SPECIFIC(DECL)->init_final)
|
||||
#define DECL_FIXED_CONSTRUCTOR_P(DECL) (DECL_LANG_SPECIFIC(DECL)->fixed_ctor)
|
||||
|
||||
/* A constructor that calls this. */
|
||||
#define DECL_INIT_CALLS_THIS(DECL) (DECL_LANG_SPECIFIC(DECL)->init_calls_this)
|
||||
|
||||
/* True when DECL aliases an outer context local variable. */
|
||||
#define FIELD_LOCAL_ALIAS(DECL) DECL_LANG_FLAG_6 (DECL)
|
||||
|
||||
|
@ -824,29 +827,18 @@ struct lang_identifier
|
|||
/* Safely tests whether FIELD_INNER_ACCESS exists or not. */
|
||||
#define FIELD_INNER_ACCESS_P(DECL) \
|
||||
DECL_LANG_SPECIFIC (DECL) && FIELD_INNER_ACCESS (DECL)
|
||||
/* True if a final variable was initialized upon its declaration. */
|
||||
/* True if a final variable was initialized upon its declaration,
|
||||
or (if a field) in an initializer. Set after definite assignment. */
|
||||
#define DECL_FIELD_FINAL_IUD(NODE) \
|
||||
(((struct lang_decl_var*)DECL_LANG_SPECIFIC(NODE))->final_iud)
|
||||
/* Set to true if a final variable is seen locally initialized on a
|
||||
ctor. */
|
||||
#define DECL_FIELD_FINAL_LIIC(NODE) \
|
||||
(((struct lang_decl_var*)DECL_LANG_SPECIFIC(NODE))->final_liic)
|
||||
/* Set to true if an initialization error was already found with this
|
||||
final variable. */
|
||||
#define DECL_FIELD_FINAL_IERR(NODE) \
|
||||
(((struct lang_decl_var*)DECL_LANG_SPECIFIC(NODE))->final_ierr)
|
||||
/* The original WFL of a final variable. */
|
||||
#define DECL_FIELD_FINAL_WFL(NODE) \
|
||||
(((struct lang_decl_var*)DECL_LANG_SPECIFIC(NODE))->wfl)
|
||||
/* True if NODE is a local final (as opposed to a final variable.)
|
||||
This macro accesses the flag to read or set it. */
|
||||
#define LOCAL_FINAL(NODE) \
|
||||
(((struct lang_decl_var*)DECL_LANG_SPECIFIC(NODE))->local_final)
|
||||
/* True if NODE is a local final. */
|
||||
#define LOCAL_FINAL_P(NODE) (DECL_LANG_SPECIFIC (NODE) && LOCAL_FINAL (NODE))
|
||||
/* True if NODE is a final variable. */
|
||||
/* True if NODE is a local variable final. */
|
||||
#define LOCAL_FINAL_P(NODE) (DECL_LANG_SPECIFIC (NODE) && DECL_FINAL (NODE))
|
||||
/* True if NODE is a final field. */
|
||||
#define FINAL_VARIABLE_P(NODE) (FIELD_FINAL (NODE) && !FIELD_STATIC (NODE))
|
||||
/* True if NODE is a class final variable. */
|
||||
/* True if NODE is a class final field. */
|
||||
#define CLASS_FINAL_VARIABLE_P(NODE) \
|
||||
(FIELD_FINAL (NODE) && FIELD_STATIC (NODE))
|
||||
/* True if NODE is a class initialization flag. This macro accesses
|
||||
|
@ -874,8 +866,9 @@ struct lang_identifier
|
|||
|
||||
/* 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_CHECK (DECL)->decl.u2.i)
|
||||
The value -1 means the variable has been definitely assigned (and not
|
||||
definitely unassigned). The value -2 means we already reported an error. */
|
||||
#define DECL_BIT_INDEX(DECL) (DECL_CHECK (DECL)->decl.pointer_alias_set)
|
||||
|
||||
/* DECL_LANG_SPECIFIC for FUNCTION_DECLs. */
|
||||
struct lang_decl
|
||||
|
@ -899,10 +892,11 @@ struct lang_decl
|
|||
tree inner_access; /* The identifier of the access method
|
||||
used for invocation from inner classes */
|
||||
int nap; /* Number of artificial parameters */
|
||||
int native : 1; /* Nonzero if this is a native method */
|
||||
int synthetic_ctor : 1; /* Nonzero if this is a synthetic ctor */
|
||||
int init_final : 1; /* Nonzero all finals are initialized */
|
||||
int fixed_ctor : 1;
|
||||
unsigned int native : 1; /* Nonzero if this is a native method */
|
||||
unsigned int synthetic_ctor : 1; /* Nonzero if this is a synthetic ctor */
|
||||
unsigned int init_final : 1; /* Nonzero all finals are initialized */
|
||||
unsigned int fixed_ctor : 1;
|
||||
unsigned int init_calls_this : 1;
|
||||
};
|
||||
|
||||
/* init_test_table hash table entry structure. */
|
||||
|
@ -922,11 +916,8 @@ struct lang_decl_var
|
|||
tree slot_chain;
|
||||
tree am; /* Access method for this field (1.1) */
|
||||
tree wfl; /* Original wfl */
|
||||
int final_iud : 1; /* Final initialized upon declaration */
|
||||
int final_liic : 1; /* Final locally initialized in ctors */
|
||||
int final_ierr : 1; /* Initialization error already detected */
|
||||
int local_final : 1; /* True if the decl is a local final */
|
||||
int cif : 1; /* True: decl is a class initialization flag */
|
||||
unsigned int final_iud : 1; /* Final initialized upon declaration */
|
||||
unsigned int cif : 1; /* True: decl is a class initialization flag */
|
||||
};
|
||||
|
||||
/* Macro to access fields in `struct lang_type'. */
|
||||
|
@ -954,7 +945,6 @@ struct lang_decl_var
|
|||
#define TYPE_IMPORT_DEMAND_LIST(T) (TYPE_LANG_SPECIFIC(T)->import_demand_list)
|
||||
#define TYPE_PRIVATE_INNER_CLASS(T) (TYPE_LANG_SPECIFIC(T)->pic)
|
||||
#define TYPE_PROTECTED_INNER_CLASS(T) (TYPE_LANG_SPECIFIC(T)->poic)
|
||||
#define TYPE_HAS_FINAL_VARIABLE(T) (TYPE_LANG_SPECIFIC(T)->hfv)
|
||||
|
||||
struct lang_type
|
||||
{
|
||||
|
@ -974,7 +964,6 @@ struct lang_type
|
|||
tree import_demand_list; /* Imported types, in the CU of this class */
|
||||
unsigned pic:1; /* Private Inner Class. */
|
||||
unsigned poic:1; /* Protected Inner Class. */
|
||||
unsigned hfv:1; /* Has final variables */
|
||||
};
|
||||
|
||||
#ifdef JAVA_USE_HANDLES
|
||||
|
@ -1106,7 +1095,7 @@ extern void parse_error_context PARAMS ((tree cl, const char *, ...))
|
|||
ATTRIBUTE_PRINTF_2;
|
||||
extern void finish_class PARAMS ((void));
|
||||
extern void java_layout_seen_class_methods PARAMS ((void));
|
||||
extern unsigned int check_for_initialization PARAMS ((tree));
|
||||
extern void check_for_initialization PARAMS ((tree, tree));
|
||||
|
||||
extern tree pushdecl_top_level PARAMS ((tree));
|
||||
extern int alloc_class_constant PARAMS ((tree));
|
||||
|
@ -1195,13 +1184,15 @@ struct rtx_def * java_lang_expand_expr PARAMS ((tree, rtx, enum machine_mode,
|
|||
enum expand_modifier));
|
||||
#endif /* TREE_CODE && RTX_CODE && HAVE_MACHINE_MODES && ARGS_SIZE_RTX */
|
||||
|
||||
#define DECL_FINAL(DECL) DECL_LANG_FLAG_3 (DECL)
|
||||
|
||||
/* Access flags etc for a method (a FUNCTION_DECL): */
|
||||
|
||||
#define METHOD_PUBLIC(DECL) DECL_LANG_FLAG_1 (DECL)
|
||||
#define METHOD_PRIVATE(DECL) TREE_PRIVATE (DECL)
|
||||
#define METHOD_PROTECTED(DECL) TREE_PROTECTED (DECL)
|
||||
#define METHOD_STATIC(DECL) DECL_LANG_FLAG_2 (DECL)
|
||||
#define METHOD_FINAL(DECL) DECL_LANG_FLAG_3 (DECL)
|
||||
#define METHOD_FINAL(DECL) DECL_FINAL (DECL)
|
||||
#define METHOD_SYNCHRONIZED(DECL) DECL_LANG_FLAG_4 (DECL)
|
||||
#define METHOD_NATIVE(DECL) (DECL_LANG_SPECIFIC(DECL)->native)
|
||||
#define METHOD_ABSTRACT(DECL) DECL_LANG_FLAG_5 (DECL)
|
||||
|
@ -1239,14 +1230,14 @@ struct rtx_def * java_lang_expand_expr PARAMS ((tree, rtx, enum machine_mode,
|
|||
#define FIELD_PROTECTED(DECL) TREE_PROTECTED (DECL)
|
||||
#define FIELD_PUBLIC(DECL) DECL_LANG_FLAG_1 (DECL)
|
||||
#define FIELD_STATIC(DECL) TREE_STATIC (DECL)
|
||||
#define FIELD_FINAL(DECL) DECL_LANG_FLAG_3 (DECL)
|
||||
#define FIELD_FINAL(DECL) DECL_FINAL (DECL)
|
||||
#define FIELD_VOLATILE(DECL) DECL_LANG_FLAG_4 (DECL)
|
||||
#define FIELD_TRANSIENT(DECL) DECL_LANG_FLAG_5 (DECL)
|
||||
|
||||
/* Access flags etc for a class (a TYPE_DECL): */
|
||||
|
||||
#define CLASS_PUBLIC(DECL) DECL_LANG_FLAG_1 (DECL)
|
||||
#define CLASS_FINAL(DECL) DECL_LANG_FLAG_3 (DECL)
|
||||
#define CLASS_FINAL(DECL) DECL_FINAL (DECL)
|
||||
#define CLASS_INTERFACE(DECL) DECL_LANG_FLAG_4 (DECL)
|
||||
#define CLASS_ABSTRACT(DECL) DECL_LANG_FLAG_5 (DECL)
|
||||
#define CLASS_SUPER(DECL) DECL_LANG_FLAG_6 (DECL)
|
||||
|
@ -1350,6 +1341,9 @@ extern tree *type_map;
|
|||
/* True iff TYPE is a Java array type. */
|
||||
#define TYPE_ARRAY_P(TYPE) TYPE_LANG_FLAG_1 (TYPE)
|
||||
|
||||
/* True for an INDIRECT_REF created from a 'ARRAY.length' operation. */
|
||||
#define IS_ARRAY_LENGTH_ACCESS(NODE) TREE_LANG_FLAG_4 (NODE)
|
||||
|
||||
/* If FUNCTION_TYPE or METHOD_TYPE: cache for build_java_argument_signature. */
|
||||
#define TYPE_ARGUMENT_SIGNATURE(TYPE) TYPE_VFIELD(TYPE)
|
||||
|
||||
|
@ -1610,9 +1604,10 @@ extern tree *type_map;
|
|||
|
||||
/* True when we can perform static class initialization optimization */
|
||||
#define STATIC_CLASS_INIT_OPT_P() \
|
||||
(flag_optimize_sci && (optimize >= 2) && ! flag_emit_class_files)
|
||||
0 /* ??? Temporarily turn off this optimization -PB */
|
||||
/* (flag_optimize_sci && (optimize >= 2) && ! flag_emit_class_files)*/
|
||||
|
||||
extern int java_error_count; \
|
||||
extern int java_error_count;
|
||||
|
||||
/* Make the current function where this macro is invoked report error
|
||||
messages and and return, if any */
|
||||
|
|
373
gcc/java/parse.y
373
gcc/java/parse.y
|
@ -101,12 +101,6 @@ static int process_imports PARAMS ((void));
|
|||
static void read_import_dir PARAMS ((tree));
|
||||
static int find_in_imports_on_demand PARAMS ((tree, tree));
|
||||
static void find_in_imports PARAMS ((tree, tree));
|
||||
static void check_static_final_variable_assignment_flag PARAMS ((tree));
|
||||
static void reset_static_final_variable_assignment_flag PARAMS ((tree));
|
||||
static void check_final_variable_local_assignment_flag PARAMS ((tree, tree));
|
||||
static void reset_final_variable_local_assignment_flag PARAMS ((tree));
|
||||
static int check_final_variable_indirect_assignment PARAMS ((tree));
|
||||
static void check_final_variable_global_assignment_flag PARAMS ((tree));
|
||||
static void check_inner_class_access PARAMS ((tree, tree, tree));
|
||||
static int check_pkg_class_access PARAMS ((tree, tree, bool));
|
||||
static void register_package PARAMS ((tree));
|
||||
|
@ -163,7 +157,6 @@ static tree build_method_invocation PARAMS ((tree, tree));
|
|||
static tree build_new_invocation PARAMS ((tree, tree));
|
||||
static tree build_assignment PARAMS ((int, int, tree, tree));
|
||||
static tree build_binop PARAMS ((enum tree_code, int, tree, tree));
|
||||
static int check_final_assignment PARAMS ((tree ,tree));
|
||||
static tree patch_assignment PARAMS ((tree, tree));
|
||||
static tree patch_binop PARAMS ((tree, tree, tree));
|
||||
static tree build_unaryop PARAMS ((int, int, tree));
|
||||
|
@ -415,7 +408,7 @@ static tree wpv_id;
|
|||
/* The list of all packages we've seen so far */
|
||||
static tree package_list = NULL_TREE;
|
||||
|
||||
/* Hold THIS for the scope of the current public method decl. */
|
||||
/* Hold THIS for the scope of the current method decl. */
|
||||
static tree current_this;
|
||||
|
||||
/* Hold a list of catch clauses list. The first element of this list is
|
||||
|
@ -4351,14 +4344,12 @@ register_fields (flags, type, variable_list)
|
|||
/* If the field denotes a final instance variable, then we
|
||||
allocate a LANG_DECL_SPECIFIC part to keep track of its
|
||||
initialization. We also mark whether the field was
|
||||
initialized upon it's declaration. We don't do that if the
|
||||
initialized upon its declaration. We don't do that if the
|
||||
created field is an alias to a final local. */
|
||||
if (!ARG_FINAL_P (current) && (flags & ACC_FINAL))
|
||||
{
|
||||
MAYBE_CREATE_VAR_LANG_DECL_SPECIFIC (field_decl);
|
||||
DECL_FIELD_FINAL_WFL (field_decl) = cl;
|
||||
if ((flags & ACC_STATIC) && init)
|
||||
DECL_FIELD_FINAL_IUD (field_decl) = 1;
|
||||
}
|
||||
|
||||
/* If the couple initializer/initialized is marked ARG_FINAL_P,
|
||||
|
@ -7273,7 +7264,7 @@ declare_local_variables (modifier, type, vlist)
|
|||
will be entered */
|
||||
decl = build_decl (VAR_DECL, name, real_type);
|
||||
MAYBE_CREATE_VAR_LANG_DECL_SPECIFIC (decl);
|
||||
LOCAL_FINAL (decl) = final_p;
|
||||
DECL_FINAL (decl) = final_p;
|
||||
BLOCK_CHAIN_DECL (decl);
|
||||
|
||||
/* If doing xreferencing, replace the line number with the WFL
|
||||
|
@ -7348,11 +7339,11 @@ source_start_java_method (fndecl)
|
|||
parm_decl = build_decl (PARM_DECL, name, type);
|
||||
|
||||
/* Remember if a local variable was declared final (via its
|
||||
TREE_LIST of type/name.) Set LOCAL_FINAL accordingly. */
|
||||
TREE_LIST of type/name.) Set DECL_FINAL accordingly. */
|
||||
if (ARG_FINAL_P (tem))
|
||||
{
|
||||
MAYBE_CREATE_VAR_LANG_DECL_SPECIFIC (parm_decl);
|
||||
LOCAL_FINAL (parm_decl) = 1;
|
||||
DECL_FINAL (parm_decl) = 1;
|
||||
}
|
||||
|
||||
BLOCK_CHAIN_DECL (parm_decl);
|
||||
|
@ -7658,14 +7649,6 @@ java_complete_expand_methods (class_decl)
|
|||
|
||||
current_class = TREE_TYPE (class_decl);
|
||||
|
||||
/* Find whether the class has final variables */
|
||||
for (decl = TYPE_FIELDS (current_class); decl; decl = TREE_CHAIN (decl))
|
||||
if (FIELD_FINAL (decl))
|
||||
{
|
||||
TYPE_HAS_FINAL_VARIABLE (current_class) = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Initialize a new constant pool */
|
||||
init_outgoing_cpool ();
|
||||
|
||||
|
@ -7700,16 +7683,8 @@ java_complete_expand_methods (class_decl)
|
|||
if (no_body)
|
||||
restore_line_number_status (1);
|
||||
|
||||
/* Reset the final local variable assignment flags */
|
||||
if (TYPE_HAS_FINAL_VARIABLE (current_class))
|
||||
reset_final_variable_local_assignment_flag (current_class);
|
||||
|
||||
java_complete_expand_method (decl);
|
||||
|
||||
/* Check for missed out final variable assignment */
|
||||
if (TYPE_HAS_FINAL_VARIABLE (current_class))
|
||||
check_final_variable_local_assignment_flag (current_class, decl);
|
||||
|
||||
if (no_body)
|
||||
restore_line_number_status (0);
|
||||
}
|
||||
|
@ -7741,17 +7716,10 @@ java_complete_expand_methods (class_decl)
|
|||
/* If there is indeed a <clinit>, fully expand it now */
|
||||
if (clinit)
|
||||
{
|
||||
/* Reset the final local variable assignment flags */
|
||||
if (TYPE_HAS_FINAL_VARIABLE (current_class))
|
||||
reset_static_final_variable_assignment_flag (current_class);
|
||||
/* Prevent the use of `this' inside <clinit> */
|
||||
ctxp->explicit_constructor_p = 1;
|
||||
java_complete_expand_method (clinit);
|
||||
ctxp->explicit_constructor_p = 0;
|
||||
/* Check for missed out static final variable assignment */
|
||||
if (TYPE_HAS_FINAL_VARIABLE (current_class)
|
||||
&& !CLASS_INTERFACE (class_decl))
|
||||
check_static_final_variable_assignment_flag (current_class);
|
||||
}
|
||||
|
||||
/* We might have generated a class$ that we now want to expand */
|
||||
|
@ -7766,15 +7734,6 @@ java_complete_expand_methods (class_decl)
|
|||
&& verify_constructor_circularity (decl, decl))
|
||||
break;
|
||||
|
||||
/* Final check on the initialization of final variables. */
|
||||
if (TYPE_HAS_FINAL_VARIABLE (current_class))
|
||||
{
|
||||
check_final_variable_global_assignment_flag (current_class);
|
||||
/* If we have an interface, check for uninitialized fields. */
|
||||
if (CLASS_INTERFACE (class_decl))
|
||||
check_static_final_variable_assignment_flag (current_class);
|
||||
}
|
||||
|
||||
/* Save the constant pool. We'll need to restore it later. */
|
||||
TYPE_CPOOL (current_class) = outgoing_cpool;
|
||||
}
|
||||
|
@ -8052,25 +8011,27 @@ java_complete_expand_method (mdecl)
|
|||
|
||||
if (! flag_emit_xref && ! METHOD_NATIVE (mdecl))
|
||||
{
|
||||
unsigned int state = check_for_initialization (block_body);
|
||||
check_for_initialization (block_body, mdecl);
|
||||
|
||||
/* Go through all the flags marking the initialization of
|
||||
static variables and see whether they're definitively
|
||||
assigned, in which case the type is remembered as
|
||||
definitively initialized in MDECL. */
|
||||
/* FIXME this doesn't work state is too short.
|
||||
if (STATIC_CLASS_INIT_OPT_P ())
|
||||
{
|
||||
hash_traverse (&DECL_FUNCTION_INIT_TEST_TABLE (mdecl),
|
||||
attach_initialized_static_class, (PTR)&state);
|
||||
|
||||
/* Always register the context as properly initialized in
|
||||
/ * Always register the context as properly initialized in
|
||||
MDECL. This used with caution helps removing extra
|
||||
initialization of self. */
|
||||
initialization of self. * /
|
||||
if (METHOD_STATIC (mdecl))
|
||||
hash_lookup (&DECL_FUNCTION_INITIALIZED_CLASS_TABLE (mdecl),
|
||||
(hash_table_key) DECL_CONTEXT (mdecl),
|
||||
TRUE, NULL);
|
||||
}
|
||||
*/
|
||||
}
|
||||
ctxp->explicit_constructor_p = 0;
|
||||
}
|
||||
|
@ -8292,14 +8253,6 @@ outer_field_access_fix (wfl, node, rhs)
|
|||
|
||||
if (outer_field_expanded_access_p (node, &name, &arg_type, &arg))
|
||||
{
|
||||
/* At any rate, check whether we're trying to assign a value to
|
||||
a final. */
|
||||
tree accessed = (JDECL_P (node) ? node :
|
||||
(TREE_CODE (node) == COMPONENT_REF ?
|
||||
TREE_OPERAND (node, 1) : node));
|
||||
if (check_final_assignment (accessed, wfl))
|
||||
return error_mark_node;
|
||||
|
||||
node = build_outer_field_access_expr (EXPR_WFL_LINECOL (wfl),
|
||||
arg_type, name, arg, rhs);
|
||||
return java_complete_tree (node);
|
||||
|
@ -8886,6 +8839,8 @@ fix_constructors (mdecl)
|
|||
TREE_OPERAND (found_call, 0) = empty_stmt_node;
|
||||
}
|
||||
|
||||
DECL_INIT_CALLS_THIS (mdecl) = invokes_this;
|
||||
|
||||
/* Insert the instance initializer block right after. */
|
||||
if (!invokes_this && (iii = build_instinit_invocation (class_type)))
|
||||
compound = add_stmt_to_compound (compound, NULL_TREE, iii);
|
||||
|
@ -11457,8 +11412,8 @@ java_complete_tree (node)
|
|||
DECL_INITIAL (node) = value;
|
||||
if (value != NULL_TREE)
|
||||
{
|
||||
/* fold_constant_for_init sometimes widen the original type
|
||||
of the constant (i.e. byte to int.) It's not desirable,
|
||||
/* fold_constant_for_init sometimes widens the original type
|
||||
of the constant (i.e. byte to int). It's not desirable,
|
||||
especially if NODE is a function argument. */
|
||||
if ((TREE_CODE (value) == INTEGER_CST
|
||||
|| TREE_CODE (value) == REAL_CST)
|
||||
|
@ -11467,8 +11422,6 @@ java_complete_tree (node)
|
|||
else
|
||||
return value;
|
||||
}
|
||||
else
|
||||
DECL_FIELD_FINAL_IUD (node) = 0;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
@ -11662,7 +11615,7 @@ java_complete_lhs (node)
|
|||
TREE_OPERAND (cn, 1));
|
||||
}
|
||||
/* Accept final locals too. */
|
||||
else if (TREE_CODE (cn) == VAR_DECL && LOCAL_FINAL (cn))
|
||||
else if (TREE_CODE (cn) == VAR_DECL && DECL_FINAL (cn))
|
||||
cn = fold_constant_for_init (DECL_INITIAL (cn), cn);
|
||||
|
||||
if (!TREE_CONSTANT (cn) && !flag_emit_xref)
|
||||
|
@ -11972,13 +11925,12 @@ java_complete_lhs (node)
|
|||
DECL_INITIAL (nn) = patched;
|
||||
else
|
||||
DECL_INITIAL (nn) = TREE_OPERAND (node, 1);
|
||||
DECL_FIELD_FINAL_IUD (nn) = 1;
|
||||
return empty_stmt_node;
|
||||
}
|
||||
}
|
||||
if (! flag_emit_class_files)
|
||||
DECL_INITIAL (nn) = NULL_TREE;
|
||||
if (CLASS_FINAL_VARIABLE_P (nn))
|
||||
DECL_FIELD_FINAL_IUD (nn) = 0;
|
||||
}
|
||||
wfl_op2 = TREE_OPERAND (node, 1);
|
||||
|
||||
|
@ -12056,9 +12008,6 @@ java_complete_lhs (node)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* Can't assign to a (blank) final. */
|
||||
if (check_final_assignment (TREE_OPERAND (node, 0), wfl_op1))
|
||||
return error_mark_node;
|
||||
node = patch_assignment (node, wfl_op1);
|
||||
/* Reorganize the tree if necessary. */
|
||||
if (flag && (!JREFERENCE_TYPE_P (TREE_TYPE (node))
|
||||
|
@ -12541,271 +12490,9 @@ print_int_node (node)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* This section of the code handle assignment check with FINAL
|
||||
variables. */
|
||||
|
||||
static void
|
||||
reset_static_final_variable_assignment_flag (class)
|
||||
tree class;
|
||||
{
|
||||
tree field;
|
||||
for (field = TYPE_FIELDS (class); field; field = TREE_CHAIN (field))
|
||||
if (CLASS_FINAL_VARIABLE_P (field))
|
||||
DECL_FIELD_FINAL_LIIC (field) = 0;
|
||||
}
|
||||
|
||||
/* Figure whether all final static variable have been initialized. */
|
||||
|
||||
static void
|
||||
check_static_final_variable_assignment_flag (class)
|
||||
tree class;
|
||||
{
|
||||
tree field;
|
||||
|
||||
for (field = TYPE_FIELDS (class); field; field = TREE_CHAIN (field))
|
||||
if (CLASS_FINAL_VARIABLE_P (field)
|
||||
&& !DECL_FIELD_FINAL_IUD (field) && !DECL_FIELD_FINAL_LIIC (field))
|
||||
parse_error_context
|
||||
(DECL_FIELD_FINAL_WFL (field),
|
||||
"Blank static final variable `%s' may not have been initialized",
|
||||
IDENTIFIER_POINTER (DECL_NAME (field)));
|
||||
}
|
||||
|
||||
/* This function marks all final variable locally unassigned. */
|
||||
|
||||
static void
|
||||
reset_final_variable_local_assignment_flag (class)
|
||||
tree class;
|
||||
{
|
||||
tree field;
|
||||
for (field = TYPE_FIELDS (class); field; field = TREE_CHAIN (field))
|
||||
if (FINAL_VARIABLE_P (field))
|
||||
DECL_FIELD_FINAL_LIIC (field) = 0;
|
||||
}
|
||||
|
||||
/* Figure whether all final variables have beem initialized in MDECL
|
||||
and mark MDECL accordingly. */
|
||||
|
||||
static void
|
||||
check_final_variable_local_assignment_flag (class, mdecl)
|
||||
tree class;
|
||||
tree mdecl;
|
||||
{
|
||||
tree field;
|
||||
int initialized = 0;
|
||||
int non_initialized = 0;
|
||||
|
||||
if (DECL_FUNCTION_SYNTHETIC_CTOR (mdecl))
|
||||
return;
|
||||
|
||||
/* First find out whether all final variables or no final variable
|
||||
are initialized in this ctor. We don't take into account final
|
||||
variable that have been initialized upon declaration. */
|
||||
for (field = TYPE_FIELDS (class); field; field = TREE_CHAIN (field))
|
||||
if (FINAL_VARIABLE_P (field) && !DECL_FIELD_FINAL_IUD (field))
|
||||
{
|
||||
if (DECL_FIELD_FINAL_LIIC (field))
|
||||
initialized++;
|
||||
else
|
||||
non_initialized++;
|
||||
}
|
||||
|
||||
/* There were no non initialized variable and no initialized variable.
|
||||
This ctor is fine. */
|
||||
if (!non_initialized && !initialized)
|
||||
DECL_FUNCTION_ALL_FINAL_INITIALIZED (mdecl) = 1;
|
||||
/* If no variables have been initialized, that fine. We'll check
|
||||
later whether this ctor calls a constructor which initializes
|
||||
them. We mark the ctor as not initializing all its finals. */
|
||||
else if (initialized == 0)
|
||||
DECL_FUNCTION_ALL_FINAL_INITIALIZED (mdecl) = 0;
|
||||
/* If we have a mixed bag, then we have a problem. We need to report
|
||||
all the variables we're not initializing. */
|
||||
else if (initialized && non_initialized)
|
||||
{
|
||||
DECL_FUNCTION_ALL_FINAL_INITIALIZED (mdecl) = 0;
|
||||
for (field = TYPE_FIELDS (class); field; field = TREE_CHAIN (field))
|
||||
if (FIELD_FINAL (field)
|
||||
&& !DECL_FIELD_FINAL_IUD (field) && !DECL_FIELD_FINAL_LIIC (field))
|
||||
{
|
||||
parse_error_context
|
||||
(lookup_cl (mdecl),
|
||||
"Blank final variable `%s' may not have been initialized in this constructor",
|
||||
IDENTIFIER_POINTER (DECL_NAME (field)));
|
||||
DECL_FIELD_FINAL_IERR (field) = 1;
|
||||
}
|
||||
}
|
||||
/* Otherwise we know this ctor is initializing all its final
|
||||
variable. We mark it so. */
|
||||
else
|
||||
DECL_FUNCTION_ALL_FINAL_INITIALIZED (mdecl) = 1;
|
||||
}
|
||||
|
||||
/* This function recurses in a simple what through STMT and stops when
|
||||
it finds a constructor call. It then verifies that the called
|
||||
constructor initialized its final properly. Return 1 upon success,
|
||||
0 or -1 otherwise. */
|
||||
|
||||
static int
|
||||
check_final_variable_indirect_assignment (stmt)
|
||||
tree stmt;
|
||||
{
|
||||
int res;
|
||||
switch (TREE_CODE (stmt))
|
||||
{
|
||||
case EXPR_WITH_FILE_LOCATION:
|
||||
return check_final_variable_indirect_assignment (EXPR_WFL_NODE (stmt));
|
||||
case COMPOUND_EXPR:
|
||||
res = check_final_variable_indirect_assignment (TREE_OPERAND (stmt, 0));
|
||||
if (res > 0)
|
||||
return res;
|
||||
return check_final_variable_indirect_assignment (TREE_OPERAND (stmt, 1));
|
||||
case SAVE_EXPR:
|
||||
return check_final_variable_indirect_assignment (TREE_OPERAND (stmt, 0));
|
||||
case CALL_EXPR:
|
||||
{
|
||||
tree decl = TREE_OPERAND (stmt, 0);
|
||||
tree fbody;
|
||||
|
||||
if (TREE_CODE (decl) != FUNCTION_DECL)
|
||||
decl = TREE_OPERAND (TREE_OPERAND (decl, 0), 0);
|
||||
if (TREE_CODE (decl) != FUNCTION_DECL)
|
||||
abort ();
|
||||
if (DECL_FUNCTION_ALL_FINAL_INITIALIZED (decl))
|
||||
return 1;
|
||||
if (DECL_FINIT_P (decl) || DECL_CONTEXT (decl) != current_class)
|
||||
return -1;
|
||||
fbody = BLOCK_EXPR_BODY (DECL_FUNCTION_BODY (decl));
|
||||
if (fbody == error_mark_node)
|
||||
return -1;
|
||||
fbody = BLOCK_EXPR_BODY (fbody);
|
||||
return check_final_variable_indirect_assignment (fbody);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the last chance to catch a final variable initialization
|
||||
problem. This routine will report an error if a final variable was
|
||||
never (globally) initialized and never reported as not having been
|
||||
initialized properly. */
|
||||
|
||||
static void
|
||||
check_final_variable_global_assignment_flag (class)
|
||||
tree class;
|
||||
{
|
||||
tree field, mdecl;
|
||||
int nnctor = 0;
|
||||
int error_found = 0;
|
||||
|
||||
/* We go through all natural ctors and see whether they're
|
||||
initializing all their final variables or not. */
|
||||
current_function_decl = NULL_TREE; /* For the error report. */
|
||||
for (mdecl = TYPE_METHODS (class); mdecl; mdecl = TREE_CHAIN (mdecl))
|
||||
if (DECL_CONSTRUCTOR_P (mdecl) && ! DECL_FUNCTION_SYNTHETIC_CTOR (mdecl))
|
||||
{
|
||||
if (!DECL_FUNCTION_ALL_FINAL_INITIALIZED (mdecl))
|
||||
{
|
||||
/* It doesn't. Maybe it calls a constructor that initializes
|
||||
them. find out. */
|
||||
tree fbody = BLOCK_EXPR_BODY (DECL_FUNCTION_BODY (mdecl));
|
||||
if (fbody == error_mark_node)
|
||||
continue;
|
||||
fbody = BLOCK_EXPR_BODY (fbody);
|
||||
if (check_final_variable_indirect_assignment (fbody) == 1)
|
||||
{
|
||||
DECL_FUNCTION_ALL_FINAL_INITIALIZED (mdecl) = 1;
|
||||
nnctor++;
|
||||
}
|
||||
else
|
||||
{
|
||||
parse_error_context
|
||||
(lookup_cl (mdecl),
|
||||
"Final variable initialization error in this constructor");
|
||||
error_found = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
nnctor++;
|
||||
}
|
||||
|
||||
/* Finally we catch final variables that never were initialized */
|
||||
for (field = TYPE_FIELDS (class); field; field = TREE_CHAIN (field))
|
||||
if (FINAL_VARIABLE_P (field)
|
||||
/* If the field wasn't initialized upon declaration */
|
||||
&& !DECL_FIELD_FINAL_IUD (field)
|
||||
/* There wasn't a natural ctor in which the field could have been
|
||||
initialized or we found an error looking for one. */
|
||||
&& (error_found || !nnctor)
|
||||
/* If we never reported a problem with this field */
|
||||
&& !DECL_FIELD_FINAL_IERR (field))
|
||||
{
|
||||
current_function_decl = NULL;
|
||||
parse_error_context
|
||||
(DECL_FIELD_FINAL_WFL (field),
|
||||
"Final variable `%s' hasn't been initialized upon its declaration",
|
||||
IDENTIFIER_POINTER (DECL_NAME (field)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Return 1 if an assignment to a FINAL is attempted in a non suitable
|
||||
context. */
|
||||
|
||||
static int
|
||||
check_final_assignment (lvalue, wfl)
|
||||
tree lvalue, wfl;
|
||||
{
|
||||
if (TREE_CODE (lvalue) != COMPONENT_REF && !JDECL_P (lvalue))
|
||||
return 0;
|
||||
|
||||
if (TREE_CODE (lvalue) == COMPONENT_REF
|
||||
&& JDECL_P (TREE_OPERAND (lvalue, 1)))
|
||||
lvalue = TREE_OPERAND (lvalue, 1);
|
||||
|
||||
if (!FIELD_FINAL (lvalue))
|
||||
return 0;
|
||||
|
||||
/* Now the logic. We can modify a final VARIABLE:
|
||||
1) in finit$, (its declaration was followed by an initialization,)
|
||||
2) consistently in each natural ctor, if it wasn't initialized in
|
||||
finit$ or once in <clinit>. In any other cases, an error should be
|
||||
reported. */
|
||||
if (DECL_FINIT_P (current_function_decl))
|
||||
{
|
||||
DECL_FIELD_FINAL_IUD (lvalue) = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!DECL_FUNCTION_SYNTHETIC_CTOR (current_function_decl)
|
||||
/* Only if it wasn't given a value upon initialization */
|
||||
&& DECL_LANG_SPECIFIC (lvalue) && !DECL_FIELD_FINAL_IUD (lvalue)
|
||||
/* If it was never assigned a value in this constructor */
|
||||
&& !DECL_FIELD_FINAL_LIIC (lvalue))
|
||||
{
|
||||
/* Turn the locally assigned flag on, it will be checked later
|
||||
on to point out at discrepancies. */
|
||||
DECL_FIELD_FINAL_LIIC (lvalue) = 1;
|
||||
if (DECL_CLINIT_P (current_function_decl))
|
||||
DECL_FIELD_FINAL_IUD (lvalue) = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Other problems should be reported right away. */
|
||||
parse_error_context
|
||||
(wfl, "Can't %sassign a value to the final variable `%s'",
|
||||
(FIELD_STATIC (lvalue) ? "re" : ""),
|
||||
IDENTIFIER_POINTER (EXPR_WFL_NODE (wfl)));
|
||||
|
||||
/* Note that static field can be initialized once and only once. */
|
||||
if (FIELD_STATIC (lvalue))
|
||||
DECL_FIELD_FINAL_IERR (lvalue) = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 15.25 Assignment operators. */
|
||||
|
||||
static tree
|
||||
|
@ -12844,15 +12531,7 @@ patch_assignment (node, wfl_op1)
|
|||
else if (TREE_CODE (wfl_op1) == EXPR_WITH_FILE_LOCATION
|
||||
&& resolve_expression_name (wfl_op1, &llvalue))
|
||||
{
|
||||
if (!error_found && check_final_assignment (llvalue, wfl_op1))
|
||||
{
|
||||
/* What we should do instead is resetting the all the flags
|
||||
previously set, exchange lvalue for llvalue and continue. */
|
||||
error_found = 1;
|
||||
return error_mark_node;
|
||||
}
|
||||
else
|
||||
lhs_type = TREE_TYPE (lvalue);
|
||||
lhs_type = TREE_TYPE (lvalue);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -13003,7 +12682,7 @@ patch_assignment (node, wfl_op1)
|
|||
/* Final locals can be used as case values in switch
|
||||
statement. Prepare them for this eventuality. */
|
||||
if (TREE_CODE (lvalue) == VAR_DECL
|
||||
&& LOCAL_FINAL_P (lvalue)
|
||||
&& DECL_FINAL (lvalue)
|
||||
&& TREE_CONSTANT (new_rhs)
|
||||
&& IDENTIFIER_LOCAL_VALUE (DECL_NAME (lvalue))
|
||||
&& JINTEGRAL_TYPE_P (TREE_TYPE (lvalue))
|
||||
|
@ -14267,26 +13946,14 @@ patch_unaryop (node, wfl_op)
|
|||
&& TREE_OPERAND (decl, 1)
|
||||
&& (TREE_CODE (TREE_OPERAND (decl, 1)) == INDIRECT_REF)))
|
||||
{
|
||||
tree lvalue;
|
||||
/* Before screaming, check that we're not in fact trying to
|
||||
increment a optimized static final access, in which case
|
||||
we issue an different error message. */
|
||||
if (!(TREE_CODE (wfl_op) == EXPR_WITH_FILE_LOCATION
|
||||
&& resolve_expression_name (wfl_op, &lvalue)
|
||||
&& check_final_assignment (lvalue, wfl_op)))
|
||||
parse_error_context (wfl_operator, "Invalid argument to `%s'",
|
||||
operator_string (node));
|
||||
TREE_TYPE (node) = error_mark_node;
|
||||
error_found = 1;
|
||||
}
|
||||
|
||||
if (check_final_assignment (op, wfl_op))
|
||||
error_found = 1;
|
||||
|
||||
/* From now on, we know that op if a variable and that it has a
|
||||
valid wfl. We use wfl_op to locate errors related to the
|
||||
++/-- operand. */
|
||||
else if (!JNUMERIC_TYPE_P (op_type))
|
||||
if (!JNUMERIC_TYPE_P (op_type))
|
||||
{
|
||||
parse_error_context
|
||||
(wfl_op, "Invalid argument type `%s' to `%s'",
|
||||
|
@ -16171,8 +15838,6 @@ fold_constant_for_init (node, context)
|
|||
DECL_INITIAL (node) = NULL_TREE;
|
||||
val = fold_constant_for_init (val, node);
|
||||
DECL_INITIAL (node) = val;
|
||||
if (!val && CLASS_FINAL_VARIABLE_P (node))
|
||||
DECL_FIELD_FINAL_IUD (node) = 0;
|
||||
return val;
|
||||
|
||||
case EXPR_WITH_FILE_LOCATION:
|
||||
|
|
Loading…
Reference in New Issue