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;
}
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 *);
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,
/* 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))
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
/* 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;
};
}
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. */
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;
}
}
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)
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);
}
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
__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}.
}
/* 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
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. */
}
}
}
+
+ 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
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);
--- /dev/null
+/* { 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);
+}
--- /dev/null
+/* { 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" } */
--- /dev/null
+/* { 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" } */
+}
--- /dev/null
+/* 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" } */
+}
--- /dev/null
+/* 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)
+{
+}
#include "ipa-cp.h"
#include "ipa-prop.h"
#include "internal-fn.h"
+#include "gimple-range.h"
/* Possible lattice values. */
typedef enum
pass_post_ipa_warn::execute (function *fun)
{
basic_block bb;
+ gimple_ranger *ranger = NULL;
FOR_EACH_BB_FN (bb, 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)
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;
}
/* 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);
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))
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,
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