Hi! Here's a revision of this patch, rebased after all the changes in master in these 6 months. This time, the name is _Countof, as the C Committee has finally settled on that name. It also includes the lowercase macro and header in a separate patch, as specified by ISO C.
Here's the change list compared to v18: - Rename __countof__ => _Countof - Make the tests more robust. - Add countof and <stdcountof.h> (patch 3/3) - Rebase after 6 months of changes in master. - Add links in commit message to the new changes to this operator in ISO C. - gcc/c-family/c-common.cc: Use D_CONLY. The range-diff is at the bottom. The hashes don't match the range-diff I presented in v18 because I lost the old commits, so I had to reapply them from the v18 emails. I have done a `make bootstrap`, and tested manually the tests, but I haven't run `make check`. I'll try to do that in the following days, since I'll be traveling. I haven't yet implemented the pedantic diagnostic for old C versions. This is in my TODO list. I think that's the only thing I'm missing. Have a lovely night! Alex Alejandro Colomar (3): contrib/: Add support for Cc: and Link: tags c: Add _Countof operator c: Add <stdcountof.h> contrib/gcc-changelog/git_commit.py | 5 +- gcc/Makefile.in | 1 + gcc/c-family/c-common.cc | 26 +++++ gcc/c-family/c-common.def | 3 + gcc/c-family/c-common.h | 2 + gcc/c/c-decl.cc | 22 +++- gcc/c/c-parser.cc | 59 +++++++--- gcc/c/c-tree.h | 4 + gcc/c/c-typeck.cc | 115 +++++++++++++++++- gcc/doc/extend.texi | 30 +++++ gcc/ginclude/stdcountof.h | 31 +++++ gcc/testsuite/gcc.dg/countof-compile.c | 130 +++++++++++++++++++++ gcc/testsuite/gcc.dg/countof-vla.c | 51 ++++++++ gcc/testsuite/gcc.dg/countof.c | 154 +++++++++++++++++++++++++ 14 files changed, 608 insertions(+), 25 deletions(-) create mode 100644 gcc/ginclude/stdcountof.h create mode 100644 gcc/testsuite/gcc.dg/countof-compile.c create mode 100644 gcc/testsuite/gcc.dg/countof-vla.c create mode 100644 gcc/testsuite/gcc.dg/countof.c Range-diff against v18: 1: f7787bae38cb = 1: 796c82b0cba1 contrib/: Add support for Cc: and Link: tags 2: f1a3df94b52c ! 2: ae4691c8b451 c: Add __countof__ operator @@ Metadata Author: Alejandro Colomar <a...@kernel.org> ## Commit message ## - c: Add __countof__ operator + c: Add _Countof operator This operator is similar to sizeof but can only be applied to an array, and returns its number of elements. @@ Commit message gcc/ChangeLog: - * doc/extend.texi: Document __countof__ operator. + * doc/extend.texi: Document _Countof operator. gcc/c-family/ChangeLog: * c-common.h * c-common.def - * c-common.cc (c_countof_type): Add __countof__ operator. + * c-common.cc (c_countof_type): Add _Countof operator. gcc/c/ChangeLog: @@ Commit message (pop_maybe_used) (is_top_array_vla) (c_expr_countof_expr, c_expr_countof_type): - Add __countof__ operator. + Add _Countof operator. gcc/testsuite/ChangeLog: * gcc.dg/countof-compile.c * gcc.dg/countof-vla.c - * gcc.dg/countof.c: Add tests for __countof__ operator. + * gcc.dg/countof.c: Add tests for _Countof operator. + Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3550.pdf> Link: <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117025> Link: <https://inbox.sourceware.org/gcc/m8s4oqy--...@tutanota.com/T/> Link: <https://inbox.sourceware.org/gcc-patches/20240728141547.302478-1-...@kernel.org/T/#t> Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3313.pdf> Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3325.pdf> Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3369.pdf> + Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3469.htm> Link: <https://github.com/llvm/llvm-project/issues/102836> + Link: <https://thephd.dev/the-big-array-size-survey-for-c> + Link: <https://thephd.dev/the-big-array-size-survey-for-c-results> Link: <https://stackoverflow.com/questions/37538/#57537491> Suggested-by: Xavier Del Campo Romero <xavi....@tutanota.com> Co-authored-by: Martin Uecker <uec...@tugraz.at> @@ Commit message ## gcc/c-family/c-common.cc ## @@ gcc/c-family/c-common.cc: const struct c_common_resword c_common_reswords[] = - { "__inline", RID_INLINE, 0 }, - { "__inline__", RID_INLINE, 0 }, - { "__label__", RID_LABEL, 0 }, -+ { "__countof__", RID_COUNTOF, 0 }, - { "__null", RID_NULL, 0 }, - { "__real", RID_REALPART, 0 }, - { "__real__", RID_REALPART, 0 }, + { + { "_Alignas", RID_ALIGNAS, D_CONLY }, + { "_Alignof", RID_ALIGNOF, D_CONLY }, ++ { "_Countof", RID_COUNTOF, D_CONLY }, + { "_Atomic", RID_ATOMIC, D_CONLY }, + { "_BitInt", RID_BITINT, D_CONLY }, + { "_Bool", RID_BOOL, D_CONLY }, @@ gcc/c-family/c-common.cc: c_alignof_expr (location_t loc, tree expr) return fold_convert_loc (loc, size_type_node, t); } + -+/* Implement the countof keyword: ++/* Implement the _Countof keyword: + Return the number of elements of an array. */ + +tree @@ gcc/c-family/c-common.cc: c_alignof_expr (location_t loc, tree expr) + type_code = TREE_CODE (type); + if (type_code != ARRAY_TYPE) + { -+ error_at (loc, "invalid application of %<__countof__%> to type %qT", type); ++ error_at (loc, "invalid application of %<_Countof%> to type %qT", type); + return error_mark_node; + } + if (!COMPLETE_TYPE_P (type)) + { + error_at (loc, -+ "invalid application of %<__countof__%> to incomplete type %qT", ++ "invalid application of %<_Countof%> to incomplete type %qT", + type); + return error_mark_node; + } @@ gcc/c-family/c-common.h: enum rid /* C extensions */ RID_ASM, RID_TYPEOF, RID_TYPEOF_UNQUAL, RID_ALIGNOF, RID_ATTRIBUTE, + RID_COUNTOF, - RID_VA_ARG, + RID_C23_VA_START, RID_VA_ARG, RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL, RID_CHOOSE_EXPR, RID_TYPES_COMPATIBLE_P, RID_BUILTIN_COMPLEX, RID_BUILTIN_SHUFFLE, @@ gcc/c-family/c-common.h: extern tree c_common_truthvalue_conversion (location_t, tree); @@ gcc/c/c-decl.cc: start_struct (location_t loc, enum tree_code code, tree name, + ? "typeof" + : (in_alignof + ? "alignof" -+ : "__countof__")))); ++ : "_Countof")))); if (in_underspecified_init) error_at (loc, "%qT defined in underspecified object initializer", ref); @@ gcc/c/c-decl.cc: start_enum (location_t loc, struct c_enum_contents *the_enum, t + ? "typeof" + : (in_alignof + ? "alignof" -+ : "__countof__")))); ++ : "_Countof")))); if (in_underspecified_init) error_at (loc, "%qT defined in underspecified object initializer", @@ gcc/c/c-decl.cc: finish_enum (tree enumtype, tree values, tree attributes) ## gcc/c/c-parser.cc ## @@ gcc/c/c-parser.cc: along with GCC; see the file COPYING3. If not see - #include "toplev.h" #include "asan.h" #include "c-family/c-ubsan.h" + #include "gcc-urlifier.h" + +#define c_parser_sizeof_expression(parser) \ +( \ @@ gcc/c/c-parser.cc: c_parser_unary_expression (c_parser *parser) -c_parser_sizeof_expression (c_parser *parser) +c_parser_sizeof_or_countof_expression (c_parser *parser, enum rid rid) { -+ const char *op_name = (rid == RID_COUNTOF) ? "__countof__" : "sizeof"; ++ const char *op_name = (rid == RID_COUNTOF) ? "_Countof" : "sizeof"; struct c_expr expr; struct c_expr result; location_t expr_loc; @@ gcc/c/c-parser.cc: c_parser_sizeof_expression (c_parser *parser) && c_token_starts_compound_literal (c_parser_peek_2nd_token (parser))) { @@ gcc/c/c-parser.cc: c_parser_sizeof_expression (c_parser *parser) + for parsing error; the parsing of the expression could have + called record_maybe_used_decl. */ + expr.set_error (); +- goto sizeof_expr; ++ goto Xof_expr; + } + if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) { - struct c_expr ret; - c_inhibit_evaluation_warnings--; -- in_sizeof--; -+ if (rid == RID_COUNTOF) -+ in_countof--; -+ else -+ in_sizeof--; - ret.set_error (); - ret.original_code = ERROR_MARK; - ret.original_type = NULL; @@ gcc/c/c-parser.cc: c_parser_sizeof_expression (c_parser *parser) type_name, expr_loc); @@ gcc/c/c-typeck.cc: c_expr_sizeof_type (location_t loc, struct c_type_name *t) PARAMS is a list--a chain of TREE_LIST nodes--in which the ## gcc/doc/extend.texi ## -@@ gcc/doc/extend.texi: If the operand of the @code{__alignof__} expression is a function, - the expression evaluates to the alignment of the function which may - be specified by attribute @code{aligned} (@pxref{Common Function Attributes}). +@@ gcc/doc/extend.texi: library. + @xref{OpenMP and OpenACC Options}, for additional options useful with + @option{-fopenacc}. -+@node __countof__ ++@node _Countof +@section Determining the Number of Elements of Arrays -+@cindex __countof__ ++@cindex _Countof +@cindex number of elements + -+The keyword @code{__countof__} determines ++The keyword @code{_Countof} determines +the number of elements of an array operand. +Its syntax is similar to @code{sizeof}. +The operand must be @@ gcc/doc/extend.texi: If the operand of the @code{__alignof__} expression is a fu + +@smallexample +int a[n]; -+__countof__ (a); // returns n -+__countof__ (int [7][3]); // returns 7 ++_Countof (a); // returns n ++_Countof (int [7][3]); // returns 7 +@end smallexample + +The result of this operator is an integer constant expression, @@ gcc/doc/extend.texi: If the operand of the @code{__alignof__} expression is a fu +For example: + +@smallexample -+__countof__ (int [7][n++]); // integer constant expression -+__countof__ (int [n++][7]); // run-time value; n++ is evaluated ++_Countof (int [7][n++]); // integer constant expression ++_Countof (int [n++][7]); // run-time value; n++ is evaluated +@end smallexample + @node Inline @@ gcc/testsuite/gcc.dg/countof-compile.c (new) +/* { dg-do compile } */ +/* { dg-options "-Wno-declaration-after-statement -Wno-pedantic -Wno-vla" } */ + ++#define NULL ((void *) 0) ++ +extern int x[]; + +static int w[] = {1, 2, 3}; + +static int z[0]; -+static int y[__countof__(z)]; ++static int y[_Countof(z)]; + +void +completed (void) @@ gcc/testsuite/gcc.dg/countof-compile.c (new) + int i = 42; + int a[] = {1, 2, i}; + -+ _Static_assert(__countof__ (w) == 3); -+ __countof__ (a); ++ _Static_assert(_Countof (w) == 3); ++ _Static_assert(_Countof (a) == 3); +} + +void +incomplete (int p[]) +{ -+ __countof__ (x); /* { dg-error "incomplete" } */ ++ _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" } */ ++ _Countof (p); /* { dg-error "invalid" } */ +} + +void @@ gcc/testsuite/gcc.dg/countof-compile.c (new) + int fam[]; + } s; + -+ __countof__ (s.fam); /* { dg-error "incomplete" } */ ++ _Countof (s.fam); /* { dg-error "incomplete" } */ +} + +void @@ gcc/testsuite/gcc.dg/countof-compile.c (new) +{ + /* We want to support array parameters in the future, + which would make this work. */ -+ __countof__ (p); /* { dg-error "invalid" } */ ++ _Countof (p); /* { dg-error "invalid" } */ +} + -+void fix_fix (int i, char (*a)[3][5], int (*x)[__countof__ (*a)]); -+void fix_var (int i, char (*a)[3][i], int (*x)[__countof__ (*a)]); -+void fix_uns (int i, char (*a)[3][*], int (*x)[__countof__ (*a)]); ++void fix_fix (int i, char (*a)[3][5], int (*x)[_Countof (*a)], ++ short (*)[_Generic(x, int (*)[3]: 1)]); ++void fix_var (int i, char (*a)[3][i], int (*x)[_Countof (*a)], ++ short (*)[_Generic(x, int (*)[3]: 1)]); ++void fix_uns (int i, char (*a)[3][*], int (*x)[_Countof (*a)], ++ short (*)[_Generic(x, int (*)[3]: 1)]); + +void +func (void) @@ gcc/testsuite/gcc.dg/countof-compile.c (new) + int i5[5]; + char c35[3][5]; + -+ fix_fix (5, &c35, &i3); -+ fix_fix (5, &c35, &i5); /* { dg-error "incompatible-pointer-types" } */ ++ fix_fix (5, &c35, &i3, NULL); ++ fix_fix (5, &c35, &i5, NULL); /* { dg-error "incompatible-pointer-types" } */ + -+ fix_var (5, &c35, &i3); -+ fix_var (5, &c35, &i5); /* { dg-error "incompatible-pointer-types" } */ ++ fix_var (5, &c35, &i3, NULL); ++ fix_var (5, &c35, &i5, NULL); /* { dg-error "incompatible-pointer-types" } */ + -+ fix_uns (5, &c35, &i3); -+ fix_uns (5, &c35, &i5); /* { dg-error "incompatible-pointer-types" } */ ++ fix_uns (5, &c35, &i3, NULL); ++ fix_uns (5, &c35, &i5, NULL); /* { dg-error "incompatible-pointer-types" } */ +} + +void @@ gcc/testsuite/gcc.dg/countof-compile.c (new) + int x[3]; + } s; + -+ __countof__ (x); /* { dg-error "invalid" } */ -+ __countof__ (int); /* { dg-error "invalid" } */ -+ __countof__ (s); /* { dg-error "invalid" } */ -+ __countof__ (struct s); /* { dg-error "invalid" } */ -+ __countof__ (&x); /* { dg-error "invalid" } */ -+ __countof__ (p); /* { dg-error "invalid" } */ -+ __countof__ (int *); /* { dg-error "invalid" } */ -+ __countof__ (&s.x); /* { dg-error "invalid" } */ -+ __countof__ (int (*)[3]); /* { dg-error "invalid" } */ ++ _Countof (x); /* { dg-error "invalid" } */ ++ _Countof (int); /* { dg-error "invalid" } */ ++ _Countof (s); /* { dg-error "invalid" } */ ++ _Countof (struct s); /* { dg-error "invalid" } */ ++ _Countof (&x); /* { dg-error "invalid" } */ ++ _Countof (p); /* { dg-error "invalid" } */ ++ _Countof (int *); /* { dg-error "invalid" } */ ++ _Countof (&s.x); /* { dg-error "invalid" } */ ++ _Countof (int (*)[3]); /* { dg-error "invalid" } */ +} + +static int f1(); +static int f2(); /* { dg-warning "never defined" } */ -+int a[10][10]; ++int a[10][9]; +int n; + +void @@ gcc/testsuite/gcc.dg/countof-compile.c (new) +{ + int b[n][n]; + -+ __countof__ (a[f1()]); -+ __countof__ (b[f2()]); ++ _Static_assert (_Countof (a[f1()]) == 9); ++ _Countof (b[f2()]); +} + +void +no_parens(void) +{ -+ __countof__ a; -+ __countof__ *a; -+ __countof__ (int [3]) {}; ++ _Static_assert(_Countof a == 10); ++ _Static_assert(_Countof *a == 9); ++ _Static_assert(_Countof (int [3]) {} == 3); + -+ __countof__ int [3]; /* { dg-error "expected expression before" } */ ++ _Countof int [3]; /* { dg-error "expected expression before" } */ +} + +void @@ gcc/testsuite/gcc.dg/countof-compile.c (new) +{ + int n = 7; + -+ _Static_assert (__countof__ (int [3][n]) == 3); -+ _Static_assert (__countof__ (int [n][3]) == 7); /* { dg-error "not constant" } */ -+ _Static_assert (__countof__ (int [0][3]) == 0); -+ _Static_assert (__countof__ (int [0]) == 0); -+ _Static_assert (__countof__ (int [0][n]) == 0); ++ _Static_assert (_Countof (int [3][n]) == 3); ++ _Static_assert (_Countof (int [n][3]) == 7); /* { dg-error "not constant" } */ ++ _Static_assert (_Countof (int [0][3]) == 0); ++ _Static_assert (_Countof (int [0]) == 0); ++ _Static_assert (_Countof (int [0][n]) == 0); +} ## gcc/testsuite/gcc.dg/countof-vla.c (new) ## @@ gcc/testsuite/gcc.dg/countof-vla.c (new) + +void fix_fix (int i, + char (*a)[3][5], -+ int (*x)[__countof__ (*a)]); ++ int (*x)[_Countof (*a)], ++ short (*)[_Generic(x, int (*)[3]: 1)]); +void fix_var (int i, + char (*a)[3][i], /* dg-warn "variable" */ -+ int (*x)[__countof__ (*a)]); ++ int (*x)[_Countof (*a)], ++ short (*)[_Generic(x, int (*)[3]: 1)]); +void fix_uns (int i, + char (*a)[3][*], -+ int (*x)[__countof__ (*a)]); ++ int (*x)[_Countof (*a)], ++ short (*)[_Generic(x, int (*)[3]: 1)]); + +void zro_fix (int i, + char (*a)[0][5], -+ int (*x)[__countof__ (*a)]); ++ int (*x)[_Countof (*a)], ++ short (*)[_Generic(x, int (*)[3]: 1)]); +void zro_var (int i, + char (*a)[0][i], /* dg-warn "variable" */ -+ int (*x)[__countof__ (*a)]); ++ int (*x)[_Countof (*a)], ++ short (*)[_Generic(x, int (*)[3]: 1)]); +void zro_uns (int i, + char (*a)[0][*], -+ int (*x)[__countof__ (*a)]); ++ int (*x)[_Countof (*a)], ++ short (*)[_Generic(x, int (*)[3]: 1)]); + +void var_fix (int i, + char (*a)[i][5], /* dg-warn "variable" */ -+ int (*x)[__countof__ (*a)]); /* dg-warn "variable" */ ++ int (*x)[_Countof (*a)]); /* dg-warn "variable" */ +void var_var (int i, + char (*a)[i][i], /* dg-warn "variable" */ -+ int (*x)[__countof__ (*a)]); /* dg-warn "variable" */ ++ int (*x)[_Countof (*a)]); /* dg-warn "variable" */ +void var_uns (int i, + char (*a)[i][*], /* dg-warn "variable" */ -+ int (*x)[__countof__ (*a)]); /* dg-warn "variable" */ ++ int (*x)[_Countof (*a)]); /* dg-warn "variable" */ + +void uns_fix (int i, + char (*a)[*][5], -+ int (*x)[__countof__ (*a)]); ++ int (*x)[_Countof (*a)]); +void uns_var (int i, + char (*a)[*][i], /* dg-warn "variable" */ -+ int (*x)[__countof__ (*a)]); ++ int (*x)[_Countof (*a)]); +void uns_uns (int i, + char (*a)[*][*], -+ int (*x)[__countof__ (*a)]); ++ int (*x)[_Countof (*a)]); + +static int z2[0]; -+static int y2[__countof__(z2)]; ++static int y2[_Countof (z2)]; ## gcc/testsuite/gcc.dg/countof.c (new) ## @@ @@ gcc/testsuite/gcc.dg/countof.c (new) +{ + short a[7]; + -+ static_assert (__countof__ (a) == 7); -+ static_assert (__countof__ (long [0]) == 0); -+ static_assert (__countof__ (unsigned [99]) == 99); ++ static_assert (_Countof (a) == 7); ++ static_assert (_Countof (long [0]) == 0); ++ static_assert (_Countof (unsigned [99]) == 99); +} + +void @@ gcc/testsuite/gcc.dg/countof.c (new) + int a[] = {1, 2, 3}; + int z[] = {}; + -+ static_assert (__countof__ (a) == 3); -+ static_assert (__countof__ (z) == 0); ++ static_assert (_Countof (a) == 3); ++ static_assert (_Countof (z) == 0); +} + +void @@ gcc/testsuite/gcc.dg/countof.c (new) + unsigned n; + + n = 99; -+ assert (__countof__ (short [n - 10]) == 99 - 10); ++ assert (_Countof (short [n - 10]) == 99 - 10); + + int v[n / 2]; -+ assert (__countof__ (v) == 99 / 2); ++ assert (_Countof (v) == 99 / 2); + + n = 0; + int z[n]; -+ assert (__countof__ (z) == 0); ++ assert (_Countof (z) == 0); +} + +void @@ gcc/testsuite/gcc.dg/countof.c (new) + int a[8]; + } s; + -+ static_assert (__countof__ (s.a) == 8); ++ static_assert (_Countof (s.a) == 8); +} + +void @@ gcc/testsuite/gcc.dg/countof.c (new) + int i; + + i = 7; -+ assert (__countof__ (struct {int x;}[i++]) == 7); ++ assert (_Countof (struct {int x;}[i++]) == 7); + assert (i == 7 + 1); + + int v[i]; + int (*p)[i]; + p = &v; -+ assert (__countof__ (*p++) == i); ++ assert (_Countof (*p++) == i); + assert (p - 1 == &v); +} + @@ gcc/testsuite/gcc.dg/countof.c (new) + int i; + + i = 3; -+ static_assert (__countof__ (struct {int x[i++];}[3]) == 3); ++ static_assert (_Countof (struct {int x[i++];}[3]) == 3); + assert (i == 3); +} + @@ gcc/testsuite/gcc.dg/countof.c (new) +array_noeval (void) +{ + long a[5]; -+ long (*p)[__countof__ (a)]; ++ long (*p)[_Countof (a)]; + + p = &a; -+ static_assert (__countof__ (*p++) == 5); ++ static_assert (_Countof (*p++) == 5); + assert (p == &a); +} + @@ gcc/testsuite/gcc.dg/countof.c (new) +{ + int i; + -+ static_assert (__countof__ (int [0][4]) == 0); ++ static_assert (_Countof (int [0][4]) == 0); + i = 3; -+ assert (__countof__ (int [0][i]) == 0); ++ static_assert (_Countof (int [0][i]) == 0); +} + +void @@ gcc/testsuite/gcc.dg/countof.c (new) +{ + int i; + -+ static_assert (__countof__ (int [7][4]) == 7); ++ static_assert (_Countof (int [7][4]) == 7); + i = 3; -+ static_assert (__countof__ (int [7][i]) == 7); ++ static_assert (_Countof (int [7][i]) == 7); +} + +void @@ gcc/testsuite/gcc.dg/countof.c (new) + int i, j; + + i = 7; -+ assert (__countof__ (int [i++][4]) == 7); ++ assert (_Countof (int [i++][4]) == 7); + assert (i == 7 + 1); + ++ i = 0; ++ assert (_Countof (int [i++][4]) == 0); ++ assert (i == 0 + 1); ++ + i = 9; + j = 3; -+ assert (__countof__ (int [i++][j]) == 9); ++ assert (_Countof (int [i++][j]) == 9); + assert (i == 9 + 1); +} + @@ gcc/testsuite/gcc.dg/countof.c (new) + int a[7]; + int v[n]; + -+ static_assert (__countof__ a == 7); -+ assert (__countof__ v == 3); ++ static_assert (_Countof a == 7); ++ assert (_Countof v == 3); +} + +int -: ------------ > 3: f4700c6d7dce c: Add <stdcountof.h> base-commit: 5a674367c6da870184f3bdb7ec110b96aa91bb2b -- 2.49.0