mbox series

[v14,0/4] c: Add __countof__ operator

Message ID cover.1728377205.git.alx@kernel.org
Headers show
Series c: Add __countof__ operator | expand

Message

Alejandro Colomar Oct. 8, 2024, 9:11 a.m. UTC
Hi!

Here's v14 with some changes:

A)  Rename s/__lengthof__/__countof__/  (rationale(s) below).
B)  Remove the concept "top-level array" from the docs (rationale below).

At the bottom of this email is a range diff comparing to v13.

A.1)

WG14 had only weak consensus for prefering lengthof over nelementsof or
countof.  And it didn't really address my concerns of overloading the
term length.  "length" is currently exclusively used in C to refer to
the number of non-zero characters in a string; at least in a consistent
manner.  In uses in the wild, 'len' rarely appears in a variable name
that refers to the number of elements of an array.  In ISO C, one may be
led to think that VLA uses the term length to refer to the number of
elements of the array; but it actually refers to the size[1].  There are
a few residual uses of length to inconsistently refer to the number of
elements of an array, but they are only colloquial uses with no major
relevance.

[1]:  <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3327.pdf>

On the other hand, there's a consistent use of 'n' as a name of a
variable to refer to the *n*umber of elements of an array.

A novel overload of the term length to refer to the number of elements
of an array would be dangerous, as it would naturally promote the use of
'len' as a variable name for holding the number of elements, which would
cause confusion with the length of strings contained in such arrays, and
cause off-by-one bugs, which would in turn result in buffer overflows.

This is a security issue that must be addressed.  lengthof cannot be a
name for this operator.

A.2)

Another argument that was used in favour of length is that other
widely used languages like Python use length to refer to the number of
elements of an array, and for consistency with those languages we should
follow.

That omits the fact that those languages don't use null-terminated
strings, and thus they don't have the security problem that C would have
by overloading length.  In Python, it would seem natural to have length
mean the number of elements of <whatever>, be it number of elements of
an array, or number of elements of a string.  It won't result in
off-by-one bugs and buffer overflows, as would in C.

A.3)

While nelementsof() was considered too long of a name by some, we can
use countof(), which is even shorter than lengthof() (by one byte), and
cannot be confused with anything else that currently exists in the
language.

Also, countof() is consistent with the similar __counted_by() attribute.

B)

C does not have multi-dimensional arrays.  It has a very generic way of
composing aggregate types that allows one to think of multidimensional
arrays, but they're not a distinct concept in the language.  I've
changed the manual to use language that is simpler, and more consistent
with how C really is.


I will present a new paper to WG14 for the next meeting, proposing
countof().

This proposal uses double underscores because C2y is too far, and even
if we used the same name as WG14 wants, we would prefer to have a name
that is under our control, in case WG14 changes anything before the
release of C2y.


Have a lovely day!
Alex


Alejandro Colomar (4):
  contrib/: Add support for Cc: and Link: tags
  gcc/: Rename array_type_nelts() => array_type_nelts_minus_one()
  Merge definitions of array_type_nelts_top()
  c: Add __countof__ operator

 contrib/gcc-changelog/git_commit.py    |   5 +-
 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                        |  32 ++++--
 gcc/c/c-fold.cc                        |   7 +-
 gcc/c/c-parser.cc                      |  62 +++++++---
 gcc/c/c-tree.h                         |   4 +
 gcc/c/c-typeck.cc                      | 118 ++++++++++++++++++-
 gcc/config/aarch64/aarch64.cc          |   2 +-
 gcc/config/i386/i386.cc                |   2 +-
 gcc/cp/cp-tree.h                       |   1 -
 gcc/cp/decl.cc                         |   2 +-
 gcc/cp/init.cc                         |   8 +-
 gcc/cp/lambda.cc                       |   3 +-
 gcc/cp/operators.def                   |   1 +
 gcc/cp/tree.cc                         |  13 ---
 gcc/doc/extend.texi                    |  30 +++++
 gcc/expr.cc                            |   8 +-
 gcc/fortran/trans-array.cc             |   2 +-
 gcc/fortran/trans-openmp.cc            |   4 +-
 gcc/rust/backend/rust-tree.cc          |  13 ---
 gcc/rust/backend/rust-tree.h           |   2 -
 gcc/target.h                           |   3 +
 gcc/testsuite/gcc.dg/countof-compile.c | 115 +++++++++++++++++++
 gcc/testsuite/gcc.dg/countof-vla.c     |  46 ++++++++
 gcc/testsuite/gcc.dg/countof.c         | 150 +++++++++++++++++++++++++
 gcc/tree.cc                            |  17 ++-
 gcc/tree.h                             |   3 +-
 29 files changed, 604 insertions(+), 80 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

