Similar to pr96003, bug 98646 reports a spurious instance of -Wnonnull
calling a member function on the result of static_cast.  The difference
here is that the cast argument is a function call, and, besides casting
down an inheritance hierarchy, the cast also adds the const qualifier.
GCC sets the NO_WARNING bit for the COND_EXPR it emits for the cast
in these cases to avoid issuing spurious -Wnonnull warnings but then
doesn't preserve the bit in a call to cp_fold_convert() on it, leading
to the false positive.

The attached patch arranges for cp_fold_convert() to propagate the bit
to the result, preventing the front end -Wnonnull handler from issuing
the warning.

In addition, it also improves the wording of the message (suggested by
users in the past).

Tested on x86_64-linux.

Martin
PR c++/98646 - spurious -Wnonnull calling a member on the result of static_cast

gcc/c-family/ChangeLog:

	PR c++/98646
	* c-common.c (check_nonnull_arg): Adjust warning text.

gcc/cp/ChangeLog:

	PR c++/98646
	* cvt.c (cp_fold_convert): Propagate TREE_NO_WARNING.

gcc/ChangeLog:

	PR c++/98646
	* tree-ssa-ccp.c (pass_post_ipa_warn::execute): Adjust warning text.

gcc/testsuite/ChangeLog:

	PR c++/98646
	* g++.dg/warn/Wnonnull5.C: Adjust text of an expected warning.
	* g++.dg/warn/Wnonnull10.C: New test.
	* g++.dg/warn/Wnonnull9.C: New test.

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 2028e93b4d7..813212cc21d 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5595,7 +5595,7 @@ check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num)
   if (param_num == 0)
     {
       warned = warning_at (loc, OPT_Wnonnull,
-			   "%qs pointer null", "this");
+			   "%qs pointer is null", "this");
       if (warned && pctx->fndecl)
 	inform (DECL_SOURCE_LOCATION (pctx->fndecl),
 		"in a call to non-static member function %qD",
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index f94488b1114..aa4871dc614 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -599,11 +599,13 @@ ignore_overflows (tree expr, tree orig)
 }
 
 /* Fold away simple conversions, but make sure TREE_OVERFLOW is set
-   properly.  */
+   properly and propagate TREE_NO_WARNING.  */
 
 tree
 cp_fold_convert (tree type, tree expr)
 {
+  bool nowarn = TREE_NO_WARNING (expr);
+
   tree conv;
   if (TREE_TYPE (expr) == type)
     conv = expr;
@@ -626,6 +628,10 @@ cp_fold_convert (tree type, tree expr)
       conv = fold_convert (type, expr);
       conv = ignore_overflows (conv, expr);
     }
+
+  if (nowarn)
+    TREE_NO_WARNING (conv) = nowarn;
+
   return conv;
 }
 
