From: Martin Uecker <uec...@tugraz.at>

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.

Due to how the array information is stored, it sometimes decays less
than real arrays, and sometimes decays more.  This might be a bit
surprising, but only results in benign differences, that don't seem
error-prone or dangerous.

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  | 76 +++++++++++++++++++
 gcc/testsuite/gcc.dg/countof-param-pedantic.c | 11 +++
 gcc/testsuite/gcc.dg/countof-param.c          | 25 ++++++
 6 files changed, 166 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..0080da494a9b 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 (folded_expr))
+           {
+             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..9d06cc7c32b1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/countof-param-compile.c
@@ -0,0 +1,76 @@
+/* { 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" } */
+
+  int *p = a;
+
+  _Countof (p);  /* { dg-error "invalid" } */
+
+  typeof(a) a2;
+
+  _Countof (a2);  /* { dg-error "invalid" } */
+}
+
+/* These would decay if they were arrays, but being a parameter, it's works.  
*/
+void
+no_decay_surprising (int a[2])
+{
+  _Static_assert (2 == _Countof (a + 0));
+  _Static_assert (2 == _Countof (&*a));
+  _Static_assert (2 == _Countof (42, a));
+  _Static_assert (2 == _Countof (42 ? a : a));
+  _Static_assert (2 == _Countof (42 ? a : NULL));
+}
+
+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" } */
+}
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