Range-diff against v13:
1:  d7fca49888a = 1:  d7fca49888a contrib/: Add support for Cc: and Link: tags
2:  e65245ac294 = 2:  e65245ac294 gcc/: Rename array_type_nelts() => array_type_nelts_minus_one()
3:  03de2d67bb1 = 3:  03de2d67bb1 Merge definitions of array_type_nelts_top()
4:  f635871da1f ! 4:  6714852dd93 c: Add __lengthof__ operator
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    c: Add __lengthof__ 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 __lengthof__ operator.
    -            * target.h (enum type_context_kind): Add __lengthof__ operator.
    +            * doc/extend.texi: Document __countof__ operator.
    +            * target.h (enum type_context_kind): Add __countof__ operator.
     
         gcc/c-family/ChangeLog:
     
                 * c-common.h
                 * c-common.def:
    -            * c-common.cc (c_lengthof_type): Add __lengthof__ operator.
    +            * c-common.cc (c_countof_type): Add __countof__ operator.
     
         gcc/c/ChangeLog:
     
                 * c-tree.h
    -            (c_expr_lengthof_expr, c_expr_lengthof_type)
    +            (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_lengthof_expression)
    -            (c_parser_sizeof_or_lengthof_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_lengthof_expr, c_expr_lengthof_type):
    -            Add __lengthof__operator.
    +            (c_expr_countof_expr, c_expr_countof_type):
    +            Add __countof__operator.
     
         gcc/cp/ChangeLog:
     
    -            * operators.def: Add __lengthof__ operator.
    +            * operators.def: Add __countof__ operator.
     
         gcc/testsuite/ChangeLog:
     
    -            * gcc.dg/lengthof-compile.c
    -            * gcc.dg/lengthof-vla.c
    -            * gcc.dg/lengthof.c: Add tests for __lengthof__ operator.
    +            * gcc.dg/countof-compile.c
    +            * gcc.dg/countof-vla.c
    +            * gcc.dg/countof.c: Add tests for __countof__ operator.
     
         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://inbox.sourceware.org/gcc/M8S4oQy--3-2@tutanota.com/T/>
         Link: <https://github.com/llvm/llvm-project/issues/102836>
         Cc: Joseph Myers <josmyers@redhat.com>
    @@ 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 },
    -+  { "__lengthof__",	RID_LENGTHOF,	0 },
    ++  { "__countof__",	RID_COUNTOF,	0 },
        { "__null",		RID_NULL,	0 },
        { "__real",		RID_REALPART,	0 },
        { "__real__",		RID_REALPART,	0 },
    @@ gcc/c-family/c-common.cc: c_alignof_expr (location_t loc, tree expr)
     +   Return the number of elements of an array.  */
     +
     +tree
    -+c_lengthof_type (location_t loc, tree type)
    ++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 %<lengthof%> 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 %<lengthof%> to incomplete type %qT",
    ++		"invalid application of %<countof%> to incomplete type %qT",
     +		type);
     +      return error_mark_node;
     +    }
    @@ gcc/c-family/c-common.def: DEFTREECODE (EXCESS_PRECISION_EXPR, "excess_precision
         number.  */
      DEFTREECODE (USERDEF_LITERAL, "userdef_literal", tcc_exceptional, 3)
      
    -+/* Represents a 'lengthof' expression.  */
    -+DEFTREECODE (LENGTHOF_EXPR, "lengthof_expr", tcc_expression, 1)
    ++/* 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.  */
    @@ gcc/c-family/c-common.h: enum rid
      
        /* C extensions */
        RID_ASM,       RID_TYPEOF,   RID_TYPEOF_UNQUAL, RID_ALIGNOF,  RID_ATTRIBUTE,
    -+  RID_LENGTHOF,
    ++  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,
    @@ gcc/c-family/c-common.h: extern tree c_common_truthvalue_conversion (location_t,
      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_lengthof_type (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);
    @@ gcc/c/c-decl.cc: start_struct (location_t loc, enum tree_code code, tree name,
           sizeof anyhow.  */
     -  if (warn_cxx_compat && (in_sizeof || in_typeof || in_alignof))
     +  if (warn_cxx_compat
    -+      && (in_sizeof || in_typeof || in_alignof || in_lengthof))
    ++      && (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
    @@ gcc/c/c-decl.cc: start_struct (location_t loc, enum tree_code code, tree name,
     +		    ? "typeof"
     +		    : (in_alignof
     +		       ? "alignof"
    -+		       : "lengthof"))));
    ++		       : "countof"))));
      
        if (in_underspecified_init)
          error_at (loc, "%qT defined in underspecified object initializer", ref);
    @@ gcc/c/c-decl.cc: finish_struct (location_t loc, tree t, tree fieldlist, tree att
            if (warn_cxx_compat
      	  && struct_parse_info != NULL
     -	  && !in_sizeof && !in_typeof && !in_alignof)
    -+	  && !in_sizeof && !in_typeof && !in_alignof && !in_lengthof)
    ++	  && !in_sizeof && !in_typeof && !in_alignof && !in_countof)
      	struct_parse_info->struct_types.safe_push (t);
           }
      
    @@ gcc/c/c-decl.cc: start_enum (location_t loc, struct c_enum_contents *the_enum, t
           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_lengthof))
    ++      && (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
    @@ gcc/c/c-decl.cc: start_enum (location_t loc, struct c_enum_contents *the_enum, t
     +		    ? "typeof"
     +		    : (in_alignof
     +		       ? "alignof"
    -+		       : "lengthof"))));
    ++		       : "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)
        if (warn_cxx_compat
            && struct_parse_info != NULL
     -      && !in_sizeof && !in_typeof && !in_alignof)
    -+      && !in_sizeof && !in_typeof && !in_alignof && !in_lengthof)
    ++      && !in_sizeof && !in_typeof && !in_alignof && !in_countof)
          struct_parse_info->struct_types.safe_push (enumtype);
      
        /* Check for consistency with previous definition */
    @@ gcc/c/c-parser.cc: along with GCC; see the file COPYING3.  If not see
     +
     +#define c_parser_sizeof_expression(parser)                                    \
     +(                                                                             \
    -+  c_parser_sizeof_or_lengthof_expression (parser, RID_SIZEOF)                 \
    ++  c_parser_sizeof_or_countof_expression (parser, RID_SIZEOF)                  \
     +)
      
    -+#define c_parser_lengthof_expression(parser)                                  \
    ++#define c_parser_countof_expression(parser)                                   \
     +(                                                                             \
    -+  c_parser_sizeof_or_lengthof_expression (parser, RID_LENGTHOF)               \
    ++  c_parser_sizeof_or_countof_expression (parser, RID_COUNTOF)                 \
     +)
     +
      /* We need to walk over decls with incomplete struct/union/enum types
    @@ gcc/c/c-parser.cc: static struct c_expr c_parser_binary_expression (c_parser *,
      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_lengthof_expression (c_parser *,
    -+								enum rid);
    ++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 *,
    @@ gcc/c/c-parser.cc: c_parser_unary_expression (c_parser *parser)
          case CPP_KEYWORD:
            switch (c_parser_peek_token (parser)->keyword)
      	{
    -+	case RID_LENGTHOF:
    -+	  return c_parser_lengthof_expression (parser);
    ++	case RID_COUNTOF:
    ++	  return c_parser_countof_expression (parser);
      	case RID_SIZEOF:
      	  return c_parser_sizeof_expression (parser);
      	case RID_ALIGNOF:
    @@ gcc/c/c-parser.cc: c_parser_unary_expression (c_parser *parser)
      
      static struct c_expr
     -c_parser_sizeof_expression (c_parser *parser)
    -+c_parser_sizeof_or_lengthof_expression (c_parser *parser, enum rid rid)
    ++c_parser_sizeof_or_countof_expression (c_parser *parser, enum rid rid)
      {
    -+  const char *op_name = (rid == RID_LENGTHOF) ? "lengthof" : "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_parser_consume_token (parser);
        c_inhibit_evaluation_warnings++;
     -  in_sizeof++;
    -+  if (rid == RID_LENGTHOF)
    -+    in_lengthof++;
    ++  if (rid == RID_COUNTOF)
    ++    in_countof++;
     +  else
     +    in_sizeof++;
        if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)
    @@ gcc/c/c-parser.cc: c_parser_sizeof_expression (c_parser *parser)
      	  struct c_expr ret;
      	  c_inhibit_evaluation_warnings--;
     -	  in_sizeof--;
    -+	  if (rid == RID_LENGTHOF)
    -+	    in_lengthof--;
    ++	  if (rid == RID_COUNTOF)
    ++	    in_countof--;
     +	  else
     +	    in_sizeof--;
      	  ret.set_error ();
    @@ gcc/c/c-parser.cc: c_parser_sizeof_expression (c_parser *parser)
            c_inhibit_evaluation_warnings--;
     -      in_sizeof--;
     -      result = c_expr_sizeof_type (expr_loc, type_name);
    -+      if (rid == RID_LENGTHOF)
    ++      if (rid == RID_COUNTOF)
     +	{
    -+	  in_lengthof--;
    -+	  result = c_expr_lengthof_type (expr_loc, type_name);
    ++	  in_countof--;
    ++	  result = c_expr_countof_type (expr_loc, type_name);
     +	}
     +      else
     +	{
    @@ gcc/c/c-parser.cc: c_parser_sizeof_expression (c_parser *parser)
     +    Xof_expr:
            c_inhibit_evaluation_warnings--;
     -      in_sizeof--;
    -+      if (rid == RID_LENGTHOF)
    -+	in_lengthof--;
    ++      if (rid == RID_COUNTOF)
    ++	in_countof--;
     +      else
     +	in_sizeof--;
            mark_exp_read (expr.value);
    @@ gcc/c/c-parser.cc: c_parser_sizeof_expression (c_parser *parser)
     -	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_LENGTHOF)
    -+	result = c_expr_lengthof_expr (expr_loc, expr);
    ++      if (rid == RID_COUNTOF)
    ++	result = c_expr_countof_expr (expr_loc, expr);
     +      else
     +	result = c_expr_sizeof_expr (expr_loc, expr);
          }
    @@ gcc/c/c-tree.h: extern int c_type_dwarf_attribute (const_tree, int);
      /* in c-typeck.cc */
      extern int in_alignof;
      extern int in_sizeof;
    -+extern int in_lengthof;
    ++extern int in_countof;
      extern int in_typeof;
      extern bool c_in_omp_for;
      extern bool c_omp_array_section_p;
    @@ gcc/c/c-tree.h: 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_lengthof_expr (location_t, struct c_expr);
    -+extern struct c_expr c_expr_lengthof_type (location_t loc,
    -+					      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,
    @@ gcc/c/c-typeck.cc: int in_alignof;
      /* The level of nesting inside "sizeof".  */
      int in_sizeof;
      
    -+/* The level of nesting inside "lengthof".  */
    -+int in_lengthof;
    ++/* The level of nesting inside "countof".  */
    ++int in_countof;
     +
      /* The level of nesting inside "typeof".  */
      int in_typeof;
    @@ gcc/c/c-typeck.cc: build_external_ref (location_t loc, tree id, bool fun, tree *
        if (TREE_CODE (ref) == FUNCTION_DECL && !in_alignof)
          {
     -      if (!in_sizeof && !in_typeof)
    -+      if (!in_sizeof && !in_typeof && !in_lengthof)
    ++      if (!in_sizeof && !in_typeof && !in_countof)
      	C_DECL_USED (ref) = 1;
            else if (DECL_INITIAL (ref) == NULL_TREE
      	       && DECL_EXTERNAL (ref)
    @@ gcc/c/c-typeck.cc: 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_lengthof).  */
    ++  /* 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;
    @@ gcc/c/c-typeck.cc: 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_lengthof;
    ++  t->level = in_sizeof + in_typeof + in_countof;
        t->next = maybe_used_decls;
        maybe_used_decls = t;
      }
    @@ gcc/c/c-typeck.cc: void
      {
        struct maybe_used_decl *p = maybe_used_decls;
     -  int cur_level = in_sizeof + in_typeof;
    -+  int cur_level = in_sizeof + in_typeof + in_lengthof;
    ++  int cur_level = in_sizeof + in_typeof + in_countof;
        while (p && p->level > cur_level)
          {
            if (used)
    @@ gcc/c/c-typeck.cc: c_expr_sizeof_type (location_t loc, struct c_type_name *t)
     +  return var;
     +}
     +
    -+/* Return the result of lengthof applied to EXPR.  */
    ++/* Return the result of countof applied to EXPR.  */
     +
     +struct c_expr
    -+c_expr_lengthof_expr (location_t loc, struct c_expr expr)
    ++c_expr_countof_expr (location_t loc, struct c_expr expr)
     +{
     +  struct c_expr ret;
     +  if (expr.value == error_mark_node)
    @@ gcc/c/c-typeck.cc: c_expr_sizeof_type (location_t loc, struct c_type_name *t)
     +
     +      tree folded_expr = c_fully_fold (expr.value, require_constant_value,
     +				       &expr_const_operands);
    -+      ret.value = c_lengthof_type (loc, TREE_TYPE (folded_expr));
    ++      ret.value = c_countof_type (loc, TREE_TYPE (folded_expr));
     +      c_last_sizeof_arg = expr.value;
     +      c_last_sizeof_loc = loc;
    -+      ret.original_code = LENGTHOF_EXPR;
    ++      ret.original_code = COUNTOF_EXPR;
     +      ret.original_type = NULL;
     +      ret.m_decimal = 0;
     +      if (is_top_array_vla (TREE_TYPE (folded_expr)))
     +	{
    -+	  /* lengthof is evaluated when given a vla.  */
    ++	  /* 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;
    @@ gcc/c/c-typeck.cc: c_expr_sizeof_type (location_t loc, struct c_type_name *t)
     +  return ret;
     +}
     +
    -+/* Return the result of lengthof applied to T, a structure for the type
    -+   name passed to lengthof (rather than the type itself).  LOC is the
    ++/* 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_lengthof_type (location_t loc, struct c_type_name *t)
    ++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_lengthof_type (loc, type);
    ++  ret.value = c_countof_type (loc, type);
     +  c_last_sizeof_arg = type;
     +  c_last_sizeof_loc = loc;
    -+  ret.original_code = LENGTHOF_EXPR;
    ++  ret.original_code = COUNTOF_EXPR;
     +  ret.original_type = NULL;
     +  ret.m_decimal = 0;
     +  if (type == error_mark_node)
    @@ gcc/c/c-typeck.cc: c_expr_sizeof_type (location_t loc, struct c_type_name *t)
     +    {
     +      /* 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 lengthof does not get folded to a constant by
    ++	 the result of countof does not get folded to a constant by
     +	 c_fully_fold, because if the length is evaluated the result is
     +	 not constant and so constraints on zero or negative size
    -+	 arrays must not be applied when this lengthof call is inside
    ++	 arrays must not be applied when this countof call is inside
     +	 another array declarator.  */
     +      if (!type_expr)
     +	type_expr = integer_zero_node;
    @@ gcc/cp/operators.def: DEF_OPERATOR ("co_await", CO_AWAIT_EXPR, "aw", OVL_OP_FLAG
      
      /* These are extensions.  */
      DEF_OPERATOR ("alignof", ALIGNOF_EXPR, "az", OVL_OP_FLAG_UNARY)
    -+DEF_OPERATOR ("__lengthof__", LENGTHOF_EXPR, "lz", OVL_OP_FLAG_UNARY)
    ++DEF_OPERATOR ("__countof__", COUNTOF_EXPR, "lz", OVL_OP_FLAG_UNARY)
      DEF_OPERATOR ("__imag__", IMAGPART_EXPR, "v18__imag__", OVL_OP_FLAG_UNARY)
      DEF_OPERATOR ("__real__", REALPART_EXPR, "v18__real__", OVL_OP_FLAG_UNARY)
      
    @@ gcc/doc/extend.texi: If the operand of the @code{__alignof__} expression is a fu
      the expression evaluates to the alignment of the function which may
      be specified by attribute @code{aligned} (@pxref{Common Function Attributes}).
      
    -+@node lengthof
    ++@node countof
     +@section Determining the Number of Elements of Arrays
    -+@cindex lengthof
    ++@cindex countof
     +@cindex number of elements
     +
    -+The keyword @code{__lengthof__} determines the length of an array operand,
    ++The keyword @code{__countof__} determines the length of an array operand,
     +that is, the number of elements in the array.
     +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];
    -+__lengthof__ (a);  // returns n
    -+__lengthof__ (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,
    -+unless the top-level array is a variable-length array.
    ++unless the array has a variable number of elements.
     +The operand is only evaluated
    -+if the top-level array is a variable-length array.
    ++if the array has a variable number of elements.
     +For example:
     +
     +@smallexample
    -+__lengthof__ (int [7][n++]);  // integer constant expression
    -+__lengthof__ (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/target.h: enum type_context_kind {
        TCTX_ALIGNOF,
      
     +  /* Directly measuring the number of elements of array T.  */
    -+  TCTX_LENGTHOF,
    ++  TCTX_COUNTOF,
     +
        /* Creating objects of type T with static storage duration.  */
        TCTX_STATIC_STORAGE,
      
     
    - ## gcc/testsuite/gcc.dg/nelementsof-compile.c (new) ##
    + ## gcc/testsuite/gcc.dg/countof-compile.c (new) ##
     @@
     +/* { dg-do compile } */
     +/* { dg-options "-Wno-declaration-after-statement -Wno-pedantic -Wno-vla" } */
    @@ gcc/testsuite/gcc.dg/nelementsof-compile.c (new)
     +static int w[] = {1, 2, 3};
     +
     +static int z[0];
    -+static int y[__lengthof__(z)];
    ++static int y[__countof__(z)];
     +
     +void
     +automatic(void)
     +{
    -+  __lengthof__ (w);
    ++  __countof__ (w);
     +}
     +
     +void
     +incomplete (int p[])
     +{
    -+  __lengthof__ (x);  /* { dg-error "incomplete" } */
    ++  __countof__ (x);  /* { dg-error "incomplete" } */
     +
     +  /* We want to support the following one in the future,
     +     but for now it should fail.  */
    -+  __lengthof__ (p);  /* { dg-error "invalid" } */
    ++  __countof__ (p);  /* { dg-error "invalid" } */
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/nelementsof-compile.c (new)
     +    int fam[];
     +  } s;
     +
    -+  __lengthof__ (s.fam); /* { dg-error "incomplete" } */
    ++  __countof__ (s.fam); /* { dg-error "incomplete" } */
     +}
     +
    -+void fix_fix (int i, char (*a)[3][5], int (*x)[__lengthof__ (*a)]);
    -+void fix_var (int i, char (*a)[3][i], int (*x)[__lengthof__ (*a)]);
    -+void fix_uns (int i, char (*a)[3][*], int (*x)[__lengthof__ (*a)]);
    ++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)
    @@ gcc/testsuite/gcc.dg/nelementsof-compile.c (new)
     +    int x[3];
     +  } s;
     +
    -+  __lengthof__ (x); /* { dg-error "invalid" } */
    -+  __lengthof__ (int); /* { dg-error "invalid" } */
    -+  __lengthof__ (s); /* { dg-error "invalid" } */
    -+  __lengthof__ (struct s); /* { dg-error "invalid" } */
    -+  __lengthof__ (&x); /* { dg-error "invalid" } */
    -+  __lengthof__ (p); /* { dg-error "invalid" } */
    -+  __lengthof__ (int *); /* { dg-error "invalid" } */
    -+  __lengthof__ (&s.x); /* { dg-error "invalid" } */
    -+  __lengthof__ (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();
    @@ gcc/testsuite/gcc.dg/nelementsof-compile.c (new)
     +{
     +  int b[n][n];
     +
    -+  __lengthof__ (a[f1()]);
    -+  __lengthof__ (b[f2()]);
    ++  __countof__ (a[f1()]);
    ++  __countof__ (b[f2()]);
     +}
     +
     +void
     +no_parens(void)
     +{
    -+  __lengthof__ a;
    -+  __lengthof__ *a;
    -+  __lengthof__ (int [3]) {};
    ++  __countof__ a;
    ++  __countof__ *a;
    ++  __countof__ (int [3]) {};
     +
    -+  __lengthof__ int [3]; /* { dg-error "expected expression before" } */
    ++  __countof__ int [3]; /* { dg-error "expected expression before" } */
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/nelementsof-compile.c (new)
     +{
     +  int n = 7;
     +
    -+  _Static_assert (__lengthof__ (int [3][n]) == 3);
    -+  _Static_assert (__lengthof__ (int [n][3]) == 7); /* { dg-error "not constant" } */
    -+  _Static_assert (__lengthof__ (int [0][3]) == 0);
    -+  _Static_assert (__lengthof__ (int [0]) == 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);
     +
    -+  /* FIXME: lengthof(int [0][n]) should result in a constant expression.  */
    -+  _Static_assert (__lengthof__ (int [0][n]) == 0); /* { dg-error "not constant" } */
    ++  /* FIXME: countof(int [0][n]) should result in a constant expression.  */
    ++  _Static_assert (__countof__ (int [0][n]) == 0); /* { dg-error "not constant" } */
     +}
     
    - ## gcc/testsuite/gcc.dg/nelementsof-vla.c (new) ##
    + ## gcc/testsuite/gcc.dg/countof-vla.c (new) ##
     @@
     +/* { dg-do compile } */
     +/* { dg-options "-Wno-pedantic -Wvla-parameter" } */
     +
     +void fix_fix (int i,
     +	      char (*a)[3][5],
    -+	      int (*x)[__lengthof__ (*a)]);
    ++	      int (*x)[__countof__ (*a)]);
     +void fix_var (int i,
     +	      char (*a)[3][i], /* dg-warn "variable" */
    -+	      int (*x)[__lengthof__ (*a)]);
    ++	      int (*x)[__countof__ (*a)]);
     +void fix_uns (int i,
     +	      char (*a)[3][*],
    -+	      int (*x)[__lengthof__ (*a)]);
    ++	      int (*x)[__countof__ (*a)]);
     +
     +void zro_fix (int i,
     +	      char (*a)[0][5],
    -+	      int (*x)[__lengthof__ (*a)]);
    ++	      int (*x)[__countof__ (*a)]);
     +void zro_var (int i,
     +	      char (*a)[0][i], /* dg-warn "variable" */
    -+	      int (*x)[__lengthof__ (*a)]);
    ++	      int (*x)[__countof__ (*a)]);
     +void zro_uns (int i,
     +	      char (*a)[0][*],
    -+	      int (*x)[__lengthof__ (*a)]);
    ++	      int (*x)[__countof__ (*a)]);
     +
     +void var_fix (int i,
     +	      char (*a)[i][5], /* dg-warn "variable" */
    -+	      int (*x)[__lengthof__ (*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)[__lengthof__ (*a)]); /* dg-warn "variable" */
    ++	      int (*x)[__countof__ (*a)]); /* dg-warn "variable" */
     +void var_uns (int i,
     +	      char (*a)[i][*], /* dg-warn "variable" */
    -+	      int (*x)[__lengthof__ (*a)]); /* dg-warn "variable" */
    ++	      int (*x)[__countof__ (*a)]); /* dg-warn "variable" */
     +
     +void uns_fix (int i,
     +	      char (*a)[*][5],
    -+	      int (*x)[__lengthof__ (*a)]);
    ++	      int (*x)[__countof__ (*a)]);
     +void uns_var (int i,
     +	      char (*a)[*][i], /* dg-warn "variable" */
    -+	      int (*x)[__lengthof__ (*a)]);
    ++	      int (*x)[__countof__ (*a)]);
     +void uns_uns (int i,
     +	      char (*a)[*][*],
    -+	      int (*x)[__lengthof__ (*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[__lengthof__(z2)];
    ++//static int y2[__countof__(z2)];
     
    - ## gcc/testsuite/gcc.dg/nelementsof.c (new) ##
    + ## gcc/testsuite/gcc.dg/countof.c (new) ##
     @@
     +/* { dg-do run } */
     +/* { dg-options "-Wno-declaration-after-statement -Wno-pedantic -Wno-vla" } */
    @@ gcc/testsuite/gcc.dg/nelementsof.c (new)
     +{
     +  short a[7];
     +
    -+  static_assert (__lengthof__ (a) == 7);
    -+  static_assert (__lengthof__ (long [0]) == 0);
    -+  static_assert (__lengthof__ (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/nelementsof.c (new)
     +  int a[] = {1, 2, 3};
     +  int z[] = {};
     +
    -+  static_assert (__lengthof__ (a) == 3);
    -+  static_assert (__lengthof__ (z) == 0);
    ++  static_assert (__countof__ (a) == 3);
    ++  static_assert (__countof__ (z) == 0);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/nelementsof.c (new)
     +  unsigned n;
     +
     +  n = 99;
    -+  assert (__lengthof__ (short [n - 10]) == 99 - 10);
    ++  assert (__countof__ (short [n - 10]) == 99 - 10);
     +
     +  int v[n / 2];
    -+  assert (__lengthof__ (v) == 99 / 2);
    ++  assert (__countof__ (v) == 99 / 2);
     +
     +  n = 0;
     +  int z[n];
    -+  assert (__lengthof__ (z) == 0);
    ++  assert (__countof__ (z) == 0);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/nelementsof.c (new)
     +    int a[8];
     +  } s;
     +
    -+  static_assert (__lengthof__ (s.a) == 8);
    ++  static_assert (__countof__ (s.a) == 8);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/nelementsof.c (new)
     +  int i;
     +
     +  i = 7;
    -+  assert (__lengthof__ (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 (__lengthof__ (*p++) == i);
    ++  assert (__countof__ (*p++) == i);
     +  assert (p - 1 == &v);
     +}
     +
    @@ gcc/testsuite/gcc.dg/nelementsof.c (new)
     +  int i;
     +
     +  i = 3;
    -+  static_assert (__lengthof__ (struct {int x[i++];}[3]) == 3);
    ++  static_assert (__countof__ (struct {int x[i++];}[3]) == 3);
     +  assert (i == 3);
     +}
     +
    @@ gcc/testsuite/gcc.dg/nelementsof.c (new)
     +array_noeval (void)
     +{
     +  long a[5];
    -+  long (*p)[__lengthof__ (a)];
    ++  long (*p)[__countof__ (a)];
     +
     +  p = &a;
    -+  static_assert (__lengthof__ (*p++) == 5);
    ++  static_assert (__countof__ (*p++) == 5);
     +  assert (p == &a);
     +}
     +
    @@ gcc/testsuite/gcc.dg/nelementsof.c (new)
     +{
     +  int i;
     +
    -+  static_assert (__lengthof__ (int [0][4]) == 0);
    ++  static_assert (__countof__ (int [0][4]) == 0);
     +  i = 3;
    -+  assert (__lengthof__ (int [0][i]) == 0);
    ++  assert (__countof__ (int [0][i]) == 0);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/nelementsof.c (new)
     +{
     +  int i;
     +
    -+  static_assert (__lengthof__ (int [7][4]) == 7);
    ++  static_assert (__countof__ (int [7][4]) == 7);
     +  i = 3;
    -+  static_assert (__lengthof__ (int [7][i]) == 7);
    ++  static_assert (__countof__ (int [7][i]) == 7);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/nelementsof.c (new)
     +  int i, j;
     +
     +  i = 7;
    -+  assert (__lengthof__ (int [i++][4]) == 7);
    ++  assert (__countof__ (int [i++][4]) == 7);
     +  assert (i == 7 + 1);
     +
     +  i = 9;
     +  j = 3;
    -+  assert (__lengthof__ (int [i++][j]) == 9);
    ++  assert (__countof__ (int [i++][j]) == 9);
     +  assert (i == 9 + 1);
     +}
     +
    @@ gcc/testsuite/gcc.dg/nelementsof.c (new)
     +  int a[7];
     +  int v[n];
     +
    -+  static_assert (__lengthof__ a == 7); 
    -+  assert (__lengthof__ v == 3); 
    ++  static_assert (__countof__ a == 7); 
    ++  assert (__countof__ v == 3); 
     +}
     +
     +int