Here is another attempt to improve the situation with uninitialized
variables.  Instead of modifying -Wjump-misses-init, it adds a new
warning that is called -Wuse-after-missed-init.  

Thoughts?  Should this go into -Wall or only -Wextra?

Bootstrapped and regression tested for x86_64.


Martin


    c: Add -Wuse-after-missed-init [PR87038]
    
    This change adds a warning -Wuse-after-missed-init which is similar to
    -Wjump-misses-init but better supports idiomatic C code by emitting a
    diagnostic only when the variable is used somewhere after the label,
    e.g. no warning is emitted in the following example.  The new warning
    is activated for -Wall.
    
    void f(void)
    {
      goto err;
      int x = 1;    // missed initialization
      h(&x);
    err:
      return;       // no use of 'x'
    }
    
    In contrast to -Wmaybe-uninitialized it does not rely on optimization
    but warns about any unsafe use, e.g. as in the following example.
    
    int g(bool a)
    {
      if  (a) goto err;
      int x = 1;    // missed initialization
      h(&x);
    err:
      return x;     // use of 'x'
    }
    
    The warning is implemented by recording potential warnings in a data
    structure, resetting DECL_READ (while recording the current value),
    and emitting warnings at the end of the scope only for declarations
    hat were read.  Diagnostics are still emitted directly for variably
    modified types, for omp allocations, and for -Wjump-misses-init
    (and -Wc++-compat).
    
    There is overlap with -Wuninitialized and -Wmaybe-uninitialized, but
    -Wuse-after-missed-init does not depend on optimization and captures some
    new situations, e.g. when the address of the variable escapes as in the
    example above.  The two warnings also provide complementary information,
    so it seems useful to emit both.
    
    Finally, we now do emit labels for switch cases even when there is an
    error to prevent incorrect warnings about unreachable switch statements.
    
            PR c/87038
    
    gcc/c-family/ChangeLog:
            * c.opts: Add -Wuse-after-missed-init and add it to -Wall.
    
    gcc/c/ChangeLog:
            * c-tree.h (c_check_switch_jump_warnings): Change return type to 
void.
            * c-decl.cc (decl_jump_unsafe): Adapt to new warning.
            (emit_deferred_jump_warnings): New function.
            (warn_about_goto): Forward to warn_about_jump.
            (warn_about_jump): Emit or record warnings for goto or switch when
            missing an initialization.
            (pop_scope): Call emit_deferred_jump_warnings.
            (c_check_switch_jump_warnings): Refactor to use warn_about_jump.
            * c-typeck.cc (do_case): Adapt call to c_check_switch_jump_warnings.
    
    gcc/doc/ChangeLog:
            * invoke.texi: Document new option.
    
    gcc/testsuite/ChangeLog:
            * c-c++-common/gomp/allocate-11.c: Remove incorrect warning.
            * gcc.dg/Wuse-after-missed-init-1.c: New test.
            * gcc.dg/Wuse-after-missed-init-2.c: New test.
            * gcc.dg/Wuse-after-missed-init-3.c: New test.
            * gcc.dg/c23-labels-3.c: Use -Wno-use-after-missed-init.
            * gcc.dg/pr63748.c: Use -Wno-use-after-missed-init.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 3f5e2f0874d..3726d8e9871 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -949,6 +949,11 @@ Wjump-misses-init
 C ObjC Var(warn_jump_misses_init) Warning LangEnabledBy(C ObjC,Wc++-compat)
 Warn when a jump misses a variable initialization.
 
+Wuse-after-missed-init
+C ObjC Var(warn_use_after_missed_init) Warning LangEnabledBy(C ObjC,Wall)
+Warn when a variable is used after a jump that misses the initialization.
+
+
 Enum
 Name(warn_leading_whitespace_kind) Type(int) UnknownError(argument %qs to 
%<-Wleading-whitespace=%> not recognized)
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 77006cacdb7..3497fa345d5 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -749,9 +749,9 @@ decl_jump_unsafe (tree decl)
       && c_type_variably_modified_p (TREE_TYPE (decl)))
     return true;
 
