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