This operator is similar to sizeof but can only be applied to an array,
and returns its number of elements.
FUTURE DIRECTIONS:
- We should make it work with array parameters to functions,
and somehow magically return the number of elements of the array,
regardless of it being really a pointer.
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--3-2@tutanota.com/T/>
Link: <https://inbox.sourceware.org/gcc-patches/
20240728141547.302478-1-alx@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>
PR c/117025
gcc/ChangeLog:
* doc/extend.texi: Document _Countof operator.
gcc/c-family/ChangeLog:
* c-common.h (enum rid): Add RID_COUNTOF.
(c_countof_type): New function prototype.
* c-common.def (COUNTOF_EXPR): New tree.
* c-common.cc (c_common_reswords): Add RID_COUNTOF entry.
(c_countof_type): New function.
gcc/c/ChangeLog:
* c-tree.h (in_countof): Add global variable declaration.
(c_expr_countof_expr): Add function prototype.
(c_expr_countof_type): Add function prototype.
* c-decl.cc (start_struct, finish_struct): Add support for
_Countof.
(start_enum, finish_enum): Add support for _Countof.
* c-parser.cc (c_parser_sizeof_expression): New macro.
(c_parser_countof_expression): New macro.
(c_parser_sizeof_or_countof_expression): Rename function and add
support for _Countof.
(c_parser_unary_expression): Add RID_COUNTOF entry.
* c-typeck.cc (in_countof): Add global variable.
(build_external_ref): Add support for _Countof.
(record_maybe_used_decl): Add support for _Countof.
(pop_maybe_used): Add support for _Countof.
(is_top_array_vla): New function.
(c_expr_countof_expr, c_expr_countof_type): New functions.
gcc/testsuite/ChangeLog:
* gcc.dg/countof-compile.c: New test.
* gcc.dg/countof-vla.c: New test.
* gcc.dg/countof-vmt.c: New test.
* gcc.dg/countof-zero-compile.c: New test.
* gcc.dg/countof-zero.c: New test.
* gcc.dg/countof.c: New test.
Suggested-by: Xavier Del Campo Romero <xavi.dcr@tutanota.com>
Co-authored-by: Martin Uecker <uecker@tugraz.at>
Acked-by: "James K. Lowden" <jklowden@schemamania.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
{
{ "_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 },
return fold_convert_loc (loc, size_type_node, t);
}
+
+/* Implement the _Countof keyword:
+ Return the number of elements of an array. */
+
+tree
+c_countof_type (location_t loc, tree type)
+{
+ enum tree_code type_code;
+
+ type_code = TREE_CODE (type);
+ if (type_code != ARRAY_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",
+ type);
+ return error_mark_node;
+ }
+
+ return array_type_nelts_top (type);
+}
\f
/* Handle C and C++ default attributes. */
number. */
DEFTREECODE (USERDEF_LITERAL, "userdef_literal", tcc_exceptional, 3)
+/* Represents a 'countof' expression. */
+DEFTREECODE (COUNTOF_EXPR, "countof_expr", tcc_expression, 1)
+
/* Represents a 'sizeof' expression during C++ template expansion,
or for the purpose of -Wsizeof-pointer-memaccess warning. */
DEFTREECODE (SIZEOF_EXPR, "sizeof_expr", tcc_expression, 1)
/* C extensions */
RID_ASM, RID_TYPEOF, RID_TYPEOF_UNQUAL, RID_ALIGNOF, RID_ATTRIBUTE,
+ RID_COUNTOF,
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,
extern void c_apply_type_quals_to_decl (int, tree);
extern tree c_sizeof_or_alignof_type (location_t, tree, bool, bool, int);
extern tree c_alignof_expr (location_t, tree);
+extern tree c_countof_type (location_t, tree);
/* Print an error message for invalid operands to arith operation CODE.
NOP_EXPR is used as a special case (see truthvalue_conversion). */
extern void binary_op_error (rich_location *, enum tree_code, tree, tree);
within a statement expr used within sizeof, et. al. This is not
terribly serious as C++ doesn't permit statement exprs within
sizeof anyhow. */
- if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof))
+ if (warn_cxx_compat
+ && (in_sizeof || in_typeof || in_alignof || in_countof))
warning_at (loc, OPT_Wc___compat,
"defining type in %qs expression is invalid in C++",
- (in_sizeof
- ? "sizeof"
- : (in_typeof ? "typeof" : "alignof")));
+ (in_sizeof ? "sizeof"
+ : in_typeof ? "typeof"
+ : in_alignof ? "alignof"
+ : "_Countof"));
if (in_underspecified_init)
error_at (loc, "%qT defined in underspecified object initializer", ref);
struct_types. */
if (warn_cxx_compat
&& struct_parse_info != NULL
- && !in_sizeof && !in_typeof && !in_alignof)
+ && !in_sizeof && !in_typeof && !in_alignof && !in_countof)
struct_parse_info->struct_types.safe_push (t);
}
/* FIXME: This will issue a warning for a use of a type defined
within sizeof in a statement expr. This is not terribly serious
as C++ doesn't permit statement exprs within sizeof anyhow. */
- if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof))
+ if (warn_cxx_compat
+ && (in_sizeof || in_typeof || in_alignof || in_countof))
warning_at (loc, OPT_Wc___compat,
"defining type in %qs expression is invalid in C++",
- (in_sizeof
- ? "sizeof"
- : (in_typeof ? "typeof" : "alignof")));
+ (in_sizeof ? "sizeof"
+ : in_typeof ? "typeof"
+ : in_alignof ? "alignof"
+ : "_Countof"));
if (in_underspecified_init)
error_at (loc, "%qT defined in underspecified object initializer",
struct_types. */
if (warn_cxx_compat
&& struct_parse_info != NULL
- && !in_sizeof && !in_typeof && !in_alignof)
+ && !in_sizeof && !in_typeof && !in_alignof && !in_countof)
struct_parse_info->struct_types.safe_push (enumtype);
/* Check for consistency with previous definition */
#include "asan.h"
#include "c-family/c-ubsan.h"
#include "gcc-urlifier.h"
-
+\f
/* We need to walk over decls with incomplete struct/union/enum types
after parsing the whole translation unit.
In finish_decl(), if the decl is static, has incomplete
tree);
static struct c_expr c_parser_cast_expression (c_parser *, struct c_expr *);
static struct c_expr c_parser_unary_expression (c_parser *);
-static struct c_expr c_parser_sizeof_expression (c_parser *);
+static inline struct c_expr c_parser_sizeof_expression (c_parser *);
+static inline struct c_expr c_parser_countof_expression (c_parser *);
+static struct c_expr c_parser_sizeof_or_countof_expression (c_parser *,
+ enum rid);
static struct c_expr c_parser_alignof_expression (c_parser *);
static struct c_expr c_parser_postfix_expression (c_parser *);
static struct c_expr c_parser_postfix_expression_after_paren_type (c_parser *,
++ unary-expression
-- unary-expression
unary-operator cast-expression
+ _Countof unary-expression
+ _Countof ( type-name )
sizeof unary-expression
sizeof ( type-name )
+ (_Countof is new in C2y.)
+
unary-operator: one of
& * + - ~ !
case CPP_KEYWORD:
switch (c_parser_peek_token (parser)->keyword)
{
+ case RID_COUNTOF:
+ return c_parser_countof_expression (parser);
case RID_SIZEOF:
return c_parser_sizeof_expression (parser);
case RID_ALIGNOF:
/* Parse a sizeof expression. */
-static struct c_expr
+static inline struct c_expr
c_parser_sizeof_expression (c_parser *parser)
{
+ return c_parser_sizeof_or_countof_expression (parser, RID_SIZEOF);
+}
+
+/* Parse a _Countof expression. */
+
+static inline struct c_expr
+c_parser_countof_expression (c_parser *parser)
+{
+ return c_parser_sizeof_or_countof_expression (parser, RID_COUNTOF);
+}
+
+/* Parse a sizeof or _Countof expression. */
+
+static struct c_expr
+c_parser_sizeof_or_countof_expression (c_parser *parser, enum rid rid)
+{
+ const char *op_name = (rid == RID_COUNTOF) ? "_Countof" : "sizeof";
struct c_expr expr;
struct c_expr result;
location_t expr_loc;
- gcc_assert (c_parser_next_token_is_keyword (parser, RID_SIZEOF));
+ gcc_assert (c_parser_next_token_is_keyword (parser, rid));
location_t start;
location_t finish = UNKNOWN_LOCATION;
c_parser_consume_token (parser);
c_inhibit_evaluation_warnings++;
- in_sizeof++;
+ if (rid == RID_COUNTOF)
+ in_countof++;
+ else
+ in_sizeof++;
if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)
&& c_token_starts_compound_literal (c_parser_peek_2nd_token (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))
{
type_name,
expr_loc);
finish = expr.get_finish ();
- goto sizeof_expr;
+ goto Xof_expr;
}
/* sizeof ( type-name ). */
if (scspecs)
- error_at (expr_loc, "storage class specifier in %<sizeof%>");
+ error_at (expr_loc, "storage class specifier in %qs", op_name);
if (type_name->specs->alignas_p)
error_at (type_name->specs->locations[cdw_alignas],
- "alignment specified for type name in %<sizeof%>");
+ "alignment specified for type name in %qs", op_name);
c_inhibit_evaluation_warnings--;
- in_sizeof--;
- result = c_expr_sizeof_type (expr_loc, type_name);
+ if (rid == RID_COUNTOF)
+ {
+ in_countof--;
+ result = c_expr_countof_type (expr_loc, type_name);
+ }
+ else
+ {
+ in_sizeof--;
+ result = c_expr_sizeof_type (expr_loc, type_name);
+ }
}
else
{
expr_loc = c_parser_peek_token (parser)->location;
expr = c_parser_unary_expression (parser);
finish = expr.get_finish ();
- sizeof_expr:
+ Xof_expr:
c_inhibit_evaluation_warnings--;
- in_sizeof--;
+ if (rid == RID_COUNTOF)
+ in_countof--;
+ else
+ in_sizeof--;
mark_exp_read (expr.value);
if (TREE_CODE (expr.value) == COMPONENT_REF
&& DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1)))
- error_at (expr_loc, "%<sizeof%> applied to a bit-field");
- result = c_expr_sizeof_expr (expr_loc, expr);
+ error_at (expr_loc, "%qs applied to a bit-field", op_name);
+ if (rid == RID_COUNTOF)
+ result = c_expr_countof_expr (expr_loc, expr);
+ else
+ result = c_expr_sizeof_expr (expr_loc, expr);
}
if (finish == UNKNOWN_LOCATION)
finish = start;
/* in c-typeck.cc */
extern int in_alignof;
extern int in_sizeof;
+extern int in_countof;
extern int in_typeof;
extern bool c_in_omp_for;
extern bool c_omp_array_section_p;
extern void pop_maybe_used (bool);
extern struct c_expr c_expr_sizeof_expr (location_t, struct c_expr);
extern struct c_expr c_expr_sizeof_type (location_t, struct c_type_name *);
+extern struct c_expr c_expr_countof_expr (location_t, struct c_expr);
+extern struct c_expr c_expr_countof_type (location_t loc,
+ struct c_type_name *);
extern struct c_expr parser_build_unary_op (location_t, enum tree_code,
struct c_expr);
extern struct c_expr parser_build_binary_op (location_t,
/* The level of nesting inside "sizeof". */
int in_sizeof;
+/* The level of nesting inside "countof". */
+int in_countof;
+
/* The level of nesting inside "typeof". */
int in_typeof;
if (TREE_CODE (ref) == FUNCTION_DECL && !in_alignof)
{
- if (!in_sizeof && !in_typeof)
+ if (!in_sizeof && !in_typeof && !in_countof)
C_DECL_USED (ref) = 1;
else if (DECL_INITIAL (ref) == NULL_TREE
&& DECL_EXTERNAL (ref)
{
/* The decl. */
tree decl;
- /* The level seen at (in_sizeof + in_typeof). */
+ /* The level seen at (in_sizeof + in_typeof + in_countof). */
int level;
/* The next one at this level or above, or NULL. */
struct maybe_used_decl *next;
{
struct maybe_used_decl *t = XOBNEW (&parser_obstack, struct maybe_used_decl);
t->decl = decl;
- t->level = in_sizeof + in_typeof;
+ t->level = in_sizeof + in_typeof + in_countof;
t->next = maybe_used_decls;
maybe_used_decls = t;
}
pop_maybe_used (bool used)
{
struct maybe_used_decl *p = maybe_used_decls;
- int cur_level = in_sizeof + in_typeof;
+ int cur_level = in_sizeof + in_typeof + in_countof;
while (p && p->level > cur_level)
{
if (used)
return ret;
}
+static bool
+is_top_array_vla (tree type)
+{
+ bool zero, var;
+ tree d;
+
+ if (TREE_CODE (type) != ARRAY_TYPE)
+ return false;
+ if (!COMPLETE_TYPE_P (type))
+ return false;
+
+ d = TYPE_DOMAIN (type);
+ zero = !TYPE_MAX_VALUE (d);
+ if (zero)
+ return false;
+
+ var = (TREE_CODE (TYPE_MIN_VALUE (d)) != INTEGER_CST
+ || TREE_CODE (TYPE_MAX_VALUE (d)) != INTEGER_CST);
+ return var;
+}
+
+/* Return the result of countof applied to EXPR. */
+
+struct c_expr
+c_expr_countof_expr (location_t loc, struct c_expr expr)
+{
+ struct c_expr ret;
+ if (expr.value == error_mark_node)
+ {
+ ret.value = error_mark_node;
+ ret.original_code = ERROR_MARK;
+ ret.original_type = NULL;
+ ret.m_decimal = 0;
+ pop_maybe_used (false);
+ }
+ 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)))
+ {
+ /* countof is evaluated when given a vla. */
+ ret.value = build2 (C_MAYBE_CONST_EXPR, TREE_TYPE (ret.value),
+ folded_expr, ret.value);
+ 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)));
+ }
+ return ret;
+}
+
+/* Return the result of countof applied to T, a structure for the type
+ name passed to countof (rather than the type itself). LOC is the
+ location of the original expression. */
+
+struct c_expr
+c_expr_countof_type (location_t loc, struct c_type_name *t)
+{
+ tree type;
+ struct c_expr ret;
+ tree type_expr = NULL_TREE;
+ bool type_expr_const = true;
+ type = groktypename (t, &type_expr, &type_expr_const);
+ ret.value = c_countof_type (loc, type);
+ c_last_sizeof_arg = type;
+ c_last_sizeof_loc = loc;
+ ret.original_code = COUNTOF_EXPR;
+ ret.original_type = NULL;
+ ret.m_decimal = 0;
+ if (type == error_mark_node)
+ {
+ ret.value = error_mark_node;
+ ret.original_code = ERROR_MARK;
+ }
+ else
+ if ((type_expr || TREE_CODE (ret.value) == INTEGER_CST)
+ && is_top_array_vla (type))
+ {
+ /* If the type is a [*] array, it is a VLA but is represented as
+ having a size of zero. In such a case we must ensure that
+ the result of countof does not get folded to a constant by
+ c_fully_fold, because if the number of elements is evaluated
+ the result is not constant and so
+ constraints on zero or negative size arrays must not be applied
+ when this countof call is inside another array declarator. */
+ if (!type_expr)
+ type_expr = integer_zero_node;
+ ret.value = build2 (C_MAYBE_CONST_EXPR, TREE_TYPE (ret.value),
+ type_expr, ret.value);
+ C_MAYBE_CONST_EXPR_NON_CONST (ret.value) = !type_expr_const;
+ }
+ pop_maybe_used (type != error_mark_node ? is_top_array_vla (type) : false);
+ return ret;
+}
+
/* Build a function call to function FUNCTION with parameters PARAMS.
The function call is at LOC.
PARAMS is a list--a chain of TREE_LIST nodes--in which the
@xref{OpenMP and OpenACC Options}, for additional options useful with
@option{-fopenacc}.
+@node _Countof
+@section Determining the Number of Elements of Arrays
+@cindex _Countof
+@cindex number of elements
+
+The keyword @code{_Countof} determines
+the number of elements of an array operand.
+Its syntax is similar to @code{sizeof}.
+The operand must be
+a parenthesized complete array type name
+or an expression of such a type.
+For example:
+
+@smallexample
+int a[n];
+_Countof (a); // returns n
+_Countof (int [7][3]); // returns 7
+@end smallexample
+
+The result of this operator is an integer constant expression,
+unless the array has a variable number of elements.
+The operand is only evaluated
+if the array has a variable number of elements.
+For example:
+
+@smallexample
+_Countof (int [7][n++]); // integer constant expression
+_Countof (int [n++][7]); // run-time value; n++ is evaluated
+@end smallexample
+
@node Inline
@section An Inline Function is As Fast As a Macro
@cindex inline functions
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -pedantic-errors" } */
+
+#define NULL ((void *) 0)
+
+extern int x[];
+
+static int w[] = {1, 2, 3};
+
+void
+completed (void)
+{
+ int i = 42;
+ int a[] = {1, 2, i};
+
+ _Static_assert(_Countof (w) == 3);
+ _Static_assert(_Countof (a) == 3);
+}
+
+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
+fam (void)
+{
+ struct {
+ int x;
+ int fam[];
+ } s;
+
+ _Countof (s.fam); /* { dg-error "incomplete" } */
+}
+
+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" } */
+}
+
+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)
+{
+ int i3[3];
+ int i5[5];
+ char c35[3][5];
+
+ fix_fix (5, &c35, &i3, NULL);
+ fix_fix (5, &c35, &i5, NULL); /* { 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, NULL);
+ fix_uns (5, &c35, &i5, NULL); /* { dg-error "incompatible-pointer-types" } */
+}
+
+void
+non_arr(void)
+{
+ int x;
+ int *p;
+ struct s {
+ 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" } */
+}
+
+static int f1();
+static int f2(); /* { dg-error "never defined" } */
+int a[10][9];
+int n;
+
+void
+syms(void)
+{
+ int b[n][n];
+
+ _Countof (a[f1()]);
+ _Countof (b[f2()]);
+}
+
+void
+no_parens(void)
+{
+ _Static_assert(_Countof a == 10);
+ _Static_assert(_Countof *a == 9);
+ _Static_assert(_Countof (int [3]) {} == 3);
+
+ _Countof int [3]; /* { dg-error "expected expression before" } */
+}
+
+void
+const_expr(void)
+{
+ int n = 7;
+
+ _Static_assert (_Countof (int [3][n]) == 3);
+ _Static_assert (_Countof (int [n][3]) == 7); /* { dg-error "not constant" } */
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -pedantic-errors -Wvla-parameter" } */
+
+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], /* dg-warn "variable" */
+ 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 var_fix (int i,
+ char (*a)[i][5], /* 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" */
+void var_uns (int i,
+ char (*a)[i][*], /* dg-warn "variable" */
+ int (*x)[_Countof (*a)]); /* dg-warn "variable" */
+
+void uns_fix (int i,
+ char (*a)[*][5],
+ int (*x)[_Countof (*a)]);
+void uns_var (int i,
+ char (*a)[*][i], /* dg-warn "variable" */
+ int (*x)[_Countof (*a)]);
+void uns_uns (int i,
+ char (*a)[*][*],
+ int (*x)[_Countof (*a)]);
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-std=gnu2y" } */
+
+#define assert(e) ((e) ? (void) 0 : __builtin_abort ())
+
+void
+inner_vla_noeval (void)
+{
+ int i;
+
+ i = 3;
+ static_assert (_Countof (struct {int x[i++];}[3]) == 3);
+ assert (i == 3);
+}
+
+int
+main (void)
+{
+ inner_vla_noeval ();
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2y" } */
+
+static int z[0];
+static int y[_Countof (z)];
+
+_Static_assert(_Countof (y) == 0);
+
+void
+completed (void)
+{
+ int z[] = {};
+
+ static_assert (_Countof (z) == 0);
+}
+
+void zro_fix (int i,
+ char (*a)[0][5],
+ int (*x)[_Countof (*a)],
+ short (*)[_Generic(x, int (*)[0]: 1)]);
+void zro_var (int i,
+ char (*a)[0][i], /* dg-warn "variable" */
+ int (*x)[_Countof (*a)],
+ short (*)[_Generic(x, int (*)[0]: 1)]);
+void zro_uns (int i,
+ char (*a)[0][*],
+ int (*x)[_Countof (*a)],
+ short (*)[_Generic(x, int (*)[0]: 1)]);
+
+void
+const_expr(void)
+{
+ int n = 7;
+
+ _Static_assert (_Countof (int [0][3]) == 0);
+ _Static_assert (_Countof (int [0]) == 0);
+ _Static_assert (_Countof (int [0][n]) == 0);
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-std=gnu2y" } */
+
+#define assert(e) ((e) ? (void) 0 : __builtin_abort ())
+
+void
+vla (void)
+{
+ unsigned n;
+
+ n = 0;
+ int z[n];
+ assert (_Countof (z) == 0);
+}
+
+void
+matrix_vla (void)
+{
+ int i;
+
+ i = 0;
+ assert (_Countof (int [i++][4]) == 0);
+ assert (i == 0 + 1);
+}
+
+int
+main (void)
+{
+ vla ();
+ matrix_vla ();
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-std=c2y -pedantic-errors" } */
+
+#define assert(e) ((e) ? (void) 0 : __builtin_abort ())
+
+void
+array (void)
+{
+ short a[7];
+
+ static_assert (_Countof (a) == 7);
+ static_assert (_Countof (unsigned [99]) == 99);
+}
+
+void
+completed (void)
+{
+ int a[] = {1, 2, 3};
+
+ static_assert (_Countof (a) == 3);
+}
+
+void
+vla (void)
+{
+ unsigned n;
+
+ n = 99;
+ assert (_Countof (short [n - 10]) == 99 - 10);
+
+ int v[n / 2];
+ assert (_Countof (v) == 99 / 2);
+}
+
+void
+member (void)
+{
+ struct {
+ int a[8];
+ } s;
+
+ static_assert (_Countof (s.a) == 8);
+}
+
+void
+vla_eval (void)
+{
+ int i;
+
+ 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 (p - 1 == &v);
+}
+
+void
+array_noeval (void)
+{
+ long a[5];
+ long (*p)[_Countof (a)];
+
+ p = &a;
+ static_assert (_Countof (*p++) == 5);
+ assert (p == &a);
+}
+
+void
+matrix_fixed (void)
+{
+ int i;
+
+ static_assert (_Countof (int [7][4]) == 7);
+ i = 3;
+ static_assert (_Countof (int [7][i]) == 7);
+}
+
+void
+matrix_vla (void)
+{
+ int i, j;
+
+ i = 7;
+ assert (_Countof (int [i++][4]) == 7);
+ assert (i == 7 + 1);
+
+ i = 9;
+ j = 3;
+ assert (_Countof (int [i++][j]) == 9);
+ assert (i == 9 + 1);
+}
+
+void
+no_parens(void)
+{
+ int n = 3;
+ int a[7];
+ int v[n];
+
+ static_assert (_Countof a == 7);
+ assert (_Countof v == 3);
+}
+
+int
+main (void)
+{
+ array ();
+ completed ();
+ vla ();
+ member ();
+ vla_eval ();
+ array_noeval ();
+ matrix_fixed ();
+ matrix_vla ();
+ no_parens ();
+}