-  /* Otherwise, only warn if -Wgoto-misses-init and this is an
-     initialized automatic decl.  */
-  if (warn_jump_misses_init
+  /* Otherwise, only warn for -Wjump-misses-init and -Wuse_after_missed_init
+     and this is an initialized automatic decl.  */
+  if ((warn_jump_misses_init || warn_use_after_missed_init)
       && VAR_P (decl)
       && !TREE_STATIC (decl)
       && DECL_INITIAL (decl) != NULL_TREE)
@@ -1227,6 +1227,94 @@ update_label_decls (struct c_scope *scope)
     }
 }
 
+
+enum jump_type { JUMP_GOTO, JUMP_SWITCH };
+
+
+/* Issue a warning about a missed initialization for a goto or switch
+   statement.  */
+
+static void
+emit_warn_about_jump (enum jump_type jtype, location_t jump_loc, tree label,
+                     tree decl, location_t label_loc)
+{
+  int warning_code = OPT_Wuse_after_missed_init;
+  if (warn_jump_misses_init)
+    warning_code = OPT_Wjump_misses_init;
+  auto_diagnostic_group d;
+  switch (jtype)
+    {
+    case JUMP_GOTO:
+      if (c_type_variably_modified_p (TREE_TYPE (decl)))
+        error_at (jump_loc, "jump into scope of identifier with "
+                            "variably modified type");
+      else if (flag_openmp
+              && lookup_attribute ("omp allocate", DECL_ATTRIBUTES (decl)))
+       error_at (jump_loc, "jump skips OpenMP %<allocate%> allocation");
+      else if (!warning_at (jump_loc, warning_code,
+                           "jump skips variable initialization"))
+       return;
+      inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", label);
+      inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl);
+      break;
+    case JUMP_SWITCH:
+      if (c_type_variably_modified_p (TREE_TYPE (decl)))
+       error_at (label_loc, "switch jumps into scope of identifier with "
+                            "variably modified type");
+      else if (flag_openmp
+              && lookup_attribute ("omp allocate", DECL_ATTRIBUTES (decl)))
+       error_at (label_loc, "switch jumps over OpenMP %<allocate%> 
allocation");
+      else if (!warning_at (label_loc, warning_code,
+                           "switch jumps over variable initialization"))
+       return;
+      inform (jump_loc, "switch starts here");
+      inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl);
+      break;
+    }
+}
+
+
+
+struct deferred_jump_warning {
+  enum jump_type jump_type;
+  bool decl_read;
+  location_t goto_loc;
+  location_t label_loc;
+  tree decl;
+  tree label;
+};
+
+static
+GTY (()) vec<deferred_jump_warning, va_gc> *deferred_jump_warnings;
+
+
+
+/* Emit deferred warnings for variables for which there was a jump that
+   skips over the initialization, but only when the variable was used
+   since then.  */
+static void
+emit_deferred_jump_warnings (void)
+{
+  if (!deferred_jump_warnings)
+    return;
+  unsigned int ix;
+  struct deferred_jump_warning *p;
+  /* This must be processed in reverse order to set DECL_READ_P correctly
+     in case we have the same declaration twice.  */
+  FOR_EACH_VEC_SAFE_ELT_REVERSE (deferred_jump_warnings, ix, p)
+    {
+      gcc_assert (VAR_OR_FUNCTION_DECL_P (p->decl));
+
+      if (DECL_READ_P (p->decl))
+       emit_warn_about_jump (p->jump_type, p->goto_loc, p->label, p->decl,
+                             p->label_loc);
+      /* Merge back the information from before the label was reached.  */
+      DECL_READ_P (p->decl) = DECL_READ_P (p->decl) || p->decl_read;
+    }
+  vec_free (deferred_jump_warnings);
+}
+
+
 /* Exit a scope.  Restore the state of the identifier-decl mappings
    that were in effect when this scope was entered.  Return a BLOCK
    node containing all the DECLs in this scope that are of interest
@@ -1243,6 +1331,7 @@ pop_scope (void)
   bool keep = functionbody || scope->keep || scope->bindings;
 
   update_label_decls (scope);
+  emit_deferred_jump_warnings ();
 
   /* If appropriate, create a BLOCK to record the decls for the life
      of this function.  */
@@ -4080,27 +4169,62 @@ lookup_label (tree name)
   return label;
 }
 
