]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Add support for nonnull_if_nonzero attribute [PR117023]
authorJakub Jelinek <jakub@redhat.com>
Thu, 28 Nov 2024 10:48:33 +0000 (11:48 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 28 Nov 2024 10:48:33 +0000 (11:48 +0100)
As mentioned in an earlier thread, C2Y voted in a change which made
various library APIs callable with NULL arguments in certain cases,
e.g.
memcpy (NULL, NULL, 0);
is now valid, although
memcpy (NULL, NULL, 1);
remains invalid.  This affects various APIs, including several of
GCC builtins; plus on the C library side those APIs are often declared
with nonnull attribute(s) as well.

Florian suggested using the access attribute for this, but our docs
explicitly say that access attribute doesn't imply nonnull and it doesn't
cover e.g. the qsort case where the comparison function pointer may be
also NULL if nmemb is 0, but must be non-zero otherwise.
As this case affects 21 APIs in C standard and I think is going to affect
various wrappers around those in various packages as well, I think it
is a common thing that should have its own attribute, because we should
still warn when people use
qsort (NULL, 1, 1, NULL);
etc., and similarly want to have -fsanitize=null instrumentation for those.

So, the following patch introduces nonnull_if_nonzero attribute (or would
you prefer cond_nonnull or some other name?), which has always 2 arguments,
argument index of a pointer argument (like one argument nonnull) and
argument index of an associated integral argument.  If that argument is
non-zero, it is UB to pass NULL to the pointer argument, if that argument
is zero, it is valid.  And changes various spots which already handled the
nonnull attribute to handle this one as well, with sometimes using the
ranger (or for -fsanitize=nonnull explicitly checking the associated
argument value, so instead of if (!ptr) __ubsan_... (...); it will
now do if (!ptr && sz) __ubsan_... (...);).
I've so far omitted changing gimple_infer_range (am not 100% sure how I can
use the ranger inside of the ranger) and changing the analyzer to handle it.
And I haven't changed builtins.def etc. to make use of that attribute
instead of nonnull where appropriate.

I'd then follow with the builtins.def changes (and eventually glibc
etc. would need to be adjusted too).

2024-11-28  Jakub Jelinek  <jakub@redhat.com>

PR c/117023
gcc/
* gimple.h (infer_nonnull_range_by_attribute): Add a tree *
argument defaulted to NULL.
* gimple.cc (infer_nonnull_range_by_attribute): Add op2 argument.
Handle also nonnull_if_nonzero attributes.
* tree.cc (get_nonnull_args): Fix comment typo.
* builtins.cc (validate_arglist): Handle nonnull_if_nonzero attribute.
* tree-ssa-ccp.cc (pass_post_ipa_warn::execute): Handle
nonnull_if_nonzero attributes.
* ubsan.cc (instrument_nonnull_arg): Adjust
infer_nonnull_range_by_attribute caller.  If it returned true and
filed in non-NULL arg2, check that arg2 is non-zero as another
condition next to checking that arg is zero.
* doc/extend.texi (nonnull_if_nonzero): Document new attribute.
gcc/c-family/
* c-attribs.cc (handle_nonnull_if_nonzero_attribute): New
function.
(c_common_gnu_attributes): Add nonnull_if_nonzero attribute.
(handle_nonnull_attribute): Fix comment typo.
* c-common.cc (struct nonnull_arg_ctx): Add other member.
(check_function_nonnull): Also check nonnull_if_nonzero attributes.
(check_nonnull_arg): Use different warning wording if pctx->other
is non-zero.
(check_function_arguments): Initialize ctx.other.
gcc/testsuite/
* gcc.dg/nonnull-8.c: New test.
* gcc.dg/nonnull-9.c: New test.
* gcc.dg/nonnull-10.c: New test.
* c-c++-common/ubsan/nonnull-6.c: New test.
* c-c++-common/ubsan/nonnull-7.c: New test.

14 files changed:
gcc/builtins.cc
gcc/c-family/c-attribs.cc
gcc/c-family/c-common.cc
gcc/doc/extend.texi
gcc/gimple.cc
gcc/gimple.h
gcc/testsuite/c-c++-common/ubsan/nonnull-6.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/nonnull-7.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/nonnull-10.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/nonnull-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/nonnull-9.c [new file with mode: 0644]
gcc/tree-ssa-ccp.cc
gcc/tree.cc
gcc/ubsan.cc

index d925074c547502b5b26ed33f91cdb79d7f26248b..fd7acdfc915b8333d9a1d0f2c665ad888145bbd2 100644 (file)
@@ -1149,6 +1149,24 @@ validate_arglist (const_tree callexpr, ...)
 
   BITMAP_FREE (argmap);
 
+  if (res)
+    for (tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (TREE_TYPE (fn)));
+        (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
+        attrs = TREE_CHAIN (attrs))
+      {
+       tree args = TREE_VALUE (attrs);
+       unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
+       unsigned int idx2
+         = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+       if (idx < (unsigned) call_expr_nargs (callexpr)
+           && idx2 < (unsigned) call_expr_nargs (callexpr)
+           && POINTER_TYPE_P (TREE_TYPE (CALL_EXPR_ARG (callexpr, idx)))
+           && integer_zerop (CALL_EXPR_ARG (callexpr, idx))
+           && INTEGRAL_TYPE_P (TREE_TYPE (CALL_EXPR_ARG (callexpr, idx2)))
+           && integer_nonzerop (CALL_EXPR_ARG (callexpr, idx2)))
+         return false;
+      }
+
   return res;
 }
 
