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" }
+}

Reply via email to