+
+/* Record a forbidden or possibly unsafe jump of type JTYPE from
+   GOTO_LOC to LABEL at LABEL_LOC skipping initialization of DECL.  */
+
+static void warn_about_jump(enum jump_type jtype, location_t goto_loc,
+                           location_t label_loc, tree decl, tree label)
+{
+  /* We emit the warning now if for -Wjump-misses-init, have to emit it
+     for variable modified types (including typedefs), and for openmp
+     allocations.  */
+  if (warn_jump_misses_init
+      || !VAR_OR_FUNCTION_DECL_P (decl)
+      || c_type_variably_modified_p (TREE_TYPE (decl))
+      || (flag_openmp
+         && lookup_attribute ("omp allocate", DECL_ATTRIBUTES (decl))))
+    emit_warn_about_jump (jtype, goto_loc, label, decl, label_loc);
+  else if (!lookup_name_in_scope (DECL_NAME (decl), current_scope))
+    {
+      /* The are already after the scope where the variable is declared,
+        check whether it was read and then warn directly.  */
+      if (DECL_READ_P (decl))
+       emit_warn_about_jump (jtype, goto_loc, label, decl, label_loc);
+    }
+  else
+    {
+      /* Record the required information.  We will emit a warning at the end
+        of the scope, but only if the declaration was read between the label
+        and the end of the scop.  */
+      struct deferred_jump_warning d;
+      d.jump_type = jtype;
+      d.goto_loc = goto_loc;
+      d.label_loc = label_loc;
+      d.decl = decl;
+      d.label = label;
+      d.decl_read = DECL_READ_P (decl);
+      if (!deferred_jump_warnings)
+       vec_alloc (deferred_jump_warnings, 1);
+      vec_safe_push (deferred_jump_warnings, d);
+
+      /* Temporarily clear this flag.  We want to detect when the decl is used
+        (again).  The information is merged again at the end of the scope.  */
+      DECL_READ_P (decl) = 0;
+    }
+}
+
 /* 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)
 {
-  auto_diagnostic_group d;
-  if (c_type_variably_modified_p (TREE_TYPE (decl)))
-    error_at (goto_loc,
-             "jump into scope of identifier with variably modified type");
-  else if (flag_openmp
-          && lookup_attribute ("omp allocate", DECL_ATTRIBUTES (decl)))
-    error_at (goto_loc, "jump skips OpenMP %<allocate%> allocation");
-  else
-    if (!warning_at (goto_loc, OPT_Wjump_misses_init,
-                    "jump skips variable initialization"))
-      return;
-  inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", label);
-  inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl);
+  warn_about_jump (JUMP_GOTO, goto_loc, DECL_SOURCE_LOCATION (label),
+                  decl, label);
 }
 
+
 /* Look up a label because of a goto statement.  This is like
    lookup_label, but also issues any appropriate warnings.  */
 
@@ -4336,17 +4460,14 @@ c_release_switch_bindings (struct c_spot_bindings 
*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.  */
+   warnings about decls as needed.  */
 
-bool
+void
 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)
@@ -4359,51 +4480,16 @@ c_check_switch_jump_warnings (struct c_spot_bindings 
*switch_bindings,
        continue;
 
       for (b = scope->bindings; b != NULL; b = b->prev)
-       {
-         if (decl_jump_unsafe (b->decl))
-           {
-             auto_diagnostic_group d;
-             bool emitted;
-             if (c_type_variably_modified_p (TREE_TYPE (b->decl)))
-               {
-                 saw_error = true;
-                 error_at (case_loc,
-                           ("switch jumps into scope of identifier with "
-                            "variably modified type"));
-                 emitted = true;
-               }
-             else if (flag_openmp
-                      && lookup_attribute ("omp allocate",
-                                           DECL_ATTRIBUTES (b->decl)))
-               {
-                 saw_error = true;
-                 error_at (case_loc,
-                           "switch jumps over OpenMP %<allocate%> allocation");
-                 emitted = true;
-               }
-             else
-               emitted
-                 = warning_at (case_loc, OPT_Wjump_misses_init,
-                               "switch jumps over variable initialization");
-             if (emitted)
-               {
-                 inform (switch_loc, "switch starts here");
-                 inform (DECL_SOURCE_LOCATION (b->decl), "%qD declared here",
-                         b->decl);
-               }
-           }
-       }
+       if (decl_jump_unsafe (b->decl))
+         warn_about_jump (JUMP_SWITCH, switch_loc, case_loc, b->decl, 
NULL_TREE);
     }
 
   if (switch_bindings->stmt_exprs > 0)
     {
-      saw_error = true;
       auto_diagnostic_group d;
       error_at (case_loc, "switch jumps into statement expression");
       inform (switch_loc, "switch starts here");
     }
