This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PATCH RFA: Add new warning for jump across initialized variable


This patch adds a new warning to the C frontend: -Wgoto-misses-init.
This warns about cases where a goto, or a switch, branches across the
initialization of a variable.  For example:

int
f2 (int a)
{
  if (a > 0)
    {
      if (a < 0)
	goto lab;
      return 1;
    }
  else
    {
      int i = 7;
    lab:
      return i;
    }
}

Here the branch to 'lab' skips the initialization of i.

Because I think this sort of code is rather dubious and ill-defined,
and it is also easy to fix, I've made -Wall turn on -Wgoto-misses-init
by default.  Because the C++ frontend warns about this sort of thing,
as is required by the C++ language spec, I've also made -Wc++-compat
turn on the new warning by default.

The C frontend already had warnings about two special cases of
goto/switch: a jump into the scope of a variably modified type, and a
jump into a statement expression.  I've incorporated those warnings
into the new framework I created for -Wgoto-misses-init.  I think that
will give better location information and informative notes.  In
particular, for the case of a declaration with variably modified type,
it will now report the location of the declaration.  I completely
removed the code which supported the existing warnings.  I updated
several existing test cases for the new notes, and for some changed
error text, but did not otherwise change the test cases.  I also added
two new test cases.

I encountered one minor oddity in this patch.  I added a new union
field to the c_binding struct.  Because this struct is garbage
collected, I added a GTY desc field to describe the union.  That desc
tag referred to a field in the union defined earlier, decl, with type
tree.  It turns out that during PCH fields with pointer type, like
decl, are modified to new addresses.  This is done in the order the
fields appear in the struct, which meant that the decl field was
modified before it was used in the desc tag for the union.  This led
to an obscure crash when creating a PCH.  I worked around the crash by
reordering the fields.

The first patch in this message implements the new option.  This patch
requires approval from a C frontend maintainer.

The second patch in this message fixes the three cases in the gcc
bootstrap which trigger this warning.  I think this patch counts as
obvious.  This patch looks large but is almost entirely whitespace
changes.

Bootstrapped and tested on x86_64-unknown-linux-gnu.  OK for mainline?

Ian


gcc/ChangeLog:

2009-06-10  Ian Lance Taylor  <iant@google.com>

	* c.opt (Wgoto-misses-init): New warning.
	* c-opts.c (c_common_handle_option): Set warn_goto_misses_init for
	-Wall and -Wc++-compat if not already set.
	(c_common_post_options): Clear warn_goto_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_goto_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): Add loc parameter.  Change all callers.
	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_goto_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_goto_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 -Wgoto-misses-init.
	(Warning Options): Document -Wgoto-misses-init.

gcc/objc/ChangeLog:

2009-06-10  Ian Lance Taylor  <iant@google.com>

	* objc-act.c (objc_start_function): Don't set
	label_context_stack_se or label_context_stack_vm.

gcc/testsuite/ChangeLog:

2009-06-10  Ian Lance Taylor  <iant@google.com>

	* gcc.dg/Wgoto-misses-init-1.c: New testcase.
	* gcc.dg/Wgoto-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.
	* 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.


