The C front end ordinarily merges function type attributes upon
the redeclaration of a function but it doesn't do that for those
at local scope, unless the declaration refers to a built-in.

Because the new -Warray-parameter warning relies on the internal
access attribute on the type of the function added by the C front
end for parameters declared using the array notation, it triggers
when it sees a redeclaration of a function in a local scope even
when both declarations use the same array form, issuing a false
positive.

The same problem affects other similar redeclarations involving
attributes, such as unused_result, causing false negatives there.
(Clang and ICC behave as I expect.)

The attached patch lets the front end propagate the type attributes
for all redeclarations, resolving this class of problems for all
affected attributes.

There's another similar piece of code in pushdecl() that I didn't
touch, although  I couldn't come up with a test case showing it's
necessary.  Both hunks go back ages so I wonder if they might have
been obviated by other improvements.

Martin
PR c/99420 - bogus -Warray-parameter on a function redeclaration in function scope
PR c/99972 - missing -Wunused-result on a call to a locally redeclared warn_unused_result function

gcc/c/ChangeLog:

	PR c/99420
	PR c/99972
	* c-decl.c (pushdecl): Always propagate type attribute.

gcc/testsuite/ChangeLog:

	PR c/99420
	PR c/99972
	* gcc.dg/Warray-parameter-9.c: New test.
	* gcc.dg/Wnonnull-6.c: New test.
	* gcc.dg/Wreturn-type3.c: New test.
	* gcc.dg/Wunused-result.c: New test.
	* gcc.dg/attr-noreturn.c: New test.
	* gcc.dg/attr-returns-nonnull.c: New test.

diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 3b2241bfd97..677bfd73a50 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -3263,11 +3263,10 @@ pushdecl (tree x)
 	  else
 	    thistype = type;
 	  b->u.type = TREE_TYPE (b->decl);
-	  if (TREE_CODE (b->decl) == FUNCTION_DECL
-	      && fndecl_built_in_p (b->decl))
-	    thistype
-	      = build_type_attribute_variant (thistype,
-					      TYPE_ATTRIBUTES (b->u.type));
+	  /* Propagate the type attributes to the decl.  */
+	  thistype
+	    = build_type_attribute_variant (thistype,
+					    TYPE_ATTRIBUTES (b->u.type));
 	  TREE_TYPE (b->decl) = thistype;
 	  bind (name, b->decl, scope, /*invisible=*/false, /*nested=*/true,
 		locus);
