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