The recent enhancement to treat the implicit this pointer argument as nonnull in member functions triggers spurious -Wnonnull for the synthesized conditional expression the C++ front end replaces the pointer with in some static_cast expressions. The front end already sets the no-warning bit for the test but not for the whole conditional expression, so the attached fix extends the same solution to it.
The consequence of this fix is that user-written code like this: static_cast<T*>(p ? p : 0)->f (); or static_cast<T*>(p ? p : nullptr)->f (); don't trigger the warning because they are both transformed into the same expression as: static_cast<T*>(p)->f (); What still does trigger it is this: static_cast<T*>(p ? p : (T*)0)->f (); because here it's the inner COND_EXPR's no-warning bit that's set (the outer one is clear), whereas in the former expressions it's the other way around. It would be nice if this worked consistently but I didn't see an easy way to do that and more than a quick fix seems outside the scope for this bug. Another case reported by someone else in the same bug involves a dynamic_cast. A simplified test case goes something like this: if (dynamic_cast<T*>(p)) dynamic_cast<T*>(p)->f (); The root cause is the same: the front end emitting the COND_EXPR ((p != 0) ? ((T*)__dynamic_cast(p, (& _ZTI1B), (& _ZTI1C), 0)) : 0) I decided not to suppress the warning in this case because doing so would also suppress it in unconditional calls with the result of the cast: dynamic_cast<T*>(p)->f (); and that doesn't seem helpful. Instead, I'd suggest to make the second cast in the if statement to reference to T&: if (dynamic_cast<T*>(p)) dynamic_cast<T&>(*p).f (); Martin
PR c++/96003 spurious -Wnonnull calling a member on the result of static_cast gcc/c-family/ChangeLog: PR c++/96003 * c-common.c (check_function_arguments_recurse): Return early when no-warning bit is set. gcc/cp/ChangeLog: PR c++/96003 * class.c (build_base_path): Set no-warning bit on the synthesized conditional expression in static_cast. gcc/testsuite/ChangeLog: PR c++/96003 * g++.dg/warn/Wnonnull7.C: New test. diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 51ecde69f2d..7ab9966b7c0 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -5821,6 +5821,11 @@ check_function_arguments_recurse (void (*callback) void *ctx, tree param, unsigned HOST_WIDE_INT param_num) { + /* Avoid warning in synthesized expressions like C++ static_cast. + See PR 96003. */ + if (TREE_NO_WARNING (param)) + return; + if (CONVERT_EXPR_P (param) && (TYPE_PRECISION (TREE_TYPE (param)) == TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (param, 0))))) diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 14380c7a08c..b460f8aefcd 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -516,8 +516,14 @@ build_base_path (enum tree_code code, out: if (null_test) - expr = fold_build3_loc (input_location, COND_EXPR, target_type, null_test, expr, - build_zero_cst (target_type)); + { + expr = fold_build3_loc (input_location, COND_EXPR, target_type, null_test, + expr, build_zero_cst (target_type)); + /* Avoid warning for the whole conditional expression (in addition + to NULL_TEST itself -- see above) in case the result is used in + a nonnull context that the front end -Wnonnull checks. */ + TREE_NO_WARNING (expr) = 1; + } return expr; } diff --git a/gcc/testsuite/g++.dg/warn/Wnonnull7.C b/gcc/testsuite/g++.dg/warn/Wnonnull7.C new file mode 100644 index 00000000000..b7a64c83436 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wnonnull7.C @@ -0,0 +1,25 @@ +/* PR c++/96003 - spurious -Wnonnull calling a member on the result + of static_cast + { dg-do compile } + { dg-options "-Wall" } */ + +struct D; +struct B +{ + B* next; + D* Next (); +}; + +struct D: B +{ + virtual ~D (); +}; + +struct Iterator +{ + D* p; + void advance () + { + p = static_cast<B*>(p)->Next (); // { dg-bogus "\\\[-Wnonnull" } + } +};