The attached patch avoids null pointer checking for the first
argument to calls to the member operator() of lambda objects
emitted by the C++ front end.  This avoids both the spurious
-Wnonnull warnings for such calls as well as the ICE reported
in the bug.

In addition, the patch also avoids function argument checking
for any calls when the tf_warning bit isn't set.  This isn't
strictly necessary to avoid the ICE but it seems like a good
precaution against something similar occurring in other checks
in the future(*).

Finally, while testing the fix I noticed that the common code
doesn't recognize nullptr as a poiner when processing templates.
I've extended the handling to let it handle it as well.

Martin

[*] It seems to me that a more robust solution to prevent
the diagnostic subsystem from being re-entered as a result
of callbacks into the front end would be to have the pretty
printer disable all warnings prior to the bcallbacks and
re-enable them afterwards.  That would require an efficient
and reliable way of controlling all warnings (as well as
querying their state), which I think would be a useful
feature to have in any case.  For one thing, it would make
handling warnings and responding to #pragma GCC diagnostics
more robust.
Exclude calls to variadic lambda stubs from -Wnonnull checking (PR c++/95984).

Resolves:
PR c++/95984 - Internal compiler error: Error reporting routines re-entered in -Wnonnull on a variadic lamnda
PR c++/96021 - missing -Wnonnull passing nullptr to a nonnull variadic lambda

gcc/c-family/ChangeLog:

	PR c++/95984
	* c-common.c (check_function_nonnull):
	(check_nonnull_arg):

gcc/cp/ChangeLog:

	PR c++/95984
	* call.c (build_over_call):

gcc/testsuite/ChangeLog:

	PR c++/95984
	* g++.dg/warn/Wnonnull6.C: New test.

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index aae1ddb6b89..695e6d27262 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5308,12 +5308,29 @@ check_function_nonnull (nonnull_arg_ctx &ctx, int nargs, tree *argarray)
   int firstarg = 0;
   if (TREE_CODE (ctx.fntype) == METHOD_TYPE)
     {
+      bool closure = false;
+      if (ctx.fndecl)
+	{
+	  /* For certain lambda expressions the C++ front end emits calls
+	     that pass a null this pointer as an argument named __closure
+	     to the member operator() of empty function.  Detect those
+	     and avoid checking them, but proceed to check the remaining
+	     arguments.  */
+	  tree arg0 = DECL_ARGUMENTS (ctx.fndecl);
+	  if (tree arg0name = DECL_NAME (arg0))
+	    {
+	      const char *arg0str = IDENTIFIER_POINTER (arg0name);
+	      closure = !strcmp (arg0str, "__closure");
+	    }
+	}
+
       /* In calls to C++ non-static member functions check the this
 	 pointer regardless of whether the function is declared with
 	 attribute nonnull.  */
       firstarg = 1;
-      check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[0],
-					firstarg);
+      if (!closure)
+	check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[0],
+					  firstarg);
     }
 
   tree attrs = lookup_attribute ("nonnull", TYPE_ATTRIBUTES (ctx.fntype));
@@ -5503,7 +5520,9 @@ check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num)
      happen if the "nonnull" attribute was given without an operand
      list (which means to check every pointer argument).  */
 
-  if (TREE_CODE (TREE_TYPE (param)) != POINTER_TYPE)
+  tree paramtype = TREE_TYPE (param);
+  if (TREE_CODE (paramtype) != POINTER_TYPE
+      && TREE_CODE (paramtype) != NULLPTR_TYPE)
     return;
 
   /* Diagnose the simple cases of null arguments.  */
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index d8923be1d68..5341a572980 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8842,15 +8842,16 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   gcc_assert (j <= nargs);
   nargs = j;
 
-  /* Avoid to do argument-transformation, if warnings for format, and for
-     nonnull are disabled.  Just in case that at least one of them is active
+  /* Avoid performing argument transformation if warnings are disabled.
+     When tf_warning is set and at least one of the warnings is active
      the check_function_arguments function might warn about something.  */
 
   bool warned_p = false;
-  if (warn_nonnull
-      || warn_format
-      || warn_suggest_attribute_format
-      || warn_restrict)
+  if ((complain & tf_warning)
+      && (warn_nonnull
+	  || warn_format
+	  || warn_suggest_attribute_format
+	  || warn_restrict))
     {
       tree *fargs = (!nargs ? argarray
 			    : (tree *) alloca (nargs * sizeof (tree)));
diff --git a/gcc/testsuite/g++.dg/warn/Wnonnull6.C b/gcc/testsuite/g++.dg/warn/Wnonnull6.C
new file mode 100644
index 00000000000..dae6dd2d912
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wnonnull6.C
@@ -0,0 +1,37 @@
+/* PR c++/95984 - Internal compiler error: Error reporting routines re-entered
+   in -Wnonnull on a variadic lamnda
+   PR c++/96021 - missing -Wnonnull passing nullptr to a nonnull variadic lambda
+   { dg-do compile { target c++11 } }
+   { dg-options "-Wall" } */
+
+typedef int F (int);
+
+F* pr95984 ()
+{
+  // This also triggered the ICE.
+  return [](auto...) { return 0; };     // { dg-bogus "\\\[-Wnonnull" }
+}
+
+
+__attribute__ ((nonnull)) void f (int, ...);
+void ff ()
+{
+  f (1, nullptr);                       // { dg-warning "\\\[-Wnonnull" }
+}
+
+template <class T> void g (T t)
+{
+  t (1, nullptr);                       // { dg-warning "\\\[-Wnonnull" }
+}
+
+void gg (void)
+{
+  g ([](int, auto...) __attribute__ ((nonnull)) { });
+}
+
+template <class T> __attribute__ ((nonnull)) void h (T);
+
+void hh ()
+{
+  h (nullptr);                          //  { dg-warning "\\\[-Wnonnull" }
+}

Reply via email to