diff options
author | Ian Lance Taylor <iant@google.com> | 2009-06-16 05:12:15 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2009-06-16 05:12:15 +0000 |
commit | e1b7793c8ebdc72bd3d7283d70a29444da614c13 (patch) | |
tree | 8497f69348fcfb23ffa019c746e037539cb42cc0 /gcc/c-decl.c | |
parent | dee6f5752949e9c53e1706925ad934760bcab775 (diff) | |
download | gcc-e1b7793c8ebdc72bd3d7283d70a29444da614c13.zip gcc-e1b7793c8ebdc72bd3d7283d70a29444da614c13.tar.gz gcc-e1b7793c8ebdc72bd3d7283d70a29444da614c13.tar.bz2 |
df-problems.c (df_simulate_one_insn_forwards): Fix braces in switch.
./: * df-problems.c (df_simulate_one_insn_forwards): Fix braces in
switch.
* gcov.c (read_count_file): Add braces around variables declared
before label.
* c.opt (Wjump-misses-init): New warning.
* c-opts.c (c_common_handle_option): Set warn_jump_misses_init for
-Wall and -Wc++-compat if not already set.
(c_common_post_options): Clear warn_jump_misses_init if it was not
set.
* c-decl.c (struct c_binding): Change type field to a union with
new label field. Make it the first field in the struct. Update
references to type to use u.type instead.
(struct c_spot_bindings): Define.
(struct c_goto_bindings): Define.
(c_goto_bindings_p): Define, along with VECs.
(struct c_label_vars): Define.
(struct c_scope): Add has_label_bindings field.
(bind_label, set_spot_bindings): New static functions.
(decl_jump_unsafe, update_spot_bindings): New static functions.
(update_label_decls): New static function.
(pop_scope): Call update_label_decls. Don't call c_end_vm_scope.
Update binding u.label field to shadowed field.
(c_binding_start_stmt_expr): New function.
(c_binding_end_stmt_expr): New function.
(pushdecl): Don't call c_begin_vm_scope.
(make_label): Add defining and p_label_vars parameters. Change
all callers.
(lookup_label): Correct test for whether a label has not yet been
defined. Call bind_label rather than bind.
(warn_about_goto): New static function.
(lookup_label_for_goto): New function.
(declare_label): Call bind_label rather than bind.
(check_earlier_gotos): New static function.
(define_label): Don't give errors about jumping into statement
expressions or scopes of variably modified types. Call
set_spot_bindings and check_earlier_gotos. Call bind_label
instead of bind. Don't set label_context_stack_se or
label_context_stack_vm.
(c_get_switch_bindings): New function.
(c_release_switch_bindings): New function.
(c_check_switch_jump_warnings): New function.
(start_function): Don't set label_context_stack_se or
label_context_stack_vm.
(finish_function): Likewise.
* c-typeck.c (label_context_stack_se): Don't define.
(label_context_stack_vm): Don't define.
(c_finish_goto_label): Call lookup_label_for_goto rather than
lookup_label. Don't give errors about jumping into a statement
expression or the scope of a variably modified type. Don't set
label_context_stack_se or label_context_stack_vm.
(struct c_switch): Remove blocked_stmt_expr and blocked_vm
fields. Add bindings field.
(c_start_case): Don't set deleted fields. Set bindings field.
(do_case): Rework order of tests. Don't check blocked_stmt_expr
or blocked_vm. Call c_check_switch_jump_warnings.
(c_finish_case): Don't test blocked_stmt_expr field. Call
c_release_switch_bindings.
(c_begin_stmt_expr): Don't increment blocked_stmt_expr in
c_switch_stack. Don't walk label_context_stack_se labels. Don't
set label_context_stack_se. Call c_bindings_start_stmt_expr.
(c_finish_stmt_expr): Don't decrement blocked_stmt_expr in
c_switch_stack. Don't walk label_context_stack_se labels. Don't
set label_context_stack_se. Call c_bindings_end_stmt_expr.
(c_begin_vm_scope, c_end_vm_scope): Don't define.
* c-tree.h (C_DECL_UNJUMPABLE_STMT_EXPR): Don't define.
(C_DECL_UNDEFINABLE_STMT_EXPR): Don't define.
(C_DECL_UNJUMPABLE_VM): Don't define.
(C_DECL_UNDEFINABLE_VM): Don't define.
(struct c_label_list): Don't define.
(struct c_label_context_se): Don't define.
(struct c_label_context_vm): Don't define.
(struct c_spot_bindings): Declare.
(c_bindings_start_stmt_expr): Declare.
(c_bindings_end_stmt_expr): Declare.
(lookup_label_for_goto): Declare.
(c_get_switch_bindings, c_release_switch_bindings): Declare.
(c_check_switch_jump_warnings): Declare.
(label_context_stack_se, label_context_stack_vm): Don't declare.
(c_finish_goto_label): Update declaration.
(c_begin_vm_scope, c_end_vm_scope): Don't declare.
* doc/invoke.texi (Option Summary): Mention -Wjump-misses-init.
(Warning Options): Document -Wjump-misses-init.
cp/:
* parser.c (cp_parser_direct_declarator): Add braces around
variables declared before label.
objc/:
* objc-act.c (objc_start_function): Don't set
label_context_stack_se or label_context_stack_vm.
testsuite/:
* gcc.dg/Wjump-misses-init-1.c: New testcase.
* gcc.dg/Wjump-misses-init-2.c: New testcase.
* gcc.dg/c99-vla-jump-5.c: Adjust expected error messages.
Recognize new notes.
* gcc.dg/stmt-expr-label-2.c: Likewise.
* gcc.dg/c99-vla-jump-1.c: Recognize new notes. Fix column
numbers.
* gcc.dg/c99-vla-jump-2.c: Recognize new notes.
* gcc.dg/c99-vla-jump-3.c: Recognize new notes.
* gcc.dg/c99-vla-jump-4.c: Likewise.
* gcc.dg/stmt-expr-label-1.c: Likewise.
* gcc.dg/stmt-expr-label-3.c: Likewise.
* gcc.dg/vla-8.c: Likewise. Move error message to different
line.
From-SVN: r148512
Diffstat (limited to 'gcc/c-decl.c')
-rw-r--r-- | gcc/c-decl.c | 644 |
1 files changed, 565 insertions, 79 deletions
diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 03d6dbd..c2c2a89 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -188,7 +188,7 @@ bool c_override_global_bindings_to_false; suppress further errors about that identifier in the current function. - The ->type field stores the type of the declaration in this scope; + The ->u.type field stores the type of the declaration in this scope; if NULL, the type is the type of the ->decl field. This is only of relevance for objects with external or internal linkage which may be redeclared in inner scopes, forming composite types that only @@ -198,6 +198,9 @@ bool c_override_global_bindings_to_false; scope) stores whether an incomplete array type at file scope was completed at an inner scope to an array size other than 1. + The ->u.label field is used for labels. It points to a structure + which stores additional information used for warnings. + The depth field is copied from the scope structure that holds this decl. It is used to preserve the proper ordering of the ->shadowed field (see bind()) and also for a handful of special-case checks. @@ -208,8 +211,11 @@ bool c_override_global_bindings_to_false; invisible bit true. */ struct GTY((chain_next ("%h.prev"))) c_binding { + union GTY(()) { /* first so GTY desc can use decl */ + tree GTY((tag ("0"))) type; /* the type in this scope */ + struct c_label_vars * GTY((tag ("1"))) label; /* for warnings */ + } GTY((desc ("TREE_CODE (%0.decl) == LABEL_DECL"))) u; tree decl; /* the decl bound */ - tree type; /* the type in this scope */ tree id; /* the identifier it's bound to */ struct c_binding *prev; /* the previous decl in this scope */ struct c_binding *shadowed; /* the innermost decl shadowed by this one */ @@ -266,6 +272,67 @@ union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"), struct lang_identifier GTY ((tag ("1"))) identifier; }; +/* Track bindings and other things that matter for goto warnings. For + efficiency, we do not gather all the decls at the point of + definition. Instead, we point into the bindings structure. As + scopes are popped, we update these structures and gather the decls + that matter at that time. */ + +struct GTY(()) c_spot_bindings { + /* The currently open scope which holds bindings defined when the + label was defined or the goto statement was found. */ + struct c_scope *scope; + /* The bindings in the scope field which were defined at the point + of the label or goto. This lets us look at older or newer + bindings in the scope, as appropriate. */ + struct c_binding *bindings_in_scope; + /* The number of statement expressions that have started since this + label or goto statement was defined. This is zero if we are at + the same statement expression level. It is positive if we are in + a statement expression started since this spot. It is negative + if this spot was in a statement expression and we have left + it. */ + int stmt_exprs; + /* Whether we started in a statement expression but are no longer in + it. This is set to true if stmt_exprs ever goes negative. */ + bool left_stmt_expr; +}; + +/* This structure is used to keep track of bindings seen when a goto + statement is defined. This is only used if we see the goto + statement before we see the label. */ + +struct GTY(()) c_goto_bindings { + /* The location of the goto statement. */ + location_t loc; + /* The bindings of the goto statement. */ + struct c_spot_bindings goto_bindings; +}; + +typedef struct c_goto_bindings *c_goto_bindings_p; +DEF_VEC_P(c_goto_bindings_p); +DEF_VEC_ALLOC_P(c_goto_bindings_p,gc); + +/* The additional information we keep track of for a label binding. + These fields are updated as scopes are popped. */ + +struct GTY(()) c_label_vars { + /* The shadowed c_label_vars, when one label shadows another (which + can only happen using a __label__ declaration). */ + struct c_label_vars *shadowed; + /* The bindings when the label was defined. */ + struct c_spot_bindings label_bindings; + /* A list of decls that we care about: decls about which we should + warn if a goto branches to this label from later in the function. + Decls are added to this list as scopes are popped. We only add + the decls that matter. */ + VEC(tree,gc) *decls_in_scope; + /* A list of goto statements to this label. This is only used for + goto statements seen before the label was defined, so that we can + issue appropriate warnings for them. */ + VEC(c_goto_bindings_p,gc) *gotos; +}; + /* Each c_scope structure describes the complete contents of one scope. Four scopes are distinguished specially: the innermost or current scope, the innermost function scope, the file scope (always @@ -354,6 +421,11 @@ struct GTY((chain_next ("%h.outer"))) c_scope { /* True means that an unsuffixed float constant is _Decimal64. */ BOOL_BITFIELD float_const_decimal64 : 1; + + /* True if this scope has any label bindings. This is used to speed + up searching for labels when popping scopes, particularly since + labels are normally only found at function scope. */ + BOOL_BITFIELD has_label_bindings : 1; }; /* The scope currently in effect. */ @@ -518,7 +590,7 @@ bind (tree name, tree decl, struct c_scope *scope, bool invisible, b->inner_comp = 0; b->locus = locus; - b->type = 0; + b->u.type = NULL; b->prev = scope->bindings; scope->bindings = b; @@ -569,6 +641,24 @@ free_binding_and_advance (struct c_binding *b) return prev; } +/* Bind a label. Like bind, but skip fields which aren't used for + labels, and add the LABEL_VARS value. */ +static void +bind_label (tree name, tree label, struct c_scope *scope, + struct c_label_vars *label_vars) +{ + struct c_binding *b; + + bind (name, label, scope, /*invisible=*/false, /*nested=*/false, + UNKNOWN_LOCATION); + + scope->has_label_bindings = true; + + b = scope->bindings; + gcc_assert (b->decl == label); + label_vars->shadowed = b->u.label; + b->u.label = label_vars; +} /* Hook called at end of compilation to assume 1 elt for a file-scope tentative array defn that wasn't complete before. */ @@ -641,6 +731,73 @@ check_inline_statics (void) c_inline_statics = NULL; } +/* Fill in a c_spot_bindings structure. If DEFINING is true, set it + for the current state, otherwise set it to uninitialized. */ + +static void +set_spot_bindings (struct c_spot_bindings *p, bool defining) +{ + if (defining) + { + p->scope = current_scope; + p->bindings_in_scope = current_scope->bindings; + } + else + { + p->scope = NULL; + p->bindings_in_scope = NULL; + } + p->stmt_exprs = 0; + p->left_stmt_expr = false; +} + +/* Return true if we will want to say something if a goto statement + crosses DECL. */ + +static bool +decl_jump_unsafe (tree decl) +{ + if (decl == error_mark_node || TREE_TYPE (decl) == error_mark_node) + return false; + + /* Always warn about crossing variably modified types. */ + if ((TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == TYPE_DECL) + && variably_modified_type_p (TREE_TYPE (decl), NULL_TREE)) + return true; + + /* Otherwise, only warn if -Wgoto-misses-init and this is an + initialized automatic decl. */ + if (warn_jump_misses_init + && TREE_CODE (decl) == VAR_DECL + && !TREE_STATIC (decl) + && DECL_INITIAL (decl) != NULL_TREE) + return true; + + return false; +} + +/* Update spot bindings P as we pop out of SCOPE. Return true if we + should push decls for a label. */ + +static bool +update_spot_bindings (struct c_scope *scope, struct c_spot_bindings *p) +{ + if (p->scope != scope) + { + /* This label or goto is defined in some other scope, or it is a + label which is not yet defined. There is nothing to + update. */ + return false; + } + + /* Adjust the spot bindings to refer to the bindings already defined + in the enclosing scope. */ + p->scope = scope->outer; + p->bindings_in_scope = p->scope->bindings; + + return true; +} + /* The Objective-C front-end often needs to determine the current scope. */ void * @@ -784,6 +941,67 @@ push_scope (void) } } +/* This is called when we are leaving SCOPE. For each label defined + in SCOPE, add any appropriate decls to its decls_in_scope fields. + These are the decls whose initialization will be skipped by a goto + later in the function. */ + +static void +update_label_decls (struct c_scope *scope) +{ + struct c_scope *s; + + s = scope; + while (s != NULL) + { + if (s->has_label_bindings) + { + struct c_binding *b; + + for (b = s->bindings; b != NULL; b = b->prev) + { + struct c_label_vars *label_vars; + struct c_binding *b1; + unsigned int ix; + struct c_goto_bindings *g; + + if (TREE_CODE (b->decl) != LABEL_DECL) + continue; + label_vars = b->u.label; + + b1 = label_vars->label_bindings.bindings_in_scope; + if (update_spot_bindings (scope, &label_vars->label_bindings)) + { + /* This label is defined in this scope. */ + for (; b1 != NULL; b1 = b1->prev) + { + /* A goto from later in the function to this + label will never see the initialization of + B1, if any. Save it to issue a warning if + needed. */ + if (decl_jump_unsafe (b1->decl)) + VEC_safe_push (tree, gc, label_vars->decls_in_scope, + b1->decl); + } + } + + /* Update the bindings of any goto statements associated + with this label. */ + for (ix = 0; + VEC_iterate (c_goto_bindings_p, label_vars->gotos, ix, g); + ++ix) + update_spot_bindings (scope, &g->goto_bindings); + } + } + + /* Don't search beyond the current function. */ + if (s == current_function_scope) + break; + + s = s->outer; + } +} + /* Set the TYPE_CONTEXT of all of TYPE's variants to CONTEXT. */ static void @@ -809,7 +1027,7 @@ pop_scope (void) bool functionbody = scope->function_body; bool keep = functionbody || scope->keep || scope->bindings; - c_end_vm_scope (scope->depth); + update_label_decls (scope); /* If appropriate, create a BLOCK to record the decls for the life of this function. */ @@ -874,6 +1092,10 @@ pop_scope (void) BLOCK_VARS (block) = p; gcc_assert (I_LABEL_BINDING (b->id) == b); I_LABEL_BINDING (b->id) = b->shadowed; + + /* Also pop back to the shadowed label_vars. */ + release_tree_vector (b->u.label->decls_in_scope); + b->u.label = b->u.label->shadowed; break; case ENUMERAL_TYPE: @@ -999,8 +1221,8 @@ pop_scope (void) { gcc_assert (I_SYMBOL_BINDING (b->id) == b); I_SYMBOL_BINDING (b->id) = b->shadowed; - if (b->shadowed && b->shadowed->type) - TREE_TYPE (b->shadowed->decl) = b->shadowed->type; + if (b->shadowed && b->shadowed->u.type) + TREE_TYPE (b->shadowed->decl) = b->shadowed->u.type; } break; @@ -1087,7 +1309,91 @@ pop_file_scope (void) maybe_apply_pending_pragma_weaks (); cgraph_finalize_compilation_unit (); } + +/* Adjust the bindings for the start of a statement expression. */ + +void +c_bindings_start_stmt_expr (struct c_spot_bindings* switch_bindings) +{ + struct c_scope *scope; + + for (scope = current_scope; scope != NULL; scope = scope->outer) + { + struct c_binding *b; + + if (!scope->has_label_bindings) + continue; + + for (b = scope->bindings; b != NULL; b = b->prev) + { + struct c_label_vars *label_vars; + unsigned int ix; + struct c_goto_bindings *g; + + if (TREE_CODE (b->decl) != LABEL_DECL) + continue; + label_vars = b->u.label; + ++label_vars->label_bindings.stmt_exprs; + for (ix = 0; + VEC_iterate (c_goto_bindings_p, label_vars->gotos, ix, g); + ++ix) + ++g->goto_bindings.stmt_exprs; + } + } + + if (switch_bindings != NULL) + ++switch_bindings->stmt_exprs; +} + +/* Adjust the bindings for the end of a statement expression. */ + +void +c_bindings_end_stmt_expr (struct c_spot_bindings *switch_bindings) +{ + struct c_scope *scope; + + for (scope = current_scope; scope != NULL; scope = scope->outer) + { + struct c_binding *b; + + if (!scope->has_label_bindings) + continue; + + for (b = scope->bindings; b != NULL; b = b->prev) + { + struct c_label_vars *label_vars; + unsigned int ix; + struct c_goto_bindings *g; + + if (TREE_CODE (b->decl) != LABEL_DECL) + continue; + label_vars = b->u.label; + --label_vars->label_bindings.stmt_exprs; + if (label_vars->label_bindings.stmt_exprs < 0) + { + label_vars->label_bindings.left_stmt_expr = true; + label_vars->label_bindings.stmt_exprs = 0; + } + for (ix = 0; + VEC_iterate (c_goto_bindings_p, label_vars->gotos, ix, g); + ++ix) + { + --g->goto_bindings.stmt_exprs; + if (g->goto_bindings.stmt_exprs < 0) + { + g->goto_bindings.left_stmt_expr = true; + g->goto_bindings.stmt_exprs = 0; + } + } + } + } + if (switch_bindings != NULL) + { + --switch_bindings->stmt_exprs; + gcc_assert (switch_bindings->stmt_exprs >= 0); + } +} /* Push a definition or a declaration of struct, union or enum tag "name". "type" should be the type node. @@ -2182,12 +2488,6 @@ pushdecl (tree x) || DECL_INITIAL (x) || !DECL_EXTERNAL (x))) DECL_CONTEXT (x) = current_function_decl; - /* If this is of variably modified type, prevent jumping into its - scope. */ - if ((TREE_CODE (x) == VAR_DECL || TREE_CODE (x) == TYPE_DECL) - && variably_modified_type_p (TREE_TYPE (x), NULL_TREE)) - c_begin_vm_scope (scope->depth); - /* Anonymous decls are just inserted in the scope. */ if (!name) { @@ -2226,8 +2526,8 @@ pushdecl (tree x) if (b_ext) { b_use = b_ext; - if (b_use->type) - TREE_TYPE (b_use->decl) = b_use->type; + if (b_use->u.type) + TREE_TYPE (b_use->decl) = b_use->u.type; } } if (duplicate_decls (x, b_use->decl)) @@ -2241,13 +2541,13 @@ pushdecl (tree x) thistype = composite_type (vistype, type); else thistype = TREE_TYPE (b_use->decl); - b_use->type = TREE_TYPE (b_use->decl); + b_use->u.type = TREE_TYPE (b_use->decl); if (TREE_CODE (b_use->decl) == FUNCTION_DECL && DECL_BUILT_IN (b_use->decl)) thistype = build_type_attribute_variant (thistype, TYPE_ATTRIBUTES - (b_use->type)); + (b_use->u.type)); TREE_TYPE (b_use->decl) = thistype; } return b_use->decl; @@ -2298,7 +2598,7 @@ pushdecl (tree x) their scopes will not have been re-entered. */ if (DECL_P (b->decl) && DECL_FILE_SCOPE_P (b->decl) && !type_saved) { - b->type = TREE_TYPE (b->decl); + b->u.type = TREE_TYPE (b->decl); type_saved = true; } if (B_IN_FILE_SCOPE (b) @@ -2324,8 +2624,8 @@ pushdecl (tree x) After the consistency checks, it will be reset to the composite of the visible types only. */ if (b && (TREE_PUBLIC (x) || same_translation_unit_p (x, b->decl)) - && b->type) - TREE_TYPE (b->decl) = b->type; + && b->u.type) + TREE_TYPE (b->decl) = b->u.type; /* The point of the same_translation_unit_p check here is, we want to detect a duplicate decl for a construct like @@ -2346,11 +2646,11 @@ pushdecl (tree x) } else thistype = type; - b->type = TREE_TYPE (b->decl); + b->u.type = TREE_TYPE (b->decl); if (TREE_CODE (b->decl) == FUNCTION_DECL && DECL_BUILT_IN (b->decl)) thistype = build_type_attribute_variant (thistype, - TYPE_ATTRIBUTES (b->type)); + TYPE_ATTRIBUTES (b->u.type)); TREE_TYPE (b->decl) = thistype; bind (name, b->decl, scope, /*invisible=*/false, /*nested=*/true, locus); @@ -2500,8 +2800,8 @@ implicitly_declare (location_t loc, tree functionid) else { tree newtype = default_function_type; - if (b->type) - TREE_TYPE (decl) = b->type; + if (b->u.type) + TREE_TYPE (decl) = b->u.type; /* Implicit declaration of a function already declared (somehow) in a different scope, or as a built-in. If this is the first time this has happened, warn; @@ -2531,7 +2831,7 @@ implicitly_declare (location_t loc, tree functionid) locate_old_decl (decl); } } - b->type = TREE_TYPE (decl); + b->u.type = TREE_TYPE (decl); TREE_TYPE (decl) = newtype; bind (functionid, decl, current_scope, /*invisible=*/false, /*nested=*/true, @@ -2603,16 +2903,26 @@ undeclared_variable (location_t loc, tree id) } /* Subroutine of lookup_label, declare_label, define_label: construct a - LABEL_DECL with all the proper frills. */ + LABEL_DECL with all the proper frills. Also create a struct + c_label_vars initialized for the current scope. */ static tree -make_label (location_t location, tree name) +make_label (location_t location, tree name, bool defining, + struct c_label_vars **p_label_vars) { tree label = build_decl (location, LABEL_DECL, name, void_type_node); + struct c_label_vars *label_vars; DECL_CONTEXT (label) = current_function_decl; DECL_MODE (label) = VOIDmode; + label_vars = GGC_NEW (struct c_label_vars); + label_vars->shadowed = NULL; + set_spot_bindings (&label_vars->label_bindings, defining); + label_vars->decls_in_scope = make_tree_vector (); + label_vars->gotos = VEC_alloc (c_goto_bindings_p, gc, 0); + *p_label_vars = label_vars; + return label; } @@ -2625,6 +2935,7 @@ tree lookup_label (tree name) { tree label; + struct c_label_vars *label_vars; if (current_function_decl == 0) { @@ -2642,17 +2953,91 @@ lookup_label (tree name) /* If the label has only been declared, update its apparent location to point here, for better diagnostics if it turns out not to have been defined. */ - if (!TREE_USED (label)) + if (DECL_INITIAL (label) == NULL_TREE) DECL_SOURCE_LOCATION (label) = input_location; return label; } /* No label binding for that identifier; make one. */ - label = make_label (input_location, name); + label = make_label (input_location, name, false, &label_vars); /* Ordinary labels go in the current function scope. */ - bind (name, label, current_function_scope, - /*invisible=*/false, /*nested=*/false, UNKNOWN_LOCATION); + bind_label (name, label, current_function_scope, label_vars); + + return label; +} + +/* Issue a warning about DECL for a goto statement at GOTO_LOC going + to LABEL. */ + +static void +warn_about_goto (location_t goto_loc, tree label, tree decl) +{ + if (variably_modified_type_p (TREE_TYPE (decl), NULL_TREE)) + error_at (goto_loc, + "jump into scope of identifier with variably modified type"); + else + warning_at (goto_loc, OPT_Wjump_misses_init, + "jump skips variable initialization"); + inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", label); + inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl); +} + +/* Look up a label because of a goto statement. This is like + lookup_label, but also issues any appropriate warnings. */ + +tree +lookup_label_for_goto (location_t loc, tree name) +{ + tree label; + struct c_label_vars *label_vars; + unsigned int ix; + tree decl; + + label = lookup_label (name); + if (label == NULL_TREE) + return NULL_TREE; + + /* If we are jumping to a different function, we can't issue any + useful warnings. */ + if (DECL_CONTEXT (label) != current_function_decl) + { + gcc_assert (C_DECLARED_LABEL_FLAG (label)); + return label; + } + + label_vars = I_LABEL_BINDING (name)->u.label; + + /* If the label has not yet been defined, then push this goto on a + list for possible later warnings. */ + if (label_vars->label_bindings.scope == NULL) + { + struct c_goto_bindings *g; + + g = GGC_NEW (struct c_goto_bindings); + g->loc = loc; + set_spot_bindings (&g->goto_bindings, true); + VEC_safe_push (c_goto_bindings_p, gc, label_vars->gotos, g); + return label; + } + + /* If there are any decls in label_vars->decls_in_scope, then this + goto has missed the declaration of the decl. This happens for a + case like + int i = 1; + lab: + ... + goto lab; + Issue a warning or error. */ + for (ix = 0; VEC_iterate (tree, label_vars->decls_in_scope, ix, decl); ++ix) + warn_about_goto (loc, label, decl); + + if (label_vars->label_bindings.left_stmt_expr) + { + error_at (loc, "jump into statement expression"); + inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", label); + } + return label; } @@ -2665,6 +3050,7 @@ declare_label (tree name) { struct c_binding *b = I_LABEL_BINDING (name); tree label; + struct c_label_vars *label_vars; /* Check to make sure that the label hasn't already been declared at this scope */ @@ -2677,15 +3063,74 @@ declare_label (tree name) return b->decl; } - label = make_label (input_location, name); + label = make_label (input_location, name, false, &label_vars); C_DECLARED_LABEL_FLAG (label) = 1; /* Declared labels go in the current scope. */ - bind (name, label, current_scope, - /*invisible=*/false, /*nested=*/false, UNKNOWN_LOCATION); + bind_label (name, label, current_scope, label_vars); + return label; } +/* When we define a label, issue any appropriate warnings if there are + any gotos earlier in the function which jump to this label. */ + +static void +check_earlier_gotos (tree label, struct c_label_vars* label_vars) +{ + unsigned int ix; + struct c_goto_bindings *g; + + for (ix = 0; + VEC_iterate (c_goto_bindings_p, label_vars->gotos, ix, g); + ++ix) + { + struct c_binding *b; + struct c_scope *scope; + + /* We have a goto to this label. The goto is going forward. In + g->scope, the goto is going to skip any binding which was + defined after g->bindings_in_scope. */ + for (b = g->goto_bindings.scope->bindings; + b != g->goto_bindings.bindings_in_scope; + b = b->prev) + { + if (decl_jump_unsafe (b->decl)) + warn_about_goto (g->loc, label, b->decl); + } + + /* We also need to warn about decls defined in any scopes + between the scope of the label and the scope of the goto. */ + for (scope = label_vars->label_bindings.scope; + scope != g->goto_bindings.scope; + scope = scope->outer) + { + gcc_assert (scope != NULL); + if (scope == label_vars->label_bindings.scope) + b = label_vars->label_bindings.bindings_in_scope; + else + b = scope->bindings; + for (; b != NULL; b = b->prev) + { + if (decl_jump_unsafe (b->decl)) + warn_about_goto (g->loc, label, b->decl); + } + } + + if (g->goto_bindings.stmt_exprs > 0) + { + error_at (g->loc, "jump into statement expression"); + inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", + label); + } + } + + /* Now that the label is defined, we will issue warnings about + subsequent gotos to this label when we see them. */ + VEC_truncate (c_goto_bindings_p, label_vars->gotos, 0); + label_vars->gotos = NULL; +} + /* Define a label, specifying the location in the source file. Return the LABEL_DECL node for the label, if the definition is valid. Otherwise return 0. */ @@ -2698,7 +3143,6 @@ define_label (location_t location, tree name) if there is a containing function with a declared label with the same name. */ tree label = I_LABEL_DECL (name); - struct c_label_list *nlist_se, *nlist_vm; if (label && ((DECL_CONTEXT (label) == current_function_decl @@ -2712,24 +3156,27 @@ define_label (location_t location, tree name) } else if (label && DECL_CONTEXT (label) == current_function_decl) { + struct c_label_vars *label_vars = I_LABEL_BINDING (name)->u.label; + /* The label has been used or declared already in this function, but not defined. Update its location to point to this definition. */ - if (C_DECL_UNDEFINABLE_STMT_EXPR (label)) - error_at (location, "jump into statement expression"); - if (C_DECL_UNDEFINABLE_VM (label)) - error_at (location, - "jump into scope of identifier with variably modified type"); DECL_SOURCE_LOCATION (label) = location; + set_spot_bindings (&label_vars->label_bindings, true); + + /* Issue warnings as required about any goto statements from + earlier in the function. */ + check_earlier_gotos (label, label_vars); } else { + struct c_label_vars *label_vars; + /* No label binding for that identifier; make one. */ - label = make_label (location, name); + label = make_label (location, name, true, &label_vars); /* Ordinary labels go in the current function scope. */ - bind (name, label, current_function_scope, - /*invisible=*/false, /*nested=*/false, UNKNOWN_LOCATION); + bind_label (name, label, current_function_scope, label_vars); } if (!in_system_header && lookup_name (name)) @@ -2737,21 +3184,82 @@ define_label (location_t location, tree name) "traditional C lacks a separate namespace " "for labels, identifier %qE conflicts", name); - nlist_se = XOBNEW (&parser_obstack, struct c_label_list); - nlist_se->next = label_context_stack_se->labels_def; - nlist_se->label = label; - label_context_stack_se->labels_def = nlist_se; - - nlist_vm = XOBNEW (&parser_obstack, struct c_label_list); - nlist_vm->next = label_context_stack_vm->labels_def; - nlist_vm->label = label; - label_context_stack_vm->labels_def = nlist_vm; - /* Mark label as having been defined. */ DECL_INITIAL (label) = error_mark_node; return label; } +/* Get the bindings for a new switch statement. This is used to issue + warnings as appropriate for jumps from the switch to case or + default labels. */ + +struct c_spot_bindings * +c_get_switch_bindings (void) +{ + struct c_spot_bindings *switch_bindings; + + switch_bindings = XNEW (struct c_spot_bindings); + set_spot_bindings (switch_bindings, true); + return switch_bindings; +} + +void +c_release_switch_bindings (struct c_spot_bindings *bindings) +{ + gcc_assert (bindings->stmt_exprs == 0 && !bindings->left_stmt_expr); + XDELETE (bindings); +} + +/* This is called at the point of a case or default label to issue + warnings about decls as needed. It returns true if it found an + error, not just a warning. */ + +bool +c_check_switch_jump_warnings (struct c_spot_bindings *switch_bindings, + location_t switch_loc, location_t case_loc) +{ + bool saw_error; + struct c_scope *scope; + + saw_error = false; + for (scope = current_scope; + scope != switch_bindings->scope; + scope = scope->outer) + { + struct c_binding *b; + + gcc_assert (scope != NULL); + for (b = scope->bindings; b != NULL; b = b->prev) + { + if (decl_jump_unsafe (b->decl)) + { + if (variably_modified_type_p (TREE_TYPE (b->decl), NULL_TREE)) + { + saw_error = true; + error_at (case_loc, + ("switch jumps into scope of identifier with " + "variably modified type")); + } + else + warning_at (case_loc, OPT_Wjump_misses_init, + "switch jumps over variable initialization"); + inform (switch_loc, "switch starts here"); + inform (DECL_SOURCE_LOCATION (b->decl), "%qD declared here", + b->decl); + } + } + } + + if (switch_bindings->stmt_exprs > 0) + { + saw_error = true; + error_at (case_loc, "switch jumps into statement expression"); + inform (switch_loc, "switch starts here"); + } + + return saw_error; +} + /* Given NAME, an IDENTIFIER_NODE, return the structure (or union or enum) definition for that name. If THISLEVEL_ONLY is nonzero, searches only the current_scope. @@ -3610,10 +4118,10 @@ finish_decl (tree decl, location_t init_loc, tree init, b_ext = b_ext->shadowed; if (b_ext) { - if (b_ext->type) - b_ext->type = composite_type (b_ext->type, type); + if (b_ext->u.type) + b_ext->u.type = composite_type (b_ext->u.type, type); else - b_ext->type = type; + b_ext->u.type = type; } } break; @@ -6610,8 +7118,6 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator, { tree decl1, old_decl; tree restype, resdecl; - struct c_label_context_se *nstack_se; - struct c_label_context_vm *nstack_vm; location_t loc; current_function_returns_value = 0; /* Assume, until we see it does. */ @@ -6620,19 +7126,6 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator, warn_about_return_type = 0; c_switch_stack = NULL; - nstack_se = XOBNEW (&parser_obstack, struct c_label_context_se); - nstack_se->labels_def = NULL; - nstack_se->labels_used = NULL; - nstack_se->next = label_context_stack_se; - label_context_stack_se = nstack_se; - - nstack_vm = XOBNEW (&parser_obstack, struct c_label_context_vm); - nstack_vm->labels_def = NULL; - nstack_vm->labels_used = NULL; - nstack_vm->scope = 0; - nstack_vm->next = label_context_stack_vm; - label_context_stack_vm = nstack_vm; - /* Indicate no valid break/continue context by setting these variables to some non-null, non-label value. We'll notice and emit the proper error message in c_finish_bc_stmt. */ @@ -6644,11 +7137,7 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator, /* If the declarator is not suitable for a function definition, cause a syntax error. */ if (decl1 == 0) - { - label_context_stack_se = label_context_stack_se->next; - label_context_stack_vm = label_context_stack_vm->next; - return 0; - } + return 0; loc = DECL_SOURCE_LOCATION (decl1); @@ -6730,7 +7219,7 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator, { tree ext_decl, ext_type; ext_decl = b->decl; - ext_type = b->type ? b->type : TREE_TYPE (ext_decl); + ext_type = b->u.type ? b->u.type : TREE_TYPE (ext_decl); if (TREE_CODE (ext_type) == FUNCTION_TYPE && comptypes (TREE_TYPE (TREE_TYPE (decl1)), TREE_TYPE (ext_type))) @@ -7282,9 +7771,6 @@ finish_function (void) { tree fndecl = current_function_decl; - label_context_stack_se = label_context_stack_se->next; - label_context_stack_vm = label_context_stack_vm->next; - if (TREE_CODE (fndecl) == FUNCTION_DECL && targetm.calls.promote_prototypes (TREE_TYPE (fndecl))) { |