index 5a71749d2f9c7a6f62814e9c70290601acd1dac0..5b64805f97de4452cd008db9d6472d504804c6e5 100644 (file)
@@ -138,6 +138,8 @@ static tree handle_vector_size_attribute (tree *, tree, tree, int,
 static tree handle_vector_mask_attribute (tree *, tree, tree, int,
                                          bool *) ATTRIBUTE_NONNULL(3);
 static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *);
+static tree handle_nonnull_if_nonzero_attribute (tree *, tree, tree, int,
+                                                bool *);
 static tree handle_nonstring_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *);
 static tree handle_expected_throw_attribute (tree *, tree, tree, int, bool *);
@@ -487,6 +489,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
                              handle_tls_model_attribute, NULL },
   { "nonnull",                0, -1, false, true, true, false,
                              handle_nonnull_attribute, NULL },
+  { "nonnull_if_nonzero",     2, 2, false, true, true, false,
+                             handle_nonnull_if_nonzero_attribute, NULL },
   { "nonstring",              0, 0, true, false, false, false,
                              handle_nonstring_attribute, NULL },
   { "nothrow",                0, 0, true,  false, false, false,
@@ -5002,7 +5006,7 @@ handle_nonnull_attribute (tree *node, tree name,
       /* NEXT is null when the attribute includes just one argument.
         That's used to tell positional_argument to avoid mentioning
         the argument number in diagnostics (since there's just one
-        mentioning it is unnecessary and coule be confusing).  */
+        mentioning it is unnecessary and could be confusing).  */
       tree next = TREE_CHAIN (args);
       if (tree val = positional_argument (type, name, pos, POINTER_TYPE,
                                          next || i > 1 ? i : 0))
@@ -5018,6 +5022,29 @@ handle_nonnull_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+/* Handle the "nonnull_if_nonzero" attribute.  */
+
+static tree
+handle_nonnull_if_nonzero_attribute (tree *node, tree name,
+                                    tree args, int ARG_UNUSED (flags),
+                                    bool *no_add_attrs)
+{
+  tree type = *node;
+  tree pos = TREE_VALUE (args);
+  tree pos2 = TREE_VALUE (TREE_CHAIN (args));
+  tree val = positional_argument (type, name, pos, POINTER_TYPE, 1);
+  tree val2 = positional_argument (type, name, pos2, INTEGER_TYPE, 2);
+  if (val && val2)
+    {
+      TREE_VALUE (args) = val;
+      TREE_VALUE (TREE_CHAIN (args)) = val2;
+    }
+  else
+    *no_add_attrs = true;
+
+  return NULL_TREE;
+}
+
 /* Handle the "fd_arg", "fd_arg_read" and "fd_arg_write" attributes */
 
 static tree
index 721407157bc3967952580fdbdd30b036629f3390..a8f25d6cb9442b7ae7d99b66f036c809005de944 100644 (file)
@@ -5721,6 +5721,8 @@ struct nonnull_arg_ctx
   /* The function whose arguments are being checked and its type (used
      for calls through function pointers).  */
   const_tree fndecl, fntype;
+  /* For nonnull_if_nonzero, index of the other argument.  */
+  unsigned HOST_WIDE_INT other;
   /* True if a warning has been issued.  */
   bool warned_p;
 };
@@ -5759,23 +5761,19 @@ check_function_nonnull (nonnull_arg_ctx &ctx, int nargs, tree *argarray)
     }
 
   tree attrs = lookup_attribute ("nonnull", TYPE_ATTRIBUTES (ctx.fntype));
-  if (attrs == NULL_TREE)
-    return ctx.warned_p;
 
   tree a = attrs;
   /* See if any of the nonnull attributes has no arguments.  If so,
      then every pointer argument is checked (in which case the check
      for pointer type is done in check_nonnull_arg).  */
-  if (TREE_VALUE (a) != NULL_TREE)
-    do
-      a = lookup_attribute ("nonnull", TREE_CHAIN (a));
-    while (a != NULL_TREE && TREE_VALUE (a) != NULL_TREE);
+  while (a != NULL_TREE && TREE_VALUE (a) != NULL_TREE)
+    a = lookup_attribute ("nonnull", TREE_CHAIN (a));
 
   if (a != NULL_TREE)
     for (int i = firstarg; i < nargs; i++)
       check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[i],
                                        i + 1, OPT_Wnonnull);
-  else
+  else if (attrs)
     {
       /* Walk the argument list.  If we encounter an argument number we
         should check for non-null, do it.  */
@@ -5794,6 +5792,28 @@ check_function_nonnull (nonnull_arg_ctx &ctx, int nargs, tree *argarray)
                                              OPT_Wnonnull);
        }
     }
+  if (a == NULL_TREE)
+    for (attrs = TYPE_ATTRIBUTES (ctx.fntype);
+        (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
+        attrs = TREE_CHAIN (attrs))
+      {
+       tree args = TREE_VALUE (attrs);
+       unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
+       unsigned int idx2
+         = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+       if (idx < (unsigned) nargs - firstarg
+           && idx2 < (unsigned) nargs - firstarg
+           && INTEGRAL_TYPE_P (TREE_TYPE (argarray[firstarg + idx2]))
+           && integer_nonzerop (argarray[firstarg + idx2]))
+         {
+           ctx.other = firstarg + idx2 + 1;
+           check_function_arguments_recurse (check_nonnull_arg, &ctx,
+                                             argarray[firstarg + idx],
+                                             firstarg + idx + 1,
+                                             OPT_Wnonnull);
+           ctx.other = 0;
+         }
+      }
   return ctx.warned_p;
 }
 
