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.

-  Fix support for [0].

gcc/ChangeLog:

        * 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.

gcc/c/ChangeLog:

        * c-tree.h
        (c_expr_countof_expr, c_expr_countof_type)
        * c-decl.cc
        (start_struct, finish_struct)
        (start_enum, finish_enum)
        * c-parser.cc
        (c_parser_sizeof_expression)
        (c_parser_countof_expression)
        (c_parser_sizeof_or_countof_expression)
        (c_parser_unary_expression)
        * c-typeck.cc
        (build_external_ref)
        (record_maybe_used_decl)
        (pop_maybe_used)
        (is_top_array_vla)
        (c_expr_countof_expr, c_expr_countof_type):
        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.

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://github.com/llvm/llvm-project/issues/102836>
Link: <https://stackoverflow.com/questions/37538/#57537491>
Suggested-by: Xavier Del Campo Romero <xavi....@tutanota.com>
Co-authored-by: Martin Uecker <uec...@tugraz.at>
Acked-by: "James K. Lowden" <jklow...@schemamania.org>
Cc: Joseph Myers <josmy...@redhat.com>
Cc: Gabriel Ravier <gabrav...@gmail.com>
Cc: Jakub Jelinek <ja...@redhat.com>
Cc: Kees Cook <keesc...@chromium.org>
Cc: Qing Zhao <qing.z...@oracle.com>
Cc: Jens Gustedt <jens.gust...@inria.fr>
Cc: David Brown <david.br...@hesbynett.no>
Cc: Florian Weimer <fwei...@redhat.com>
Cc: Andreas Schwab <sch...@linux-m68k.org>
Cc: Timm Baeder <tbae...@redhat.com>
Cc: Daniel Plakosh <dplak...@cert.org>
Cc: "A. Jiang" <d...@live.cn>
Cc: Eugene Zelenko <eugene.zele...@gmail.com>
Cc: Aaron Ballman <aaron.ball...@intel.com>
Cc: Paul Koning <paulkon...@comcast.net>
Cc: Daniel Lundin <daniel.lundin.m...@gmail.com>
Cc: Nikolaos Strimpas <strni...@protonmail.com>
Cc: JeanHeyd Meneide <phdoftheho...@gmail.com>
Cc: Fernando Borretti <ferna...@borretti.me>
Cc: Jonathan Protzenko <jonathan.protze...@ens-lyon.org>
Cc: Chris Bazley <chris.baz...@arm.com>
Cc: Ville Voutilainen <ville.voutilai...@gmail.com>
Cc: Alex Celeste <alexg.n...@gmail.com>
Cc: Jakub Ɓukasiewicz <jakublukasiew...@outlook.com>
Cc: Douglas McIlroy <douglas.mcil...@dartmouth.edu>
Cc: Jason Merrill <ja...@redhat.com>
Cc: "Gustavo A. R. Silva" <gustavo...@kernel.org>
Cc: Patrizia Kaye <patri...@ethernull.org>
Cc: Ori Bernstein <o...@eigenstate.org>
Cc: Robert Seacord <rcseac...@gmail.com>
Cc: Marek Polacek <mpola...@gcc.gnu.org>
Cc: Sam James <s...@gentoo.org>
Cc: Richard Biener <richard.guent...@gmail.com>
Signed-off-by: Alejandro Colomar <a...@kernel.org>
---
 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                      |  62 +++++++---
 gcc/c/c-tree.h                         |   4 +
 gcc/c/c-typeck.cc                      | 118 ++++++++++++++++++-
 gcc/doc/extend.texi                    |  30 +++++
 gcc/testsuite/gcc.dg/countof-compile.c | 127 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/countof-vla.c     |  46 ++++++++
 gcc/testsuite/gcc.dg/countof.c         | 150 +++++++++++++++++++++++++
 11 files changed, 566 insertions(+), 24 deletions(-)
 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

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 7494a2dac0a..9f48fea6543 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -466,6 +466,7 @@ 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 },
@@ -4071,6 +4072,31 @@ c_alignof_expr (location_t loc, tree expr)
 
   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);
+}
 
 /* Handle C and C++ default attributes.  */
 
diff --git a/gcc/c-family/c-common.def b/gcc/c-family/c-common.def
index dc49ad09e2f..f2ae784cefe 100644
--- a/gcc/c-family/c-common.def
+++ b/gcc/c-family/c-common.def
@@ -50,6 +50,9 @@ DEFTREECODE (EXCESS_PRECISION_EXPR, "excess_precision_expr", 
tcc_expression, 1)
    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)
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 1e80939d379..6c6ee08925e 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -105,6 +105,7 @@ enum rid
 
   /* C extensions */
   RID_ASM,       RID_TYPEOF,   RID_TYPEOF_UNQUAL, RID_ALIGNOF,  RID_ATTRIBUTE,