diff --git a/gcc/testsuite/gcc.dg/Warray-parameter-9.c b/gcc/testsuite/gcc.dg/Warray-parameter-9.c
new file mode 100644
index 00000000000..b5d3d963c88
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-parameter-9.c
@@ -0,0 +1,54 @@
+/* PR c/99420 - bogus -Warray-parameter on a function redeclaration
+   in function scope
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+extern int a1[1], a2[2], a3[3], a4[4];
+
+void fa1 (int [1]);     // { dg-message "previously declared as 'int\\\[1]'" }
+void fa1 (int [1]);
+
+
+void nested_decl (void)
+{
+  void fa2 (int [2]);
+
+  fa2 (a1);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" }
+  fa2 (a2);
+  fa2 (a3);
+
+  void fa3 (int [3]);
+
+  fa3 (a2);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" }
+  fa3 (a3);
+}
+
+
+void nested_redecl (void)
+{
+  void fa1 (int [2]);   // { dg-warning "argument 1 of type 'int\\\[2]' with mismatched bound" }
+
+  fa1 (a1 + 1);         // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" }
+  fa1 (a1);
+
+  void fa2 (int [2]);   // { dg-bogus "\\\[-Warray-parameter" }
+
+  fa2 (a1);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" }
+  fa2 (a2);
+  fa2 (a3);
+
+  void fa3 (int [3]);   // { dg-bogus "\\\[-Warray-parameter" }
+
+  fa3 (a2);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" }
+  fa3 (a3);
+
+  void fa4 (int [4]);
+}
+
+void fa4 (int [5]);     // { dg-warning "\\\[-Warray-parameter" }
+
+void call_fa4 (void)
+{
+  fa4 (a4);
+  fa4 (a3);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wnonnull-6.c b/gcc/testsuite/gcc.dg/Wnonnull-6.c
new file mode 100644
index 00000000000..48f09da996f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wnonnull-6.c
@@ -0,0 +1,93 @@
+/* Verify that attribute nonnull on global and local function declarations
+   or those to pointers to functions is merged.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+void fnonnull_local_local (void)
+{
+  extern __attribute__ ((nonnull)) void fnonnull1 (void*);
+
+  fnonnull1 (0);    // { dg-warning "\\\[-Wnonnull" }
+}
+
+void gnonnull_local_local (void)
+{
+  extern void fnonnull1 (void*);
+
+  fnonnull1 (0);    // { dg-warning "\\\[-Wnonnull" }
+}
+
+
+void fnonnull_local_global (void)
+{
+  extern __attribute__ ((nonnull)) void fnonnull2 (void*);
+
+  fnonnull2 (0);    // { dg-warning "\\\[-Wnonnull" }
+}
+
+extern void fnonnull2 (void*);
+
+void gnonnull_local_global (void)
+{
+  fnonnull2 (0);    // { dg-warning "\\\[-Wnonnull" }
+}
+
+
+extern __attribute__ ((nonnull)) void fnonnull3 (void*);
+
+void fnonnull_global_local (void)
+{
+  fnonnull3 (0);    // { dg-warning "\\\[-Wnonnull" }
+}
+
+void gnonnull_global_local (void)
+{
+  extern void fnonnull3 (void*);
+
+  fnonnull3 (0);    // { dg-warning "\\\[-Wnonnull" }
+}
+
+
+void pfnonnull_local_local (void)
+{
+  extern __attribute__ ((nonnull)) void (*pfnonnull1) (void*);
+
+  pfnonnull1 (0);   // { dg-warning "\\\[-Wnonnull" }
+}
+
+void gpnonnull_local_local (void)
+{
+  extern void (*pfnonnull1) (void*);
+
+  pfnonnull1 (0);   // { dg-warning "\\\[-Wnonnull" "pr?????" { xfail *-*-* } }
+}
+
+
+void pfnonnull_local_global (void)
+{
+  extern __attribute__ ((nonnull)) void (*pfnonnull2) (void*);
+
+  pfnonnull2 (0);   // { dg-warning "\\\[-Wnonnull" }
+}
+
+extern void (*pfnonnull2) (void*);
+
+void gpnonnull_local_global (void)
+{
+  pfnonnull2 (0);   // { dg-warning "\\\[-Wnonnull" "pr?????" { xfail *-*-* } }
+}
+
+
+extern __attribute__ ((nonnull)) void (*pfnonnull3) (void*);
+
+void pfnonnull_global_local (void)
+{
+  pfnonnull3 (0);   // { dg-warning "\\\[-Wnonnull" }
+}
+
+void gpnonnull_global_local (void)
+{
+  extern void (*pfnonnull3) (void*);
+
+  pfnonnull3 (0);   // { dg-warning "\\\[-Wnonnull" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wreturn-type3.c b/gcc/testsuite/gcc.dg/Wreturn-type3.c
new file mode 100644
index 00000000000..93596b399f1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wreturn-type3.c
@@ -0,0 +1,54 @@
+/* Verify that attribute noreturn on global and local function declarations
+   is merged.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+int fnr_local_local (void)
+{
+  __attribute__ ((noreturn)) void fnr1 (void);
+
+  fnr1 ();
+  // no return, no warning (good)
+}
+
+int gnr_local_local (void)
+{
+  void fnr1 (void);
+
+  fnr1 ();
+  // no return, no warning (good)
+}
+
+
+int fnr_local_global (void)
+{
+  __attribute__ ((noreturn)) void fnr2 (void);
+
+  fnr2 ();
+  // no return, no warning (good)
+}
+
+void fnr2 (void);
+
+int gnr_local_global (void)
+{
+  fnr2 ();
+  // no return, no warning (good)
+}
+
+
+__attribute__ ((noreturn)) void fnr3 (void);
+
+int fnr_global_local (void)
+{
+  fnr3 ();
+  // no return, no warning (good)
+}
+
+int gnr_global_local (void)
+{
+  void fnr3 (void);
+
+  fnr3 ();
+  // no return, no warning (good)
+}
diff --git a/gcc/testsuite/gcc.dg/Wunused-result.c b/gcc/testsuite/gcc.dg/Wunused-result.c
new file mode 100644
index 00000000000..c0bb9ae35e2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wunused-result.c
@@ -0,0 +1,50 @@
+/* PR c/99972 - missing -Wunused-result on a call to a locally redeclared
+   warn_unused_result function
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+void gwur_local_local (void)
+{
+  __attribute__ ((warn_unused_result)) int fwur1 (void);
+
+  fwur1 ();         // { dg-warning "\\\[-Wunused-result" }
+}
+
+void hwur_local_local (void)
+{
+  /* Verify the attribute from the declaration above is copied/merged
+     into the declaration below.  */
+  int fwur1 (void);
+
+  fwur1 ();          // { dg-warning "\\\[-Wunused-result" }
+}
+
+
+void gwur_local_global (void)
+{
+  __attribute__ ((warn_unused_result)) int fwur2 (void);
+
+  fwur2 ();         // { dg-warning "\\\[-Wunused-result" }
+}
+
+int fwur2 (void);
+
+void hwur_local_global (void)
+{
+  fwur2 ();         // { dg-warning "\\\[-Wunused-result" }
+}
+
+
+__attribute__ ((warn_unused_result)) int fwur3 (void);
+
+void gwur_global_local (void)
+{
+  fwur3 ();         // { dg-warning "\\\[-Wunused-result" }
+}
+
+void hwur_global_local (void)
+{
+  int fwur3 (void);
+
+  fwur3 ();         // { dg-warning "\\\[-Wunused-result" }
+}
diff --git a/gcc/testsuite/gcc.dg/attr-noreturn.c b/gcc/testsuite/gcc.dg/attr-noreturn.c
new file mode 100644
index 00000000000..8d58f6ece9b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-noreturn.c
@@ -0,0 +1,64 @@
+/* Verify that attribute noreturn on global and local function declarations
+   is merged.
+   { dg-do compile }
+   { dg-options "-Wall -fdump-tree-optimized" } */
+
+void foo (void);
+
+int fnr_local_local (void)
+{
+  __attribute__ ((noreturn)) void fnr1 (void);
+
+  fnr1 ();
+
+  foo ();
+}
+
+int gnr_local_local (void)
+{
+  void fnr1 (void);
+
+  fnr1 ();
+
+  foo ();
+}
+
+
+int fnr_local_global (void)
+{
+  __attribute__ ((noreturn)) void fnr2 (void);
+
+  fnr2 ();
+
+  foo ();
+}
+
+void fnr2 (void);
+
+int gnr_local_global (void)
+{
+  fnr2 ();
+
+  foo ();
+}
+
+
+__attribute__ ((noreturn)) void fnr3 (void);
+
+int fnr_global_local (void)
+{
+  fnr3 ();
+
+  foo ();
+}
+
+int gnr_global_local (void)
+{
+  void fnr3 (void);
+
+  fnr3 ();
+
+  foo ();
+}
+
+/* { dg-final { scan-tree-dump-not "foo" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/attr-returns-nonnull.c b/gcc/testsuite/gcc.dg/attr-returns-nonnull.c
new file mode 100644
index 00000000000..22ee30ac5df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-returns-nonnull.c
@@ -0,0 +1,58 @@
+/* Verify that attribute returns_nonnull on global and local function
+   declarations is merged.
+   { dg-do compile }
+   { dg-options "-Wall -fdump-tree-optimized" } */
+
+void foo (void);
+
+
+void frnn_local_local (void)
+{
+  __attribute__ ((returns_nonnull)) void* frnn1 (void);
+
+  if (!frnn1 ())
+    foo ();
+}
+
+void gnr_local_local (void)
+{
+  void* frnn1 (void);
+
+  if (!frnn1 ())
+    foo ();
+}
+
+void frnn_local_global (void)
+{
+  __attribute__ ((returns_nonnull)) void* frnn2 (void);
+
+  if (!frnn2 ())
+    foo ();
+}
+
+void* frnn2 (void);
+
+void gnr_local_global (void)
+{
+  if (!frnn2 ())
+    foo ();
+}
+
+__attribute__ ((returns_nonnull)) void* frnn3 (void);
+
+void frnn_global_local (void)
+{
+  if (!frnn3 ())
+    foo ();
+}
+
+void gnr_global_local (void)
+{
+  void* frnn3 (void);
+
+  if (!frnn3 ())
+    foo ();
+}
+
+
+/* { dg-final { scan-tree-dump-not "foo" "optimized" } } */

Reply via email to