[TCWG CI] Regression caused by gcc: Enhance -Waddress to detect more suspicious expressions [PR102103].: commit 4dc7ce6fb3917958d1a6036d8acf2953b9c1b868 Author: Martin Sebor msebor@redhat.com
Enhance -Waddress to detect more suspicious expressions [PR102103].
Results regressed to # reset_artifacts: -10 # build_abe binutils: -9 # build_abe stage1: -5 # build_abe qemu: -2 # linux_n_obj: 21397 # First few build errors in logs: # 00:02:12 sound/core/oss/mixer_oss.c:1035:21: error: ‘slot’ is used uninitialized [-Werror=uninitialized] # 00:02:15 make[3]: *** [scripts/Makefile.build:288: sound/core/oss/mixer_oss.o] Error 1 # 00:02:15 sound/core/oss/pcm_oss.c:108:29: error: ‘t’ is used uninitialized [-Werror=uninitialized] # 00:02:15 sound/core/oss/pcm_oss.c:2475:34: error: ‘setup’ is used uninitialized [-Werror=uninitialized] # 00:02:15 sound/core/oss/pcm_oss.c:2985:51: error: ‘template’ is used uninitialized [-Werror=uninitialized] # 00:02:15 sound/core/seq/oss/seq_oss_init.c:350:35: error: ‘qinfo’ is used uninitialized [-Werror=uninitialized] # 00:02:15 sound/core/seq/oss/seq_oss_init.c:370:35: error: ‘qinfo’ is used uninitialized [-Werror=uninitialized] # 00:02:16 make[4]: *** [scripts/Makefile.build:288: sound/core/seq/oss/seq_oss_init.o] Error 1 # 00:02:22 make[3]: *** [scripts/Makefile.build:288: sound/core/oss/pcm_oss.o] Error 1 # 00:02:28 make[3]: *** [scripts/Makefile.build:571: sound/core/seq/oss] Error 2
from # reset_artifacts: -10 # build_abe binutils: -9 # build_abe stage1: -5 # build_abe qemu: -2 # linux_n_obj: 21403
THIS IS THE END OF INTERESTING STUFF. BELOW ARE LINKS TO BUILDS, REPRODUCTION INSTRUCTIONS, AND THE RAW COMMIT.
This commit has regressed these CI configurations: - tcwg_kernel/gnu-master-aarch64-next-allmodconfig
First_bad build: https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-next-all... Last_good build: https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-next-all... Baseline build: https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-next-all... Even more details: https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-next-all...
Reproduce builds: <cut> mkdir investigate-gcc-4dc7ce6fb3917958d1a6036d8acf2953b9c1b868 cd investigate-gcc-4dc7ce6fb3917958d1a6036d8acf2953b9c1b868
# Fetch scripts git clone https://git.linaro.org/toolchain/jenkins-scripts
# Fetch manifests and test.sh script mkdir -p artifacts/manifests curl -o artifacts/manifests/build-baseline.sh https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-next-all... --fail curl -o artifacts/manifests/build-parameters.sh https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-next-all... --fail curl -o artifacts/test.sh https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-next-all... --fail chmod +x artifacts/test.sh
# Reproduce the baseline build (build all pre-requisites) ./jenkins-scripts/tcwg_kernel-build.sh @@ artifacts/manifests/build-baseline.sh
# Save baseline build state (which is then restored in artifacts/test.sh) mkdir -p ./bisect rsync -a --del --delete-excluded --exclude /bisect/ --exclude /artifacts/ --exclude /gcc/ ./ ./bisect/baseline/
cd gcc
# Reproduce first_bad build git checkout --detach 4dc7ce6fb3917958d1a6036d8acf2953b9c1b868 ../artifacts/test.sh
# Reproduce last_good build git checkout --detach f1710910087fb1f4a7706e9ce838163ffcbc50b4 ../artifacts/test.sh
cd .. </cut>
Full commit (up to 1000 lines): <cut> commit 4dc7ce6fb3917958d1a6036d8acf2953b9c1b868 Author: Martin Sebor msebor@redhat.com Date: Fri Oct 1 11:50:25 2021 -0600
Enhance -Waddress to detect more suspicious expressions [PR102103].
Resolves: PR c/102103 - missing warning comparing array address to null
gcc/ChangeLog:
PR c/102103 * doc/invoke.texi (-Waddress): Update. * gengtype.c (write_types): Avoid -Waddress. * poly-int.h (POLY_SET_COEFF): Avoid using null.
gcc/c-family/ChangeLog:
PR c/102103 * c-common.c (decl_with_nonnull_addr_p): Handle members. Check and perform warning suppression. (c_common_truthvalue_conversion): Enhance warning suppression.
gcc/c/ChangeLog:
PR c/102103 * c-typeck.c (maybe_warn_for_null_address): New function. (build_binary_op): Call it.
gcc/cp/ChangeLog:
PR c/102103 * typeck.c (warn_for_null_address): Enhance. (cp_build_binary_op): Call it also for member pointers.
gcc/fortran/ChangeLog:
PR c/102103 * array.c: Remove an unnecessary test. * trans-array.c: Same.
gcc/testsuite/ChangeLog:
PR c/102103 * g++.dg/cpp0x/constexpr-array-ptr10.C: Suppress a valid warning. * g++.dg/warn/Wreturn-local-addr-6.C: Correct a cast. * gcc.dg/Waddress.c: Expect a warning. * c-c++-common/Waddress-3.c: New test. * c-c++-common/Waddress-4.c: New test. * g++.dg/warn/Waddress-5.C: New test. * g++.dg/warn/Waddress-6.C: New test. * g++.dg/warn/pr101219.C: Expect a warning. * gcc.dg/Waddress-3.c: New test. --- gcc/c-family/c-common.c | 29 +++-- gcc/c/c-typeck.c | 140 ++++++++++++++++----- gcc/cp/typeck.c | 94 ++++++++++++-- gcc/doc/invoke.texi | 48 +++++-- gcc/fortran/array.c | 2 +- gcc/fortran/trans-array.c | 1 - gcc/gengtype.c | 4 +- gcc/poly-int.h | 4 +- gcc/testsuite/c-c++-common/Waddress-3.c | 125 ++++++++++++++++++ gcc/testsuite/c-c++-common/Waddress-4.c | 106 ++++++++++++++++ gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C | 5 +- gcc/testsuite/g++.dg/warn/Waddress-5.C | 115 +++++++++++++++++ gcc/testsuite/g++.dg/warn/Waddress-6.C | 79 ++++++++++++ gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C | 4 +- gcc/testsuite/g++.dg/warn/pr101219.C | 4 +- gcc/testsuite/gcc.dg/Waddress-3.c | 35 ++++++ gcc/testsuite/gcc.dg/Waddress.c | 2 +- 17 files changed, 722 insertions(+), 75 deletions(-)
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 5845c675e85..9d19e352725 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -3393,14 +3393,16 @@ c_wrap_maybe_const (tree expr, bool non_const) return expr; }
-/* Return whether EXPR is a declaration whose address can never be - NULL. */ +/* Return whether EXPR is a declaration whose address can never be NULL. + The address of the first struct member could be NULL only if it were + accessed through a NULL pointer, and such an access would be invalid. */
bool decl_with_nonnull_addr_p (const_tree expr) { return (DECL_P (expr) - && (TREE_CODE (expr) == PARM_DECL + && (TREE_CODE (expr) == FIELD_DECL + || TREE_CODE (expr) == PARM_DECL || TREE_CODE (expr) == LABEL_DECL || !DECL_WEAK (expr))); } @@ -3488,13 +3490,17 @@ c_common_truthvalue_conversion (location_t location, tree expr) case ADDR_EXPR: { tree inner = TREE_OPERAND (expr, 0); - if (decl_with_nonnull_addr_p (inner)) + if (decl_with_nonnull_addr_p (inner) + /* Check both EXPR and INNER for suppression. */ + && !warning_suppressed_p (expr, OPT_Waddress) + && !warning_suppressed_p (inner, OPT_Waddress)) { - /* Common Ada programmer's mistake. */ + /* Common Ada programmer's mistake. */ warning_at (location, OPT_Waddress, "the address of %qD will always evaluate as %<true%>", inner); + suppress_warning (inner, OPT_Waddress); return truthvalue_true_node; } break; @@ -3627,8 +3633,17 @@ c_common_truthvalue_conversion (location_t location, tree expr) break; /* If this isn't narrowing the argument, we can ignore it. */ if (TYPE_PRECISION (totype) >= TYPE_PRECISION (fromtype)) - return c_common_truthvalue_conversion (location, - TREE_OPERAND (expr, 0)); + { + tree op0 = TREE_OPERAND (expr, 0); + if ((TREE_CODE (fromtype) == POINTER_TYPE + && TREE_CODE (totype) == INTEGER_TYPE) + || warning_suppressed_p (expr, OPT_Waddress)) + /* Suppress -Waddress for casts to intptr_t, propagating + any suppression from the enclosing expression to its + operand. */ + suppress_warning (op0, OPT_Waddress); + return c_common_truthvalue_conversion (location, op0); + } } break;
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index c74f876e667..33963d7555a 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -11554,6 +11554,110 @@ build_vec_cmp (tree_code code, tree type, return build3 (VEC_COND_EXPR, type, cmp, minus_one_vec, zero_vec); }
+/* Possibly warn about an address of OP never being NULL in a comparison + operation CODE involving null. */ + +static void +maybe_warn_for_null_address (location_t loc, tree op, tree_code code) +{ + if (!warn_address || warning_suppressed_p (op, OPT_Waddress)) + return; + + if (TREE_CODE (op) == NOP_EXPR) + { + /* Allow casts to intptr_t to suppress the warning. */ + tree type = TREE_TYPE (op); + if (TREE_CODE (type) == INTEGER_TYPE) + return; + op = TREE_OPERAND (op, 0); + } + + if (TREE_CODE (op) == POINTER_PLUS_EXPR) + { + /* Allow a cast to void* to suppress the warning. */ + tree type = TREE_TYPE (TREE_TYPE (op)); + if (VOID_TYPE_P (type)) + return; + + /* Adding any value to a null pointer, including zero, is undefined + in C. This includes the expression &p[0] where p is the null + pointer, although &p[0] will have been folded to p by this point + and so not diagnosed. */ + if (code == EQ_EXPR) + warning_at (loc, OPT_Waddress, + "the comparison will always evaluate as %<false%> " + "for the pointer operand in %qE must not be NULL", + op); + else + warning_at (loc, OPT_Waddress, + "the comparison will always evaluate as %<true%> " + "for the pointer operand in %qE must not be NULL", + op); + + return; + } + + if (TREE_CODE (op) != ADDR_EXPR) + return; + + op = TREE_OPERAND (op, 0); + + if (TREE_CODE (op) == IMAGPART_EXPR + || TREE_CODE (op) == REALPART_EXPR) + { + /* The address of either complex part may not be null. */ + if (code == EQ_EXPR) + warning_at (loc, OPT_Waddress, + "the comparison will always evaluate as %<false%> " + "for the address of %qE will never be NULL", + op); + else + warning_at (loc, OPT_Waddress, + "the comparison will always evaluate as %<true%> " + "for the address of %qE will never be NULL", + op); + return; + } + + /* Set to true in the loop below if OP dereferences is operand. + In such a case the ultimate target need not be a decl for + the null [in]equality test to be constant. */ + bool deref = false; + + /* Get the outermost array or object, or member. */ + while (handled_component_p (op)) + { + if (TREE_CODE (op) == COMPONENT_REF) + { + /* Get the member (its address is never null). */ + op = TREE_OPERAND (op, 1); + break; + } + + /* Get the outer array/object to refer to in the warning. */ + op = TREE_OPERAND (op, 0); + deref = true; + } + + if ((!deref && !decl_with_nonnull_addr_p (op)) + || from_macro_expansion_at (loc)) + return; + + if (code == EQ_EXPR) + warning_at (loc, OPT_Waddress, + "the comparison will always evaluate as %<false%> " + "for the address of %qE will never be NULL", + op); + else + warning_at (loc, OPT_Waddress, + "the comparison will always evaluate as %<true%> " + "for the address of %qE will never be NULL", + op); + + if (DECL_P (op)) + inform (DECL_SOURCE_LOCATION (op), "%qD declared here", op); +} + /* Build a binary-operation expression without default conversions. CODE is the kind of expression to build. LOCATION is the operator's location. @@ -12189,44 +12293,12 @@ build_binary_op (location_t location, enum tree_code code, short_compare = 1; else if (code0 == POINTER_TYPE && null_pointer_constant_p (orig_op1)) { - if (TREE_CODE (op0) == ADDR_EXPR - && decl_with_nonnull_addr_p (TREE_OPERAND (op0, 0)) - && !from_macro_expansion_at (location)) - { - if (code == EQ_EXPR) - warning_at (location, - OPT_Waddress, - "the comparison will always evaluate as %<false%> " - "for the address of %qD will never be NULL", - TREE_OPERAND (op0, 0)); - else - warning_at (location, - OPT_Waddress, - "the comparison will always evaluate as %<true%> " - "for the address of %qD will never be NULL", - TREE_OPERAND (op0, 0)); - } + maybe_warn_for_null_address (location, op0, code); result_type = type0; } else if (code1 == POINTER_TYPE && null_pointer_constant_p (orig_op0)) { - if (TREE_CODE (op1) == ADDR_EXPR - && decl_with_nonnull_addr_p (TREE_OPERAND (op1, 0)) - && !from_macro_expansion_at (location)) - { - if (code == EQ_EXPR) - warning_at (location, - OPT_Waddress, - "the comparison will always evaluate as %<false%> " - "for the address of %qD will never be NULL", - TREE_OPERAND (op1, 0)); - else - warning_at (location, - OPT_Waddress, - "the comparison will always evaluate as %<true%> " - "for the address of %qD will never be NULL", - TREE_OPERAND (op1, 0)); - } + maybe_warn_for_null_address (location, op1, code); result_type = type1; } else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE) diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index cd130f16a66..e880d34dcfe 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -4603,25 +4603,93 @@ warn_for_null_address (location_t location, tree op, tsubst_flags_t complain) || warning_suppressed_p (op, OPT_Waddress)) return;
+ if (TREE_CODE (op) == NON_DEPENDENT_EXPR) + op = TREE_OPERAND (op, 0); + tree cop = fold_for_warn (op);
- if (TREE_CODE (cop) == ADDR_EXPR - && decl_with_nonnull_addr_p (TREE_OPERAND (cop, 0)) - && !warning_suppressed_p (cop, OPT_Waddress)) - warning_at (location, OPT_Waddress, "the address of %qD will never " - "be NULL", TREE_OPERAND (cop, 0)); + if (TREE_CODE (cop) == NON_LVALUE_EXPR) + /* Unwrap the expression for C++ 98. */ + cop = TREE_OPERAND (cop, 0);
- if (CONVERT_EXPR_P (op) + if (TREE_CODE (cop) == PTRMEM_CST) + { + /* The address of a nonstatic data member is never null. */ + warning_at (location, OPT_Waddress, + "the address %qE will never be NULL", + cop); + return; + } + + if (TREE_CODE (cop) == NOP_EXPR) + { + /* Allow casts to intptr_t to suppress the warning. */ + tree type = TREE_TYPE (cop); + if (TREE_CODE (type) == INTEGER_TYPE) + return; + + STRIP_NOPS (cop); + } + + bool warned = false; + if (TREE_CODE (cop) == ADDR_EXPR) + { + cop = TREE_OPERAND (cop, 0); + + /* Set to true in the loop below if OP dereferences its operand. + In such a case the ultimate target need not be a decl for + the null [in]equality test to be necessarily constant. */ + bool deref = false; + + /* Get the outermost array or object, or member. */ + while (handled_component_p (cop)) + { + if (TREE_CODE (cop) == COMPONENT_REF) + { + /* Get the member (its address is never null). */ + cop = TREE_OPERAND (cop, 1); + break; + } + + /* Get the outer array/object to refer to in the warning. */ + cop = TREE_OPERAND (cop, 0); + deref = true; + } + + if ((!deref && !decl_with_nonnull_addr_p (cop)) + || from_macro_expansion_at (location) + || warning_suppressed_p (cop, OPT_Waddress)) + return; + + warned = warning_at (location, OPT_Waddress, + "the address of %qD will never be NULL", cop); + op = cop; + } + else if (TREE_CODE (cop) == POINTER_PLUS_EXPR) + { + /* Adding zero to the null pointer is well-defined in C++. When + the offset is unknown (i.e., not a constant) warn anyway since + it's less likely that the pointer operand is null than not. */ + tree off = TREE_OPERAND (cop, 1); + if (!integer_zerop (off) + && !warning_suppressed_p (cop, OPT_Waddress)) + warning_at (location, OPT_Waddress, "comparing the result of pointer " + "addition %qE and NULL", cop); + return; + } + else if (CONVERT_EXPR_P (op) && TYPE_REF_P (TREE_TYPE (TREE_OPERAND (op, 0)))) { - tree inner_op = op; - STRIP_NOPS (inner_op); + STRIP_NOPS (op);
- if (DECL_P (inner_op)) - warning_at (location, OPT_Waddress, - "the compiler can assume that the address of " - "%qD will never be NULL", inner_op); + if (DECL_P (op)) + warned = warning_at (location, OPT_Waddress, + "the compiler can assume that the address of " + "%qD will never be NULL", op); } + + if (warned && DECL_P (op)) + inform (DECL_SOURCE_LOCATION (op), "%qD declared here", op); }
/* Warn about [expr.arith.conv]/2: If one operand is of enumeration type and @@ -5411,6 +5479,8 @@ cp_build_binary_op (const op_location_t &location, op1 = cp_convert (TREE_TYPE (op0), op1, complain); } result_type = TREE_TYPE (op0); + + warn_for_null_address (location, orig_op0, complain); } else if (TYPE_PTRMEMFUNC_P (type1) && null_ptr_cst_p (orig_op0)) return cp_build_binary_op (location, code, op1, op0, complain); diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 5f39b208049..d35114c0727 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -8551,17 +8551,43 @@ by @option{-Wall}. @item -Waddress @opindex Waddress @opindex Wno-address -Warn about suspicious uses of memory addresses. These include using -the address of a function in a conditional expression, such as -@code{void func(void); if (func)}, and comparisons against the memory -address of a string literal, such as @code{if (x == "abc")}. Such -uses typically indicate a programmer error: the address of a function -always evaluates to true, so their use in a conditional usually -indicate that the programmer forgot the parentheses in a function -call; and comparisons against string literals result in unspecified -behavior and are not portable in C, so they usually indicate that the -programmer intended to use @code{strcmp}. This warning is enabled by -@option{-Wall}. +Warn about suspicious uses of address expressions. These include comparing +the address of a function or a declared object to the null pointer constant +such as in +@smallexample +void f (void); +void g (void) +@{ + if (!func) // warning: expression evaluates to false + abort (); +@} +@end smallexample +comparisons of a pointer to a string literal, such as in +@smallexample +void f (const char *x) +@{ + if (x == "abc") // warning: expression evaluates to false + puts ("equal"); +@} +@end smallexample +and tests of the results of pointer addition or subtraction for equality +to null, such as in +@smallexample +void f (const int *p, int i) +@{ + return p + i == NULL; +@} +@end smallexample +Such uses typically indicate a programmer error: the address of most +functions and objects necessarily evaluates to true (the exception are +weak symbols), so their use in a conditional might indicate missing +parentheses in a function call or a missing dereference in an array +expression. The subset of the warning for object pointers can be +suppressed by casting the pointer operand to an integer type such +as @code{inptr_t} or @code{uinptr_t}. +Comparisons against string literals result in unspecified behavior +and are not portable, and suggest the intent was to call @code{strcmp}. +@option{-Waddress} warning is enabled by @option{-Wall}.
@item -Wno-address-of-packed-member @opindex Waddress-of-packed-member diff --git a/gcc/fortran/array.c b/gcc/fortran/array.c index a4d1cb4c72d..6552eaf3b0c 100644 --- a/gcc/fortran/array.c +++ b/gcc/fortran/array.c @@ -2581,7 +2581,7 @@ gfc_array_dimen_size (gfc_expr *array, int dimen, mpz_t *result) } }
- if (array->shape && array->shape[dimen]) + if (array->shape) { mpz_init_set (*result, array->shape[dimen]); return true; diff --git a/gcc/fortran/trans-array.c b/gcc/fortran/trans-array.c index b8061f37772..e2f59e0823c 100644 --- a/gcc/fortran/trans-array.c +++ b/gcc/fortran/trans-array.c @@ -5104,7 +5104,6 @@ set_loop_bounds (gfc_loopinfo *loop)
if (info->shape) { - gcc_assert (info->shape[dim]); /* The frontend has worked out the size for us. */ if (!loopspec[n] || !specinfo->shape diff --git a/gcc/gengtype.c b/gcc/gengtype.c index 31d4bf4e5d0..a77cfd92bfa 100644 --- a/gcc/gengtype.c +++ b/gcc/gengtype.c @@ -3685,8 +3685,8 @@ write_types (outf_p output_header, type_p structures, output_mangled_typename (output_header, s); oprintf (output_header, "(X) do { \\n"); oprintf (output_header, - " if (X != NULL) gt_%sx_%s (X);\\n", wtd->prefix, - s_id_for_tag); + " if ((intptr_t)(X) != 0) gt_%sx_%s (X);\\n", + wtd->prefix, s_id_for_tag); oprintf (output_header, " } while (0)\n");
for (opt = s->u.s.opt; opt; opt = opt->next) diff --git a/gcc/poly-int.h b/gcc/poly-int.h index f47f9e436a8..94e7b701f64 100644 --- a/gcc/poly-int.h +++ b/gcc/poly-int.h @@ -324,10 +324,10 @@ struct poly_result<T1, T2, 2> routine can take the address of RES rather than the address of a temporary.
- The dummy comparison against a null C * is just a way of checking + The dummy self-comparison against C * is just a way of checking that C gives the right type. */ #define POLY_SET_COEFF(C, RES, I, VALUE) \ - ((void) (&(RES).coeffs[0] == (C *) 0), \ + ((void) (&(RES).coeffs[0] == (C *) (void *) &(RES).coeffs[0]), \ wi::int_traits<C>::precision_type == wi::FLEXIBLE_PRECISION \ ? (void) ((RES).coeffs[I] = VALUE) \ : (void) ((RES).coeffs[I].~C (), new (&(RES).coeffs[I]) C (VALUE))) diff --git a/gcc/testsuite/c-c++-common/Waddress-3.c b/gcc/testsuite/c-c++-common/Waddress-3.c new file mode 100644 index 00000000000..9a13a444045 --- /dev/null +++ b/gcc/testsuite/c-c++-common/Waddress-3.c @@ -0,0 +1,125 @@ +/* PR c/102103 - missing warning comparing array address to null + { dg-do compile } + { dg-options "-Wall" } */ + +typedef __INTPTR_TYPE__ intptr_t; +typedef __UINTPTR_TYPE__ uintptr_t; + +#ifndef __cplusplus +# define bool _Bool +#endif + +struct S { void *p, *a1[2], *a2[2][2]; } s, *p; + +extern const void *a1[2]; +extern void *a2[2][2], *ax[]; + +void T (bool); + +void test_array_eq_0 (int i) +{ + // Verify that casts intptr_t suppress the warning. + T ((intptr_t)a1 == 0); + T ((uintptr_t)a1 == 0); + T (a1 == 0); // { dg-warning "-Waddress" } + T (0 == &a1); // { dg-warning "-Waddress" } + // Verify that casts to other pointer types don't suppress it. + T ((void *)a1 == 0); // { dg-warning "-Waddress" } + T ((char *)a1 == 0); // { dg-warning "-Waddress" } + T (a1[0] == 0); + T (0 == (intptr_t)&a1[0]); + T (0 == &a1[0]); // { dg-warning "-Waddress" } + T (a1[i] == 0); + T (0 == (uintptr_t)&a1[i]); + T (0 == &a1[i]); // { dg-warning "-Waddress" } + + T ((intptr_t)a2 == 0); + T (a2 == 0); // { dg-warning "-Waddress" } + T (0 == &a2); // { dg-warning "-Waddress" } + T (a2[0] == 0); // { dg-warning "-Waddress" } + T (0 == &a1[0]); // { dg-warning "-Waddress" } + T (a2[i] == 0); // { dg-warning "-Waddress" } + T (0 == &a2[i]); // { dg-warning "-Waddress" } + T (a2[0][0] == 0); + T (0 == &a2[0][0]); // { dg-warning "-Waddress" } + T (&ax == 0); // { dg-warning "-Waddress" } + T (0 == &ax); // { dg-warning "-Waddress" } + T (&ax[0] == 0); // { dg-warning "-Waddress" } + T (0 == ax[0]); +} + + +void test_array_neq_0 (int i) +{ + // Verify that casts to intptr_t suppress the warning. + T ((uintptr_t)a1); + + T (a1); // { dg-warning "-Waddress" } + T ((void *)a1); // { dg-warning "-Waddress" } + T (&a1 != 0); // { dg-warning "-Waddress" } + T (a1[0]); + T (&a1[0] != 0); // { dg-warning "-Waddress" } + T (a1[i]); + T (&a1[i] != 0); // { dg-warning "-Waddress" } + + T ((intptr_t)a2); + T (a2); // { dg-warning "-Waddress" } + T ((void *)a2); // { dg-warning "-Waddress" } + T ((char *)a2); // { dg-warning "-Waddress" } + T (&a2 != 0); // { dg-warning "-Waddress" } + T (a2[0]); // { dg-warning "-Waddress" } + T (&a1[0] != 0); // { dg-warning "-Waddress" } + T (a2[i]); // { dg-warning "-Waddress" } + T (&a2[i] != 0); // { dg-warning "-Waddress" } + T (a2[0][0]); + T (&a2[0][0] != 0); // { dg-warning "-Waddress" } +} + + +void test_member_array_eq_0 (int i) +{ + // Verify that casts to intptr_t suppress the warning. + T ((intptr_t)s.a1 == 0); + T (s.a1 == 0); // { dg-warning "-Waddress" } + T (0 == &a1); // { dg-warning "-Waddress" } + T (s.a1[0] == 0); + T ((void*)s.a1); // { dg-warning "-Waddress" } + T (0 == &a1[0]); // { dg-warning "-Waddress" } + T (s.a1[i] == 0); + T (0 == &a1[i]); // { dg-warning "-Waddress" } + + T ((uintptr_t)s.a2 == 0); + T (s.a2 == 0); // { dg-warning "-Waddress" } + T (0 == &a2); // { dg-warning "-Waddress" } + T ((void *)s.a2 == 0);// { dg-warning "-Waddress" } + T (s.a2[0] == 0); // { dg-warning "-Waddress" } + T (0 == &a1[0]); // { dg-warning "-Waddress" } + T (s.a2[i] == 0); // { dg-warning "-Waddress" } + T (0 == &a2[i]); // { dg-warning "-Waddress" } + T (s.a2[0][0] == 0); + T (0 == &a2[0][0]); // { dg-warning "-Waddress" } +} + + +void test_member_array_neq_0 (int i) +{ + // Verify that casts to intptr_t suppress the warning. + T ((uintptr_t)s.a1); + T (s.a1); // { dg-warning "-Waddress" } + T (&s.a1 != 0); // { dg-warning "-Waddress" } + T ((void *)&s.a1[0]); // { dg-warning "-Waddress" } + T (s.a1[0]); + T (&s.a1[0] != 0); // { dg-warning "-Waddress" } + T (s.a1[i]); + T (&s.a1[i] != 0); // { dg-warning "-Waddress" } + + T ((intptr_t)s.a2); + T (s.a2); // { dg-warning "-Waddress" } + T (&s.a2 != 0); // { dg-warning "-Waddress" } + T (s.a2[0]); // { dg-warning "-Waddress" } + T (&s.a1[0] != 0); // { dg-warning "-Waddress" } + T (s.a2[i]); // { dg-warning "-Waddress" } + T (&s.a2[i] != 0); // { dg-warning "-Waddress" } + T (s.a2[0][0]); + T (&s.a2[0][0] != 0); // { dg-warning "-Waddress" } +} diff --git a/gcc/testsuite/c-c++-common/Waddress-4.c b/gcc/testsuite/c-c++-common/Waddress-4.c new file mode 100644 index 00000000000..489a0cd717c --- /dev/null +++ b/gcc/testsuite/c-c++-common/Waddress-4.c @@ -0,0 +1,106 @@ +/* PR c/102103 - missing warning comparing array address to null + { dg-do compile } + { dg-options "-Wall" } */ + +typedef __INTPTR_TYPE__ intptr_t; +typedef __INTPTR_TYPE__ uintptr_t; + +extern char *ax[], *a2[][2]; + +void T (int); + +void test_ax_plus_eq_0 (int i) +{ + // Verify that casts to intptr_t suppress the warning. + T ((intptr_t)(ax + 0) == 0); + T ((uintptr_t)(ax + 1) == 0); + + T (ax + 0 == 0); // { dg-warning "-Waddress" } + T (&ax[0] == 0); // { dg-warning "-Waddress" } + T (ax - 1 == 0); // { dg-warning "-Waddress" } + T (0 == &ax[-1]); // { dg-warning "-Waddress" } + T ((void *)(&ax[0] + 2) == 0); // { dg-warning "-Waddress" } + T (&ax[0] + 2 == 0); // { dg-warning "-Waddress" } + T (ax + 3 == 0); // { dg-warning "-Waddress" } + T (0 == &ax[-4]); // { dg-warning "-Waddress" } + T (ax - i == 0); // { dg-warning "-Waddress" } + T (&ax[i] == 0); // { dg-warning "-Waddress" } + T (0 == &ax[1] + i); // { dg-warning "-Waddress" } +} + +void test_a2_plus_eq_0 (int i) +{ + // Verify that casts to intptr_t suppress the warning. + T ((intptr_t)(a2 + 0) == 0); + T ((uintptr_t)(a2 + 1) == 0); + + T (a2 + 0 == 0); // { dg-warning "-Waddress" } + // Verify that a cast to another pointer type doesn't suppress it. + T ((void*)(a2 + 0) == 0); // { dg-warning "-Waddress" } + T ((char*)a2 + 1 == 0); // { dg-warning "-Waddress" } + T (&a2[0] == 0); // { dg-warning "-Waddress" } + T (a2 - 1 == 0); // { dg-warning "-Waddress" } + T (0 == &a2[-1]); // { dg-warning "-Waddress" } + T (a2 + 2 == 0); // { dg-warning "-Waddress" } + T (0 == &a2[-2]); // { dg-warning "-Waddress" } + T (a2 - i == 0); // { dg-warning "-Waddress" } + T (&a2[i] == 0); // { dg-warning "-Waddress" } +} + +// Exercise a pointer. +void test_p_plus_eq_0 (int *p, int i) +{ + /* P + 0 and equivalently &P[0] are invalid for a null P but they're + folded to p before the warning has a chance to trigger. */ + T (p + 0 == 0); // { dg-warning "-Waddress" "pr102555" { xfail *-*-* } } + T (&p[0] == 0); // { dg-warning "-Waddress" "pr102555" { xfail *-*-* } } + + T (p - 1 == 0); // { dg-warning "-Waddress" } + T (0 == &p[-1]); // { dg-warning "-Waddress" } + T (p + 2 == 0); // { dg-warning "-Waddress" } + T (0 == &p[-2]); // { dg-warning "-Waddress" } + T (p - i == 0); // { dg-warning "-Waddress" } + T (&p[i] == 0); // { dg-warning "-Waddress" } +} + +// Exercise pointer to array. +void test_pa_plus_eq_0 (int (*p)[], int (*p2)[][2], int i) +{ + // The array pointer may be null. + T (*p == 0); + /* &**P is equivalent to *P and might be the result od macro expansion. + Verify it doesn't cause a warning. */ + T (&**p == 0); + + /* *P + 0 is invalid but folded to *P before the warning has a chance + to trigger. */ + T (*p + 0 == 0); // { dg-warning "-Waddress" "pr102555" { xfail *-*-* } } + + T (&(*p)[0] == 0); // { dg-warning "-Waddress" } + T (*p - 1 == 0); // { dg-warning "-Waddress" } + T (0 == &(*p)[-1]); // { dg-warning "-Waddress" } + T (*p + 2 == 0); // { dg-warning "-Waddress" } + T (0 == &(*p)[-2]); // { dg-warning "-Waddress" } + T (*p - i == 0); // { dg-warning "-Waddress" } + T (&(*p)[i] == 0); // { dg-warning "-Waddress" } + + + /* Similar to the above but for a pointer to a two-dimensional array, + referring to the higher-level element (i.e., an array itself). */ + T (*p2 == 0); + T (**p2 == 0); // { dg-warning "-Waddress" "pr102555" { xfail *-*-* } } + T (&**p2 == 0); // { dg-warning "-Waddress" "pr102555" { xfail *-*-* } } + T (&***p2 == 0); // { dg-warning "-Waddress" "pr102555" { xfail *-*-* } } + T (&**p2 == 0); + + T (*p2 + 0 == 0); // { dg-warning "-Waddress" "pr102555" { xfail *-*-* } } + T (&(*p2)[0] == 0); // { dg-warning "-Waddress" } + T (&(*p2)[0][1] == 0); // { dg-warning "-Waddress" } + T (*p2 - 1 == 0); // { dg-warning "-Waddress" } + T (0 == &(*p2)[-1]); // { dg-warning "-Waddress" } + T (0 == &(*p2)[1][2]); // { dg-warning "-Waddress" } + T (*p2 + 2 == 0); // { dg-warning "-Waddress" } + T (0 == &(*p2)[-2]); // { dg-warning "-Waddress" } + T (*p2 - i == 0); // { dg-warning "-Waddress" } + T (&(*p2)[i] == 0); // { dg-warning "-Waddress" } +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C index 5224bb14234..63295230d51 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C @@ -85,8 +85,11 @@ extern __attribute__ ((weak)) int i; constexpr int *p1 = &i + 1;
#pragma GCC diagnostic push +// Suppress warning: ordered comparison of pointer with integer zero. #pragma GCC diagnostic ignored "-Wextra" -// Suppress warning: ordered comparison of pointer with integer zero +// Also suppress -Waddress for comparisons of constant addresses to +// to null. +#pragma GCC diagnostic ignored "-Waddress"
constexpr bool b0 = p1; // { dg-error "not a constant expression" } constexpr bool b1 = p1 == 0; // { dg-error "not a constant expression" } diff --git a/gcc/testsuite/g++.dg/warn/Waddress-5.C b/gcc/testsuite/g++.dg/warn/Waddress-5.C new file mode 100644 index 00000000000..b1ad38a8112 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Waddress-5.C @@ -0,0 +1,115 @@ +/* PR c/102103 - missing warning comparing array address to null + { dg-do compile } + { dg-options "-Wall" } */ + +#if __cplusplus < 201103L +# define nullptr __null +#endif + +struct A +{ + void f (); + virtual void vf (); + virtual void pvf () = 0; + + void sf (); + + int *p; + int a[2]; +}; + +void T (bool); + +void warn_memptr_if () +{ + // Exercise warnings for addresses of nonstatic member functions. + if (&A::f == 0) // { dg-warning "the address '&A::f'" } + T (0); + + if (&A::vf) // { dg-warning "-Waddress" } + T (0); + + if (&A::pvf != 0) // { dg-warning "-Waddress" } + T (0); + + // Exercise warnings for addresses of static member functions. + if (&A::sf == 0) // { dg-warning "-Waddress" } + T (0); + + if (&A::sf) // { dg-warning "-Waddress" } + T (0); + + // Exercise warnings for addresses of nonstatic data members. + if (&A::p == 0) // { dg-warning "the address '&A::p'" } + T (0); + + if (&A::a == nullptr) // { dg-warning "-Waddress" } + T (0); +} + +void warn_memptr_bool () +{ + // Exercise warnings for addresses of nonstatic member functions. + T (&A::f == 0); // { dg-warning "-Waddress" } + T (&A::vf); // { dg-warning "-Waddress" } + T (&A::pvf != 0); // { dg-warning "-Waddress" } + + // Exercise warnings for addresses of static member functions. + T (&A::sf == 0); // { dg-warning "-Waddress" } + T (&A::sf); // { dg-warning "-Waddress" } + + // Exercise warnings for addresses of nonstatic data members. + T (&A::p == 0); // { dg-warning "-Waddress" } + T (&A::a == nullptr); // { dg-warning "-Waddress" } +} + + +/* Verify that no warnings are issued for a dependent expression in + a template. */ + +template <int> +struct B +{ + // This is why. + struct F { void* operator& () const { return 0; } } f; +}; + +template <class Type, int N> +void nowarn_dependent (Type targ) +{ + T (&Type::x == 0); + T (&targ == 0); + + Type tarr[1]; + T (&tarr[0] == nullptr); + + T (&B<N>::f == 0); + + /* Like in the case above, the address-of operator could be a member + of B<N>::vf that returns zero. */ + T (&B<N>::vf); + T (&B<N>::pvf != 0); + T (&B<N>::p == 0); + T (&B<N>::a == 0); +} + + +/* Verify that in an uninstantiated template warnings are not issued + for dependent expressions but are issued otherwise. */ + +template <class Type> +void warn_non_dependent (Type targ, Type *tptr, int i) +{ + /* The address of a pointer to a dependent type cannot be null but + the warning doesn't have a chance to see it. */ + T (&tptr == 0); // { dg-warning "-Waddress" "pr102378" { xfail *-*-* } } + T (&i == 0); // { dg-warning "-Waddress" } + + int iarr[1]; + T (&iarr == 0); // { dg-warning "-Waddress" } + T (&*iarr != 0); // { dg-warning "-Waddress" "pr102378" { xfail *-*-* } } + T (&iarr[0] == 0); // { dg-warning "-Waddress" } + + Type tarr[1]; + T (&tarr == nullptr); // { dg-warning "-Waddress" "pr102378" { xfail *-*-* } } +} diff --git a/gcc/testsuite/g++.dg/warn/Waddress-6.C b/gcc/testsuite/g++.dg/warn/Waddress-6.C new file mode 100644 index 00000000000..c22a83a0dd7 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Waddress-6.C @@ -0,0 +1,79 @@ +/* PR c/102103 - missing warning comparing array address to null + { dg-do compile } + Verify -Waddress for member arrays of structs and notes. + { dg-options "-Wall" } */ + +#if __cplusplus < 201103L +# define nullptr __null +#endif + +void T (bool); + +struct A +{ + int n; + int ia[]; // { dg-message "'A::ia' declared here" } +}; + +struct B +{ + A a[3]; // { dg-message "'B::a' declared here" } +}; + +struct C +{ + B b[3]; // { dg-message "'C::b' declared here" } +}; + +struct D +{ + C c[3]; // { dg-message "'D::c' declared here" } +}; + + +void test_waddress_1d () +{ + D d[2]; // { dg-message "'d' declared here" } + + T (d); // { dg-warning "address of 'd'" } + T (d == nullptr); // { dg-warning "address of 'd'" } + T (&d); // { dg-warning "address of 'd'" } + T (d->c); // { dg-warning "address of 'D::c'" } + T (d->c != nullptr); // { dg-warning "address of 'D::c'" } + T (d->c->b); // { dg-warning "address of 'C::b'" } + T (d->c[1].b->a); // { dg-warning "address of 'B::a'" } + T (d->c->b[2].a->ia); // { dg-warning "address of 'A::ia'" } + + if (d->c->b[2].a[1].ia) // { dg-warning "address of 'A::ia'" } + T (0); + + if (bool b = d->c->b[1].a) // { dg-warning "address of 'B::a'" } + T (b); + + /* The following is represented as a declaration of P followed + by an if statement and so it isn't diagnosed. It's not clear + that it should be since the pointer is then used. + void *p = d->c->b[2].a; + if (p) ... + */ + if (void *p = d->c->b[2].a) // { dg-warning "address of 'A::ia'" "" { xfail *-*-* } } + T (p); +} + + +void test_waddress_2d (int i) +{ + D d[2][3]; // { dg-message "'d' declared here" } + + T (d); // { dg-warning "address of 'd'" } + T (d == nullptr); // { dg-warning "address of 'd'" } + T (&d); // { dg-warning "address of 'd'" } + T (*d); // { dg-warning "address of 'd'" } + T (d[1] != nullptr); // { dg-warning "address of 'd'" } + T (&d[1]->c); // { dg-warning "address of 'D::c'" } + T (d[1]->c); // { dg-warning "address of 'D::c'" } + T (d[1]->c == nullptr); // { dg-warning "address of 'D::c'" } + T (d[i]->c[1].b); // { dg-warning "address of 'C::b'" } + T ((*(d + i))->c->b->a); // { dg-warning "address of 'B::a'" } + T (d[1][2].c->b->a->ia); // { dg-warning "address of 'A::ia'" } +} </cut>