-
-  return saw_error;
 }
 
 /* Given NAME, an IDENTIFIER_NODE,
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index afec03e25eb..40ff2cf2062 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -677,7 +677,7 @@ 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_jump_warnings (struct c_spot_bindings *,
+extern void c_check_switch_jump_warnings (struct c_spot_bindings *,
                                          location_t, location_t);
 extern void finish_decl (tree, location_t, tree, tree, tree);
 extern tree finish_enum (tree, tree, tree);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 3c6f7d996e9..a83a09c0931 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -13366,10 +13366,9 @@ do_case (location_t loc, tree low_value, tree 
high_value, tree attrs)
       return NULL_TREE;
     }
 
-  if (c_check_switch_jump_warnings (c_switch_stack->bindings,
-                                   EXPR_LOCATION (c_switch_stack->switch_stmt),
-                                   loc))
-    return NULL_TREE;
+  c_check_switch_jump_warnings (c_switch_stack->bindings,
+                               EXPR_LOCATION (c_switch_stack->switch_stmt),
+                               loc);
 
   label = c_add_case_label (loc, c_switch_stack->cases,
                            SWITCH_STMT_COND (c_switch_stack->switch_stmt),
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 4bec80ac205..8ffaf85a468 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -444,7 +444,8 @@ Objective-C and Objective-C++ Dialects}.
 -Wunused-macros
 -Wunused-parameter  -Wno-unused-result
 -Wunused-value  -Wunused-variable
--Wuse-after-free  -Wuse-after-free=@var{n}  -Wuseless-cast
+-Wuse-after-free  -Wuse-after-free=@var{n}
+-Wuse-after-missed-init  -Wuseless-cast
 -Wno-varargs  -Wvariadic-macros
 -Wvector-operation-performance
 -Wvla  -Wvla-larger-than=@var{byte-size}  -Wno-vla-larger-than
@@ -10178,6 +10179,23 @@ error in any case.
 @option{-Wjump-misses-init} is included in @option{-Wc++-compat}.  It
 can be disabled with the @option{-Wno-jump-misses-init} option.
 
+
+@opindex Wuse-after-missed-init
+@opindex Wno-use-after-missed-init
+@item -Wuse-after-missed-init @r{(C, Objective-C only)}
+Warn if a @code{goto} statement or a @code{switch} statement jumps
+forward across the initialization of a variable and the variable
+is used before the end of the block.  This warning is similar to
+@option{-Wjump-misses-init} but does not warn for the common C idiom
+where @code{goto} is used for error handling.  It also is related
+to @option{-Wuninitialized} and @option{-Wmaybe-uninitialized} but
+does not rely on optimization and predictably warns for any unsafe
+use of the variable.
+
+@option{-Wuse-after-missed-init} is included in @option{-Wall}.  It
+can be disabled with the @option{-Wno-use-jump-misses-init} option.
+
+
 @opindex Wsign-compare
 @opindex Wno-sign-compare
 @cindex warning for comparison of signed and unsigned values
diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-11.c 
b/gcc/testsuite/c-c++-common/gomp/allocate-11.c
index dceb97f8c5f..26f895cf8d8 100644
--- a/gcc/testsuite/c-c++-common/gomp/allocate-11.c
+++ b/gcc/testsuite/c-c++-common/gomp/allocate-11.c
@@ -13,7 +13,6 @@ f (int i)
       #pragma omp allocate(j)
     case 42:  /* { dg-error "switch jumps over OpenMP 'allocate' allocation" } 
*/
       bar ();
-      /* { dg-warning "statement will never be executed 
\\\[-Wswitch-unreachable\\\]" "" { target *-*-* } .-1 } */
       break;
     case 51:  /* { dg-error "switch jumps over OpenMP 'allocate' allocation" } 
*/
       use (&j);