@@ -5975,13 +5995,23 @@ check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num)
     }
   else
     {
-      warned = warning_at (loc, OPT_Wnonnull,
-                          "argument %u null where non-null expected",
-                          (unsigned) param_num);
+      if (pctx->other)
+       warned = warning_at (loc, OPT_Wnonnull,
+                            "argument %u null where non-null expected "
+                            "because argument %u is nonzero",
+                            (unsigned) param_num,
+                            TREE_CODE (pctx->fntype) == METHOD_TYPE
+                            ? (unsigned) pctx->other - 1
+                            : (unsigned) pctx->other);
+      else
+       warned = warning_at (loc, OPT_Wnonnull,
+                            "argument %u null where non-null expected",
+                            (unsigned) param_num);
       if (warned && pctx->fndecl)
        inform (DECL_SOURCE_LOCATION (pctx->fndecl),
                "in a call to function %qD declared %qs",
-               pctx->fndecl, "nonnull");
+               pctx->fndecl,
+               pctx->other ? "nonnull_if_nonzero" : "nonnull");
     }
 
   if (warned)
@@ -6227,7 +6257,7 @@ check_function_arguments (location_t loc, const_tree fndecl, const_tree fntype,
      to do this if format checking is enabled.  */
   if (warn_nonnull)
     {
-      nonnull_arg_ctx ctx = { loc, fndecl, fntype, false };
+      nonnull_arg_ctx ctx = { loc, fndecl, fntype, 0, false };
       warned_p = check_function_nonnull (ctx, nargs, argarray);
     }
 
index 2fc513efdb585d823a15d7b745477fc71ecf5237..8497aa8603f26a79f4955b10719f0e52c30da141 100644 (file)
@@ -2754,9 +2754,10 @@ object size, for example in functions that call @code{__builtin_object_size}.
 Note that the @code{access} attribute merely specifies how an object
 referenced by the pointer argument can be accessed; it does not imply that
 an access @strong{will} happen.  Also, the @code{access} attribute does not
-imply the attribute @code{nonnull}; it may be appropriate to add both attributes
-at the declaration of a function that unconditionally manipulates a buffer via
-a pointer argument.  See the @code{nonnull} attribute for more information and
+imply the attribute @code{nonnull} nor the attribute @code{nonnull_if_nonzero};
+it may be appropriate to add both attributes at the declaration of a function
+that unconditionally manipulates a buffer via a pointer argument.  See the
+@code{nonnull} or @code{nonnull_if_nonzero} attributes for more information and
 caveats.
 
 @cindex @code{alias} function attribute
@@ -3788,6 +3789,34 @@ my_memcpy (void *dest, const void *src, size_t len)
         __attribute__((nonnull));
 @end smallexample
 
+@cindex @code{nonnull_if_nonzero} function attribute
+@item nonnull_if_nonzero
+@itemx nonnull_if_nonzero (@var{arg-index}, @var{arg2-index})
+The @code{nonnull_if_nonzero} attribute is a conditional version of the
+@code{nonnull} attribute.  It has two arguments, the first argument
+shall be argument index of a pointer argument which must be in some
+cases non-null and the second argument shall be argument index of an
+integral argument (other than boolean).  If the integral argument is
+zero, the pointer argument can be null, if it is non-zero, the pointer
+argument must not be null.
+
+@smallexample
+extern void *
+my_memcpy (void *dest, const void *src, size_t len)
+        __attribute__((nonnull (1, 2)));
+extern void *
+my_memcpy2 (void *dest, const void *src, size_t len)
+        __attribute__((nonnull_if_nonzero (1, 3),
+                       nonnull_if_nonzero (2, 3)));
+@end smallexample
+
+With these declarations, it is invalid to call
+@code{my_memcpy (NULL, NULL, 0);} or to
+call @code{my_memcpy2 (NULL, NULL, 4);} but it is valid
+to call @code{my_memcpy2 (NULL, NULL, 0);}.  This attribute should be
+used on declarations which have e.g.@: an exception for zero sizes,
+in which case null may be passed.
+
 @cindex @code{noplt} function attribute
 @item noplt
 The @code{noplt} attribute is the counterpart to option @option{-fno-plt}.
index c6d0991ded90098bddce8090e1d3bb19466da2b6..477315cb1b8669f4c333fa59d3153f9d383b4573 100644 (file)
@@ -3142,10 +3142,17 @@ infer_nonnull_range_by_dereference (gimple *stmt, tree op)
 }
 
 /* Return true if OP can be inferred to be a non-NULL after STMT
-   executes by using attributes.  */
+   executes by using attributes.  If OP2 is non-NULL and nonnull_if_nonzero
+   is the only attribute implying OP being non-NULL and the corresponding
+   argument isn't non-zero INTEGER_CST, set *OP2 to the corresponding
+   argument and return true (in that case returning true doesn't mean
+   OP can be unconditionally inferred to be non-NULL, but conditionally).  */
 bool
-infer_nonnull_range_by_attribute (gimple *stmt, tree op)
+infer_nonnull_range_by_attribute (gimple *stmt, tree op, tree *op2)
 {
+  if (op2)
+    *op2 = NULL_TREE;
+
   /* We can only assume that a pointer dereference will yield
      non-NULL if -fdelete-null-pointer-checks is enabled.  */
   if (!flag_delete_null_pointer_checks
@@ -3162,9 +3169,10 @@ infer_nonnull_range_by_attribute (gimple *stmt, tree op)
          attrs = lookup_attribute ("nonnull", attrs);
 
          /* If "nonnull" wasn't specified, we know nothing about
-            the argument.  */
+            the argument, unless "nonnull_if_nonzero" attribute is
+            present.  */
          if (attrs == NULL_TREE)
-           return false;
+           break;
 
          /* If "nonnull" applies to all the arguments, then ARG
             is non-null if it's in the argument list.  */
@@ -3191,6 +3199,37 @@ infer_nonnull_range_by_attribute (gimple *stmt, tree op)
                }
            }
        }