diff --git a/gcc/testsuite/g++.dg/warn/Wnonnull10.C b/gcc/testsuite/g++.dg/warn/Wnonnull10.C
new file mode 100644
index 00000000000..a7e795ceb8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wnonnull10.C
@@ -0,0 +1,63 @@
+/* Very that -Wnonnull is issued for calls to inline member functions
+   with a null this pointer.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#if __cplusplus < 201103L
+# define nullptr 0
+#endif
+
+struct S
+{
+  void f () { }
+  static void g () { }
+  virtual void h () { }
+};
+
+void f0 ()
+{
+  static_cast<S*>(0)->f ();         // { dg-warning "-Wnonnull" }
+  static_cast<S*>(0)->g ();
+  static_cast<S*>(0)->h ();         // { dg-warning "-Wnonnull" }
+}
+
+void f1 ()
+{
+  static_cast<S*>(nullptr)->f ();   // { dg-warning "-Wnonnull" }
+  static_cast<S*>(nullptr)->g ();
+  static_cast<S*>(nullptr)->h ();   // { dg-warning "-Wnonnull" }
+}
+
+void f2 ()
+{
+  S* const p = 0;
+
+  p->f ();                          // { dg-warning "-Wnonnull" }
+  p->g ();
+  p->h ();                          // { dg-warning "-Wnonnull" }
+}
+
+
+#pragma GCC optimize "1"
+
+void f3 ()
+{
+  S *p = 0;
+
+  p->f ();                          // { dg-warning "-Wnonnull" }
+  p->g ();
+  p->h ();                          // { dg-warning "-Wnonnull" }
+}
+
+
+#pragma GCC optimize "2"
+
+void f4 (S *p)
+{
+  if (p)
+    return;
+
+  p->f ();                          // { dg-warning "-Wnonnull" }
+  p->g ();
+  p->h ();                          // { dg-warning "-Wnonnull" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wnonnull5.C b/gcc/testsuite/g++.dg/warn/Wnonnull5.C
index 78862d48993..959cf1840f8 100644
--- a/gcc/testsuite/g++.dg/warn/Wnonnull5.C
+++ b/gcc/testsuite/g++.dg/warn/Wnonnull5.C
@@ -35,21 +35,21 @@ struct S
 
 void warn_nullptr_this ()
 {
-  ((S*)nullptr)->f0 ("");        // { dg-warning "3:'this' pointer null" "pr86568" { xfail *-*-* } }
-                                 // { dg-warning "this' pointer null" "pr86568 second variant" { target *-*-* } .-1 }
+  ((S*)nullptr)->f0 ("");        // { dg-warning "3:'this' pointer is null" "pr86568" { xfail *-*-* } }
+                                 // { dg-warning "this' pointer is null" "pr86568 second variant" { target *-*-* } .-1 }
 }
 
 void warn_null_this_cst ()
 {
   S* const null = 0;
-  null->f1 ("");                  // { dg-warning "3:'this' pointer null" }
+  null->f1 ("");                  // { dg-warning "3:'this' pointer is null" }
 }
 
 void warn_null_this_var ()
 {
   S* null = 0;
-  null->f2 (&null);               // { dg-warning "3:'this' pointer null" "pr86568" { xfail *-*-* } }
-                                  // { dg-warning "'this' pointer null" "pr86568 second variant" { target *-*-* } .-1 }
+  null->f2 (&null);               // { dg-warning "3:'this' pointer is null" "pr86568" { xfail *-*-* } }
+                                  // { dg-warning "'this' pointer is null" "pr86568 second variant" { target *-*-* } .-1 }
 }
 
 void warn_nullptr (S s)
diff --git a/gcc/testsuite/g++.dg/warn/Wnonnull9.C b/gcc/testsuite/g++.dg/warn/Wnonnull9.C
new file mode 100644
index 00000000000..b6135c4a624
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wnonnull9.C
@@ -0,0 +1,117 @@
+/* PR c++/98646 - spurious -Wnonnull calling a member on the result
+   of static_cast
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+struct A { virtual ~A (); };
+struct B
+{
+  virtual ~B ();
+  B* bptr ();
+  B& bref ();
+};
+
+struct C: A, B { virtual ~C (); void g () const; };
+
+
+void c_cast_C_ptr (B *p)
+{
+  ((C*)p->bptr ())->g ();
+}
+
+void c_cast_const_C_ptr (B *p)
+{
+  ((const C*)p->bptr ())->g ();
+}
+
+void static_cast_C_ptr (B *p)
+{
+  static_cast<C*>(p->bptr ())->g ();
+}
+
+void static_cast_const_C_ptr (B *p)
+{
+  /* The static_cast can't fail so verify that no warning is issued
+     here, even though GCC emits a null check for its argument.  */
+  static_cast<const C*>(p->bptr ())->g ();    // { dg-bogus "\\\[-Wnonnull" }
+}
+
+void dynamic_cast_C_ptr (B *p)
+{
+  // The dynamic_cast might fail so a warning is justified.
+  dynamic_cast<C*>(p->bptr ())->g ();         // { dg-warning "\\\[-Wnonnull" }
+}
+
+void dynamic_cast_const_C_ptr (B *p)
+{
+  dynamic_cast<const C*>(p->bptr ())->g ();   // { dg-warning "\\\[-Wnonnull" }
+}
+
+
+void c_cast_C_ref (B *p)
+{
+  ((C&)p->bref ()).g ();
+}
+
+void c_cast_const_C_ref (B *p)
+{
+  ((const C&)p->bref ()).g ();
+}
+
+void static_cast_C_ref (B *p)
+{
+  static_cast<C&>(p->bref ()).g ();
+}
+
+void static_cast_const_C_ref (B *p)
+{
+  static_cast<const C&>(p->bref ()).g ();
+}
+
+void dynamic_cast_C_ref (B *p)
+{
+  /* The dynamic_cast fails by throwing an exception so verify that
+     no warning is issued.  */
+  dynamic_cast<C&>(p->bref ()).g ();
+}
+
+void dynamic_cast_const_C_ref (B *p)
+{
+  dynamic_cast<const C&>(p->bref ()).g ();
+}
+
+
+struct D: B, A { virtual ~D (); void g () const; };
+
+void c_cast_D_ptr (B *p)
+{
+  ((D*)p->bptr ())->g ();
+}
+
+void c_cast_const_D_ptr (B *p)
+{
+  ((const D*)p->bptr ())->g ();
+}
+
+void static_cast_D_ptr (B *p)
+{
+  static_cast<D*>(p->bptr ())->g ();
+}
+
+void static_cast_const_D_ptr (B *p)
+{
+  /* The static_cast can't fail so verify that no warning is issued
+     here, even though GCC emits a null check for its argument.  */
+  static_cast<const D*>(p->bptr ())->g ();    // { dg-bogus "\\\[-Wnonnull" }
+}
+
+void dynamic_cast_D_ptr (B *p)
+{
+  // The dynamic_cast might fail so a warning is justified.
+  dynamic_cast<D*>(p->bptr ())->g ();         // { dg-warning "\\\[-Wnonnull" }
+}
+
+void dynamic_cast_const_D_ptr (B *p)
+{
+  dynamic_cast<const D*>(p->bptr ())->g ();   // { dg-warning "\\\[-Wnonnull" }
+}
diff --git a/gcc/tree-ssa-ccp.c b/gcc/tree-ssa-ccp.c
index 965f092cccc..3bfd4a6265c 100644
--- a/gcc/tree-ssa-ccp.c
+++ b/gcc/tree-ssa-ccp.c
@@ -3564,7 +3564,7 @@ pass_post_ipa_warn::execute (function *fun)
 	      if (argno == 0)
 		{
 		  if (warning_at (loc, OPT_Wnonnull,
-				  "%G%qs pointer null", stmt, "this")
+				  "%G%qs pointer is null", stmt, "this")
 		      && fndecl)
 		    inform (DECL_SOURCE_LOCATION (fndecl),
 			    "in a call to non-static member function %qD",

Reply via email to