As a GNU extension.  I'll propose it for standardization, but first we'd
need to make array parameters meaningful in the standard.

This extension will allow using _Countof() in more places, reducing the
chances of specifying an incorrect size accidentally.

We should eventually add -farray-parameters-are-const to make array
parameters be implicitly const-qualified (similar to -Wwrite-strings).

Link: <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121271>

gcc/ChangeLog:

        * doc/extend.texi (_Countof): Document this extension.

gcc/c/ChangeLog:

        * c-typeck.cc (c_array_parm_orig_type): New function.
        (c_expr_countof_expr): Add support for array parameters.

gcc/testsuite/ChangeLog:

        * gcc.dg/countof-compile.c (incomplete): Remove test.
        (param): Adapt test.
        * gcc.dg/countof-param-compile.c: New test.
        * gcc.dg/countof-param-pedantic.c: New test.
        * gcc.dg/countof-param.c: New test.

Co-authored-by: Martin Uecker <uec...@tugraz.at>
Signed-off-by: Alejandro Colomar <a...@kernel.org>
---
 gcc/c/c-typeck.cc                             | 50 ++++++++++--
 gcc/doc/extend.texi                           | 11 +++
 gcc/testsuite/gcc.dg/countof-compile.c        |  8 +-
 gcc/testsuite/gcc.dg/countof-param-compile.c  | 78 +++++++++++++++++++
 gcc/testsuite/gcc.dg/countof-param-pedantic.c | 11 +++
 gcc/testsuite/gcc.dg/countof-param.c          | 25 ++++++
 6 files changed, 168 insertions(+), 15 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/countof-param-compile.c
 create mode 100644 gcc/testsuite/gcc.dg/countof-param-pedantic.c
 create mode 100644 gcc/testsuite/gcc.dg/countof-param.c

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 7a95f72c5f2d..f3281a46c250 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -3817,6 +3817,26 @@ is_top_array_vla (tree type)
   return var;
 }
 
+
+tree
+c_array_parm_orig_type (tree expr)
+{
+  gcc_assert (POINTER_TYPE_P (TREE_TYPE (expr)));
+
+  if (TREE_CODE (expr) == PARM_DECL
+      && C_ARRAY_PARAMETER (expr))
+    {
+      if (tree attr = lookup_attribute ("arg spec", DECL_ATTRIBUTES (expr)))
+       {
+         tree arg = TREE_CHAIN (TREE_VALUE (attr));
+         if (arg)
+           return TREE_VALUE (arg);
+       }
+    }
+
+  return NULL_TREE;
+}
+
 /* Return the result of countof applied to EXPR.  */
 
 struct c_expr
@@ -3834,16 +3854,30 @@ c_expr_countof_expr (location_t loc, struct c_expr expr)
   else
     {
       bool expr_const_operands = true;
-
-      tree folded_expr = c_fully_fold (expr.value, require_constant_value,
-                                      &expr_const_operands);
-      ret.value = c_countof_type (loc, TREE_TYPE (folded_expr));
-      c_last_sizeof_arg = expr.value;
-      c_last_sizeof_loc = loc;
       ret.original_code = COUNTOF_EXPR;
       ret.original_type = NULL;
       ret.m_decimal = 0;
-      if (is_top_array_vla (TREE_TYPE (folded_expr)))
+
+      tree folded_expr = c_fully_fold (expr.value, require_constant_value,
+                                      &expr_const_operands);
+
+      tree type = TREE_TYPE (folded_expr);
+      if (POINTER_TYPE_P (type))
+       {
+         if (tree rv = c_array_parm_orig_type (expr.value))
+           {
+             pedwarn (input_location, OPT_Wpedantic,
+                      "ISO C does not support array parameters in %qs",
+                      "_Countof");
+             type = rv;
+           }
+       }
+
+      ret.value = c_countof_type (loc, type);
+      c_last_sizeof_arg = expr.value;
+      c_last_sizeof_loc = loc;
+
+      if (is_top_array_vla (type))
        {
          /* countof is evaluated when given a vla.  */
          ret.value = build2 (C_MAYBE_CONST_EXPR, TREE_TYPE (ret.value),
@@ -3851,7 +3885,7 @@ c_expr_countof_expr (location_t loc, struct c_expr expr)
          C_MAYBE_CONST_EXPR_NON_CONST (ret.value) = !expr_const_operands;
          SET_EXPR_LOCATION (ret.value, loc);
        }
-      pop_maybe_used (is_top_array_vla (TREE_TYPE (folded_expr)));
+      pop_maybe_used (is_top_array_vla (type));
     }
   return ret;
 }
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 224d6197d639..550caf78a09c 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -10763,6 +10763,17 @@ _Countof (int [7][n++]);  // integer constant 
expression
 _Countof (int [n++][7]);  // run-time value; n++ is evaluated
 @end smallexample
 
