On Tue, 16 Feb 2016, Jakub Jelinek wrote:

> Hi!
> 
> As has been reported today on gcc@, the new -Wnonnull warning addition
> warns even about nonnull parameters that were changed (or could be changed)
> in the function.  IMHO the nonnull attribute only talks about the value of
> the parameter upon entry to the function, if you assign something else
> to the argument afterwards and test that for NULL or non-NULL, it is like
> any other variable.
> But, we don't have the infrastructure to detect this in the FE, so this
> patch moves the warning soon after we go into SSA form.
> As -Wnonnull is a C/C++/ObjC/ObjC++ only warning, I've added a new
> -Wnonnull-compare switch for it too (and enable it for C/C++/ObjC/ObjC++
> from -Wall).
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Not sure if it matters but wouldn't walking over function parameters
and their default def SSA names immediate uses be cheaper than
matching all conditions?  Esp. as nonnull_arg_p walks over all
DECL_ARGUMENTS (up to the searched index) over and over again?
[we should simply set a flag on the PARM_DECL!]

Richard.

> 2016-02-16  Jakub Jelinek  <ja...@redhat.com>
> 
>       PR c/69835
>       * common.opt (Wnonnull-compare): New warning.
>       * doc/invoke.texi (-Wnonnull): Remove text about comparison
>       of arguments against NULL.
>       (-Wnonnull-compare): Document.
>       * tree-ssa-uninit.c (warn_uninitialized_vars): Add warn_nonnull_cmp
>       argument, if true, warn about SSA_NAME (D) of nonnull_arg_p
>       comparisons against NULL pointers.
>       (pass_late_warn_uninitialized::execute): Adjust caller.
>       (execute_early_warn_uninitialized): Likewise.
>       (gate_early_warn_uninitialized): New function.
>       (pass_early_warn_uninitialized::gate): Call it instead of
>       gate_warn_uninitialized.
> c-family/
>       * c.opt (Wnonnull-compare): Enable for -Wall.
> c/
>       * c-typeck.c (build_binary_op): Revert 2015-09-09 change.
> cp/
>       * typeck.c (cp_build_binary_op): Revert 2015-09-09 change.
> testsuite/
>       * c-c++-common/nonnull-1.c: Use -Wnonnull-compare instead of
>       -Wnonnull in dg-options.
>       * c-c++-common/nonnull-2.c: New test.
> 
> --- gcc/common.opt.jj 2016-01-27 19:47:35.000000000 +0100
> +++ gcc/common.opt    2016-02-16 11:52:42.641623690 +0100
> @@ -616,6 +616,10 @@ Wlarger-than=
>  Common RejectNegative Joined UInteger Warning
>  -Wlarger-than=<number>       Warn if an object is larger than <number> bytes.
>  
> +Wnonnull-compare
> +Var(warn_nonnull_compare) Warning
> +Warn if comparing pointer parameter with nonnull attribute with NULL.
> +
>  Wnull-dereference
>  Common Var(warn_null_dereference) Warning
>  Warn if dereferencing a NULL pointer may lead to erroneous or undefined 
> behavior.
> --- gcc/doc/invoke.texi.jj    2016-02-08 18:39:16.000000000 +0100
> +++ gcc/doc/invoke.texi       2016-02-16 12:31:42.037232875 +0100
> @@ -276,7 +276,8 @@ Objective-C and Objective-C++ Dialects}.
>  -Wmain -Wmaybe-uninitialized -Wmemset-transposed-args @gol
>  -Wmisleading-indentation -Wmissing-braces @gol
>  -Wmissing-field-initializers -Wmissing-include-dirs @gol
> --Wno-multichar  -Wnonnull  
> -Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol
> +-Wno-multichar -Wnonnull -Wnonnull-compare @gol
> +-Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol
>  -Wnull-dereference -Wodr  -Wno-overflow  -Wopenmp-simd  @gol
>  -Woverride-init-side-effects -Woverlength-strings @gol
>  -Wpacked  -Wpacked-bitfield-compat  -Wpadded @gol
> @@ -3537,6 +3538,7 @@ Options} and @ref{Objective-C and Object
>  -Wmissing-braces @r{(only for C/ObjC)} @gol
>  -Wnarrowing @r{(only for C++)}  @gol
>  -Wnonnull  @gol
> +-Wnonnull-compare  @gol
>  -Wopenmp-simd @gol
>  -Wparentheses  @gol
>  -Wpointer-sign  @gol
> @@ -3795,12 +3797,18 @@ formats that may yield only a two-digit
>  Warn about passing a null pointer for arguments marked as
>  requiring a non-null value by the @code{nonnull} function attribute.
>  
> -Also warns when comparing an argument marked with the @code{nonnull}
> -function attribute against null inside the function.
> -
>  @option{-Wnonnull} is included in @option{-Wall} and @option{-Wformat}.  It
>  can be disabled with the @option{-Wno-nonnull} option.
>  
> +@item -Wnonnull-compare
> +@opindex Wnonnull-compare
> +@opindex Wno-nonnull-compare
> +Warn when comparing an argument marked with the @code{nonnull}
> +function attribute against null inside the function.
> +
> +@option{-Wnonnull-compare} is included in @option{-Wall}.  It
> +can be disabled with the @option{-Wno-nonnull-compare} option.
> +
>  @item -Wnull-dereference
>  @opindex Wnull-dereference
>  @opindex Wno-null-dereference
> --- gcc/tree-ssa-uninit.c.jj  2016-01-13 13:28:41.000000000 +0100
> +++ gcc/tree-ssa-uninit.c     2016-02-16 12:50:30.390619823 +0100
> @@ -171,7 +171,8 @@ warn_uninit (enum opt_code wc, tree t, t
>  }
>  
>  static unsigned int
> -warn_uninitialized_vars (bool warn_possibly_uninitialized)
> +warn_uninitialized_vars (bool warn_possibly_uninitialized,
> +                      bool warn_nonnull_cmp)
>  {
>    gimple_stmt_iterator gsi;
>    basic_block bb;
> @@ -190,6 +191,60 @@ warn_uninitialized_vars (bool warn_possi
>         if (is_gimple_debug (stmt))
>           continue;
>  
> +       if (warn_nonnull_cmp)
> +         {
> +           tree op0 = NULL_TREE, op1 = NULL_TREE;
> +           location_t loc = gimple_location (stmt);
> +           if (gimple_code (stmt) == GIMPLE_COND)

For new if (gimple_code () ...) code I'd prefer dyn_cast and gcond *
and gassign *, that should be marginally faster, esp. with checking.

> +             switch (gimple_cond_code (stmt))
> +               {
> +               case EQ_EXPR:
> +               case NE_EXPR:
> +                 op0 = gimple_cond_lhs (stmt);
> +                 op1 = gimple_cond_rhs (stmt);
> +                 break;
> +               default:
> +                 break;
> +               }
> +           else if (is_gimple_assign (stmt))
> +             switch (gimple_assign_rhs_code (stmt))
> +               {
> +               case EQ_EXPR:
> +               case NE_EXPR:
> +                 op0 = gimple_assign_rhs1 (stmt);
> +                 op1 = gimple_assign_rhs2 (stmt);
> +                 break;
> +               case COND_EXPR:
> +                 switch (TREE_CODE (gimple_assign_rhs1 (stmt)))
> +                   {
> +                   case EQ_EXPR:
> +                   case NE_EXPR:
> +                     op1 = gimple_assign_rhs1 (stmt);
> +                     loc = EXPR_LOC_OR_LOC (op1, loc);
> +                     op0 = TREE_OPERAND (op1, 0);
> +                     op1 = TREE_OPERAND (op1, 1);
> +                     break;
> +                   default:
> +                     break;
> +                   }
> +                 break;
> +               default:
> +                 break;
> +               }
> +           if (op0
> +               && TREE_CODE (op0) == SSA_NAME
> +               && SSA_NAME_IS_DEFAULT_DEF (op0)
> +               && TREE_CODE (SSA_NAME_VAR (op0)) == PARM_DECL
> +               && (POINTER_TYPE_P (TREE_TYPE (op0))
> +                   || TREE_CODE (TREE_TYPE (op0)) == OFFSET_TYPE)
> +               && nonnull_arg_p (SSA_NAME_VAR (op0))
> +               && (POINTER_TYPE_P (TREE_TYPE (op0))
> +                   ? integer_zerop (op1) : integer_minus_onep (op1)))

Why do we have OFFSET_TYPE and why do we say "nonnull" for -1 offset?

Other than the walking issue the patch looks reasonable.

Thanks,
Richard.

> +             warning_at (gimple_location (stmt), OPT_Wnonnull_compare,
> +                         "nonnull argument %qD compared to NULL",
> +                         SSA_NAME_VAR (op0));
> +         }
> +
>         /* We only do data flow with SSA_NAMEs, so that's all we
>            can warn about.  */
>         FOR_EACH_SSA_USE_OPERAND (use_p, stmt, op_iter, SSA_OP_USE)
> @@ -2416,7 +2471,7 @@ pass_late_warn_uninitialized::execute (f
>    /* Re-do the plain uninitialized variable check, as optimization may have
>       straightened control flow.  Do this first so that we don't accidentally
>       get a "may be" warning when we'd have seen an "is" warning later.  */
> -  warn_uninitialized_vars (/*warn_possibly_uninitialized=*/1);
> +  warn_uninitialized_vars (/*warn_possibly_uninitialized=*/true, false);
>  
>    timevar_push (TV_TREE_UNINIT);
>  
> @@ -2478,6 +2533,14 @@ make_pass_late_warn_uninitialized (gcc::
>  }
>  
>  
> +static bool
> +gate_early_warn_uninitialized (void)
> +{
> +  return (warn_uninitialized
> +       || warn_maybe_uninitialized
> +       || warn_nonnull_compare);
> +}
> +
>  static unsigned int
>  execute_early_warn_uninitialized (void)
>  {
> @@ -2488,7 +2551,8 @@ execute_early_warn_uninitialized (void)
>       optimization we need to warn here about "may be uninitialized".  */
>    calculate_dominance_info (CDI_POST_DOMINATORS);
>  
> -  warn_uninitialized_vars (/*warn_possibly_uninitialized=*/!optimize);
> +  warn_uninitialized_vars (/*warn_possibly_uninitialized=*/!optimize,
> +                        warn_nonnull_compare);
>  
>    /* Post-dominator information can not be reliably updated. Free it
>       after the use.  */
> @@ -2521,7 +2585,7 @@ public:
>    {}
>  
>    /* opt_pass methods: */
> -  virtual bool gate (function *) { return gate_warn_uninitialized (); }
> +  virtual bool gate (function *) { return gate_early_warn_uninitialized (); }
>    virtual unsigned int execute (function *)
>      {
>        return execute_early_warn_uninitialized ();
> --- gcc/c-family/c.opt.jj     2016-02-08 18:39:17.000000000 +0100
> +++ gcc/c-family/c.opt        2016-02-16 12:30:00.299641823 +0100
> @@ -677,6 +677,10 @@ Wnonnull
>  C ObjC C++ ObjC++ LangEnabledBy(C ObjC C++ ObjC++,Wall)
>  ;
>  
> +Wnonnull-compare
> +C ObjC C++ ObjC++ LangEnabledBy(C ObjC C++ ObjC++,Wall)
> +;
> +
>  Wnormalized
>  C ObjC C++ ObjC++ Warning Alias(Wnormalized=,nfc,none)
>  ;
> --- gcc/c/c-typeck.c.jj       2016-02-12 10:23:50.000000000 +0100
> +++ gcc/c/c-typeck.c  2016-02-16 11:40:08.474048701 +0100
> @@ -11086,11 +11086,6 @@ build_binary_op (location_t location, en
>       short_compare = 1;
>        else if (code0 == POINTER_TYPE && null_pointer_constant_p (orig_op1))
>       {
> -       if (warn_nonnull
> -           && TREE_CODE (op0) == PARM_DECL && nonnull_arg_p (op0))
> -         warning_at (location, OPT_Wnonnull,
> -                     "nonnull argument %qD compared to NULL", op0);
> -
>         if (TREE_CODE (op0) == ADDR_EXPR
>             && decl_with_nonnull_addr_p (TREE_OPERAND (op0, 0)))
>           {
> @@ -11111,11 +11106,6 @@ build_binary_op (location_t location, en
>       }
>        else if (code1 == POINTER_TYPE && null_pointer_constant_p (orig_op0))
>       {
> -       if (warn_nonnull
> -           && TREE_CODE (op1) == PARM_DECL && nonnull_arg_p (op1))
> -         warning_at (location, OPT_Wnonnull,
> -                     "nonnull argument %qD compared to NULL", op1);
> -
>         if (TREE_CODE (op1) == ADDR_EXPR
>             && decl_with_nonnull_addr_p (TREE_OPERAND (op1, 0)))
>           {
> --- gcc/cp/typeck.c.jj        2016-02-12 10:23:50.000000000 +0100
> +++ gcc/cp/typeck.c   2016-02-16 11:40:37.783643278 +0100
> @@ -4514,11 +4514,6 @@ cp_build_binary_op (location_t location,
>              || (code0 == POINTER_TYPE
>                  && TYPE_PTR_P (type1) && integer_zerop (op1)))
>       {
> -       if (warn_nonnull
> -           && TREE_CODE (op0) == PARM_DECL && nonnull_arg_p (op0))
> -         warning_at (location, OPT_Wnonnull,
> -                     "nonnull argument %qD compared to NULL", op0);
> -
>         if (TYPE_PTR_P (type1))
>           result_type = composite_pointer_type (type0, type1, op0, op1,
>                                                 CPO_COMPARISON, complain);
> @@ -4558,11 +4553,6 @@ cp_build_binary_op (location_t location,
>              || (code1 == POINTER_TYPE
>                  && TYPE_PTR_P (type0) && integer_zerop (op0)))
>       {
> -       if (warn_nonnull
> -           && TREE_CODE (op1) == PARM_DECL && nonnull_arg_p (op1))
> -         warning_at (location, OPT_Wnonnull,
> -                     "nonnull argument %qD compared to NULL", op1);
> -
>         if (TYPE_PTR_P (type0))
>           result_type = composite_pointer_type (type0, type1, op0, op1,
>                                                 CPO_COMPARISON, complain);
> --- gcc/testsuite/c-c++-common/nonnull-1.c.jj 2015-10-11 19:11:06.000000000 
> +0200
> +++ gcc/testsuite/c-c++-common/nonnull-1.c    2016-02-16 12:38:53.917256278 
> +0100
> @@ -1,7 +1,7 @@
>  /* Test for the bad usage of "nonnull" function attribute parms.  */
>  /*  */
>  /* { dg-do compile } */
> -/* { dg-options "-Wnonnull" } */
> +/* { dg-options "-Wnonnull-compare" } */
>  
>  #include <stddef.h>
>  #include <stdlib.h>
> --- gcc/testsuite/c-c++-common/nonnull-2.c.jj 2016-02-16 12:52:17.149142596 
> +0100
> +++ gcc/testsuite/c-c++-common/nonnull-2.c    2016-02-16 12:56:29.904645198 
> +0100
> @@ -0,0 +1,26 @@
> +/* Test for the bad usage of "nonnull" function attribute parms.  */
> +/* { dg-do compile } */
> +/* { dg-options "-Wnonnull-compare" } */
> +
> +void bar (char **);
> +
> +__attribute__((nonnull (1, 3))) int
> +foo (char *cp1, char *cp2, char *cp3, char *cp4)
> +{
> +  if (cp1 == (char *) 0) /* { dg-warning "nonnull argument" "cp1 compared to 
> NULL" } */
> +    return 1;
> +
> +  cp1 = cp2;
> +  if (cp1 == (char *) 0) /* { dg-bogus "nonnull argument" } */
> +    return 2;
> +
> +  if (!cp4)     /* { dg-bogus "nonnull argument" } */
> +    return 3;
> +
> +  char **p = &cp3;
> +  bar (p);
> +  if (cp3 == (char *) 0) /* { dg-bogus "nonnull argument" } */
> +    return 4;
> +
> +  return 5;
> +}
> 
>       Jakub
> 
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 
21284 (AG Nuernberg)

Reply via email to