diff --git a/gcc/testsuite/gcc.dg/Wuse-after-missed-init-1.c 
b/gcc/testsuite/gcc.dg/Wuse-after-missed-init-1.c
new file mode 100644
index 00000000000..acfc6478ed3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wuse-after-missed-init-1.c
@@ -0,0 +1,100 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuse-after-missed-init" } */
+
+int f1()
+{
+       int b = 1;
+       goto a;
+a:
+       return b;
+}
+
+int f2()
+{
+       goto a;         /* { dg-warning "jump" } */
+       int b = 1;      /* { dg-message "here" } */
+a:                     /* { dg-message "here" } */
+       return b;
+}
+
+int f2b()
+{
+       goto a;
+       int b = 1;
+a:
+}
+
+int f2c()
+{
+       {
+               goto a;
+       }
+       int b = 1;
+a:
+}
+
+int f3()
+{
+       goto a;
+a:
+       int b = 1;
+       return b;
+}
+
+int f4()
+{
+       int b = 1;
+a:
+       goto a;
+       return b;
+}
+
+int f5()
+{
+a:
+       int b = 1;
+       goto a;
+       return b;
+}
+
+int f6()
+{
+a:
+       goto a;
+       int b = 1;
+       return b;
+}
+
+int f7(int n)
+{
+       goto a;                 /* { dg-error "jump" } */
+       char x[n];              /* { dg-message "here" } */
+a:     ;                       /* { dg-message "here" } */
+}
+
+int f8(int n)
+{
+       goto a;                 /* { dg-error "jump" } */
+       typedef char x[n];      /* { dg-message "here" } */
+a:     ;                       /* { dg-message "here" } */
+}
+
+int f9a()
+{
+       goto foo;               /* { dg-warning "jump" } */
+       goto bar;
+       int x = 1;              /* { dg-message "here" } */
+foo:                           /* { dg-message "here" } */
+       return x;
+bar:
+}
+
+int f9b()
+{
+       goto foo;
+       goto bar;               /* { dg-warning "jump" } */
+       int x = 1;              /* { dg-message "here" } */
+bar:                           /* { dg-message "here" } */
+       return x;               
+foo:
+}
diff --git a/gcc/testsuite/gcc.dg/Wuse-after-missed-init-2.c 
b/gcc/testsuite/gcc.dg/Wuse-after-missed-init-2.c
new file mode 100644
index 00000000000..3297e1285e9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wuse-after-missed-init-2.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuse-after-missed-init" } */
+
+
+int f()
+{
+  goto L2;     /* { dg-warning "jump skips variable initialization" } */
+  goto L1;
+  int m = 0;
+L2:
+  return m;
+L1:
+}
+
+int g()
+{
+  if (0) goto L1;
+  goto L2;     /* { dg-warning "jump skips variable initialization" } */
+  int m = 0;
+L2:
+ return m;
+L1:
+}
+
diff --git a/gcc/testsuite/gcc.dg/Wuse-after-missed-init-3.c 
b/gcc/testsuite/gcc.dg/Wuse-after-missed-init-3.c
new file mode 100644
index 00000000000..8e80c67cde1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wuse-after-missed-init-3.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuse-after-missed-init" } */
+
+int
+test1 (int op)
+{
+  if (op)
+    {
+      int n = 0;
+
+    do_call_it:
+
+      return n;
+    }
+
+  goto do_call_it;     /* { dg-warning "jump skips" } */
+}
+
+int
+test2 (int op)
+{
+  if (op)
+    {
+      int n = 0;
+
+    do_call_it:
+    }
+
+  goto do_call_it;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-labels-3.c 
b/gcc/testsuite/gcc.dg/c23-labels-3.c
index 2d75e34ab1b..784c3bc6b49 100644
--- a/gcc/testsuite/gcc.dg/c23-labels-3.c
+++ b/gcc/testsuite/gcc.dg/c23-labels-3.c
@@ -1,7 +1,7 @@
 /* Tests for labels before declarations and at ends of compound statements
  * in combination with attributes. */
 /* { dg-do compile } */
-/* { dg-options "-std=c23 -Wall" } */
+/* { dg-options "-std=c23 -Wall -Wno-use-after-missed-init" } */
 
 int f(void) 
 { 
diff --git a/gcc/testsuite/gcc.dg/pr63748.c b/gcc/testsuite/gcc.dg/pr63748.c
index cc353a7e5c3..6e0d2ff4a85 100644
--- a/gcc/testsuite/gcc.dg/pr63748.c
+++ b/gcc/testsuite/gcc.dg/pr63748.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -Wall" } */
+/* { dg-options "-O2 -Wall -Wno-use-after-missed-init" } */
 /* { dg-require-effective-target nonlocal_goto } */
 
 #include <setjmp.h>

Reply via email to