+It also works with array parameters.
+For example:
+
+@smallexample
+void
+f (int n, int a[n])
+@{
+  assert(n == _Countof(a));
+@}
+@end smallexample
+
 @node Inline
 @section An Inline Function is As Fast As a Macro
 @cindex inline functions
diff --git a/gcc/testsuite/gcc.dg/countof-compile.c 
b/gcc/testsuite/gcc.dg/countof-compile.c
index afd5659618b4..69b67a0b3afa 100644
--- a/gcc/testsuite/gcc.dg/countof-compile.c
+++ b/gcc/testsuite/gcc.dg/countof-compile.c
@@ -21,10 +21,6 @@ void
 incomplete (int p[])
 {
   _Countof (x);  /* { dg-error "incomplete" } */
-
-  /* We want to support array parameters in the future,
-     which should change this from "invalid" to "incomplete".  */
-  _Countof (p);  /* { dg-error "invalid" } */
 }
 
 void
@@ -41,9 +37,7 @@ fam (void)
 void
 param (int n, int p[n])
 {
-  /* We want to support array parameters in the future,
-     which would make this work.  */
-  _Countof (p);  /* { dg-error "invalid" } */
+  _Countof (p);  /* { dg-error "ISO C does not support array parameters" } */
 }
 
 void fix_fix (int i, char (*a)[3][5], int (*x)[_Countof (*a)],
diff --git a/gcc/testsuite/gcc.dg/countof-param-compile.c 
b/gcc/testsuite/gcc.dg/countof-param-compile.c
new file mode 100644
index 000000000000..923626aa03c0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/countof-param-compile.c
@@ -0,0 +1,78 @@
+/* { dg-do compile } */
+/* { dg-options "-Wvla-parameter -Warray-parameter=2" } */
+
+#define NULL ((void *) 0)
+
+#define must_be(e)                                                    \
+(                                                                     \
+  0 * (int) sizeof                                                    \
+    (                                                                 \
+      struct must_be                                                  \
+        {                                                             \
+          _Static_assert (e, "");                                     \
+          int ISO_C_forbids_a_struct_with_no_members;                 \
+        }                                                             \
+    )                                                                 \
+)
+
+int f1 (int  , int a[2]) { return _Countof (a); }
+int f2 (int n, int a[n]) { return _Countof (a); }
+int f3 (int  , int a[*]) { return _Countof (a); }  /* { dg-error "not allowed 
in other than function prototype scope" } */
+int f4 (int  , int a[ ]) { return _Countof (a); }  /* { dg-error "incomplete" 
} */
+int f5 (int  , int *a)   { return _Countof (a); }  /* { dg-error "invalid" } */
+
+int g1 (int  , int a[2], int b[_Countof (a)]);
+int g2 (int n, int a[n], int b[_Countof (a)]);
+int g3 (int  , int a[*], int b[_Countof (a)]);
+
+int g1 (int  , int a[2], int b[_Countof (a)]) { return _Countof (b); }
+int g2 (int n, int a[n], int b[_Countof (a)]) { return _Countof (b); }
+int g3 (int  , int a[*], int b[_Countof (a)]) { return _Countof (b); }  /* { 
dg-error "not allowed in other than function prototype scope" } */
+
+int h1 (int  , int a[2], int b[7 + must_be (_Countof (a) == 2)]);
+int h2 (int n, int a[n], int b[7 + must_be (_Countof (a) == 2)]);  /* { 
dg-error "expression in static assertion is not constant" } */
+int h3 (int  , int a[*], int b[7 + must_be (_Countof (a) == 2)]);  /* { 
dg-error "expression in static assertion is not constant" } */
+
+void
+decay_obvious (int a[2])
+{
+  _Countof (typeof (a));  /* { dg-error "invalid" } */
+  _Countof (a + 1);  /* { dg-error "invalid" } */
+  _Countof (42 ? NULL : a);  /* { dg-error "invalid" } */
+  _Countof (a + 0);  /* { dg-error "invalid" } */
+  _Countof (&*a);  /* { dg-error "invalid" } */
+  _Countof (42, a);  /* { dg-error "invalid" } */
+  _Countof (42 ? a : a);  /* { dg-error "invalid" } */
+  _Countof (42 ? a : NULL);  /* { dg-error "invalid" } */
+
+  int *p = a;
+
+  _Countof (p);  /* { dg-error "invalid" } */
+
+  typeof(a) a2;
+
+  _Countof (a2);  /* { dg-error "invalid" } */
+}
+
+void
+no_decay_obvious (int a[2])
+{
+  _Static_assert (2 == _Countof (a));
+  _Static_assert (2 == _Countof (*&a));
+  _Static_assert (2 == _Countof (*(&a + 0)));
+}
+
+/* It would not decay if it were an array, but being a parameter, it decays.  
*/
+void
+decay_surprising (int a[2])
+{
+  _Countof (*(&a + 1));  /* { dg-error "invalid" } */
+}
+
+/* We should warn: <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121271>.  */
+void
+non_const (int a[2])
+{
+  a++;
+  _Countof (a);
+}
diff --git a/gcc/testsuite/gcc.dg/countof-param-pedantic.c 
b/gcc/testsuite/gcc.dg/countof-param-pedantic.c
new file mode 100644
index 000000000000..7d52f27a72bb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/countof-param-pedantic.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -pedantic" } */
+
+#include <stdcountof.h>
+
+int
+f (int a[1])
+{
+  countof(a);  /* { dg-warning "ISO C does not support array parameters" } */
+  _Countof(a);  /* { dg-warning "ISO C does not support array parameters" } */
+}
diff --git a/gcc/testsuite/gcc.dg/countof-param.c 
b/gcc/testsuite/gcc.dg/countof-param.c
new file mode 100644
index 000000000000..e03f8b9389db
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/countof-param.c
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-options "-Wvla-parameter -Warray-parameter=2" } */
+
+#define assert(e)  ((e) ? (void) 0 : __builtin_abort ())
+
+int f1 (int  , int a[3]) { return _Countof(a); }
+int f2 (int  , int a[2]) { return _Countof(a); }
+
+int f1 (int  , int a[2]);  /* { dg-warning "with mismatched bound" } */
+int f2 (int n, int a[n]);  /* { dg-warning "declared as a variable length 
array" } */
+
+int g1 (int  , int a[3]);
+int g2 (int  , int a[2]);
+
+int g1 (int  , int a[2]) { return _Countof(a); }  // { dg-warning "with 
mismatched bound" } */
+int g2 (int n, int a[n]) { return _Countof(a); }  // { dg-warning "declared as 
a variable length array" } */
+
+int
+main (void)
+{
+  assert (3  == f1 (42, (int [42]){}));
+  assert (2  == f2 (42, (int [42]){}));
+  assert (2  == g1 (42, (int [42]){}));
+  assert (42 == g2 (42, (int [42]){}));
+}
-- 
2.50.1

Reply via email to