+
+      for (attrs = TYPE_ATTRIBUTES (fntype);
+          (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
+          attrs = TREE_CHAIN (attrs))
+       {
+         tree args = TREE_VALUE (attrs);
+         unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
+         unsigned int idx2
+           = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+         if (idx < gimple_call_num_args (stmt)
+             && idx2 < gimple_call_num_args (stmt)
+             && operand_equal_p (op, gimple_call_arg (stmt, idx), 0))
+           {
+             tree arg2 = gimple_call_arg (stmt, idx2);
+             if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
+               return false;
+             if (integer_nonzerop (arg2))
+               return true;
+             if (integer_zerop (arg2))
+               return false;
+             if (op2)
+               {
+                 /* This case is meant for ubsan instrumentation.
+                    The caller can check at runtime if *OP2 is
+                    non-zero and OP is null.  */
+                 *op2 = arg2;
+                 return true;
+               }
+             return tree_expr_nonzero_p (arg2);
+           }
+       }
     }
 
   /* If this function is marked as returning non-null, then we can
index b6967e63de23a2c8de2bd0c73e11a27a694d2858..039ed66eab5d93c48c0cc5ee38c673029580330d 100644 (file)
@@ -1662,7 +1662,7 @@ extern bool nonfreeing_call_p (gimple *);
 extern bool nonbarrier_call_p (gimple *);
 extern bool infer_nonnull_range (gimple *, tree);
 extern bool infer_nonnull_range_by_dereference (gimple *, tree);
-extern bool infer_nonnull_range_by_attribute (gimple *, tree);
+extern bool infer_nonnull_range_by_attribute (gimple *, tree, tree * = NULL);
 extern void sort_case_labels (vec<tree> &);
 extern void preprocess_case_label_vec_for_gimple (vec<tree> &, tree, tree *);
 extern void gimple_seq_set_location (gimple_seq, location_t);
diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c
new file mode 100644 (file)
index 0000000..8e072fe
--- /dev/null
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */
+
+__attribute__((noipa, nonnull_if_nonzero (1, 4)))
+__attribute__((nonnull (3), nonnull_if_nonzero (5, 2))) void
+foo (void *a, unsigned long b, void *c, int d, void *e)
+{
+  (void) a;
+  (void) b;
+  (void) c;
+  (void) d;
+  (void) e;
+}
+
+__attribute__((noipa))
+void
+bar (void *a, unsigned long b, void *c, int d, void *e)
+{
+  foo (a, b, c, d, e);
+}
+
+int
+main ()
+{
+  char x;
+  bar (&x, 42, &x, 1, &x);
+  bar (0, 0, &x, 0, 0);
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-7.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-7.c
new file mode 100644 (file)
index 0000000..a8be2a7
--- /dev/null
@@ -0,0 +1,39 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=nonnull-attribute" } */
+
+__attribute__((noipa, nonnull_if_nonzero (1, 4)))
+__attribute__((nonnull (3), nonnull_if_nonzero (5, 2))) void
+foo (void *a, unsigned long b, void *c, int d, void *e)
+{
+  (void) a;
+  (void) b;
+  (void) c;
+  (void) d;
+  (void) e;
+}
+
+__attribute__((noipa))
+void
+bar (void *a, unsigned long b, void *c, int d, void *e)
+{
+  foo (a, b, c, d, e);
+}
+
+int
+main ()
+{
+  char x;
+  bar (&x, 42, 0, 1, &x);
+  bar (0, 25, &x, 7, &x);
+  bar (&x, -82, &x, 68, 0);
+  foo (&x, 42, 0, 1, &x);
+  foo (0, 25, &x, 7, &x);
+  foo (&x, -82, &x, 68, 0);
+}
+
+/* { dg-output "\.c:19:\[0-9]*:\[^\n\r]*null pointer passed as argument 3, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:19:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:19:\[0-9]*:\[^\n\r]*null pointer passed as argument 5, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:29:\[0-9]*:\[^\n\r]*null pointer passed as argument 3, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:30:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:31:\[0-9]*:\[^\n\r]*null pointer passed as argument 5, which is declared to never be null" } */
diff --git a/gcc/testsuite/gcc.dg/nonnull-10.c b/gcc/testsuite/gcc.dg/nonnull-10.c
new file mode 100644 (file)
index 0000000..447ad30
--- /dev/null
@@ -0,0 +1,162 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wnonnull" } */
+
+#define N(x, y) __attribute__ ((nonnull_if_nonzero (x, y)))
+
+void N (1, 2) f1_1 (void *, int);
+
+void N (1, 3) f2_1 (void *, void *, int);
+void N (1, 3) N (2, 3) f2_1_2 (void *, void *, int);
+
+void N (1, 4) N (3, 5) f3_1_3 (void *, void *, void *, int, int);
+
+void N (1, 5) N (2, 5) N (4, 5) g4_1_2_4 (void *, void *, void *, void *, long);
+void N (1, 5) N (3, 5) N (4, 5) g4_1_3_4 (void *, void *, void *, void *, long);
+void N (2, 5) N (3, 5) N (4, 5) g4_2_3_4 (void *, void *, void *, void *, long);
+
+void N (1, 17) N (3, 17) N (5, 17) N (7, 17) N (11, 17) N (13, 17)
+g16_1_3_5_7_11_13 (void *, void *, void *, void *,
+                  void *, void *, void *, void *,
+                  void *, void *, void *, void *,
+                  void *, void *, void *, void *, int);
+
+static void *null (void) { return 0; }
+
+void
+test (int t, long u)
+{
+  void *p0 = null ();
+  void *px = &px;
+
+  f1_1 (p0, 0);
+  f1_1 (p0, t);
+  f1_1 (p0, 42); /* { dg-warning "argument 1 null where non-null expected because argument 2 is nonzero" } */
+  if (t)
+    f1_1 (p0, t); /* { dg-warning "argument 1 null where non-null expected because argument 2 is nonzero" } */
+  f1_1 (px, 17);
+
+  f2_1 (p0, px, 0);
+  f2_1 (p0, px, t);
+  f2_1 (p0, px, 5);  /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+  if (t > 4)
+    f2_1 (p0, px, t);  /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+  f2_1 (px, p0, 17);
+  f2_1 (p0, p0, 0);
+  if (t < 0)
+    f2_1 (p0, p0, t);  /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+
+  f2_1_2 (p0, p0, 0);
+  f2_1_2 (p0, p0, t);
+  f2_1_2 (p0, px, 1); /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+  if (t > 8)
+    f2_1_2 (p0, px, t); /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+  f2_1_2 (px, p0, -3); /* { dg-warning "argument 2 null where non-null expected because argument 3 is nonzero" } */
+  if (t < -2)
+    f2_1_2 (px, p0, t); /* { dg-warning "argument 2 null where non-null expected because argument 3 is nonzero" } */
+  f2_1_2 (p0, p0, 8); /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+  /* { dg-warning "argument 2 null where non-null expected because argument 3 is nonzero" "argument 2" { target *-*-* } .-1 } */
+  if (t > 7)
+    f2_1_2 (p0, p0, t); /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+  /* { dg-warning "argument 2 null where non-null expected because argument 3 is nonzero" "argument 2" { target *-*-* } .-1 } */
+
+  f3_1_3 (p0, p0, p0, 0, 0);
+  f3_1_3 (p0, p0, px, 0, 6);
+  f3_1_3 (px, p0, p0, 2, 0);
+  f3_1_3 (p0, p0, p0, t, t);
+  f3_1_3 (p0, p0, px, t, 6);
+  f3_1_3 (px, p0, p0, 2, t);
+  f3_1_3 (p0, px, px, 8, 2); /* { dg-warning "argument 1 null where non-null expected because argument 4 is nonzero" } */
+  if (t > 9)
+    f3_1_3 (p0, px, px, t, 3); /* { dg-warning "argument 1 null where non-null expected because argument 4 is nonzero" } */
+  f3_1_3 (px, p0, px, 9, 10);
+  if (t > 11)
+    f3_1_3 (px, p0, px, t, t);
+  f3_1_3 (px, px, p0, 10, 11); /* { dg-warning "argument 3 null where non-null expected because argument 5 is nonzero" } */
+  if (t < -5)
+    f3_1_3 (px, px, p0, 0, t); /* { dg-warning "argument 3 null where non-null expected because argument 5 is nonzero" } */
+  f3_1_3 (p0, p0, px, 11, 12); /* { dg-warning "argument 1 null where non-null expected because argument 4 is nonzero" } */
+  if (t > 26)
+    f3_1_3 (p0, p0, px, t, 0); /* { dg-warning "argument 1 null where non-null expected because argument 4 is nonzero" } */
+  f3_1_3 (px, p0, p0, 12, 13); /* { dg-warning "argument 3 null where non-null expected because argument 5 is nonzero" } */
+  if (t > 31)
+    f3_1_3 (px, p0, p0, 12, t); /* { dg-warning "argument 3 null where non-null expected because argument 5 is nonzero" } */
+  f3_1_3 (p0, p0, p0, 13, 14); /* { dg-warning "argument 1 null where non-null expected because argument 4 is nonzero" } */
+  /* { dg-warning "argument 3 null where non-null expected because argument 5 is nonzero" "argument 3" { target *-*-* } .-1 } */
+  if (t > 28)
+    f3_1_3 (p0, p0, p0, t, t + 1); /* { dg-warning "argument 1 null where non-null expected because argument 4 is nonzero" } */
+  /* { dg-warning "argument 3 null where non-null expected because argument 5 is nonzero" "argument 3" { target *-*-* } .-1 } */
+
+  g4_1_2_4 (p0, px, px, px, u);
+  g4_1_2_4 (px, p0, px, px, u);
+  g4_1_2_4 (px, px, p0, px, u);
+  g4_1_2_4 (px, px, px, p0, u);
+  g4_1_2_4 (p0, px, px, px, 0);
+  g4_1_2_4 (px, p0, px, px, 0);
+  g4_1_2_4 (px, px, p0, px, 0);
+  g4_1_2_4 (px, px, px, p0, 0);
+  g4_1_2_4 (p0, px, px, px, 15); /* { dg-warning "argument 1 null where non-null expected because argument 5 is nonzero" } */
+  if (u)
+    g4_1_2_4 (p0, px, px, px, u); /* { dg-warning "argument 1 null where non-null expected because argument 5 is nonzero" } */
+  g4_1_2_4 (px, p0, px, px, 16); /* { dg-warning "argument 2 null where non-null expected because argument 5 is nonzero" } */
+  if (u > 2)
+    g4_1_2_4 (px, p0, px, px, u); /* { dg-warning "argument 2 null where non-null expected because argument 5 is nonzero" } */
+  g4_1_2_4 (px, px, p0, px, 17);
+  if (u > 3)
+    g4_1_2_4 (px, px, p0, px, u);
+  g4_1_2_4 (px, px, px, p0, 18); /* { dg-warning "argument 4 null where non-null expected because argument 5 is nonzero" } */
+  if (u < -2 || u > 10)
+    g4_1_2_4 (px, px, px, p0, u); /* { dg-warning "argument 4 null where non-null expected because argument 5 is nonzero" } */
+
+  g4_1_3_4 (p0, px, px, px, u);
+  g4_1_3_4 (px, p0, px, px, u);
+  g4_1_3_4 (px, px, p0, px, u);
+  g4_1_3_4 (px, px, px, p0, u);
+  g4_1_3_4 (p0, px, px, px, 0);
+  g4_1_3_4 (px, p0, px, px, 0);
+  g4_1_3_4 (px, px, p0, px, 0);
+  g4_1_3_4 (px, px, px, p0, 0);
+  g4_1_3_4 (p0, px, px, px, 20); /* { dg-warning "argument 1 null where non-null expected because argument 5 is nonzero" } */
+  if (u > 4)
+    g4_1_3_4 (p0, px, px, px, u); /* { dg-warning "argument 1 null where non-null expected because argument 5 is nonzero" } */
+  g4_1_3_4 (px, p0, px, px, 21);
+  if (u > 6 || u < -24)
+    g4_1_3_4 (px, p0, px, px, u);
+  g4_1_3_4 (px, px, p0, px, 22); /* { dg-warning "argument 3 null where non-null expected because argument 5 is nonzero" } */
+  if (u > 9)
+    g4_1_3_4 (px, px, p0, px, u - 3); /* { dg-warning "argument 3 null where non-null expected because argument 5 is nonzero" } */
+  g4_1_3_4 (px, px, px, p0, 23); /* { dg-warning "argument 4 null where non-null expected because argument 5 is nonzero" } */
+  if (u > 10)
+    g4_1_3_4 (px, px, px, p0, u); /* { dg-warning "argument 4 null where non-null expected because argument 5 is nonzero" } */
+
+  g4_2_3_4 (p0, px, px, px, u);
+  g4_2_3_4 (px, p0, px, px, u);
+  g4_2_3_4 (px, px, p0, px, u);
+  g4_2_3_4 (px, px, px, p0, u);
+  g4_2_3_4 (p0, px, px, px, 0);
+  g4_2_3_4 (px, p0, px, px, 0);
+  g4_2_3_4 (px, px, p0, px, 0);
+  g4_2_3_4 (px, px, px, p0, 0);
+  g4_2_3_4 (p0, px, px, px, 1);
+  if (u > 12)
+    g4_2_3_4 (p0, px, px, px, u);
+  g4_2_3_4 (px, p0, px, px, 2); /* { dg-warning "argument 2 null where non-null expected because argument 5 is nonzero" } */
+  if (u > 17)
+    g4_2_3_4 (px, p0, px, px, u - 3); /* { dg-warning "argument 2 null where non-null expected because argument 5 is nonzero" } */
+  g4_2_3_4 (px, px, p0, px, 3); /* { dg-warning "argument 3 null where non-null expected because argument 5 is nonzero" } */
+  if (u > 24)
+    g4_2_3_4 (px, px, p0, px, u); /* { dg-warning "argument 3 null where non-null expected because argument 5 is nonzero" } */
+  g4_2_3_4 (px, px, px, p0, 4); /* { dg-warning "argument 4 null where non-null expected because argument 5 is nonzero" } */
+  if (u > 42)
+    g4_2_3_4 (px, px, px, p0, u); /* { dg-warning "argument 4 null where non-null expected because argument 5 is nonzero" } */
+
+  g16_1_3_5_7_11_13 (px, px, px, px, px, px, px, px,
+                    px, px, px, px, px, px, px, px, 17);
+  g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0,
+                    p0, p0, p0, p0, p0, p0, p0, p0, t);
+  g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0,
+                    p0, p0, p0, p0, p0, p0, p0, p0, 0);
+
+  g16_1_3_5_7_11_13 (px, p0, px, p0, px, p0, px, p0, p0, p0, px, p0, p0, p0, p0, p0, 2); /* { dg-warning "argument 13 null where non-null expected because argument 17 is nonzero" } */
+  if (t > 122)
+    g16_1_3_5_7_11_13 (px, p0, px, p0, px, p0, px, p0, p0, p0, px, p0, p0, p0, p0, p0, t); /* { dg-warning "argument 13 null where non-null expected because argument 17 is nonzero" } */
+}
diff --git a/gcc/testsuite/gcc.dg/nonnull-8.c b/gcc/testsuite/gcc.dg/nonnull-8.c
new file mode 100644 (file)
index 0000000..64e36f9
--- /dev/null
@@ -0,0 +1,57 @@
+/* Test for the "nonnull_if_nonzero" function attribute.  */
+/* { dg-do compile } */
+/* { dg-options "-Wnonnull" } */
+
+#include <stddef.h>
+
+extern void func1 (char *, char *, int)
+  __attribute__((nonnull_if_nonzero (1, 3), nonnull_if_nonzero (2, 3)));
+
+extern void func2 (char *, char *, unsigned long)
+  __attribute__((nonnull_if_nonzero (1, 3)));
+
+enum E { E0 = 0, E1 = __INT_MAX__ };
+extern void func3 (char *, int, char *, enum E)
+  __attribute__((nonnull_if_nonzero (1, 4), nonnull_if_nonzero (3, 2)));
+
+extern void func4 (long, char *, char *, long)
+  __attribute__((nonnull_if_nonzero (2, 1)))
+  __attribute__((nonnull_if_nonzero (3, 4)));
+
+void
+foo (int i1, int i2, int i3, char *cp1, char *cp2, char *cp3)
+{
+  func1 (cp1, cp2, i1);
+  func1 (cp1, cp2, 0);
+  func1 (cp1, cp2, 42);
+  func1 (NULL, NULL, 0);
+  func1 (NULL, NULL, i1);
+
+  func1 (NULL, cp2, 42); /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+  func1 (cp1, NULL, 1); /* { dg-warning "argument 2 null where non-null expected because argument 3 is nonzero" } */
+
+  func2 (cp1, NULL, 17);
+  func2 (NULL, cp2, 0);
+  func2 (NULL, cp1, 2); /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+
+  func3 (NULL, i2, cp3, i3);
+  func3 (cp1, i2, NULL, i3);
+  func3 (NULL, i2, cp3, E0);
+  func3 (cp1, 0, NULL, E1);
+  func3 (NULL, i2, cp3, E1); /* { dg-warning "argument 1 null where non-null expected because argument 4 is nonzero" } */
+  func3 (cp3, 5, NULL, i3); /* { dg-warning "argument 3 null where non-null expected because argument 2 is nonzero" } */
+
+  func1 (i2 ? cp1 : NULL, cp2, i3);
+  func1 (i2 ? NULL : cp1, cp2, i3);
+  func1 (i2 ? (i3 ? cp1 : NULL) : cp2, cp3, i1);
+  func1 (i1 ? cp1 : NULL, cp2, 0);
+  func1 (i1 ? NULL : cp1, cp2, 0);
+  func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 0);
+  func1 (i1 ? cp1 : NULL, cp2, 1); /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+  func1 (i1 ? NULL : cp1, cp2, 2); /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+  func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 3); /* { dg-warning "argument 1 null where non-null expected because argument 3 is nonzero" } */
+
+  func4 (0, NULL, NULL, 0);
+  func4 (-1, NULL, cp1, 0); /* { dg-warning "argument 2 null where non-null expected because argument 1 is nonzero" } */
+  func4 (0, cp1, NULL, 77); /* { dg-warning "argument 3 null where non-null expected because argument 4 is nonzero" } */
+}
diff --git a/gcc/testsuite/gcc.dg/nonnull-9.c b/gcc/testsuite/gcc.dg/nonnull-9.c
new file mode 100644 (file)
index 0000000..c0f95ca
--- /dev/null
@@ -0,0 +1,40 @@
+/* Test for the invalid use of the "nonnull_if_nonzero" function attribute.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu17 -pedantic-errors" } */
+
+extern void func1 () __attribute__((nonnull_if_nonzero)); /* { dg-error "wrong number of arguments specified for 'nonnull_if_nonzero' attribute" } */
+/* { dg-message "expected 2, found 0" "" { target *-*-* } .-1 } */
+
+extern void func2 (char *) __attribute__((nonnull_if_nonzero(1))); /* { dg-error "wrong number of arguments specified for 'nonnull_if_nonzero' attribute" } */
+/* { dg-message "expected 2, found 1" "" { target *-*-* } .-1 } */
+
+extern void func3 (char *) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { dg-error "wrong number of arguments specified for 'nonnull_if_nonzero' attribute" } */
+/* { dg-message "expected 2, found 3" "" { target *-*-* } .-1 } */
+
+extern void func4 (char *, int) __attribute__((nonnull_if_nonzero(3, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 1 value '3' exceeds the number of function parameters 2" } */
+
+extern void func5 (char *, int) __attribute__((nonnull_if_nonzero(1, 3))); /* { dg-warning "nonnull_if_nonzero' attribute argument 2 value '3' exceeds the number of function parameters 2" } */
+
+extern void func6 (char *, int) __attribute__((nonnull_if_nonzero (foo, 2))); /* { dg-warning ".nonnull_if_nonzero. attribute argument 1 is invalid" } */
+/* { dg-error ".foo. undeclared" "undeclared argument" { target *-*-* } .-1 } */
+
+extern void func7 (char *, int) __attribute__((nonnull_if_nonzero (1, bar))); /* { dg-warning ".nonnull_if_nonzero. attribute argument 2 is invalid" } */
+/* { dg-error ".bar. undeclared" "undeclared argument" { target *-*-* } .-1 } */
+
+extern void func8 (int, int) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 1 value '1' refers to parameter type 'int'" } */
+
+extern void func9 (char *, float) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to parameter type 'float'" } */
+
+extern void func10 (char *, _Bool) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to parameter type '_Bool'" } */
+
+extern void func11 (char *, char *) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to parameter type 'char \\\*'" } */
+
+void
+foo (void)
+{
+}
+
+void
+bar (void)
+{
+}
index 21c5440c9a25d844f9ccba2f70dca277e65ef446..0c08ed62cda2261bb84bc62d373d7890cead3ee6 100644 (file)
@@ -154,6 +154,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ipa-cp.h"
 #include "ipa-prop.h"
 #include "internal-fn.h"
+#include "gimple-range.h"
 
 /* Possible lattice values.  */
 typedef enum
@@ -4546,6 +4547,7 @@ unsigned int
 pass_post_ipa_warn::execute (function *fun)
 {
   basic_block bb;
+  gimple_ranger *ranger = NULL;
 
   FOR_EACH_BB_FN (bb, fun)
     {
@@ -4557,14 +4559,15 @@ pass_post_ipa_warn::execute (function *fun)
            continue;
 
          tree fntype = gimple_call_fntype (stmt);
-         bitmap nonnullargs = get_nonnull_args (fntype);
-         if (!nonnullargs)
+         if (!fntype)
            continue;
+         bitmap nonnullargs = get_nonnull_args (fntype);
 
          tree fndecl = gimple_call_fndecl (stmt);
          const bool closure = fndecl && DECL_LAMBDA_FUNCTION_P (fndecl);
 
-         for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
+         for (unsigned i = nonnullargs ? 0 : ~0U;
+              i < gimple_call_num_args (stmt); i++)
            {
              tree arg = gimple_call_arg (stmt, i);
              if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
@@ -4612,8 +4615,67 @@ pass_post_ipa_warn::execute (function *fun)
                        fndecl, "nonnull");
            }
          BITMAP_FREE (nonnullargs);
+
+         for (tree attrs = TYPE_ATTRIBUTES (fntype);
+              (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
+              attrs = TREE_CHAIN (attrs))
+           {
+             tree args = TREE_VALUE (attrs);
+             unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
+             unsigned int idx2
+               = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+             if (idx < gimple_call_num_args (stmt)
+                 && idx2 < gimple_call_num_args (stmt))
+               {
+                 tree arg = gimple_call_arg (stmt, idx);
+                 tree arg2 = gimple_call_arg (stmt, idx2);
+                 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE
+                     || !integer_zerop (arg)
+                     || !INTEGRAL_TYPE_P (TREE_TYPE (arg2))
+                     || integer_zerop (arg2)
+                     || ((TREE_CODE (fntype) == METHOD_TYPE || closure)
+                         && (idx == 0 || idx2 == 0)))
+                   continue;
+                 if (!integer_nonzerop (arg2)
+                     && !tree_expr_nonzero_p (arg2))
+                   {
+                     if (TREE_CODE (arg2) != SSA_NAME || optimize < 2)
+                       continue;
+                     if (!ranger)
+                       ranger = enable_ranger (cfun);
+
+                     int_range_max vr;
+                     get_range_query (cfun)->range_of_expr (vr, arg2, stmt);
+                     if (range_includes_zero_p (vr))
+                       continue;
+                   }
+                 unsigned argno = idx + 1;
+                 unsigned argno2 = idx2 + 1;
+                 location_t loc = (EXPR_HAS_LOCATION (arg)
+                                   ? EXPR_LOCATION (arg)
+                                   : gimple_location (stmt));
+                 auto_diagnostic_group d;
+
+                 if (!warning_at (loc, OPT_Wnonnull,
+                                  "argument %u null where non-null "
+                                  "expected because argument %u is "
+                                  "nonzero", argno, argno2))
+                   continue;
+
+                 tree fndecl = gimple_call_fndecl (stmt);
+                 if (fndecl && DECL_IS_UNDECLARED_BUILTIN (fndecl))
+                   inform (loc, "in a call to built-in function %qD",
+                           fndecl);
+                 else if (fndecl)
+                   inform (DECL_SOURCE_LOCATION (fndecl),
+                           "in a call to function %qD declared %qs",
+                           fndecl, "nonnull_if_nonzero");
+               }
+           }
        }
     }
+  if (ranger)
+    disable_ranger (cfun);
   return 0;
 }
 
index 3ef1b6b483b3188e0131230917720167dea251c8..74d13a8d583233c6325f1951be54fe03ba38a5e6 100644 (file)
@@ -14785,7 +14785,7 @@ get_nonnull_args (const_tree fntype)
   /* A function declaration can specify multiple attribute nonnull,
      each with zero or more arguments.  The loop below creates a bitmap
      representing a union of all the arguments.  An empty (but non-null)
-     bitmap means that all arguments have been declaraed nonnull.  */
+     bitmap means that all arguments have been declared nonnull.  */
   for ( ; attrs; attrs = TREE_CHAIN (attrs))
     {
       attrs = lookup_attribute ("nonnull", attrs);
index b858795aaf724bb909a0cb7bd36ef0418a972fc7..1a8faed6cc29a17b4ca211b0397675aafb285ac2 100644 (file)
@@ -2046,8 +2046,9 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi)
   for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++)
     {
       tree arg = gimple_call_arg (stmt, i);
+      tree arg2;
       if (POINTER_TYPE_P (TREE_TYPE (arg))
-         && infer_nonnull_range_by_attribute (stmt, arg))
+         && infer_nonnull_range_by_attribute (stmt, arg, &arg2))
        {
          gimple *g;
          if (!is_gimple_val (arg))
@@ -2057,6 +2058,13 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi)
              gsi_safe_insert_before (gsi, g);
              arg = gimple_assign_lhs (g);
            }
+         if (arg2 && !is_gimple_val (arg2))
+           {
+             g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg2)), arg2);
+             gimple_set_location (g, loc[0]);
+             gsi_safe_insert_before (gsi, g);
+             arg2 = gimple_assign_lhs (g);
+           }
 
          basic_block then_bb, fallthru_bb;
          *gsi = create_cond_insert_point (gsi, true, false, true,
@@ -2068,6 +2076,18 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi)
          gsi_insert_after (gsi, g, GSI_NEW_STMT);
 
          *gsi = gsi_after_labels (then_bb);
+         if (arg2)
+           {
+             *gsi = create_cond_insert_point (gsi, true, false, true,
+                                              &then_bb, &fallthru_bb);
+             g = gimple_build_cond (NE_EXPR, arg2,
+                                    build_zero_cst (TREE_TYPE (arg2)),
+                                    NULL_TREE, NULL_TREE);
+             gimple_set_location (g, loc[0]);
+             gsi_insert_after (gsi, g, GSI_NEW_STMT);
+
+             *gsi = gsi_after_labels (then_bb);
+           }
          if (flag_sanitize_trap & SANITIZE_NONNULL_ATTRIBUTE)
            g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
          else