Hi! C2Y voted in the https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3466.pdf paper, which clarifies some of the conditional nonnull cases. For strncat/__strncat_chk no changes are necessary, we already use __attribute__((nonnull (1), nonnull_if_nonzero (2, 3))) attributes on the builtin and glibc can do the same too, meaning that first argument must be nonnull always and second must be nonnull if the third one is nonzero.
The problem is with the fread/fwrite changes, where the paper adds: If size or nmemb is zero, +ptr may be a null pointer, fread returns zero and the contents of the array and the state of the stream remain unchanged. and ditto for fwrite, so the two argument nonnull_if_nonzero attribute isn't usable to express that, because whether the pointer can be null depends on 2 integral arguments rather than one. The following patch extends the nonnull_if_nonzero attribute, so that instead of requiring 2 arguments it allows 2 or 3, the first one is still the pointer argument index which sometimes must not be null and the other one or two are integral arguments, if there are 2, the invalid case is only if pointer is null and both the integral arguments are nonzero. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2025-06-04 Jakub Jelinek <ja...@redhat.com> PR c/120520 PR c/117023 gcc/ * builtin-attrs.def (DEF_LIST_INT_INT_INT): Define it and use for 1,2,3. (ATTR_NONNULL_IF123_LIST): New DEF_ATTR_TREE_LIST. (ATTR_NONNULL_4_IF123_LIST): Likewise. * builtins.def (BUILT_IN_FWRITE): Use ATTR_NONNULL_4_IF123_LIST instead of ATTR_NONNULL_LIST. (BUILT_IN_FWRITE_UNLOCKED): Likewise. * gimple.h (infer_nonnull_range_by_attribute): Add another optional tree * argument defaulted to NULL. * gimple.cc (infer_nonnull_range_by_attribute): Add OP3 argument, handle 3 argument nonnull_if_nonzero attribute. * builtins.cc (validate_arglist): Handle 3 argument nonnull_if_nonzero attribute. * tree-ssa-ccp.cc (pass_post_ipa_warn::execute): Likewise. * ubsan.cc (instrument_nonnull_arg): Adjust infer_nonnull_range_by_attribute caller, handle 3 argument nonnull_if_nonzero attribute. * gimple-range-infer.cc (gimple_infer_range::gimple_infer_range): Handle 3 argument nonnull_if_nonzero attribute. * doc/extend.texi (nonnull_if_nonzero): Document 3 argument version of the attribute. gcc/c-family/ * c-attribs.cc (c_common_gnu_attributes): Allow 2 or 3 arguments for nonnull_if_nonzero attribute instead of only 2. (handle_nonnull_if_nonzero_attribute): Handle 3 argument nonnull_if_nonzero. * c-common.cc (struct nonnull_arg_ctx): Rename other member to other1, add other2 member. (check_function_nonnull): Clear a if nonnull attribute has an argument. Adjust for nonnull_arg_ctx changes. Handle 3 argument nonnull_if_nonzero attribute. (check_nonnull_arg): Adjust for nonnull_arg_ctx changes, emit different diagnostics for 3 argument nonnull_if_nonzero attributes. (check_function_arguments): Adjust ctx var initialization. gcc/analyzer/ * sm-malloc.cc (malloc_state_machine::on_stmt): Handle 3 argument nonnull_if_nonzero attribute. gcc/testsuite/ * gcc.dg/nonnull-9.c: Tweak for 3 argument nonnull_if_nonzero attribute support, add further tests. * gcc.dg/nonnull-12.c: New test. * gcc.dg/nonnull-13.c: New test. * gcc.dg/nonnull-14.c: New test. * c-c++-common/ubsan/nonnull-8.c: New test. * c-c++-common/ubsan/nonnull-9.c: New test. --- gcc/builtin-attrs.def.jj 2025-04-08 14:08:48.243323740 +0200 +++ gcc/builtin-attrs.def 2025-06-03 10:16:02.178728866 +0200 @@ -90,6 +90,14 @@ DEF_LIST_INT_INT (5,0) DEF_LIST_INT_INT (5,6) #undef DEF_LIST_INT_INT +/* Construct a tree for a list of three integers. */ +#define DEF_LIST_INT_INT_INT(VALUE1, VALUE2, VALUE3) \ + DEF_ATTR_TREE_LIST (ATTR_LIST_##VALUE1##_##VALUE2##_##VALUE3, \ + ATTR_NULL, ATTR_##VALUE1, \ + ATTR_LIST_##VALUE2##_##VALUE3) +DEF_LIST_INT_INT_INT (1,2,3) +#undef DEF_LIST_INT_INT_INT + /* Construct trees for identifiers used in built-in function attributes. The construction contributes to startup costs so only attributes that are used to define built-ins should be defined here. */ @@ -209,6 +217,12 @@ DEF_ATTR_TREE_LIST (ATTR_NONNULL_1, ATTR DEF_ATTR_TREE_LIST (ATTR_NONNULL_2, ATTR_NONNULL, ATTR_LIST_2, ATTR_NULL) /* Functions whose third parameter is a nonnull pointer. */ DEF_ATTR_TREE_LIST (ATTR_NONNULL_3, ATTR_NONNULL, ATTR_LIST_3, ATTR_NULL) +/* Functions whose selected pointer parameter(s) are conditionally + nonnull. */ +DEF_ATTR_TREE_LIST (ATTR_NONNULL_IF123_LIST, ATTR_NONNULL_IF_NONZERO, \ + ATTR_LIST_1_2_3, ATTR_NULL) +DEF_ATTR_TREE_LIST (ATTR_NONNULL_4_IF123_LIST, ATTR_NONNULL, \ + ATTR_LIST_4, ATTR_NONNULL_IF123_LIST) /* Nothrow functions with the sentinel(1) attribute. */ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_SENTINEL_1, ATTR_SENTINEL, ATTR_LIST_1, \ ATTR_NOTHROW_LIST) --- gcc/builtins.def.jj 2025-05-20 08:14:03.564445258 +0200 +++ gcc/builtins.def 2025-06-03 10:16:20.068493869 +0200 @@ -920,8 +920,8 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_FPUTC_U DEF_LIB_BUILTIN (BUILT_IN_FPUTS, "fputs", BT_FN_INT_CONST_STRING_FILEPTR, ATTR_NONNULL_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_FPUTS_UNLOCKED, "fputs_unlocked", BT_FN_INT_CONST_STRING_FILEPTR, ATTR_NONNULL_LIST) DEF_LIB_BUILTIN (BUILT_IN_FSCANF, "fscanf", BT_FN_INT_FILEPTR_CONST_STRING_VAR, ATTR_FORMAT_SCANF_2_3) -DEF_LIB_BUILTIN (BUILT_IN_FWRITE, "fwrite", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_LIST) -DEF_EXT_LIB_BUILTIN (BUILT_IN_FWRITE_UNLOCKED, "fwrite_unlocked", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_LIST) +DEF_LIB_BUILTIN (BUILT_IN_FWRITE, "fwrite", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_4_IF123_LIST) +DEF_EXT_LIB_BUILTIN (BUILT_IN_FWRITE_UNLOCKED, "fwrite_unlocked", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_4_IF123_LIST) DEF_LIB_BUILTIN (BUILT_IN_PRINTF, "printf", BT_FN_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_1_2) DEF_EXT_LIB_BUILTIN (BUILT_IN_PRINTF_UNLOCKED, "printf_unlocked", BT_FN_INT_CONST_STRING_VAR, ATTR_NONNULL_1_FORMAT_PRINTF_1_2) DEF_LIB_BUILTIN (BUILT_IN_PUTCHAR, "putchar", BT_FN_INT_INT, ATTR_NULL) --- gcc/gimple.h.jj 2025-05-20 08:14:06.192409154 +0200 +++ gcc/gimple.h 2025-06-03 10:39:35.545114719 +0200 @@ -1667,7 +1667,8 @@ 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, tree * = NULL); +extern bool infer_nonnull_range_by_attribute (gimple *, tree, tree * = NULL, + 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); --- gcc/gimple.cc.jj 2025-04-28 09:04:29.042893846 +0200 +++ gcc/gimple.cc 2025-06-03 11:48:34.815599375 +0200 @@ -3154,16 +3154,20 @@ infer_nonnull_range_by_dereference (gimp } /* Return true if OP can be inferred to be a non-NULL after STMT - 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). */ + executes by using attributes. If OP2 and OP3 are non-NULL and + nonnull_if_nonzero is the only attribute implying OP being non-NULL + and the corresponding argument(s) aren't non-zero INTEGER_CST, set *OP2 + and *OP3 to the corresponding arguments 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, tree *op2) +infer_nonnull_range_by_attribute (gimple *stmt, tree op, tree *op2, tree *op3) { if (op2) - *op2 = NULL_TREE; + { + *op2 = NULL_TREE; + *op3 = NULL_TREE; + } /* We can only assume that a pointer dereference will yield non-NULL if -fdelete-null-pointer-checks is enabled. */ @@ -3220,26 +3224,33 @@ infer_nonnull_range_by_attribute (gimple unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < gimple_call_num_args (stmt) && idx2 < gimple_call_num_args (stmt) + && idx3 < 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))) + tree arg3 = gimple_call_arg (stmt, idx3); + if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)) + || !INTEGRAL_TYPE_P (TREE_TYPE (arg3))) return false; - if (integer_nonzerop (arg2)) + if (integer_nonzerop (arg2) && integer_nonzerop (arg3)) return true; - if (integer_zerop (arg2)) + if (integer_zerop (arg2) || integer_zerop (arg3)) return false; if (op2) { /* This case is meant for ubsan instrumentation. - The caller can check at runtime if *OP2 is + The caller can check at runtime if *OP2 and *OP3 are non-zero and OP is null. */ *op2 = arg2; + *op3 = arg3; return true; } - return tree_expr_nonzero_p (arg2); + return tree_expr_nonzero_p (arg2) && tree_expr_nonzero_p (arg3); } } } --- gcc/builtins.cc.jj 2025-05-20 08:14:03.563445272 +0200 +++ gcc/builtins.cc 2025-06-03 11:48:34.747600257 +0200 @@ -1158,12 +1158,18 @@ validate_arglist (const_tree callexpr, . unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < (unsigned) call_expr_nargs (callexpr) && idx2 < (unsigned) call_expr_nargs (callexpr) + && idx3 < (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))) + && integer_nonzerop (CALL_EXPR_ARG (callexpr, idx2)) + && INTEGRAL_TYPE_P (TREE_TYPE (CALL_EXPR_ARG (callexpr, idx3))) + && integer_nonzerop (CALL_EXPR_ARG (callexpr, idx3))) return false; } --- gcc/tree-ssa-ccp.cc.jj 2025-04-08 14:09:29.281752432 +0200 +++ gcc/tree-ssa-ccp.cc 2025-06-03 11:48:34.747600257 +0200 @@ -4624,17 +4624,24 @@ pass_post_ipa_warn::execute (function *f unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < gimple_call_num_args (stmt) - && idx2 < gimple_call_num_args (stmt)) + && idx2 < gimple_call_num_args (stmt) + && idx3 < gimple_call_num_args (stmt)) { tree arg = gimple_call_arg (stmt, idx); tree arg2 = gimple_call_arg (stmt, idx2); + tree arg3 = gimple_call_arg (stmt, idx3); if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE || !integer_zerop (arg) || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)) + || !INTEGRAL_TYPE_P (TREE_TYPE (arg3)) || integer_zerop (arg2) + || integer_zerop (arg3) || ((TREE_CODE (fntype) == METHOD_TYPE || closure) - && (idx == 0 || idx2 == 0))) + && (idx == 0 || idx2 == 0 || idx3 == 0))) continue; if (!integer_nonzerop (arg2) && !tree_expr_nonzero_p (arg2)) @@ -4649,17 +4656,40 @@ pass_post_ipa_warn::execute (function *f if (range_includes_zero_p (vr)) continue; } + if (idx2 != idx3 + && !integer_nonzerop (arg3) + && !tree_expr_nonzero_p (arg3)) + { + if (TREE_CODE (arg3) != SSA_NAME || optimize < 2) + continue; + if (!ranger) + ranger = enable_ranger (cfun); + + int_range_max vr; + get_range_query (cfun)->range_of_expr (vr, arg3, stmt); + if (range_includes_zero_p (vr)) + continue; + } unsigned argno = idx + 1; unsigned argno2 = idx2 + 1; + unsigned argno3 = idx3 + 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)) + if (idx2 != idx3) + { + if (!warning_at (loc, OPT_Wnonnull, + "argument %u null where non-null " + "expected because arguments %u and %u " + "are nonzero", argno, argno2, argno3)) + continue; + } + else 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); --- gcc/ubsan.cc.jj 2025-04-08 14:09:38.516623871 +0200 +++ gcc/ubsan.cc 2025-06-03 11:48:34.747600257 +0200 @@ -2046,9 +2046,9 @@ instrument_nonnull_arg (gimple_stmt_iter for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++) { tree arg = gimple_call_arg (stmt, i); - tree arg2; + tree arg2, arg3; if (POINTER_TYPE_P (TREE_TYPE (arg)) - && infer_nonnull_range_by_attribute (stmt, arg, &arg2)) + && infer_nonnull_range_by_attribute (stmt, arg, &arg2, &arg3)) { gimple *g; if (!is_gimple_val (arg)) @@ -2058,6 +2058,8 @@ instrument_nonnull_arg (gimple_stmt_iter gsi_safe_insert_before (gsi, g); arg = gimple_assign_lhs (g); } + if (arg2 == arg3) + arg3 = NULL_TREE; if (arg2 && !is_gimple_val (arg2)) { g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg2)), arg2); @@ -2065,6 +2067,13 @@ instrument_nonnull_arg (gimple_stmt_iter gsi_safe_insert_before (gsi, g); arg2 = gimple_assign_lhs (g); } + if (arg3 && !is_gimple_val (arg3)) + { + g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg3)), arg3); + gimple_set_location (g, loc[0]); + gsi_safe_insert_before (gsi, g); + arg3 = gimple_assign_lhs (g); + } basic_block then_bb, fallthru_bb; *gsi = create_cond_insert_point (gsi, true, false, true, @@ -2085,6 +2094,18 @@ instrument_nonnull_arg (gimple_stmt_iter 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 (arg3) + { + *gsi = create_cond_insert_point (gsi, true, false, true, + &then_bb, &fallthru_bb); + g = gimple_build_cond (NE_EXPR, arg3, + build_zero_cst (TREE_TYPE (arg3)), + NULL_TREE, NULL_TREE); + gimple_set_location (g, loc[0]); + gsi_insert_after (gsi, g, GSI_NEW_STMT); *gsi = gsi_after_labels (then_bb); } --- gcc/gimple-range-infer.cc.jj 2025-05-02 19:40:40.694738579 +0200 +++ gcc/gimple-range-infer.cc 2025-06-03 11:48:34.774599906 +0200 @@ -197,23 +197,41 @@ gimple_infer_range::gimple_infer_range ( unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < gimple_call_num_args (s) - && idx2 < gimple_call_num_args (s)) + && idx2 < gimple_call_num_args (s) + && idx3 < gimple_call_num_args (s)) { tree arg = gimple_call_arg (s, idx); tree arg2 = gimple_call_arg (s, idx2); + tree arg3 = gimple_call_arg (s, idx3); if (!POINTER_TYPE_P (TREE_TYPE (arg)) || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)) - || integer_zerop (arg2)) + || !INTEGRAL_TYPE_P (TREE_TYPE (arg3)) + || integer_zerop (arg2) + || integer_zerop (arg3)) continue; - if (integer_nonzerop (arg2)) + if (integer_nonzerop (arg2) && integer_nonzerop (arg3)) add_nonzero (arg); else { value_range r (TREE_TYPE (arg2)); if (q->range_of_expr (r, arg2, s) && !r.contains_p (build_zero_cst (TREE_TYPE (arg2)))) - add_nonzero (arg); + { + if (idx2 == idx3) + add_nonzero (arg); + else + { + value_range r2 (TREE_TYPE (arg3)); + tree zero3 = build_zero_cst (TREE_TYPE (arg3)); + if (q->range_of_expr (r2, arg3, s) + && !r2.contains_p (zero3)) + add_nonzero (arg); + } + } } } } --- gcc/doc/extend.texi.jj 2025-06-02 11:00:15.135408947 +0200 +++ gcc/doc/extend.texi 2025-06-03 14:46:45.225122631 +0200 @@ -2887,13 +2887,18 @@ my_memcpy (void *dest, const void *src, @cindex @code{nonnull_if_nonzero} function attribute @item nonnull_if_nonzero @itemx nonnull_if_nonzero (@var{arg-index}, @var{arg2-index}) +@itemx nonnull_if_nonzero (@var{arg-index}, @var{arg2-index}, @var{arg3-index}) The @code{nonnull_if_nonzero} attribute is a conditional version of the -@code{nonnull} attribute. It has two arguments, the first argument +@code{nonnull} attribute. It has two or three 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. +argument must not be null. If three arguments are provided, the third +argument shall be argument index of another integral argument (other than +boolean) and the pointer argument can be null if either of the integral +arguments are zero and if both are non-zero, the pointer argument must not +be null. @smallexample extern void * @@ -2903,12 +2908,21 @@ extern void * my_memcpy2 (void *dest, const void *src, size_t len) __attribute__((nonnull_if_nonzero (1, 3), nonnull_if_nonzero (2, 3))); +extern size_t +my_fread (void *buf, size_t size, size_t count, FILE *stream) + __attribute__((nonnull (4), + nonnull_if_nonzero (1, 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 +call @code{my_memcpy2 (NULL, NULL, 4);} or to call +@code{my_fread(@var{buf}, 0, 0, NULL);} or to call +@code{my_fread(NULL, 1, 1, @var{stream});} but it is valid +to call @code{my_memcpy2 (NULL, NULL, 0);} or +@code{my_fread(NULL, 0, 0, @var{stream});} or +@code{my_fread(NULL, 0, 1, @var{stream});} or +@code{my_fread(NULL, 1, 0, @var{stream});}. This attribute should be used on declarations which have e.g.@: an exception for zero sizes, in which case null may be passed. --- gcc/c-family/c-attribs.cc.jj 2025-04-08 14:08:48.329322543 +0200 +++ gcc/c-family/c-attribs.cc 2025-06-03 13:17:06.990640950 +0200 @@ -489,7 +489,7 @@ const struct attribute_spec c_common_gnu 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, + { "nonnull_if_nonzero", 2, 3, false, true, true, false, handle_nonnull_if_nonzero_attribute, NULL }, { "nonstring", 0, 0, true, false, false, false, handle_nonstring_attribute, NULL }, @@ -5034,12 +5034,21 @@ handle_nonnull_if_nonzero_attribute (tre tree type = *node; tree pos = TREE_VALUE (args); tree pos2 = TREE_VALUE (TREE_CHAIN (args)); + tree chain2 = TREE_CHAIN (TREE_CHAIN (args)); + tree pos3 = NULL_TREE; + if (chain2) + pos3 = TREE_VALUE (chain2); tree val = positional_argument (type, name, pos, POINTER_TYPE, 1); tree val2 = positional_argument (type, name, pos2, INTEGER_TYPE, 2); - if (val && val2) + tree val3 = NULL_TREE; + if (chain2) + val3 = positional_argument (type, name, pos3, INTEGER_TYPE, 3); + if (val && val2 && (!chain2 || val3)) { TREE_VALUE (args) = val; TREE_VALUE (TREE_CHAIN (args)) = val2; + if (chain2) + TREE_VALUE (chain2) = val3; } else *no_add_attrs = true; --- gcc/c-family/c-common.cc.jj 2025-06-02 11:00:14.181421315 +0200 +++ gcc/c-family/c-common.cc 2025-06-03 12:54:34.900016089 +0200 @@ -5749,8 +5749,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; + /* For nonnull_if_nonzero, index of the other arguments. */ + unsigned HOST_WIDE_INT other1, other2; /* True if a warning has been issued. */ bool warned_p; }; @@ -5818,6 +5818,7 @@ check_function_nonnull (nonnull_arg_ctx check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[i], i + 1, OPT_Wnonnull); + a = NULL_TREE; } } if (a == NULL_TREE) @@ -5829,17 +5830,25 @@ check_function_nonnull (nonnull_arg_ctx unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < (unsigned) nargs - firstarg && idx2 < (unsigned) nargs - firstarg + && idx3 < (unsigned) nargs - firstarg && INTEGRAL_TYPE_P (TREE_TYPE (argarray[firstarg + idx2])) - && integer_nonzerop (argarray[firstarg + idx2])) + && integer_nonzerop (argarray[firstarg + idx2]) + && INTEGRAL_TYPE_P (TREE_TYPE (argarray[firstarg + idx3])) + && integer_nonzerop (argarray[firstarg + idx3])) { - ctx.other = firstarg + idx2 + 1; + ctx.other1 = firstarg + idx2 + 1; + ctx.other2 = firstarg + idx3 + 1; check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[firstarg + idx], firstarg + idx + 1, OPT_Wnonnull); - ctx.other = 0; + ctx.other1 = 0; + ctx.other2 = 0; } } return ctx.warned_p; @@ -6023,14 +6032,25 @@ check_nonnull_arg (void *ctx, tree param } else { - if (pctx->other) + if (pctx->other1 && pctx->other2 != pctx->other1) + warned = warning_at (loc, OPT_Wnonnull, + "argument %u null where non-null expected " + "because arguments %u and %u are nonzero", + (unsigned) param_num, + TREE_CODE (pctx->fntype) == METHOD_TYPE + ? (unsigned) pctx->other1 - 1 + : (unsigned) pctx->other1, + TREE_CODE (pctx->fntype) == METHOD_TYPE + ? (unsigned) pctx->other2 - 1 + : (unsigned) pctx->other2); + else if (pctx->other1) 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); + ? (unsigned) pctx->other1 - 1 + : (unsigned) pctx->other1); else warned = warning_at (loc, OPT_Wnonnull, "argument %u null where non-null expected", @@ -6039,7 +6059,7 @@ check_nonnull_arg (void *ctx, tree param inform (DECL_SOURCE_LOCATION (pctx->fndecl), "in a call to function %qD declared %qs", pctx->fndecl, - pctx->other ? "nonnull_if_nonzero" : "nonnull"); + pctx->other1 ? "nonnull_if_nonzero" : "nonnull"); } if (warned) @@ -6295,7 +6315,7 @@ check_function_arguments (location_t loc to do this if format checking is enabled. */ if (warn_nonnull) { - nonnull_arg_ctx ctx = { loc, fndecl, fntype, 0, false }; + nonnull_arg_ctx ctx = { loc, fndecl, fntype, 0, 0, false }; warned_p = check_function_nonnull (ctx, nargs, argarray); } --- gcc/analyzer/sm-malloc.cc.jj 2025-05-01 08:25:43.965938983 +0200 +++ gcc/analyzer/sm-malloc.cc 2025-06-03 13:49:57.469237200 +0200 @@ -2172,19 +2172,27 @@ malloc_state_machine::on_stmt (sm_contex unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < gimple_call_num_args (stmt) - && idx2 < gimple_call_num_args (stmt)) + && idx2 < gimple_call_num_args (stmt) + && idx3 < gimple_call_num_args (stmt)) { tree arg = gimple_call_arg (stmt, idx); tree arg2 = gimple_call_arg (stmt, idx2); + tree arg3 = gimple_call_arg (stmt, idx3); if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)) - || integer_zerop (arg2)) + || !INTEGRAL_TYPE_P (TREE_TYPE (arg3)) + || integer_zerop (arg2) + || integer_zerop (arg3)) continue; - if (integer_nonzerop (arg2)) + if (integer_nonzerop (arg2) && integer_nonzerop (arg3)) ; else - /* FIXME: Use ranger here to query arg2 range? */ + /* FIXME: Use ranger here to query arg2 and arg3 + ranges? */ continue; handle_nonnull (sm_ctxt, node, stmt, fndecl, arg, idx); } --- gcc/testsuite/gcc.dg/nonnull-9.c.jj 2025-04-08 14:09:02.000000000 +0200 +++ gcc/testsuite/gcc.dg/nonnull-9.c 2025-06-03 13:12:51.317981665 +0200 @@ -3,31 +3,42 @@ /* { 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 } */ +/* { dg-message "expected between 2 and 3, 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 } */ +/* { dg-message "expected between 2 and 3, 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 func3 (char *) __attribute__((nonnull_if_nonzero(1, 2, 3, 4))); /* { dg-error "wrong number of arguments specified for 'nonnull_if_nonzero' attribute" } */ +/* { dg-message "expected between 2 and 3, found 4" "" { 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" } */ +extern void func6 (char *, int) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { dg-warning "nonnull_if_nonzero' attribute argument 3 value '3' exceeds the number of function parameters 2" } */ + +extern void func7 (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" } */ +extern void func8 (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 *, int) __attribute__((nonnull_if_nonzero (1, 2, baz))); /* { dg-warning ".nonnull_if_nonzero. attribute argument 3 is invalid" } */ +/* { dg-error ".baz. undeclared" "undeclared argument" { target *-*-* } .-1 } */ + +extern void func10 (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 func11 (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 func12 (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 func13 (char *, char *) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to parameter type 'char \\\*'" } */ -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 func14 (char *, int, float) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 3 value '3' 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 func15 (char *, long, _Bool) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 3 value '3' 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 \\\*'" } */ +extern void func17 (char *, int, char *) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 3 value '3' refers to parameter type 'char \\\*'" } */ void foo (void) @@ -38,3 +49,8 @@ void bar (void) { } + +void +baz (void) +{ +} --- gcc/testsuite/gcc.dg/nonnull-12.c.jj 2025-06-03 13:26:28.546313344 +0200 +++ gcc/testsuite/gcc.dg/nonnull-12.c 2025-06-03 13:45:20.207486991 +0200 @@ -0,0 +1,73 @@ +/* Test for the "nonnull_if_nonzero" function attribute. */ +/* { dg-do compile } */ +/* { dg-options "-Wnonnull" } */ + +#include <stddef.h> + +extern void func1 (char *, char *, int, int) + __attribute__((nonnull_if_nonzero (1, 3, 4), nonnull_if_nonzero (2, 3, 4))); + +extern void func2 (char *, char *, unsigned long, unsigned long) + __attribute__((nonnull_if_nonzero (1, 3, 4))); + +enum E { E0 = 0, E1 = 1, E2 = __INT_MAX__ }; +extern void func3 (char *, int, char *, enum E, int, enum E) + __attribute__((nonnull_if_nonzero (1, 4, 6), nonnull_if_nonzero (3, 2, 5))); + +extern void func4 (long, char *, char *, long, long, long) + __attribute__((nonnull_if_nonzero (2, 1, 5))) + __attribute__((nonnull_if_nonzero (3, 4, 6))); + +void +foo (int i1, int i2, int i3, char *cp1, char *cp2, char *cp3) +{ + func1 (cp1, cp2, i1, i2); + func1 (cp1, cp2, 0, 0); + func1 (cp1, cp2, 42, 42); + func1 (NULL, NULL, 0, 0); + func1 (NULL, NULL, 0, 42); + func1 (NULL, NULL, 42, 0); + func1 (NULL, NULL, i1, i2); + + func1 (NULL, cp2, 42, 42); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + func1 (cp1, NULL, 1, 1); /* { dg-warning "argument 2 null where non-null expected because arguments 3 and 4 are nonzero" } */ + + func2 (cp1, NULL, 17, 17); + func2 (NULL, cp2, 0, 0); + func2 (NULL, cp2, 0, 17); + func2 (NULL, cp2, 17, 0); + func2 (NULL, cp1, 2, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + + func3 (NULL, i2, cp3, i3, i3, i2); + func3 (cp1, i2, NULL, i3, i3, i2); + func3 (NULL, i2, cp3, E0, i3, E0); + func3 (NULL, i2, cp3, E0, i3, E1); + func3 (NULL, i2, cp3, E1, i3, E0); + func3 (cp1, 0, NULL, E2, 0, E2); + func3 (cp1, 0, NULL, E2, 4, E2); + func3 (cp1, 4, NULL, E2, 0, E2); + func3 (NULL, i2, cp3, E2, i3, E2); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + func3 (cp3, 5, NULL, i3, 1, i2); /* { dg-warning "argument 3 null where non-null expected because arguments 2 and 5 are nonzero" } */ + + func1 (i2 ? cp1 : NULL, cp2, i3, i3); + func1 (i2 ? NULL : cp1, cp2, i3, i3); + func1 (i2 ? (i3 ? cp1 : NULL) : cp2, cp3, i1, i1); + func1 (i1 ? cp1 : NULL, cp2, 0, 0); + func1 (i1 ? cp1 : NULL, cp2, 0, 4); + func1 (i1 ? cp1 : NULL, cp2, 4, 0); + func1 (i1 ? NULL : cp1, cp2, 0, 0); + func1 (i1 ? NULL : cp1, cp2, 0, 2); + func1 (i1 ? NULL : cp1, cp2, 3, 0); + func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 0, 0); + func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 0, 1); + func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 2, 0); + func1 (i1 ? cp1 : NULL, cp2, 1, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + func1 (i1 ? NULL : cp1, cp2, 2, 3); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 3, 4); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + + func4 (0, NULL, NULL, 0, 0, 0); + func4 (0, NULL, NULL, 0, 1, 2); + func4 (3, NULL, NULL, 4, 0, 0); + func4 (-1, NULL, cp1, 0, 42, 0); /* { dg-warning "argument 2 null where non-null expected because arguments 1 and 5 are nonzero" } */ + func4 (0, cp1, NULL, 77, 0, 12); /* { dg-warning "argument 3 null where non-null expected because arguments 4 and 6 are nonzero" } */ +} --- gcc/testsuite/gcc.dg/nonnull-13.c.jj 2025-06-03 13:47:31.404941652 +0200 +++ gcc/testsuite/gcc.dg/nonnull-13.c 2025-06-03 14:45:14.548303521 +0200 @@ -0,0 +1,210 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -Wnonnull" } */ + +#define N(x, y, z) __attribute__ ((nonnull_if_nonzero (x, y, z))) + +void N (1, 2, 3) f1_1 (void *, int, int); + +void N (1, 3, 4) f2_1 (void *, void *, int, int); +void N (1, 3, 4) N (2, 3, 4) f2_1_2 (void *, void *, int, int); + +void N (1, 4, 6) N (3, 5, 7) f3_1_3 (void *, void *, void *, int, int, int, int); + +void N (1, 5, 6) N (2, 5, 6) N (4, 5, 6) g4_1_2_4 (void *, void *, void *, void *, long, long); +void N (1, 5, 6) N (3, 5, 6) N (4, 5, 6) g4_1_3_4 (void *, void *, void *, void *, long, long); +void N (2, 5, 6) N (3, 5, 6) N (4, 5, 6) g4_2_3_4 (void *, void *, void *, void *, long, long); + +void N (1, 17, 18) N (3, 17, 18) N (5, 17, 18) N (7, 17, 18) N (11, 17, 18) N (13, 17, 18) +g16_1_3_5_7_11_13 (void *, void *, void *, void *, + void *, void *, void *, void *, + void *, void *, void *, void *, + void *, void *, void *, void *, int, int); + +static void *null (void) { return 0; } + +void +test (int t, long u, int v, long w) +{ + void *p0 = null (); + void *px = &px; + + f1_1 (p0, 0, 0); + f1_1 (p0, 0, 4); + f1_1 (p0, 3, 0); + f1_1 (p0, t, v); + f1_1 (p0, t, 0); + f1_1 (p0, 0, v); + f1_1 (p0, 42, 1); /* { dg-warning "argument 1 null where non-null expected because arguments 2 and 3 are nonzero" } */ + if (t && v) + f1_1 (p0, t, v); /* { dg-warning "argument 1 null where non-null expected because arguments 2 and 3 are nonzero" } */ + f1_1 (px, 17, 17); + + f2_1 (p0, px, 0, 0); + f2_1 (p0, px, 0, 3); + f2_1 (p0, px, 7, 0); + f2_1 (p0, px, t, v); + f2_1 (p0, px, t, 0); + f2_1 (p0, px, 0, v); + f2_1 (p0, px, 5, 3); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + if (t > 4 && v > 8) + f2_1 (p0, px, t, v); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + f2_1 (px, p0, 17, 17); + f2_1 (p0, p0, 0, 0); + f2_1 (p0, p0, 0, 4); + f2_1 (p0, p0, 2, 0); + if (t < 0 && v < -3) + f2_1 (p0, p0, t, v); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + + f2_1_2 (p0, p0, 0, 0); + f2_1_2 (p0, p0, 0, 1); + f2_1_2 (p0, p0, 2, 0); + f2_1_2 (p0, p0, t, v); + f2_1_2 (p0, p0, t, 0); + f2_1_2 (p0, p0, 0, v); + f2_1_2 (p0, px, 1, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + if (t > 8 && v >= 16) + f2_1_2 (p0, px, t, v); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + f2_1_2 (px, p0, -3, -4); /* { dg-warning "argument 2 null where non-null expected because arguments 3 and 4 are nonzero" } */ + if (t < -2 && v >= 32) + f2_1_2 (px, p0, t, v); /* { dg-warning "argument 2 null where non-null expected because arguments 3 and 4 are nonzero" } */ + f2_1_2 (p0, p0, 8, 165); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + /* { dg-warning "argument 2 null where non-null expected because arguments 3 and 4 are nonzero" "argument 2" { target *-*-* } .-1 } */ + if (t > 7 && v < -2) + f2_1_2 (p0, p0, t, v); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + /* { dg-warning "argument 2 null where non-null expected because arguments 3 and 4 are nonzero" "argument 2" { target *-*-* } .-1 } */ + + f3_1_3 (p0, p0, p0, 0, 0, 0, 0); + f3_1_3 (p0, p0, p0, 0, 5, 4, 0); + f3_1_3 (p0, p0, p0, 3, 0, 0, 6); + f3_1_3 (p0, p0, px, 0, 6, 0, 6); + f3_1_3 (p0, p0, px, 0, 6, 4, 6); + f3_1_3 (p0, p0, px, 3, 6, 0, 6); + f3_1_3 (px, p0, p0, 2, 0, 2, 0); + f3_1_3 (px, p0, p0, 2, 0, 2, 4); + f3_1_3 (px, p0, p0, 2, 6, 2, 0); + f3_1_3 (p0, p0, p0, t, t, v, v); + f3_1_3 (p0, p0, px, t, 6, v, 7); + f3_1_3 (px, p0, p0, 2, t, 3, v); + f3_1_3 (p0, px, px, 8, 2, 3, 5); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + if (t > 9 && v < -19) + f3_1_3 (p0, px, px, t, 3, v, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + f3_1_3 (px, p0, px, 9, 10, 1, 2); + if (t > 11 && v > 3) + f3_1_3 (px, p0, px, t, t, v, v); + f3_1_3 (px, px, p0, 10, 11, 2, 3); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" } */ + if (t < -5 && v > 2) + f3_1_3 (px, px, p0, 0, t, 2, v); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" } */ + f3_1_3 (p0, p0, px, 11, 12, 1, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + if (t > 26 && v > 88) + f3_1_3 (p0, p0, px, t, 3, v, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + f3_1_3 (px, p0, p0, 12, 13, 1, 2); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" } */ + if (t > 31 && v < -1) + f3_1_3 (px, p0, p0, 12, t, 2, v); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" } */ + f3_1_3 (p0, p0, p0, 13, 14, 1, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" "argument 3" { target *-*-* } .-1 } */ + if (t > 28 && v > 42) + f3_1_3 (p0, p0, p0, t, t + 1, v, v + 1); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" "argument 3" { target *-*-* } .-1 } */ + + g4_1_2_4 (p0, px, px, px, u, w); + g4_1_2_4 (px, p0, px, px, u, w); + g4_1_2_4 (px, px, p0, px, u, w); + g4_1_2_4 (px, px, px, p0, u, w); + g4_1_2_4 (p0, px, px, px, 0, 0); + g4_1_2_4 (p0, px, px, px, 0, 2); + g4_1_2_4 (p0, px, px, px, 1, 0); + g4_1_2_4 (px, p0, px, px, 0, 0); + g4_1_2_4 (px, p0, px, px, 0, 3); + g4_1_2_4 (px, p0, px, px, 4, 0); + g4_1_2_4 (px, px, p0, px, 0, 0); + g4_1_2_4 (px, px, p0, px, 0, 5); + g4_1_2_4 (px, px, p0, px, 6, 0); + g4_1_2_4 (px, px, px, p0, 0, 0); + g4_1_2_4 (px, px, px, p0, 0, 7); + g4_1_2_4 (px, px, px, p0, 8, 0); + g4_1_2_4 (p0, px, px, px, 15, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u && w) + g4_1_2_4 (p0, px, px, px, u, w); /* { dg-warning "argument 1 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_1_2_4 (px, p0, px, px, 16, 2); /* { dg-warning "argument 2 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 2 && w > 3) + g4_1_2_4 (px, p0, px, px, u, w); /* { dg-warning "argument 2 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_1_2_4 (px, px, p0, px, 17, 8); + if (u > 3 && w < -2) + g4_1_2_4 (px, px, p0, px, u, w); + g4_1_2_4 (px, px, px, p0, 18, 3); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if ((u < -2 || u > 10) && (w < -4 || w > 42)) + g4_1_2_4 (px, px, px, p0, u, w); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + + g4_1_3_4 (p0, px, px, px, u, u); + g4_1_3_4 (px, p0, px, px, u, u); + g4_1_3_4 (px, px, p0, px, u, u); + g4_1_3_4 (px, px, px, p0, u, u); + g4_1_3_4 (p0, px, px, px, 0, 0); + g4_1_3_4 (p0, px, px, px, 0, 1); + g4_1_3_4 (p0, px, px, px, 2, 0); + g4_1_3_4 (px, p0, px, px, 0, 0); + g4_1_3_4 (px, p0, px, px, 0, 3); + g4_1_3_4 (px, p0, px, px, 4, 0); + g4_1_3_4 (px, px, p0, px, 0, 0); + g4_1_3_4 (px, px, p0, px, 0, 5); + g4_1_3_4 (px, px, p0, px, 6, 0); + g4_1_3_4 (px, px, px, p0, 0, 0); + g4_1_3_4 (px, px, px, p0, 0, 7); + g4_1_3_4 (px, px, px, p0, 8, 0); + g4_1_3_4 (p0, px, px, px, 20, 32); /* { dg-warning "argument 1 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 4 && w > 2) + g4_1_3_4 (p0, px, px, px, u, w); /* { dg-warning "argument 1 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_1_3_4 (px, p0, px, px, 21, 4); + if ((u > 6 || u < -24) && (w > 8 || w < -5)) + g4_1_3_4 (px, p0, px, px, u, w); + g4_1_3_4 (px, px, p0, px, 22, 4); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 9 && w > 13) + g4_1_3_4 (px, px, p0, px, u - 3, w - 8); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_1_3_4 (px, px, px, p0, 23, 8); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 10 && w > 12) + g4_1_3_4 (px, px, px, p0, u, w); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + + g4_2_3_4 (p0, px, px, px, u, u); + g4_2_3_4 (px, p0, px, px, u, u); + g4_2_3_4 (px, px, p0, px, u, u); + g4_2_3_4 (px, px, px, p0, u, u); + g4_2_3_4 (p0, px, px, px, 0, 0); + g4_2_3_4 (p0, px, px, px, 0, 1); + g4_2_3_4 (p0, px, px, px, 2, 0); + g4_2_3_4 (px, p0, px, px, 0, 0); + g4_2_3_4 (px, p0, px, px, 0, 3); + g4_2_3_4 (px, p0, px, px, 4, 0); + g4_2_3_4 (px, px, p0, px, 0, 0); + g4_2_3_4 (px, px, p0, px, 0, 5); + g4_2_3_4 (px, px, p0, px, 6, 0); + g4_2_3_4 (px, px, px, p0, 0, 0); + g4_2_3_4 (px, px, px, p0, 0, 7); + g4_2_3_4 (px, px, px, p0, 8, 0); + g4_2_3_4 (p0, px, px, px, 1, 2); + if (u > 12 && w > 16) + g4_2_3_4 (p0, px, px, px, u, w); + g4_2_3_4 (px, p0, px, px, 2, 3); /* { dg-warning "argument 2 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 17 && w > 19) + g4_2_3_4 (px, p0, px, px, u - 3, w - 2); /* { dg-warning "argument 2 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_2_3_4 (px, px, p0, px, 3, 8); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 24 && w > 22) + g4_2_3_4 (px, px, p0, px, u, w); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_2_3_4 (px, px, px, p0, 4, 2); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 42 && w > 48) + g4_2_3_4 (px, px, px, p0, u, w); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are 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, 18); + g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0, + p0, p0, p0, p0, p0, p0, p0, p0, t, v); + g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0, + p0, p0, p0, p0, p0, p0, p0, p0, 0, 0); + g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0, + p0, p0, p0, p0, p0, p0, p0, p0, 0, 4); + g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0, + p0, p0, p0, p0, p0, p0, p0, p0, 3, 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, 1); /* { dg-warning "argument 13 null where non-null expected because arguments 17 and 18 are nonzero" } */ + if (t > 122 && v > 18) + g16_1_3_5_7_11_13 (px, p0, px, p0, px, p0, px, p0, p0, p0, px, p0, p0, p0, p0, p0, t, v); /* { dg-warning "argument 13 null where non-null expected because arguments 17 and 18 are nonzero" } */ +} --- gcc/testsuite/gcc.dg/nonnull-14.c.jj 2025-06-03 14:32:16.368426685 +0200 +++ gcc/testsuite/gcc.dg/nonnull-14.c 2025-06-03 14:42:31.165427899 +0200 @@ -0,0 +1,23 @@ +/* Test for the "nonnull" function attribute on builtins. Use the + "__builtin_" style below so we don't need prototypes. */ +/* { dg-do compile } */ +/* { dg-options "-Wnonnull" } */ + +#include <stddef.h> + +void +foo (void *p, char *s) +{ + __builtin_fwrite (s, 0, 0, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite (s, 0, 2, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite (s, 1, 0, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite (NULL, 16, 0, p); + __builtin_fwrite (NULL, 0, 12, p); + __builtin_fwrite (NULL, 2, 3, p); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite_unlocked (s, 0, 0, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite_unlocked (s, 0, 2, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite_unlocked (s, 1, 0, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite_unlocked (NULL, 16, 0, p); + __builtin_fwrite_unlocked (NULL, 0, 12, p); + __builtin_fwrite_unlocked (NULL, 2, 3, p); /* { dg-warning "null" "null pointer check" } */ +} --- gcc/testsuite/c-c++-common/ubsan/nonnull-8.c.jj 2025-06-03 15:28:12.571656897 +0200 +++ gcc/testsuite/c-c++-common/ubsan/nonnull-8.c 2025-06-03 15:36:08.285443385 +0200 @@ -0,0 +1,32 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */ + +__attribute__((noipa, nonnull_if_nonzero (1, 4, 7))) +__attribute__((nonnull (3), nonnull_if_nonzero (5, 2, 6))) void +foo (void *a, unsigned long b, void *c, int d, void *e, unsigned long f, int g) +{ + (void) a; + (void) b; + (void) c; + (void) d; + (void) e; + (void) f; + (void) g; +} + +__attribute__((noipa)) +void +bar (void *a, unsigned long b, void *c, int d, void *e, unsigned long f, int g) +{ + foo (a, b, c, d, e, f, g); +} + +int +main () +{ + char x; + bar (&x, 42, &x, 1, &x, 2, 3); + bar (0, 0, &x, 0, 0, 0, 0); + bar (0, 5, &x, 4, 0, 0, 0); + bar (0, 0, &x, 0, 0, 6, 7); +} --- gcc/testsuite/c-c++-common/ubsan/nonnull-9.c.jj 2025-06-03 15:28:15.330620875 +0200 +++ gcc/testsuite/c-c++-common/ubsan/nonnull-9.c 2025-06-03 15:34:45.031530823 +0200 @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=nonnull-attribute" } */ + +__attribute__((noipa, nonnull_if_nonzero (1, 4, 7))) +__attribute__((nonnull (3), nonnull_if_nonzero (5, 2, 6))) void +foo (void *a, unsigned long b, void *c, int d, void *e, unsigned long f, int g) +{ + (void) a; + (void) b; + (void) c; + (void) d; + (void) e; + (void) f; + (void) g; +} + +__attribute__((noipa)) +void +bar (void *a, unsigned long b, void *c, int d, void *e, unsigned long f, int g) +{ + foo (a, b, c, d, e, f, g); +} + +int +main () +{ + char x; + bar (&x, 42, 0, 1, &x, 17, 18); + bar (0, 25, &x, 7, &x, 0, 8); + bar (&x, -82, &x, 68, 0, 9, 0); + foo (&x, 42, 0, 1, &x, 17, 18); + foo (0, 25, &x, 7, &x, 0, 8); + foo (&x, -82, &x, 68, 0, 9, 0); +} + +/* { dg-output "\.c:21:\[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:21:\[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:21:\[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:31:\[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:32:\[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:33:\[0-9]*:\[^\n\r]*null pointer passed as argument 5, which is declared to never be null" } */ Jakub