When nonnull was written on something that is neither a function nor a
pointer, the generic attribute handling reported

  'nonnull' attribute only applies to function types

With nonnull now also valid on a function parameter and (in C++) on a
data member, no short enumeration of valid targets is accurate, so
simply report the attribute as ignored on a variable, a typedef, or any
other non-function, non-pointer declaration.

Handle the type check inside handle_nonnull_attribute instead of relying
on the generic function_type_required gate.  The handler now performs the
pointer-to-function redirect itself (previously done by the generic code)
and, when passing through an intermediate type on the way to a function
type (ATTR_FLAG_FUNCTION_NEXT, as for a C++ pointer-to-member-function),
returns the attribute so it is retried on the real function type.

gcc/c-family/ChangeLog:

        * c-attribs.cc (c_common_gnu_attributes): Clear
        function_type_required for "nonnull".
        (handle_nonnull_attribute): Redirect a pointer-to-function to
        the pointed-to type, honor ATTR_FLAG_FUNCTION_NEXT, and ignore
        the attribute with a diagnostic on a non-function declaration.

gcc/testsuite/ChangeLog:

        * gcc.dg/Wnonnull-var-1.c: New test.
        * g++.dg/warn/Wnonnull-var-1.C: New test.

Signed-off-by: Andrei Rusanescu <[email protected]>
---
 gcc/c-family/c-attribs.cc                  | 28 ++++++++++++++++++++--
 gcc/testsuite/g++.dg/warn/Wnonnull-var-1.C | 18 ++++++++++++++
 gcc/testsuite/gcc.dg/Wnonnull-var-1.c      | 16 +++++++++++++
 3 files changed, 60 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/warn/Wnonnull-var-1.C
 create mode 100644 gcc/testsuite/gcc.dg/Wnonnull-var-1.c

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index ee9d1987ab2..06251350e02 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -511,7 +511,7 @@ const struct attribute_spec c_common_gnu_attributes[] =
                              handle_visibility_attribute, NULL },
   { "tls_model",             1, 1, true,  false, false, false,
                              handle_tls_model_attribute, NULL },
-  { "nonnull",                0, -1, false, true, true, false,
+  { "nonnull",               0, -1, false, true, false, false,
                              handle_nonnull_attribute, NULL },
   { "*nonnull parm",         0, -1, true, false, false, false,
                              handle_nonnull_parm_attribute, NULL },
@@ -5066,11 +5066,35 @@ handle_vector_mask_attribute (tree *node, tree name, 
tree,
 
 static tree
 handle_nonnull_attribute (tree *node, tree name,
-                         tree args, int ARG_UNUSED (flags),
+                         tree args, int flags,
                          bool *no_add_attrs)
 {
   tree type = *node;
 
+  /* For a pointer-to-function, nonnull belongs on the pointed-to function
+     type.  Set *node to it so the attribute ends up there, attribs.cc
+     rebuilds the pointer type after we return.  */
+  if (POINTER_TYPE_P (type) && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (type)))
+    {
+      *node = TREE_TYPE (type);
+      type = *node;
+    }
+  else if (!FUNC_OR_METHOD_TYPE_P (type))
+    {
+      /* ATTR_FLAG_FUNCTION_NEXT means we are passing through an intermediate
+        type, such as a C++ pointer-to-member-function, on the way to the
+        real function type.  Return the attribute so cp/decl2.cc can retry
+        it on the function type inside the PMF.  */
+      if (flags & (int) ATTR_FLAG_FUNCTION_NEXT)
+       {
+         *no_add_attrs = true;
+         return tree_cons (name, args, NULL_TREE);
+       }
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
   /* If no arguments are specified, all pointer arguments should be
      non-null.  Verify a full prototype is given so that the arguments
      will have the correct types when we actually check them later.
diff --git a/gcc/testsuite/g++.dg/warn/Wnonnull-var-1.C 
b/gcc/testsuite/g++.dg/warn/Wnonnull-var-1.C
new file mode 100644
index 00000000000..769e67090a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wnonnull-var-1.C
@@ -0,0 +1,18 @@
+// Test that "nonnull" on a variable or typedef is ignored.
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wattributes" }
+
+// Non-pointer variable.
+int gvar __attribute__((nonnull));             // { dg-warning ".nonnull. 
attribute ignored" }
+[[gnu::nonnull]] int gvar2;                    // { dg-warning ".nonnull. 
attribute ignored" }
+
+// Pointer variable - still ignored, nonnull is not a variable attribute.
+int *gvar_ptr __attribute__((nonnull));                // { dg-warning 
".nonnull. attribute ignored" }
+[[gnu::nonnull]] int *gvar_ptr2;               // { dg-warning ".nonnull. 
attribute ignored" }
+
+// Typedef of a non-function type.
+typedef int *nonnull_ptr_t __attribute__((nonnull));   // { dg-warning 
".nonnull. attribute ignored" }
+
+// These must still work: nonnull on a function type or pointer-to-function.
+void f (int *p, int *q) __attribute__((nonnull));
+typedef void (*fp_t)(int *) __attribute__((nonnull));
diff --git a/gcc/testsuite/gcc.dg/Wnonnull-var-1.c 
b/gcc/testsuite/gcc.dg/Wnonnull-var-1.c
new file mode 100644
index 00000000000..5805cb6c392
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wnonnull-var-1.c
@@ -0,0 +1,16 @@
+/* Test that "nonnull" on a variable or typedef is ignored.  */
+/* { dg-do compile } */
+/* { dg-options "-Wattributes" } */
+
+/* Non-pointer variable.  */
+int gvar __attribute__((nonnull));             /* { dg-warning ".nonnull. 
attribute ignored" } */
+
+/* Pointer variable - still ignored, nonnull is not a variable attribute.  */
+int *gvar_ptr __attribute__((nonnull));                /* { dg-warning 
".nonnull. attribute ignored" } */
+
+/* Typedef of a non-function type.  */
+typedef int *nonnull_ptr_t __attribute__((nonnull));   /* { dg-warning 
".nonnull. attribute ignored" } */
+
+/* These must still work: nonnull on a function type or pointer-to-function.  
*/
+void f (int *p, int *q) __attribute__((nonnull));
+typedef void (*fp_t)(int *) __attribute__((nonnull));
-- 
2.43.0

Reply via email to