Another test case added to the bug since I posted the patch shows that the no-warning bit set by the C++ front end is easily lost during early folding, triggering the -Wnonull again despite the suppression. The attached revision tweaks the folder in just this one case to let the suppression take effect in this situation.
In light of how pervasive this potential for stripping looks (none of the other folders preserves the bit) the tweak feels like a band- aid rather than a general solution. But implementing something better (and mainly developing tests to verify that it works in general) would probably be quite the project. So I leave it for some other time. Martin On 7/17/20 1:00 PM, Martin Sebor wrote:
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/ChangeLog: fold-const.c (fold_unary_loc): Set no-warning on a rebuilt COND_EXPR if the original had it set. 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..8a2d641f17c 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -5821,6 +5821,9 @@ check_function_arguments_recurse (void (*callback) void *ctx, tree param, unsigned HOST_WIDE_INT param_num) { + 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 a3913f4ce0b..e024e8a4a74 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/fold-const.c b/gcc/fold-const.c index 300d959278b..67153e3f484 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -8754,6 +8754,7 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) arg02 = fold_build1_loc (loc, code, type, fold_convert_loc (loc, TREE_TYPE (op0), arg02)); + bool nowarn = TREE_NO_WARNING (op0); tem = fold_build3_loc (loc, COND_EXPR, type, TREE_OPERAND (arg0, 0), arg01, arg02); @@ -8788,6 +8789,8 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) TREE_OPERAND (TREE_OPERAND (tem, 1), 0), TREE_OPERAND (TREE_OPERAND (tem, 2), 0))); + if (nowarn) + TREE_NO_WARNING (tem) = true; return tem; } } diff --git a/gcc/testsuite/g++.dg/warn/Wnonnull7.C b/gcc/testsuite/g++.dg/warn/Wnonnull7.C new file mode 100644 index 00000000000..7611c18d948 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wnonnull7.C @@ -0,0 +1,36 @@ +/* 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" } + } +}; + +// Test case from comment #11. + +struct S1 { virtual ~S1 (); }; +struct S2 { virtual ~S2 (); }; +struct S3: S1, S2 { void f (); }; + +void f (S2 *p) +{ + static_cast<S3 *>(p)->f (); // { dg-bogus "\\\[-Wnonnull" } +}