+  RID_COUNTOF,
   RID_VA_ARG,
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,    RID_CHOOSE_EXPR,
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,       RID_BUILTIN_SHUFFLE,
@@ -889,6 +890,7 @@ extern tree c_common_truthvalue_conversion (location_t, 
tree);
 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);
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 3733ecfc13f..23c620bbea7 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -9005,12 +9005,17 @@ start_struct (location_t loc, enum tree_code code, tree 
name,
      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_typeof
+                   ? "typeof"
+                   : (in_alignof
+                      ? "alignof"
+                      : "__countof__"))));
 
   if (in_underspecified_init)
     error_at (loc, "%qT defined in underspecified object initializer", ref);
@@ -9974,7 +9979,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, 
tree attributes,
         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);
      }
 
@@ -10148,12 +10153,17 @@ start_enum (location_t loc, struct c_enum_contents 
*the_enum, tree name,
   /* 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_typeof
+                   ? "typeof"
+                   : (in_alignof
+                      ? "alignof"
+                      : "__countof__"))));
 
   if (in_underspecified_init)
     error_at (loc, "%qT defined in underspecified object initializer",
@@ -10347,7 +10357,7 @@ finish_enum (tree enumtype, tree values, tree 
attributes)
      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 */
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 090ab1cbc08..73e419ea7ee 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -74,7 +74,17 @@ along with GCC; see the file COPYING3.  If not see
 #include "bitmap.h"
 #include "analyzer/analyzer-language.h"
 #include "toplev.h"
+
+#define c_parser_sizeof_expression(parser)                                    \
+(                                                                             \
+  c_parser_sizeof_or_countof_expression (parser, RID_SIZEOF)                  \
+)
 
+#define c_parser_countof_expression(parser)                                   \
+(                                                                             \
+  c_parser_sizeof_or_countof_expression (parser, RID_COUNTOF)                 \
+)
+
 /* 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
@@ -1695,7 +1705,8 @@ static struct c_expr c_parser_binary_expression (c_parser 
*, struct c_expr *,
                                                 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 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 *,
@@ -10196,6 +10207,8 @@ c_parser_unary_expression (c_parser *parser)
     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:
@@ -10235,12 +10248,13 @@ c_parser_unary_expression (c_parser *parser)
 /* Parse a sizeof expression.  */
 
 static struct c_expr
-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";
   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;
@@ -10249,7 +10263,10 @@ c_parser_sizeof_expression (c_parser *parser)
 
   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)))
     {
@@ -10268,7 +10285,10 @@ c_parser_sizeof_expression (c_parser *parser)
        {
          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;
@@ -10280,31 +10300,45 @@ c_parser_sizeof_expression (c_parser *parser)
                                                               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;
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index a1435e7cb0c..9bdce690374 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -764,6 +764,7 @@ extern int c_type_dwarf_attribute (const_tree, int);
 /* 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;
@@ -815,6 +816,9 @@ extern tree build_external_ref (location_t, tree, bool, 
tree *);
 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,
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 108ea5ca3e8..14313e9d649 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -71,6 +71,9 @@ int in_alignof;
 /* 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;
 
@@ -3273,7 +3276,7 @@ build_external_ref (location_t loc, tree id, bool fun, 
tree *type)
 
   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)
@@ -3329,7 +3332,7 @@ struct maybe_used_decl
 {
   /* 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;
@@ -3347,7 +3350,7 @@ record_maybe_used_decl (tree decl)
 {
   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;
 }
@@ -3361,7 +3364,7 @@ void
 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)
@@ -3471,6 +3474,113 @@ c_expr_sizeof_type (location_t loc, struct c_type_name 
*t)
   return ret;
 }
 
+static bool
+is_top_array_vla (tree type)
+{
+  bool zero, star, 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);
+  star = (zero && C_TYPE_VARIABLE_SIZE (type));
+  if (star)
+    return true;
+  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
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 42bd567119d..4bd1f637657 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -10555,6 +10555,36 @@ 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}).
 
+@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
diff --git a/gcc/testsuite/gcc.dg/countof-compile.c 
b/gcc/testsuite/gcc.dg/countof-compile.c
new file mode 100644
index 00000000000..bfb51109496
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/countof-compile.c
@@ -0,0 +1,127 @@
+/* { dg-do compile } */
+/* { dg-options "-Wno-declaration-after-statement -Wno-pedantic -Wno-vla" } */
+
+extern int x[];
+
+static int w[] = {1, 2, 3};
+
+static int z[0];
+static int y[__countof__(z)];
+
+void
+completed (void)
+{
+  int i = 42;
+  int a[] = {1, 2, i};
+
+  _Static_assert(__countof__ (w) == 3);
+  __countof__ (a);
+}
+
+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)]);
+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
+func (void)
+{
+  int  i3[3];
+  int  i5[5];
+  char c35[3][5];
+
+  fix_fix (5, &c35, &i3);
+  fix_fix (5, &c35, &i5); /* { dg-error "incompatible-pointer-types" } */
+
+  fix_var (5, &c35, &i3);
+  fix_var (5, &c35, &i5); /* { dg-error "incompatible-pointer-types" } */
+
+  fix_uns (5, &c35, &i3);
+  fix_uns (5, &c35, &i5); /* { 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-warning "never defined" } */
+int a[10][10];
+int n;
+
+void
+syms(void)
+{
+  int b[n][n];
+
+  __countof__ (a[f1()]);
+  __countof__ (b[f2()]);
+}
+
+void
+no_parens(void)
+{
+  __countof__ a;
+  __countof__ *a;
+  __countof__ (int [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" 
} */
+  _Static_assert (__countof__ (int [0][3]) == 0);
+  _Static_assert (__countof__ (int [0]) == 0);
+
+  /* FIXME: countof(int [0][n]) should result in a constant expression.  */
+  _Static_assert (__countof__ (int [0][n]) == 0); /* { dg-error "not constant" 
} */
+}
diff --git a/gcc/testsuite/gcc.dg/countof-vla.c 
b/gcc/testsuite/gcc.dg/countof-vla.c
new file mode 100644
index 00000000000..5a82aeed782
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/countof-vla.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-Wno-pedantic -Wvla-parameter" } */
+
+void fix_fix (int i,
+             char (*a)[3][5],
+             int (*x)[__countof__ (*a)]);
+void fix_var (int i,
+             char (*a)[3][i], /* dg-warn "variable" */
+             int (*x)[__countof__ (*a)]);
+void fix_uns (int i,
+             char (*a)[3][*],
+             int (*x)[__countof__ (*a)]);
+
+void zro_fix (int i,
+             char (*a)[0][5],
+             int (*x)[__countof__ (*a)]);
+void zro_var (int i,
+             char (*a)[0][i], /* dg-warn "variable" */
+             int (*x)[__countof__ (*a)]);
+void zro_uns (int i,
+             char (*a)[0][*],
+             int (*x)[__countof__ (*a)]);
+
+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)]);
+
+// Can't test due to bug: <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116284>
+//static int z2[0];
+//static int y2[__countof__(z2)];
diff --git a/gcc/testsuite/gcc.dg/countof.c b/gcc/testsuite/gcc.dg/countof.c
new file mode 100644
index 00000000000..063a207fb6b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/countof.c
@@ -0,0 +1,150 @@
+/* { dg-do run } */
+/* { dg-options "-Wno-declaration-after-statement -Wno-pedantic -Wno-vla" } */
+
+#undef NDEBUG
+#include <assert.h>
+
+void
+array (void)
+{
+  short a[7];
+
+  static_assert (__countof__ (a) == 7);
+  static_assert (__countof__ (long [0]) == 0);
+  static_assert (__countof__ (unsigned [99]) == 99);
+}
+
+void
+automatic(void)
+{
+  int a[] = {1, 2, 3};
+  int z[] = {};
+
+  static_assert (__countof__ (a) == 3);
+  static_assert (__countof__ (z) == 0);
+}
+
+void
+vla (void)
+{
+  unsigned n;
+
+  n = 99;
+  assert (__countof__ (short [n - 10]) == 99 - 10);
+
+  int v[n / 2];
+  assert (__countof__ (v) == 99 / 2);
+
+  n = 0;
+  int z[n];
+  assert (__countof__ (z) == 0);
+}
+
+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
+inner_vla_noeval (void)
+{
+  int i;
+
+  i = 3;
+  static_assert (__countof__ (struct {int x[i++];}[3]) == 3);
+  assert (i == 3);
+}
+
+void
+array_noeval (void)
+{
+  long a[5];
+  long (*p)[__countof__ (a)];
+
+  p = &a;
+  static_assert (__countof__ (*p++) == 5);
+  assert (p == &a);
+}
+
+void
+matrix_zero (void)
+{
+  int i;
+
+  static_assert (__countof__ (int [0][4]) == 0);
+  i = 3;
+  assert (__countof__ (int [0][i]) == 0);
+}
+
+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 ();
+  automatic ();
+  vla ();
+  member ();
+  vla_eval ();
+  inner_vla_noeval ();
+  array_noeval ();
+  matrix_zero ();
+  matrix_fixed ();
+  matrix_vla ();
+  no_parens ();
+}
-- 
2.45.2

Attachment: signature.asc
Description: PGP signature

Reply via email to