Index: c-tree.h
===================================================================
--- c-tree.h	(revision 148327)
+++ c-tree.h	(working copy)
@@ -108,29 +108,6 @@ struct GTY(()) lang_type {
    sizeof and typeof it is set for other function decls as well.  */
 #define C_DECL_USED(EXP) DECL_LANG_FLAG_5 (FUNCTION_DECL_CHECK (EXP))
 
-/* Record whether a label was defined in a statement expression which
-   has finished and so can no longer be jumped to.  */
-#define C_DECL_UNJUMPABLE_STMT_EXPR(EXP)	\
-  DECL_LANG_FLAG_6 (LABEL_DECL_CHECK (EXP))
-
-/* Record whether a label was the subject of a goto from outside the
-   current level of statement expression nesting and so cannot be
-   defined right now.  */
-#define C_DECL_UNDEFINABLE_STMT_EXPR(EXP)	\
-  DECL_LANG_FLAG_7 (LABEL_DECL_CHECK (EXP))
-
-/* Record whether a label was defined in the scope of an identifier
-   with variably modified type which has finished and so can no longer
-   be jumped to.  */
-#define C_DECL_UNJUMPABLE_VM(EXP)	\
-  DECL_LANG_FLAG_3 (LABEL_DECL_CHECK (EXP))
-
-/* Record whether a label was the subject of a goto from outside the
-   current level of scopes of identifiers with variably modified type
-   and so cannot be defined right now.  */
-#define C_DECL_UNDEFINABLE_VM(EXP)	\
-  DECL_LANG_FLAG_5 (LABEL_DECL_CHECK (EXP))
-
 /* Record whether a variable has been declared threadprivate by
    #pragma omp threadprivate.  */
 #define C_DECL_THREADPRIVATE_P(DECL) DECL_LANG_FLAG_3 (VAR_DECL_CHECK (DECL))
@@ -421,45 +398,6 @@ struct GTY(()) language_function {
   int warn_about_return_type;
 };
 
-/* Save lists of labels used or defined in particular contexts.
-   Allocated on the parser obstack.  */
-
-struct c_label_list
-{
-  /* The label at the head of the list.  */
-  tree label;
-  /* The rest of the list.  */
-  struct c_label_list *next;
-};
-
-/* Statement expression context.  */
-
-struct c_label_context_se
-{
-  /* The labels defined at this level of nesting.  */
-  struct c_label_list *labels_def;
-  /* The labels used at this level of nesting.  */
-  struct c_label_list *labels_used;
-  /* The next outermost context.  */
-  struct c_label_context_se *next;
-};
-
-/* Context of variably modified declarations.  */
-
-struct c_label_context_vm
-{
-  /* The labels defined at this level of nesting.  */
-  struct c_label_list *labels_def;
-  /* The labels used at this level of nesting.  */
-  struct c_label_list *labels_used;
-  /* The scope of this context.  Multiple contexts may be at the same
-     numbered scope, since each variably modified declaration starts a
-     new context.  */
-  unsigned scope;
-  /* The next outermost context.  */
-  struct c_label_context_vm *next;
-};
-
 /* Used when parsing an enum.  Initialized by start_enum.  */
 struct c_enum_contents
 {
@@ -491,6 +429,7 @@ extern void c_parse_init (void);
 extern void gen_aux_info_record (tree, int, int, int);
 
 /* in c-decl.c */
+struct c_spot_bindings;
 extern struct obstack parser_obstack;
 extern tree c_break_label;
 extern tree c_cont_label;
@@ -498,6 +437,8 @@ extern tree c_cont_label;
 extern int global_bindings_p (void);
 extern void push_scope (void);
 extern tree pop_scope (void);
+extern void c_bindings_start_stmt_expr (struct c_spot_bindings *);
+extern void c_bindings_end_stmt_expr (struct c_spot_bindings *);
 
 extern void record_inline_static (location_t, tree, tree,
 				  enum c_inline_static_type);
@@ -512,8 +453,13 @@ extern tree check_for_loop_decls (void);
 extern void mark_forward_parm_decls (void);
 extern void declare_parm_level (void);
 extern void undeclared_variable (tree, location_t);
+extern tree lookup_label_for_goto (location_t, tree);
 extern tree declare_label (tree);
 extern tree define_label (location_t, tree);
+extern struct c_spot_bindings *c_get_switch_bindings (void);
+extern void c_release_switch_bindings (struct c_spot_bindings *);
+extern bool c_check_switch_goto_warnings (struct c_spot_bindings *,
+					  location_t, location_t);
 extern void c_maybe_initialize_eh (void);
 extern void finish_decl (tree, tree, tree, tree);
 extern tree finish_enum (tree, tree, tree);
@@ -583,8 +529,6 @@ extern int in_sizeof;
 extern int in_typeof;
 
 extern struct c_switch *c_switch_stack;
-extern struct c_label_context_se *label_context_stack_se;
-extern struct c_label_context_vm *label_context_stack_vm;
 
 extern tree c_objc_common_truthvalue_conversion (location_t, tree);
 extern tree require_complete_type (tree);
@@ -640,10 +584,8 @@ extern tree c_process_expr_stmt (tree);
 extern tree c_finish_expr_stmt (tree);
 extern tree c_finish_return (tree, tree);
 extern tree c_finish_bc_stmt (tree *, bool);
-extern tree c_finish_goto_label (tree);
+extern tree c_finish_goto_label (location_t, tree);
 extern tree c_finish_goto_ptr (tree);
-extern void c_begin_vm_scope (unsigned int);
-extern void c_end_vm_scope (unsigned int);
 extern tree c_expr_to_decl (tree, bool *, bool *);
 extern tree c_begin_omp_parallel (void);
 extern tree c_finish_omp_parallel (tree, tree);
Index: c-decl.c
===================================================================
--- c-decl.c	(revision 148327)
+++ c-decl.c	(working copy)
@@ -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,70 @@ union GTY((desc ("TREE_CODE (&%h.generic
   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.  While we need to gather decls for labels
+   in all cases, since we might find a goto later on, we only need to
+   gather them for goto statements until we have found the label for
+   the goto statement.  */
+
+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 +424,11 @@ struct GTY((chain_next ("%h.outer"))) c_
 
   /* 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 +593,7 @@ bind (tree name, tree decl, struct c_sco
   b->inner_comp = 0;
   b->locus = locus;
 
-  b->type = 0;
+  b->u.type = NULL;
 
   b->prev = scope->bindings;
   scope->bindings = b;
@@ -569,6 +644,24 @@ free_binding_and_advance (struct c_bindi
   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.  */
@@ -640,6 +733,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_goto_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 *
@@ -783,6 +943,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
@@ -808,7 +1029,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.  */
@@ -872,6 +1093,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:
@@ -997,8 +1222,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;
 
@@ -1085,7 +1310,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.
@@ -2178,12 +2487,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)
     {
@@ -2222,8 +2525,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))
@@ -2237,13 +2540,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;
@@ -2294,7 +2597,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)
@@ -2320,8 +2623,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
@@ -2342,11 +2645,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);
@@ -2496,8 +2799,8 @@ implicitly_declare (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;
@@ -2528,7 +2831,7 @@ implicitly_declare (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,
@@ -2600,17 +2903,27 @@ undeclared_variable (tree id, location_t
 }
 
 /* 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 (tree name, location_t location)
+make_label (tree name, bool defining, location_t location,
+	    struct c_label_vars **p_label_vars)
 {
   tree label = build_decl (LABEL_DECL, name, void_type_node);
+  struct c_label_vars *label_vars;
 
   DECL_CONTEXT (label) = current_function_decl;
   DECL_MODE (label) = VOIDmode;
   DECL_SOURCE_LOCATION (label) = location;
 
+  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;
 }
 
@@ -2623,6 +2936,7 @@ tree
 lookup_label (tree name)
 {
   tree label;
+  struct c_label_vars *label_vars;
 
   if (current_function_decl == 0)
     {
@@ -2640,17 +2954,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 (name, input_location);
+  label = make_label (name, false, input_location, &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_Wgoto_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 = XNEW (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;
 }
 
@@ -2663,6 +3051,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 */
@@ -2675,15 +3064,74 @@ declare_label (tree name)
       return b->decl;
     }
 
-  label = make_label (name, input_location);
+  label = make_label (name, false, input_location, &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.  */
@@ -2696,7 +3144,6 @@ define_label (location_t location, tree 
      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
@@ -2710,45 +3157,108 @@ define_label (location_t location, tree 
     }
   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 ("%Jjump into statement expression", label);
-      if (C_DECL_UNDEFINABLE_VM (label))
-	error ("%Jjump into scope of identifier with variably modified type",
-	       label);
       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 (name, location);
+      label = make_label (name, true, location, &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))
     warning (OPT_Wtraditional, "%Htraditional C lacks a separate namespace "
 	     "for labels, identifier %qE conflicts", &location, 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_goto_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_Wgoto_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)
+    {
+      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.
@@ -3599,10 +4109,10 @@ finish_decl (tree decl, tree init, tree 
 		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;
@@ -6569,8 +7079,6 @@ start_function (struct c_declspecs *decl
 {
   tree decl1, old_decl;
   tree restype, resdecl;
-  struct c_label_context_se *nstack_se;
-  struct c_label_context_vm *nstack_vm;
 
   current_function_returns_value = 0;  /* Assume, until we see it does.  */
   current_function_returns_null = 0;
@@ -6578,19 +7086,6 @@ start_function (struct c_declspecs *decl
   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.  */
@@ -6602,11 +7097,7 @@ start_function (struct c_declspecs *decl
   /* 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;
 
   decl_attributes (&decl1, attributes, 0);
 
@@ -6685,7 +7176,7 @@ start_function (struct c_declspecs *decl
 	    {
 	      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)))
@@ -7200,9 +7691,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)))
     {
Index: c.opt
===================================================================
--- c.opt	(revision 148327)
+++ c.opt	(working copy)
@@ -250,6 +250,10 @@ Warn about zero-length formats
 Wformat=
 C ObjC C++ ObjC++ Joined Warning
 
+Wgoto-misses-init
+C Objc Var(warn_goto_misses_init) Init(-1) Warning
+Warn when a goto statement misses a variable initialization
+
 Wignored-qualifiers
 C C++ Var(warn_ignored_qualifiers) Init(-1) Warning
 Warn whenever type qualifiers are ignored.
Index: c-typeck.c
===================================================================
--- c-typeck.c	(revision 148327)
+++ c-typeck.c	(working copy)
@@ -70,9 +70,6 @@ int in_sizeof;
 /* The level of nesting inside "typeof".  */
 int in_typeof;
 
-struct c_label_context_se *label_context_stack_se;
-struct c_label_context_vm *label_context_stack_vm;
-
 /* Nonzero if we've already printed a "missing braces around initializer"
    message within this initializer.  */
 static int missing_braces_mentioned;
@@ -7929,47 +7926,11 @@ build_asm_expr (tree string, tree output
 /* Generate a goto statement to LABEL.  */
 
 tree
-c_finish_goto_label (tree label)
+c_finish_goto_label (location_t loc, tree label)
 {
-  tree decl = lookup_label (label);
+  tree decl = lookup_label_for_goto (loc, label);
   if (!decl)
     return NULL_TREE;
-
-  if (C_DECL_UNJUMPABLE_STMT_EXPR (decl))
-    {
-      error ("jump into statement expression");
-      return NULL_TREE;
-    }
-
-  if (C_DECL_UNJUMPABLE_VM (decl))
-    {
-      error ("jump into scope of identifier with variably modified type");
-      return NULL_TREE;
-    }
-
-  if (!C_DECL_UNDEFINABLE_STMT_EXPR (decl))
-    {
-      /* No jump from outside this statement expression context, so
-	 record that there is a jump from within this context.  */
-      struct c_label_list *nlist;
-      nlist = XOBNEW (&parser_obstack, struct c_label_list);
-      nlist->next = label_context_stack_se->labels_used;
-      nlist->label = decl;
-      label_context_stack_se->labels_used = nlist;
-    }
-
-  if (!C_DECL_UNDEFINABLE_VM (decl))
-    {
-      /* No jump from outside this context context of identifiers with
-	 variably modified type, so record that there is a jump from
-	 within this context.  */
-      struct c_label_list *nlist;
-      nlist = XOBNEW (&parser_obstack, struct c_label_list);
-      nlist->next = label_context_stack_vm->labels_used;
-      nlist->label = decl;
-      label_context_stack_vm->labels_used = nlist;
-    }
-
   TREE_USED (decl) = 1;
   return add_stmt (build1 (GOTO_EXPR, void_type_node, decl));
 }
@@ -8128,15 +8089,9 @@ struct c_switch {
      of the GNU case range extension.  */
   splay_tree cases;
 
-  /* Number of nested statement expressions within this switch
-     statement; if nonzero, case and default labels may not
-     appear.  */
-  unsigned int blocked_stmt_expr;
-
-  /* Scope of outermost declarations of identifiers with variably
-     modified type within this switch statement; if nonzero, case and
-     default labels may not appear.  */
-  unsigned int blocked_vm;
+  /* The bindings at the point of the switch.  This is used for
+     warnings crossing decls when branching to a case label.  */
+  struct c_spot_bindings *bindings;
 
   /* The next node on the stack.  */
   struct c_switch *next;
@@ -8195,8 +8150,7 @@ c_start_case (tree exp)
   cs->switch_expr = build3 (SWITCH_EXPR, orig_type, exp, NULL_TREE, NULL_TREE);
   cs->orig_type = orig_type;
   cs->cases = splay_tree_new (case_compare, NULL, NULL);
-  cs->blocked_stmt_expr = 0;
-  cs->blocked_vm = 0;
+  cs->bindings = c_get_switch_bindings ();
   cs->next = c_switch_stack;
   c_switch_stack = cs;
 
@@ -8226,39 +8180,26 @@ do_case (tree low_value, tree high_value
 		 "case label is not an integer constant expression");
     }
 
-  if (c_switch_stack && !c_switch_stack->blocked_stmt_expr
-      && !c_switch_stack->blocked_vm)
-    {
-      label = c_add_case_label (c_switch_stack->cases,
-				SWITCH_COND (c_switch_stack->switch_expr),
-				c_switch_stack->orig_type,
-				low_value, high_value);
-      if (label == error_mark_node)
-	label = NULL_TREE;
-    }
-  else if (c_switch_stack && c_switch_stack->blocked_stmt_expr)
-    {
-      if (low_value)
-	error ("case label in statement expression not containing "
-	       "enclosing switch statement");
-      else
-	error ("%<default%> label in statement expression not containing "
-	       "enclosing switch statement");
-    }
-  else if (c_switch_stack && c_switch_stack->blocked_vm)
+  if (c_switch_stack == NULL)
     {
       if (low_value)
-	error ("case label in scope of identifier with variably modified "
-	       "type not containing enclosing switch statement");
+	error ("case label not within a switch statement");
       else
-	error ("%<default%> label in scope of identifier with variably "
-	       "modified type not containing enclosing switch statement");
+	error ("%<default%> label not within a switch statement");
+      return NULL_TREE;
     }
-  else if (low_value)
-    error ("case label not within a switch statement");
-  else
-    error ("%<default%> label not within a switch statement");
 
+  if (c_check_switch_goto_warnings (c_switch_stack->bindings,
+				    EXPR_LOCATION (c_switch_stack->switch_expr),
+				    input_location))
+    return NULL_TREE;
+
+  label = c_add_case_label (c_switch_stack->cases,
+			    SWITCH_COND (c_switch_stack->switch_expr),
+			    c_switch_stack->orig_type,
+			    low_value, high_value);
+  if (label == error_mark_node)
+    label = NULL_TREE;
   return label;
 }
 
@@ -8272,11 +8213,6 @@ c_finish_case (tree body)
 
   SWITCH_BODY (cs->switch_expr) = body;
 
-  /* We must not be within a statement expression nested in the switch
-     at this point; we might, however, be within the scope of an
-     identifier with variably modified type nested in the switch.  */
-  gcc_assert (!cs->blocked_stmt_expr);
-
   /* Emit warnings as needed.  */
   if (EXPR_HAS_LOCATION (cs->switch_expr))
     switch_location = EXPR_LOCATION (cs->switch_expr);
@@ -8289,6 +8225,7 @@ c_finish_case (tree body)
   /* Pop the stack.  */
   c_switch_stack = cs->next;
   splay_tree_delete (cs->cases);
+  c_release_switch_bindings (cs->bindings);
   XDELETE (cs);
 }
 
@@ -8538,30 +8475,16 @@ tree
 c_begin_stmt_expr (void)
 {
   tree ret;
-  struct c_label_context_se *nstack;
-  struct c_label_list *glist;
 
   /* We must force a BLOCK for this level so that, if it is not expanded
      later, there is a way to turn off the entire subtree of blocks that
      are contained in it.  */
   keep_next_level ();
   ret = c_begin_compound_stmt (true);
-  if (c_switch_stack)
-    {
-      c_switch_stack->blocked_stmt_expr++;
-      gcc_assert (c_switch_stack->blocked_stmt_expr != 0);
-    }
-  for (glist = label_context_stack_se->labels_used;
-       glist != NULL;
-       glist = glist->next)
-    {
-      C_DECL_UNDEFINABLE_STMT_EXPR (glist->label) = 1;
-    }
-  nstack = XOBNEW (&parser_obstack, struct c_label_context_se);
-  nstack->labels_def = NULL;
-  nstack->labels_used = NULL;
-  nstack->next = label_context_stack_se;
-  label_context_stack_se = nstack;
+
+  c_bindings_start_stmt_expr (c_switch_stack == NULL
+			      ? NULL
+			      : c_switch_stack->bindings);
 
   /* Mark the current statement list as belonging to a statement list.  */
   STATEMENT_LIST_STMT_EXPR (ret) = 1;
@@ -8574,37 +8497,12 @@ c_finish_stmt_expr (tree body)
 {
   tree last, type, tmp, val;
   tree *last_p;
-  struct c_label_list *dlist, *glist, *glist_prev = NULL;
 
   body = c_end_compound_stmt (body, true);
-  if (c_switch_stack)
-    {
-      gcc_assert (c_switch_stack->blocked_stmt_expr != 0);
-      c_switch_stack->blocked_stmt_expr--;
-    }
-  /* It is no longer possible to jump to labels defined within this
-     statement expression.  */
-  for (dlist = label_context_stack_se->labels_def;
-       dlist != NULL;
-       dlist = dlist->next)
-    {
-      C_DECL_UNJUMPABLE_STMT_EXPR (dlist->label) = 1;
-    }
-  /* It is again possible to define labels with a goto just outside
-     this statement expression.  */
-  for (glist = label_context_stack_se->next->labels_used;
-       glist != NULL;
-       glist = glist->next)
-    {
-      C_DECL_UNDEFINABLE_STMT_EXPR (glist->label) = 0;
-      glist_prev = glist;
-    }
-  if (glist_prev != NULL)
-    glist_prev->next = label_context_stack_se->labels_used;
-  else
-    label_context_stack_se->next->labels_used
-      = label_context_stack_se->labels_used;
-  label_context_stack_se = label_context_stack_se->next;
+
+  c_bindings_end_stmt_expr (c_switch_stack == NULL
+			    ? NULL
+			    : c_switch_stack->bindings);
 
   /* Locate the last statement in BODY.  See c_end_compound_stmt
      about always returning a BIND_EXPR.  */
@@ -8684,80 +8582,6 @@ c_finish_stmt_expr (tree body)
 
   return build4 (TARGET_EXPR, type, tmp, body, NULL_TREE, NULL_TREE);
 }
-
-/* Begin the scope of an identifier of variably modified type, scope
-   number SCOPE.  Jumping from outside this scope to inside it is not
-   permitted.  */
-
-void
-c_begin_vm_scope (unsigned int scope)
-{
-  struct c_label_context_vm *nstack;
-  struct c_label_list *glist;
-
-  gcc_assert (scope > 0);
-
-  /* At file_scope, we don't have to do any processing.  */
-  if (label_context_stack_vm == NULL)
-    return;
-
-  if (c_switch_stack && !c_switch_stack->blocked_vm)
-    c_switch_stack->blocked_vm = scope;
-  for (glist = label_context_stack_vm->labels_used;
-       glist != NULL;
-       glist = glist->next)
-    {
-      C_DECL_UNDEFINABLE_VM (glist->label) = 1;
-    }
-  nstack = XOBNEW (&parser_obstack, struct c_label_context_vm);
-  nstack->labels_def = NULL;
-  nstack->labels_used = NULL;
-  nstack->scope = scope;
-  nstack->next = label_context_stack_vm;
-  label_context_stack_vm = nstack;
-}
-
-/* End a scope which may contain identifiers of variably modified
-   type, scope number SCOPE.  */
-
-void
-c_end_vm_scope (unsigned int scope)
-{
-  if (label_context_stack_vm == NULL)
-    return;
-  if (c_switch_stack && c_switch_stack->blocked_vm == scope)
-    c_switch_stack->blocked_vm = 0;
-  /* We may have a number of nested scopes of identifiers with
-     variably modified type, all at this depth.  Pop each in turn.  */
-  while (label_context_stack_vm->scope == scope)
-    {
-      struct c_label_list *dlist, *glist, *glist_prev = NULL;
-
-      /* It is no longer possible to jump to labels defined within this
-	 scope.  */
-      for (dlist = label_context_stack_vm->labels_def;
-	   dlist != NULL;
-	   dlist = dlist->next)
-	{
-	  C_DECL_UNJUMPABLE_VM (dlist->label) = 1;
-	}
-      /* It is again possible to define labels with a goto just outside
-	 this scope.  */
-      for (glist = label_context_stack_vm->next->labels_used;
-	   glist != NULL;
-	   glist = glist->next)
-	{
-	  C_DECL_UNDEFINABLE_VM (glist->label) = 0;
-	  glist_prev = glist;
-	}
-      if (glist_prev != NULL)
-	glist_prev->next = label_context_stack_vm->labels_used;
-      else
-	label_context_stack_vm->next->labels_used
-	  = label_context_stack_vm->labels_used;
-      label_context_stack_vm = label_context_stack_vm->next;
-    }
-}
 
 /* Begin and end compound statements.  This is as simple as pushing
    and popping new statement lists from the tree.  */
Index: c-opts.c
===================================================================
--- c-opts.c	(revision 148327)
+++ c-opts.c	(working copy)
@@ -395,6 +395,8 @@ c_common_handle_option (size_t scode, co
 	warn_strict_overflow = value;
       warn_array_bounds = value;
       warn_volatile_register_var = value;
+      if (warn_goto_misses_init == -1)
+	warn_goto_misses_init = value;
 
       /* Only warn about unknown pragmas that are not in system
 	 headers.  */
@@ -445,6 +447,10 @@ c_common_handle_option (size_t scode, co
 	 implies -Wenum-compare.  */
       if (warn_enum_compare == -1 && value)
 	warn_enum_compare = value;
+      /* Because C++ always warns about a goto which misses an
+	 initialization, -Wc++-compat turns on -Wgoto-misses-init.  */
+      if (warn_goto_misses_init == -1 && value)
+	warn_goto_misses_init = value;
       break;
 
     case OPT_Wdeprecated:
@@ -1083,6 +1089,8 @@ c_common_post_options (const char **pfil
     warn_strict_aliasing = 0;
   if (warn_strict_overflow == -1)
     warn_strict_overflow = 0;
+  if (warn_goto_misses_init == -1)
+    warn_goto_misses_init = 0;
 
   /* -Woverlength-strings is off by default, but is enabled by -pedantic.
      It is never enabled in C++, as the minimum limit is not normative
Index: c-parser.c
===================================================================
--- c-parser.c	(revision 148327)
+++ c-parser.c	(working copy)
@@ -3754,7 +3754,8 @@ c_parser_statement_after_labels (c_parse
 	  c_parser_consume_token (parser);
 	  if (c_parser_next_token_is (parser, CPP_NAME))
 	    {
-	      stmt = c_finish_goto_label (c_parser_peek_token (parser)->value);
+	      stmt = c_finish_goto_label (loc,
+					  c_parser_peek_token (parser)->value);
 	      c_parser_consume_token (parser);
 	    }
 	  else if (c_parser_next_token_is (parser, CPP_MULT))
Index: objc/objc-act.c
===================================================================
--- objc/objc-act.c	(revision 148327)
+++ objc/objc-act.c	(working copy)
@@ -8587,19 +8587,6 @@ objc_start_function (tree name, tree typ
   cplus_decl_attributes (&fndecl, attrs, 0);
   start_preparsed_function (fndecl, attrs, /*flags=*/SF_DEFAULT);
 #else
-  struct c_label_context_se *nstack_se;
-  struct c_label_context_vm *nstack_vm;
-  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;
   current_function_returns_value = 0;  /* Assume, until we see it does.  */
   current_function_returns_null = 0;
 
Index: doc/invoke.texi
===================================================================
--- doc/invoke.texi	(revision 148327)
+++ doc/invoke.texi	(working copy)
@@ -238,7 +238,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wfatal-errors  -Wfloat-equal  -Wformat  -Wformat=2 @gol
 -Wno-format-contains-nul -Wno-format-extra-args -Wformat-nonliteral @gol
 -Wformat-security  -Wformat-y2k @gol
--Wframe-larger-than=@var{len} -Wignored-qualifiers @gol
+-Wframe-larger-than=@var{len} -Wgoto-misses-init -Wignored-qualifiers @gol
 -Wimplicit  -Wimplicit-function-declaration  -Wimplicit-int @gol
 -Winit-self  -Winline @gol
 -Wno-int-to-pointer-cast -Wno-invalid-offsetof @gol
@@ -2973,6 +2973,20 @@ requiring a non-null value by the @code{
 @option{-Wnonnull} is included in @option{-Wall} and @option{-Wformat}.  It
 can be disabled with the @option{-Wno-nonnull} option.
 
+@item -Wgoto-misses-init @r{(C, Objective-C only)}
+@opindex Wgoto-misses-init
+@opindex Wno-goto-misses-init
+Warn if a @code{goto} statement or a @code{switch} statement jumps
+forward across the initialization of a variable, or jumps backward to a
+label after the variable has been initialized.  This only warns about
+variables which are initialized when they are declared.  This warning is
+only supported for C and Objective C; in C++ this sort of branch is an
+error in any case.
+
+@option{-Wgoto-misses-init} is included in @option{-Wall} and
+@option{-Wc++-compat}.  It can be disabled with the
+@option{-Wno-goto-misses-init} option.
+
 @item -Winit-self @r{(C, C++, Objective-C and Objective-C++ only)}
 @opindex Winit-self
 @opindex Wno-init-self
Index: testsuite/gcc.dg/c99-vla-jump-4.c
===================================================================
--- testsuite/gcc.dg/c99-vla-jump-4.c	(revision 148327)
+++ testsuite/gcc.dg/c99-vla-jump-4.c	(working copy)
@@ -303,3 +303,7 @@ void fd285 (int n) { P0A:goto P0A;{ type
 void fd286 (int n) { P0A:goto P0A;{ typedef int (*b)[n]; P01A:goto P01A;{ typedef int (*c)[n]; P012A:goto P012A;0;} P01B:goto P01B;{ typedef int (*d)[n]; P013A:goto P013A;0;} P01C:goto P01C;; typedef int (*e)[n]; P014A:goto P014A;0;}; P0B:goto P0B;{ typedef int (*f)[n]; P02A:goto P02A;{ typedef int (*g)[n]; P024A:goto P024A;0;}; P02B:goto P02B;{ typedef int (*h)[n]; P025A:goto P025A;0;}; P02C:goto P02C;; typedef int (*i)[n]; P026A:goto P026A;0;}; a:; typedef int (*j)[n]; P03A:goto P03A;0;p03B:goto p03B; goto a; P03B:goto P03B; }
 void fd287 (int n) { P0A:goto P0A;{ typedef int (*b)[n]; P01A:goto P01A;{ typedef int (*c)[n]; P012A:goto P012A;0;} P01B:goto P01B;{ typedef int (*d)[n]; P013A:goto P013A;0;} P01C:goto P01C;; typedef int (*e)[n]; P014A:goto P014A;0;}; P0B:goto P0B;{ typedef int (*f)[n]; P02A:goto P02A;{ typedef int (*g)[n]; P024A:goto P024A;0;}; P02B:goto P02B;{ typedef int (*h)[n]; P025A:goto P025A;0;}; P02C:goto P02C;; typedef int (*i)[n]; P026A:goto P026A;0;}; P0C:goto P0C;; typedef int (*j)[n]; a:0;p03B:goto p03B; goto a; P03B:goto P03B; }
 void fd288 (int n) { P0A:goto P0A;{ typedef int (*b)[n]; P01A:goto P01A;{ typedef int (*c)[n]; P012A:goto P012A;0;} P01B:goto P01B;{ typedef int (*d)[n]; P013A:goto P013A;0;} P01C:goto P01C;; typedef int (*e)[n]; P014A:goto P014A;0;}; P0B:goto P0B;{ typedef int (*f)[n]; P02A:goto P02A;{ typedef int (*g)[n]; P024A:goto P024A;0;}; P02B:goto P02B;{ typedef int (*h)[n]; P025A:goto P025A;0;}; P02C:goto P02C;; typedef int (*i)[n]; P026A:goto P026A;0;}; P0C:goto P0C;; typedef int (*j)[n]; P03A:goto P03A;0;a: goto a; P03B:goto P03B; }
+
+/* Match extra informative notes.  */
+/* { dg-message "note: label '\[^\n'\]*' defined here" "note: expected" { target *-*-* } 0 } */
+/* { dg-message "note: '\[^\n'\]*' declared here" "note: expected" { target *-*-* } 0 } */
Index: testsuite/gcc.dg/stmt-expr-label-1.c
===================================================================
--- testsuite/gcc.dg/stmt-expr-label-1.c	(revision 148327)
+++ testsuite/gcc.dg/stmt-expr-label-1.c	(working copy)
@@ -402,3 +402,6 @@ void f194 (void) { ({ ({0;}); ({0;}); 0;
 void fa194 (void) { P0A:goto P0A;({ P01A:goto P01A;({P012A:goto P012A;0;}); P01B:goto P01B;({P013A:goto P013A;0;}); P01C:goto P01C;0;}); P0B:goto P0B;({ P02A:goto P02A;({P024A:goto P024A;0;}); P02B:goto P02B;({P025A:goto P025A;0;}); P02C:goto P02C;0;}); a:0;p0D:goto p0D; goto a; P0D:goto P0D; }
 void f195 (void) { ({ ({0;}); ({0;}); 0;}); ({ ({0;}); ({0;}); 0;}); 0;a: goto a;  }
 void fa195 (void) { P0A:goto P0A;({ P01A:goto P01A;({P012A:goto P012A;0;}); P01B:goto P01B;({P013A:goto P013A;0;}); P01C:goto P01C;0;}); P0B:goto P0B;({ P02A:goto P02A;({P024A:goto P024A;0;}); P02B:goto P02B;({P025A:goto P025A;0;}); P02C:goto P02C;0;}); P0C:goto P0C;0;a: goto a; P0D:goto P0D; }
+
+/* Match extra informative notes.  */
+/* { dg-message "note: label '\[^\n'\]*' defined here" "note: expected" { target *-*-* } 0 } */
Index: testsuite/gcc.dg/c99-vla-jump-1.c
===================================================================
--- testsuite/gcc.dg/c99-vla-jump-1.c	(revision 148327)
+++ testsuite/gcc.dg/c99-vla-jump-1.c	(working copy)
@@ -303,3 +303,7 @@ void fa285 (int n) { { int b[n]; { int c
 void fa286 (int n) { { int b[n]; { int c[n]; 0;} { int d[n]; 0;} ; int e[n]; 0;}; { int f[n]; { int g[n]; 0;}; { int h[n]; 0;}; ; int i[n]; 0;}; a:; int j[n]; 0; goto a;  }
 void fa287 (int n) { { int b[n]; { int c[n]; 0;} { int d[n]; 0;} ; int e[n]; 0;}; { int f[n]; { int g[n]; 0;}; { int h[n]; 0;}; ; int i[n]; 0;}; ; int j[n]; a:0; goto a;  }
 void fa288 (int n) { { int b[n]; { int c[n]; 0;} { int d[n]; 0;} ; int e[n]; 0;}; { int f[n]; { int g[n]; 0;}; { int h[n]; 0;}; ; int i[n]; 0;}; ; int j[n]; 0;a: goto a;  }
+
+/* Match extra informative notes.  */
+/* { dg-message "note: label '\[^\n'\]*' defined here" "note: expected" { target *-*-* } 0 } */
+/* { dg-message "note: '\[^\n'\]*' declared here" "note: expected" { target *-*-* } 0 } */
Index: testsuite/gcc.dg/c99-vla-jump-5.c
===================================================================
--- testsuite/gcc.dg/c99-vla-jump-5.c	(revision 148327)
+++ testsuite/gcc.dg/c99-vla-jump-5.c	(working copy)
@@ -16,8 +16,8 @@ f (int a, int b)
 {
   switch (a) {
     int v[b];
-  case 2: /* { dg-error "case label in scope of identifier with variably modified type not containing enclosing switch statement" } */
-  default: /* { dg-error "'default' label in scope of identifier with variably modified type not containing enclosing switch statement" } */
+  case 2: /* { dg-error "switch jumps into scope of identifier with variably modified type" } */
+  default: /* { dg-error "switch jumps into scope of identifier with variably modified type" } */
   switch (a)
     {
     case 4:
@@ -28,3 +28,7 @@ f (int a, int b)
     }
   }
 }
+
+/* Match extra informative notes.  */
+/* { dg-message "note: switch starts here" "note: expected" { target *-*-* } 0 } */
+/* { dg-message "note: '\[^\n'\]*' declared here" "note: expected" { target *-*-* } 0 } */
Index: testsuite/gcc.dg/stmt-expr-label-2.c
===================================================================
--- testsuite/gcc.dg/stmt-expr-label-2.c	(revision 148327)
+++ testsuite/gcc.dg/stmt-expr-label-2.c	(working copy)
@@ -8,13 +8,13 @@
 void
 f (int a)
 {
-  switch (a)
+  switch (a) /* { dg-message "here" } */
     {
     case 0:
     case 1:
       ({
-      case 2: /* { dg-error "case label in statement expression not containing enclosing switch statement" } */
-      default: /* { dg-error "'default' label in statement expression not containing enclosing switch statement" } */
+      case 2: /* { dg-error "switch jumps into statement expression" } */
+      default: /* { dg-error "switch jumps into statement expression" } */
 	switch (a)
 	  {
 	  case 3:
Index: testsuite/gcc.dg/c99-vla-jump-2.c
===================================================================
--- testsuite/gcc.dg/c99-vla-jump-2.c	(revision 148327)
+++ testsuite/gcc.dg/c99-vla-jump-2.c	(working copy)
@@ -303,3 +303,7 @@ void fb285 (int n) { P0A:goto P0A;{ int 
 void fb286 (int n) { P0A:goto P0A;{ int b[n]; P01A:goto P01A;{ int c[n]; P012A:goto P012A;0;} P01B:goto P01B;{ int d[n]; P013A:goto P013A;0;} P01C:goto P01C;; int e[n]; P014A:goto P014A;0;}; P0B:goto P0B;{ int f[n]; P02A:goto P02A;{ int g[n]; P024A:goto P024A;0;}; P02B:goto P02B;{ int h[n]; P025A:goto P025A;0;}; P02C:goto P02C;; int i[n]; P026A:goto P026A;0;}; a:; int j[n]; P03A:goto P03A;0;p03B:goto p03B; goto a; P03B:goto P03B; }
 void fb287 (int n) { P0A:goto P0A;{ int b[n]; P01A:goto P01A;{ int c[n]; P012A:goto P012A;0;} P01B:goto P01B;{ int d[n]; P013A:goto P013A;0;} P01C:goto P01C;; int e[n]; P014A:goto P014A;0;}; P0B:goto P0B;{ int f[n]; P02A:goto P02A;{ int g[n]; P024A:goto P024A;0;}; P02B:goto P02B;{ int h[n]; P025A:goto P025A;0;}; P02C:goto P02C;; int i[n]; P026A:goto P026A;0;}; P0C:goto P0C;; int j[n]; a:0;p03B:goto p03B; goto a; P03B:goto P03B; }
 void fb288 (int n) { P0A:goto P0A;{ int b[n]; P01A:goto P01A;{ int c[n]; P012A:goto P012A;0;} P01B:goto P01B;{ int d[n]; P013A:goto P013A;0;} P01C:goto P01C;; int e[n]; P014A:goto P014A;0;}; P0B:goto P0B;{ int f[n]; P02A:goto P02A;{ int g[n]; P024A:goto P024A;0;}; P02B:goto P02B;{ int h[n]; P025A:goto P025A;0;}; P02C:goto P02C;; int i[n]; P026A:goto P026A;0;}; P0C:goto P0C;; int j[n]; P03A:goto P03A;0;a: goto a; P03B:goto P03B; }
+
+/* Match extra informative notes.  */
+/* { dg-message "note: label '\[^\n'\]*' defined here" "note: expected" { target *-*-* } 0 } */
+/* { dg-message "note: '\[^\n'\]*' declared here" "note: expected" { target *-*-* } 0 } */
Index: testsuite/gcc.dg/Wgoto-misses-init-1.c
===================================================================
--- testsuite/gcc.dg/Wgoto-misses-init-1.c	(revision 0)
+++ testsuite/gcc.dg/Wgoto-misses-init-1.c	(revision 0)
@@ -0,0 +1,156 @@
+/* { dg-do compile } */
+/* { dg-options "-Wgoto-misses-init" } */
+int
+f1 (int a)
+{
+  if (a > 0)
+    {
+      int i = 7;	/* { dg-message "here" } */
+    lab:		/* { dg-message "here" } */
+      return a;
+    }
+  else
+    {
+      if (a < 0)
+	goto lab;	/* { dg-warning "jump" } */
+      return 1;
+    }
+}
+
+int
+f2 (int a)
+{
+  if (a > 0)
+    {
+      if (a < 0)
+	goto lab;	/* { dg-warning "jump" } */
+      return 1;
+    }
+  else
+    {
+      int i = 7;	/* { dg-message "here" } */
+    lab:		/* { dg-message "here" } */
+      return a;
+    }
+}
+
+int
+f3 (int a)
+{
+  if (a > 0)
+    {
+      static int i = 7;
+    lab:
+      return a;
+    }
+  else
+    {
+      if (a < 0)
+	goto lab;
+      return 1;
+    }
+}
+
+int
+f4 (int a)
+{
+  if (a > 0)
+    {
+      if (a < 0)
+	goto lab;
+      return 1;
+    }
+  else
+    {
+      static int i = 7;
+    lab:
+      return a;
+    }
+}
+
+int
+f5 (int a)
+{
+  if (a > 0)
+    {
+      int b = 1;
+      if (a < 0)
+	goto lab;
+    }
+ lab:
+  return a;
+}
+
+int
+f6 (int a)
+{
+  if (a > 0)
+    {
+    lab:
+      return a;
+    }
+  else
+    {
+      int b = 1;
+      goto lab;
+    }
+}
+
+int
+f7 (int a)
+{
+  switch (a)		/* { dg-message "switch" } */
+    {
+      int b = 1;	/* { dg-message "here" } */
+
+    case 1:		/* { dg-warning "jump" } */
+      return a;
+    }
+}
+
+int
+f8 (int a)
+{
+  switch (a)		/* { dg-message "switch" } */
+    {
+      int b = 1;	/* { dg-message "here" } */
+
+    case 1:		/* { dg-warning "jump" } */
+      goto lab;
+    }
+ lab:
+  return a;
+}
+
+int
+f9 (int a)
+{
+  switch (a)
+    {
+    case 0:
+      {
+	int b = 1;
+	return b;
+      }
+    case 1:
+      return a;
+    }
+}
+
+int
+f10 (int a)
+{
+  switch (a)
+    {
+    case 0:
+      {
+	int b = 1;
+	goto lab;
+      }
+
+    case 1:
+      goto lab;
+    }
+ lab:
+  return a;
+}
Index: testsuite/gcc.dg/stmt-expr-label-3.c
===================================================================
--- testsuite/gcc.dg/stmt-expr-label-3.c	(revision 148327)
+++ testsuite/gcc.dg/stmt-expr-label-3.c	(working copy)
@@ -6,3 +6,6 @@
 /* { dg-options "-O2" } */
 
 void f(void) { 1 ? 1 : ({ a : 1; 1; }); goto a; } /* { dg-error "jump into statement expression" } */
+
+/* Match extra informative notes.  */
+/* { dg-message "note: label '\[^\n'\]*' defined here" "note: expected" { target *-*-* } 0 } */
Index: testsuite/gcc.dg/c99-vla-jump-3.c
===================================================================
--- testsuite/gcc.dg/c99-vla-jump-3.c	(revision 148327)
+++ testsuite/gcc.dg/c99-vla-jump-3.c	(working copy)
@@ -303,3 +303,7 @@ void fc285 (int n) { { typedef int (*b)[
 void fc286 (int n) { { typedef int (*b)[n]; { typedef int (*c)[n]; 0;} { typedef int (*d)[n]; 0;} ; typedef int (*e)[n]; 0;}; { typedef int (*f)[n]; { typedef int (*g)[n]; 0;}; { typedef int (*h)[n]; 0;}; ; typedef int (*i)[n]; 0;}; a:; typedef int (*j)[n]; 0; goto a;  }
 void fc287 (int n) { { typedef int (*b)[n]; { typedef int (*c)[n]; 0;} { typedef int (*d)[n]; 0;} ; typedef int (*e)[n]; 0;}; { typedef int (*f)[n]; { typedef int (*g)[n]; 0;}; { typedef int (*h)[n]; 0;}; ; typedef int (*i)[n]; 0;}; ; typedef int (*j)[n]; a:0; goto a;  }
 void fc288 (int n) { { typedef int (*b)[n]; { typedef int (*c)[n]; 0;} { typedef int (*d)[n]; 0;} ; typedef int (*e)[n]; 0;}; { typedef int (*f)[n]; { typedef int (*g)[n]; 0;}; { typedef int (*h)[n]; 0;}; ; typedef int (*i)[n]; 0;}; ; typedef int (*j)[n]; 0;a: goto a;  }
+
+/* Match extra informative notes.  */
+/* { dg-message "note: label '\[^\n'\]*' defined here" "note: expected" { target *-*-* } 0 } */
+/* { dg-message "note: '\[^\n'\]*' declared here" "note: expected" { target *-*-* } 0 } */
Index: testsuite/gcc.dg/Wgoto-misses-init-2.c
===================================================================
--- testsuite/gcc.dg/Wgoto-misses-init-2.c	(revision 0)
+++ testsuite/gcc.dg/Wgoto-misses-init-2.c	(revision 0)
@@ -0,0 +1,52 @@
+/* { dg-do compile } */
+/* { dg-options "-Wgoto-misses-init -std=c99" } */
+extern void f1 ();
+int
+f2 (int a)
+{
+  switch (a)		/* { dg-message "switch" } */
+    {
+    case 1:
+      f1 ();
+      int v2 = 3;	/* { dg-message "here" } */
+    case 2:		/* { dg-warning "jump" } */
+      if (v2 == 7)
+	f1 ();
+    }
+  return 0;
+}
+
+int
+f3 (int i)
+{
+  if (i)
+    goto bad;		/* { dg-warning "jump" } */
+  int a = f2 (i); 	/* { dg-message "here" } */
+ bad:			/* { dg-message "here" } */
+  return a;
+}
+
+int
+f4 (int a)
+{
+  switch (a)
+    {
+    case 1:
+      f1 ();
+      static int v2 = 3;
+    case 2:
+      if (v2 == 7)
+	f1 ();
+    }
+  return 0;
+}
+
+int
+f5 (int i)
+{
+  if (i)
+    goto bad;
+  static int a = 6;
+ bad:
+  return a;
+}
Index: testsuite/gcc.dg/vla-8.c
===================================================================
--- testsuite/gcc.dg/vla-8.c	(revision 148327)
+++ testsuite/gcc.dg/vla-8.c	(working copy)
@@ -28,3 +28,7 @@ void foo2(int n) {
  A:
   ;
 }
+
+/* Match extra informative notes.  */
+/* { dg-message "note: label '\[^\n'\]*' defined here" "note: expected" { target *-*-* } 0 } */
+/* { dg-message "note: '\[^\n'\]*' declared here" "note: expected" { target *-*-* } 0 } */

==================================================


gcc/ChangeLog:

2009-06-10  Ian Lance Taylor  <iant@google.com>

	* df-problems.c (df_simulate_one_insn_forwards): Fix braces in
	switch.
	* gcov.c (read_count_file): Add braces around variables declared
	before label.

gcc/cp/ChangeLog:

2009-06-10  Ian Lance Taylor  <iant@google.com>

	* parser.c (cp_parser_direct_declarator): Add braces around
	variables declared before label.


Index: gcov.c
===================================================================
--- gcov.c	(revision 148327)
+++ gcov.c	(working copy)
@@ -1065,27 +1065,29 @@ read_count_file (void)
 	program_count++;
       else if (tag == GCOV_TAG_FUNCTION)
 	{
-	  unsigned ident = gcov_read_unsigned ();
-	  struct function_info *fn_n = functions;
+	  {
+	    unsigned ident = gcov_read_unsigned ();
+	    struct function_info *fn_n = functions;
 
-	  /* Try to find the function in the list.
-	     To speed up the search, first start from the last function
-	     found.   */
-	  for (fn = fn ? fn->next : NULL; ; fn = fn->next)
-	    {
-	      if (fn)
-		;
-	      else if ((fn = fn_n))
-		fn_n = NULL;
-	      else
-		{
-		  fnotice (stderr, "%s:unknown function '%u'\n",
-			   da_file_name, ident);
+	    /* Try to find the function in the list.
+	       To speed up the search, first start from the last function
+	       found.   */
+	    for (fn = fn ? fn->next : NULL; ; fn = fn->next)
+	      {
+		if (fn)
+		  ;
+		else if ((fn = fn_n))
+		  fn_n = NULL;
+		else
+		  {
+		    fnotice (stderr, "%s:unknown function '%u'\n",
+			     da_file_name, ident);
+		    break;
+		  }
+		if (fn->ident == ident)
 		  break;
-		}
-	      if (fn->ident == ident)
-		break;
-	    }
+	      }
+	  }
 
 	  if (!fn)
 	    ;
Index: cp/parser.c
===================================================================
--- cp/parser.c	(revision 148327)
+++ cp/parser.c	(working copy)
@@ -13365,181 +13365,183 @@ cp_parser_direct_declarator (cp_parser* 
 	}
       else if (first && dcl_kind != CP_PARSER_DECLARATOR_ABSTRACT)
 	{
-	  tree qualifying_scope;
-	  tree unqualified_name;
-	  special_function_kind sfk;
-	  bool abstract_ok;
-          bool pack_expansion_p = false;
-	  cp_token *declarator_id_start_token;
-
-	  /* Parse a declarator-id */
-	  abstract_ok = (dcl_kind == CP_PARSER_DECLARATOR_EITHER);
-	  if (abstract_ok)
-            {
-              cp_parser_parse_tentatively (parser);
+	  {
+	    tree qualifying_scope;
+	    tree unqualified_name;
+	    special_function_kind sfk;
+	    bool abstract_ok;
+	    bool pack_expansion_p = false;
+	    cp_token *declarator_id_start_token;
+
+	    /* Parse a declarator-id */
+	    abstract_ok = (dcl_kind == CP_PARSER_DECLARATOR_EITHER);
+	    if (abstract_ok)
+	      {
+		cp_parser_parse_tentatively (parser);
 
-              /* If we see an ellipsis, we should be looking at a
-                 parameter pack. */
-              if (token->type == CPP_ELLIPSIS)
-                {
-                  /* Consume the `...' */
-                  cp_lexer_consume_token (parser->lexer);
+		/* If we see an ellipsis, we should be looking at a
+		   parameter pack. */
+		if (token->type == CPP_ELLIPSIS)
+		  {
+		    /* Consume the `...' */
+		    cp_lexer_consume_token (parser->lexer);
 
-                  pack_expansion_p = true;
-                }
-            }
+		    pack_expansion_p = true;
+		  }
+	      }
 
-	  declarator_id_start_token = cp_lexer_peek_token (parser->lexer);
-	  unqualified_name
-	    = cp_parser_declarator_id (parser, /*optional_p=*/abstract_ok);
-	  qualifying_scope = parser->scope;
-	  if (abstract_ok)
-	    {
-              bool okay = false;
+	    declarator_id_start_token = cp_lexer_peek_token (parser->lexer);
+	    unqualified_name
+	      = cp_parser_declarator_id (parser, /*optional_p=*/abstract_ok);
+	    qualifying_scope = parser->scope;
+	    if (abstract_ok)
+	      {
+		bool okay = false;
 
-              if (!unqualified_name && pack_expansion_p)
-                {
-                  /* Check whether an error occurred. */
-                  okay = !cp_parser_error_occurred (parser);
-
-                  /* We already consumed the ellipsis to mark a
-                     parameter pack, but we have no way to report it,
-                     so abort the tentative parse. We will be exiting
-                     immediately anyway. */
-                  cp_parser_abort_tentative_parse (parser);
-                }
-              else
-                okay = cp_parser_parse_definitely (parser);
-
-	      if (!okay)
-		unqualified_name = error_mark_node;
-	      else if (unqualified_name
-		       && (qualifying_scope
-			   || (TREE_CODE (unqualified_name)
-			       != IDENTIFIER_NODE)))
-		{
-		  cp_parser_error (parser, "expected unqualified-id");
-		  unqualified_name = error_mark_node;
-		}
-	    }
+		if (!unqualified_name && pack_expansion_p)
+		  {
+		    /* Check whether an error occurred. */
+		    okay = !cp_parser_error_occurred (parser);
+
+		    /* We already consumed the ellipsis to mark a
+		       parameter pack, but we have no way to report it,
+		       so abort the tentative parse. We will be exiting
+		       immediately anyway. */
+		    cp_parser_abort_tentative_parse (parser);
+		  }
+		else
+		  okay = cp_parser_parse_definitely (parser);
 
-	  if (!unqualified_name)
-	    return NULL;
-	  if (unqualified_name == error_mark_node)
-	    {
-	      declarator = cp_error_declarator;
-              pack_expansion_p = false;
-              declarator->parameter_pack_p = false;
-	      break;
-	    }
+		if (!okay)
+		  unqualified_name = error_mark_node;
+		else if (unqualified_name
+			 && (qualifying_scope
+			     || (TREE_CODE (unqualified_name)
+				 != IDENTIFIER_NODE)))
+		  {
+		    cp_parser_error (parser, "expected unqualified-id");
+		    unqualified_name = error_mark_node;
+		  }
+	      }
 
-	  if (qualifying_scope && at_namespace_scope_p ()
-	      && TREE_CODE (qualifying_scope) == TYPENAME_TYPE)
-	    {
-	      /* In the declaration of a member of a template class
-		 outside of the class itself, the SCOPE will sometimes
-		 be a TYPENAME_TYPE.  For example, given:
-
-		 template <typename T>
-		 int S<T>::R::i = 3;
-
-		 the SCOPE will be a TYPENAME_TYPE for `S<T>::R'.  In
-		 this context, we must resolve S<T>::R to an ordinary
-		 type, rather than a typename type.
-
-		 The reason we normally avoid resolving TYPENAME_TYPEs
-		 is that a specialization of `S' might render
-		 `S<T>::R' not a type.  However, if `S' is
-		 specialized, then this `i' will not be used, so there
-		 is no harm in resolving the types here.  */
-	      tree type;
+	    if (!unqualified_name)
+	      return NULL;
+	    if (unqualified_name == error_mark_node)
+	      {
+		declarator = cp_error_declarator;
+		pack_expansion_p = false;
+		declarator->parameter_pack_p = false;
+		break;
+	      }
 
-	      /* Resolve the TYPENAME_TYPE.  */
-	      type = resolve_typename_type (qualifying_scope,
-					    /*only_current_p=*/false);
-	      /* If that failed, the declarator is invalid.  */
-	      if (TREE_CODE (type) == TYPENAME_TYPE)
-		error ("%H%<%T::%E%> is not a type",
-		       &declarator_id_start_token->location,
-		       TYPE_CONTEXT (qualifying_scope),
-		       TYPE_IDENTIFIER (qualifying_scope));
-	      qualifying_scope = type;
-	    }
+	    if (qualifying_scope && at_namespace_scope_p ()
+		&& TREE_CODE (qualifying_scope) == TYPENAME_TYPE)
+	      {
+		/* In the declaration of a member of a template class
+		   outside of the class itself, the SCOPE will sometimes
+		   be a TYPENAME_TYPE.  For example, given:
+
+		   template <typename T>
+		   int S<T>::R::i = 3;
+
+		   the SCOPE will be a TYPENAME_TYPE for `S<T>::R'.  In
+		   this context, we must resolve S<T>::R to an ordinary
+		   type, rather than a typename type.
+
+		   The reason we normally avoid resolving TYPENAME_TYPEs
+		   is that a specialization of `S' might render
+		   `S<T>::R' not a type.  However, if `S' is
+		   specialized, then this `i' will not be used, so there
+		   is no harm in resolving the types here.  */
+		tree type;
 
-	  sfk = sfk_none;
+		/* Resolve the TYPENAME_TYPE.  */
+		type = resolve_typename_type (qualifying_scope,
+					      /*only_current_p=*/false);
+		/* If that failed, the declarator is invalid.  */
+		if (TREE_CODE (type) == TYPENAME_TYPE)
+		  error ("%H%<%T::%E%> is not a type",
+			 &declarator_id_start_token->location,
+			 TYPE_CONTEXT (qualifying_scope),
+			 TYPE_IDENTIFIER (qualifying_scope));
+		qualifying_scope = type;
+	      }
 
-	  if (unqualified_name)
-	    {
-	      tree class_type;
+	    sfk = sfk_none;
 
-	      if (qualifying_scope
-		  && CLASS_TYPE_P (qualifying_scope))
-		class_type = qualifying_scope;
-	      else
-		class_type = current_class_type;
+	    if (unqualified_name)
+	      {
+		tree class_type;
 
-	      if (TREE_CODE (unqualified_name) == TYPE_DECL)
-		{
-		  tree name_type = TREE_TYPE (unqualified_name);
-		  if (class_type && same_type_p (name_type, class_type))
-		    {
-		      if (qualifying_scope
-			  && CLASSTYPE_USE_TEMPLATE (name_type))
-			{
-			  error ("%Hinvalid use of constructor as a template",
-				 &declarator_id_start_token->location);
-			  inform (input_location, "use %<%T::%D%> instead of %<%T::%D%> to "
-				  "name the constructor in a qualified name",
-				  class_type,
-				  DECL_NAME (TYPE_TI_TEMPLATE (class_type)),
-				  class_type, name_type);
-			  declarator = cp_error_declarator;
-			  break;
-			}
-		      else
+		if (qualifying_scope
+		    && CLASS_TYPE_P (qualifying_scope))
+		  class_type = qualifying_scope;
+		else
+		  class_type = current_class_type;
+
+		if (TREE_CODE (unqualified_name) == TYPE_DECL)
+		  {
+		    tree name_type = TREE_TYPE (unqualified_name);
+		    if (class_type && same_type_p (name_type, class_type))
+		      {
+			if (qualifying_scope
+			    && CLASSTYPE_USE_TEMPLATE (name_type))
+			  {
+			    error ("%Hinvalid use of constructor as a template",
+				   &declarator_id_start_token->location);
+			    inform (input_location, "use %<%T::%D%> instead of %<%T::%D%> to "
+				    "name the constructor in a qualified name",
+				    class_type,
+				    DECL_NAME (TYPE_TI_TEMPLATE (class_type)),
+				    class_type, name_type);
+			    declarator = cp_error_declarator;
+			    break;
+			  }
+			else
+			  unqualified_name = constructor_name (class_type);
+		      }
+		    else
+		      {
+			/* We do not attempt to print the declarator
+			   here because we do not have enough
+			   information about its original syntactic
+			   form.  */
+			cp_parser_error (parser, "invalid declarator");
+			declarator = cp_error_declarator;
+			break;
+		      }
+		  }
+
+		if (class_type)
+		  {
+		    if (TREE_CODE (unqualified_name) == BIT_NOT_EXPR)
+		      sfk = sfk_destructor;
+		    else if (IDENTIFIER_TYPENAME_P (unqualified_name))
+		      sfk = sfk_conversion;
+		    else if (/* There's no way to declare a constructor
+				for an anonymous type, even if the type
+				got a name for linkage purposes.  */
+			     !TYPE_WAS_ANONYMOUS (class_type)
+			     && constructor_name_p (unqualified_name,
+						    class_type))
+		      {
 			unqualified_name = constructor_name (class_type);
-		    }
-		  else
-		    {
-		      /* We do not attempt to print the declarator
-			 here because we do not have enough
-			 information about its original syntactic
-			 form.  */
-		      cp_parser_error (parser, "invalid declarator");
-		      declarator = cp_error_declarator;
-		      break;
-		    }
-		}
-
-	      if (class_type)
-		{
-		  if (TREE_CODE (unqualified_name) == BIT_NOT_EXPR)
-		    sfk = sfk_destructor;
-		  else if (IDENTIFIER_TYPENAME_P (unqualified_name))
-		    sfk = sfk_conversion;
-		  else if (/* There's no way to declare a constructor
-			      for an anonymous type, even if the type
-			      got a name for linkage purposes.  */
-			   !TYPE_WAS_ANONYMOUS (class_type)
-			   && constructor_name_p (unqualified_name,
-						  class_type))
-		    {
-		      unqualified_name = constructor_name (class_type);
-		      sfk = sfk_constructor;
-		    }
-
-		  if (ctor_dtor_or_conv_p && sfk != sfk_none)
-		    *ctor_dtor_or_conv_p = -1;
-		}
-	    }
-	  declarator = make_id_declarator (qualifying_scope,
-					   unqualified_name,
-					   sfk);
-	  declarator->id_loc = token->location;
-          declarator->parameter_pack_p = pack_expansion_p;
+			sfk = sfk_constructor;
+		      }
 
-          if (pack_expansion_p)
-            maybe_warn_variadic_templates ();
+		    if (ctor_dtor_or_conv_p && sfk != sfk_none)
+		      *ctor_dtor_or_conv_p = -1;
+		  }
+	      }
+	    declarator = make_id_declarator (qualifying_scope,
+					     unqualified_name,
+					     sfk);
+	    declarator->id_loc = token->location;
+	    declarator->parameter_pack_p = pack_expansion_p;
+
+	    if (pack_expansion_p)
+	      maybe_warn_variadic_templates ();
+	  }
 
 	handle_declarator:;
 	  scope = get_scope_of_declarator (declarator);
Index: df-problems.c
===================================================================
--- df-problems.c	(revision 148327)
+++ df-problems.c	(working copy)
@@ -3912,19 +3912,21 @@ df_simulate_one_insn_forwards (basic_blo
   for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
     {
       switch (REG_NOTE_KIND (link))
+	{
 	case REG_DEAD:
 	case REG_UNUSED:
-	{
-	  rtx reg = XEXP (link, 0);
-	  int regno = REGNO (reg);
-	  if (regno < FIRST_PSEUDO_REGISTER)
-	    {
-	      int n = hard_regno_nregs[regno][GET_MODE (reg)];
-	      while (--n >= 0)
-		bitmap_clear_bit (live, regno + n);
-	    }
-	  else 
-	    bitmap_clear_bit (live, regno);
+	  {
+	    rtx reg = XEXP (link, 0);
+	    int regno = REGNO (reg);
+	    if (regno < FIRST_PSEUDO_REGISTER)
+	      {
+		int n = hard_regno_nregs[regno][GET_MODE (reg)];
+		while (--n >= 0)
+		  bitmap_clear_bit (live, regno + n);
+	      }
+	    else 
+	      bitmap_clear_bit (live, regno);
+	  }
 	  break;
 	default:
 	  break;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]