mbox series

[v10,0/3] c: Add __nelementsof__ operator

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

Message

Alejandro Colomar Aug. 19, 2024, 10:58 a.m. UTC
Hi!

This is v10 of this patch set; hopefully, we're close to an end.

I've already submitted a proposal for C2y to WG14, as n3313:
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3313.pdf>

For those who haven't been following since the start, the entire thread
of patch set revisions and their discussions can be found here:
<https://inbox.sourceware.org/gcc-patches/20240728141547.302478-1-alx@kernel.org/T/#t>
It also contains drafts of the n3313 proposal.

Changes since v9:

-  Rename s/__elementsof__/__nelementsof__/

   elementsof() doesn't mean in English something compatible with its
   programming semantics.  While there's existing uses of it in
   programming, that's an abuse of English, which could cause confusion,
   or maybe preclude a more appropriate use of that name in the future.

   nelementsof() is just one more byte, and is much more appropriate, by
   being a contraction of "_n_umber (of) elements of".

   Also, there's only one use of nelementsof() in the wild, which makes
   backwards compatibility much less of a concern.  That use is
   semantically compatible with this proposed operator.  And we could
   just notify that project the existence of a new standard operator
   with that name, if we finally settle on this name.

-  Rebase on top of git HEAD.

-  CC +=
	Daniel Lundin, Nikolaos, JeanHeyd, Fernando, Jonathan, Chris,
	Ville, Alex Celeste, Jakub Łukasiewicz

I have a draft for an updated proposal to WG14 (more recent than n3313).
It has some changes to the documentation of prior art, some typo fixes,
etc., and renames elementsof()=>nelementsof(), but the meat of the
proposal is the same.  I've also rebased it on top of the latest draft
for C2y, which is n3301.  I'll send it as a reply to this subthread.

This cover letter is sent to both gcc-patches@ and gcc@.  The patches
are only sent to gcc-patches@, and the draft for WG14 is only sent to
gcc@.

If anyone wants to add an `Acked-by:` (or `Reviewed-by:`), please do it
explicitly.  There've been some informal ones, but I prefer to pick them
from explicit ones.


Have a lovely day!

Alex


Alejandro Colomar (3):
  gcc/: Rename array_type_nelts() => array_type_nelts_minus_one()
  Merge definitions of array_type_nelts_top()
  c: Add __nelementsof__ operator

 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/nelementsof-compile.c | 115 ++++++++++++++++
 gcc/testsuite/gcc.dg/nelementsof-vla.c     |  46 +++++++
 gcc/testsuite/gcc.dg/nelementsof.c         | 150 +++++++++++++++++++++
 gcc/tree.cc                                |  17 ++-
 gcc/tree.h                                 |   3 +-
 28 files changed, 600 insertions(+), 79 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/nelementsof-compile.c
 create mode 100644 gcc/testsuite/gcc.dg/nelementsof-vla.c
 create mode 100644 gcc/testsuite/gcc.dg/nelementsof.c

Range-diff against v9:
1:  a6aa38c9013 = 1:  ab72c4cee8f gcc/: Rename array_type_nelts() => array_type_nelts_minus_one()
2:  4ce16ee4dfe ! 2:  27852be4ac0 Merge definitions of array_type_nelts_top()
    @@ Commit message
         Merge definitions of array_type_nelts_top()
     
         There were two identical definitions, and none of them are available
    -    where they are needed for implementing __elementsof__.  Merge them, and
    +    where they are needed for implementing __nelementsof__.  Merge them, and
         provide the single definition in gcc/tree.{h,cc}, where it's available
    -    for __elementsof__, which will be added in the following commit.
    +    for __nelementsof__, which will be added in the following commit.
     
         gcc/ChangeLog:
     
3:  caae5dbecb3 ! 3:  9c78ce1f66d c: Add __elementsof__ operator
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    c: Add __elementsof__ operator
    +    c: Add __nelementsof__ operator
     
         This operator is similar to sizeof but can only be applied to an array,
         and returns its number of elements.
    @@ Commit message
         Cc: Eugene Zelenko <eugene.zelenko@gmail.com>
         Cc: Aaron Ballman <aaron.ballman@intel.com>
         Cc: Paul Koning <paulkoning@comcast.net>
    +    Cc: Daniel Lundin <daniel.lundin.mail@gmail.com>
    +    Cc: Nikolaos Strimpas <Strnik86@protonmail.com>
    +    Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
    +    Cc: Fernando Borretti <fernando@borretti.me>
    +    Cc: Jonathan Protzenko <jonathan.protzenko@ens-lyon.org>
    +    Cc: Chris Bazley <Chris.Bazley@arm.com>
    +    Cc: Ville Voutilainen <ville.voutilainen@gmail.com>
    +    Cc: Alex Celeste <alexg.nvfp@gmail.com>
    +    Cc: Jakub Łukasiewicz <jakublukasiewicz@outlook.com>
     
         gcc/ChangeLog:
     
    -            * doc/extend.texi: Document __elementsof__ operator.
    -            * target.h (enum type_context_kind): Add __elementsof__ operator.
    +            * doc/extend.texi: Document __nelementsof__ operator.
    +            * target.h (enum type_context_kind): Add __nelementsof__ operator.
     
         gcc/c-family/ChangeLog:
     
                 * c-common.h:
                 * c-common.def:
    -            * c-common.cc (c_elementsof_type): Add __elementsof__ operator.
    +            * c-common.cc (c_nelementsof_type): Add __nelementsof__ operator.
     
         gcc/c/ChangeLog:
     
                 * c-tree.h
    -            (c_expr_elementsof_expr, c_expr_elementsof_type):
    +            (c_expr_nelementsof_expr, c_expr_nelementsof_type):
                 * c-decl.cc
                 (start_struct, finish_struct):
                 (start_enum, finish_enum):
                 * c-parser.cc
                 (c_parser_sizeof_expression):
    -            (c_parser_elementsof_expression):
    -            (c_parser_sizeof_or_elementsof_expression):
    +            (c_parser_nelementsof_expression):
    +            (c_parser_sizeof_or_nelementsof_expression):
                 (c_parser_unary_expression):
                 * c-typeck.cc
                 (build_external_ref):
                 (record_maybe_used_decl, pop_maybe_used):
                 (is_top_array_vla):
    -            (c_expr_elementsof_expr, c_expr_elementsof_type):
    -            Add __elementsof__operator.
    +            (c_expr_nelementsof_expr, c_expr_nelementsof_type):
    +            Add __nelementsof__operator.
     
         gcc/cp/ChangeLog:
     
    -            * operators.def: Add __elementsof__ operator.
    +            * operators.def: Add __nelementsof__ operator.
     
         gcc/testsuite/ChangeLog:
     
    -            * gcc.dg/elementsof-compile.c:
    -            * gcc.dg/elementsof-vla.c:
    -            * gcc.dg/elementsof.c: Add tests for __elementsof__ operator.
    +            * gcc.dg/nelementsof-compile.c:
    +            * gcc.dg/nelementsof-vla.c:
    +            * gcc.dg/nelementsof.c: Add tests for __nelementsof__ operator.
     
    -    Link: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2529.pdf
    +    Link: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3313.pdf
         Link: https://inbox.sourceware.org/gcc/M8S4oQy--3-2@tutanota.com/T/
         Link: https://github.com/llvm/llvm-project/issues/102836
         Suggested-by: Xavier Del Campo Romero <xavi.dcr@tutanota.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 },
    -+  { "__elementsof__",	RID_ELEMENTSOF, 0 },
    ++  { "__nelementsof__",	RID_NELEMENTSOF, 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_elementsof_type (location_t loc, tree type)
    ++c_nelementsof_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 %<elementsof%> to type %qT", type);
    ++      error_at (loc, "invalid application of %<nelementsof%> to type %qT", type);
     +      return error_mark_node;
     +    }
     +  if (!COMPLETE_TYPE_P (type))
     +    {
     +      error_at (loc,
    -+		"invalid application of %<elementsof%> to incomplete type %qT",
    ++		"invalid application of %<nelementsof%> 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 'elementsof' expression.  */
    -+DEFTREECODE (ELEMENTSOF_EXPR, "elementsof_expr", tcc_expression, 1)
    ++/* Represents a 'nelementsof' expression.  */
    ++DEFTREECODE (NELEMENTSOF_EXPR, "nelementsof_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_ELEMENTSOF,
    ++  RID_NELEMENTSOF,
        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_elementsof_type (location_t, tree);
    ++extern tree c_nelementsof_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_elementsof))
    ++      && (in_sizeof || in_typeof || in_alignof || in_nelementsof))
          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"
    -+		       : "elementsof"))));
    ++		       : "nelementsof"))));
      
        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_elementsof)
    ++	  && !in_sizeof && !in_typeof && !in_alignof && !in_nelementsof)
      	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
           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_elementsof))
    ++  if (warn_cxx_compat
    ++      && (in_sizeof || in_typeof || in_alignof || in_nelementsof))
          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"
    -+		       : "elementsof"))));
    ++		       : "nelementsof"))));
      
        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_elementsof)
    ++      && !in_sizeof && !in_typeof && !in_alignof && !in_nelementsof)
          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_elementsof_expression (parser, RID_SIZEOF)               \
    ++  c_parser_sizeof_or_nelementsof_expression (parser, RID_SIZEOF)              \
     +)
      
    -+#define c_parser_elementsof_expression(parser)                                \
    ++#define c_parser_nelementsof_expression(parser)                               \
     +(                                                                             \
    -+  c_parser_sizeof_or_elementsof_expression (parser, RID_ELEMENTSOF)           \
    ++  c_parser_sizeof_or_nelementsof_expression (parser, RID_NELEMENTSOF)         \
     +)
     +
      /* 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_elementsof_expression (c_parser *,
    -+							       enum rid);
    ++static struct c_expr c_parser_sizeof_or_nelementsof_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_ELEMENTSOF:
    -+	  return c_parser_elementsof_expression (parser);
    ++	case RID_NELEMENTSOF:
    ++	  return c_parser_nelementsof_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_elementsof_expression (c_parser *parser, enum rid rid)
    ++c_parser_sizeof_or_nelementsof_expression (c_parser *parser, enum rid rid)
      {
    -+  const char *op_name = (rid == RID_ELEMENTSOF) ? "elementsof" : "sizeof";
    ++  const char *op_name = (rid == RID_NELEMENTSOF) ? "nelementsof" : "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_ELEMENTSOF)
    -+    in_elementsof++;
    ++  if (rid == RID_NELEMENTSOF)
    ++    in_nelementsof++;
     +  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_ELEMENTSOF)
    -+	    in_elementsof--;
    ++	  if (rid == RID_NELEMENTSOF)
    ++	    in_nelementsof--;
     +	  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_ELEMENTSOF)
    ++      if (rid == RID_NELEMENTSOF)
     +	{
    -+	  in_elementsof--;
    -+	  result = c_expr_elementsof_type (expr_loc, type_name);
    ++	  in_nelementsof--;
    ++	  result = c_expr_nelementsof_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_ELEMENTSOF)
    -+	in_elementsof--;
    ++      if (rid == RID_NELEMENTSOF)
    ++	in_nelementsof--;
     +      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_ELEMENTSOF)
    -+	result = c_expr_elementsof_expr (expr_loc, expr);
    ++      if (rid == RID_NELEMENTSOF)
    ++	result = c_expr_nelementsof_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_elementsof;
    ++extern int in_nelementsof;
      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_elementsof_expr (location_t, struct c_expr);
    -+extern struct c_expr c_expr_elementsof_type (location_t loc,
    -+					     struct c_type_name *);
    ++extern struct c_expr c_expr_nelementsof_expr (location_t, struct c_expr);
    ++extern struct c_expr c_expr_nelementsof_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 "elementsof".  */
    -+int in_elementsof;
    ++/* The level of nesting inside "nelementsof".  */
    ++int in_nelementsof;
     +
      /* 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_elementsof)
    ++      if (!in_sizeof && !in_typeof && !in_nelementsof)
      	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_elementsof).  */
    ++  /* The level seen at (in_sizeof + in_typeof + in_nelementsof).  */
        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_elementsof;
    ++  t->level = in_sizeof + in_typeof + in_nelementsof;
        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_elementsof;
    ++  int cur_level = in_sizeof + in_typeof + in_nelementsof;
        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 elementsof applied to EXPR.  */
    ++/* Return the result of nelementsof applied to EXPR.  */
     +
     +struct c_expr
    -+c_expr_elementsof_expr (location_t loc, struct c_expr expr)
    ++c_expr_nelementsof_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_elementsof_type (loc, TREE_TYPE (folded_expr));
    ++      ret.value = c_nelementsof_type (loc, TREE_TYPE (folded_expr));
     +      c_last_sizeof_arg = expr.value;
     +      c_last_sizeof_loc = loc;
    -+      ret.original_code = ELEMENTSOF_EXPR;
    ++      ret.original_code = NELEMENTSOF_EXPR;
     +      ret.original_type = NULL;
     +      ret.m_decimal = 0;
     +      if (is_top_array_vla (TREE_TYPE (folded_expr)))
     +	{
    -+	  /* elementsof is evaluated when given a vla.  */
    ++	  /* nelementsof 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 elementsof applied to T, a structure for the type
    -+   name passed to elementsof (rather than the type itself).  LOC is the
    ++/* Return the result of nelementsof applied to T, a structure for the type
    ++   name passed to nelementsof (rather than the type itself).  LOC is the
     +   location of the original expression.  */
     +
     +struct c_expr
    -+c_expr_elementsof_type (location_t loc, struct c_type_name *t)
    ++c_expr_nelementsof_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_elementsof_type (loc, type);
    ++  ret.value = c_nelementsof_type (loc, type);
     +  c_last_sizeof_arg = type;
     +  c_last_sizeof_loc = loc;
    -+  ret.original_code = ELEMENTSOF_EXPR;
    ++  ret.original_code = NELEMENTSOF_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 elementsof does not get folded to a constant by
    ++	 the result of nelementsof 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 elementsof call is inside
    ++	 arrays must not be applied when this nelementsof 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 ("__elementsof__", ELEMENTSOF_EXPR, "lz", OVL_OP_FLAG_UNARY)
    ++DEF_OPERATOR ("__nelementsof__", NELEMENTSOF_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 elementsof
    ++@node nelementsof
     +@section Determining the Number of Elements of Arrays
    -+@cindex elementsof
    ++@cindex nelementsof
     +@cindex number of elements
     +
     +The keyword @code{__elemetsf__} determines the length of an array operand,
    @@ gcc/target.h: enum type_context_kind {
        TCTX_ALIGNOF,
      
     +  /* Directly measuring the number of elements of array T.  */
    -+  TCTX_ELEMENTSOF,
    ++  TCTX_NELEMENTSOF,
     +
        /* Creating objects of type T with static storage duration.  */
        TCTX_STATIC_STORAGE,
      
     
    - ## gcc/testsuite/gcc.dg/elementsof-compile.c (new) ##
    + ## gcc/testsuite/gcc.dg/nelementsof-compile.c (new) ##
     @@
     +/* { dg-do compile } */
     +/* { dg-options "-Wno-declaration-after-statement -Wno-pedantic -Wno-vla" } */
    @@ gcc/testsuite/gcc.dg/elementsof-compile.c (new)
     +static int w[] = {1, 2, 3};
     +
     +static int z[0];
    -+static int y[__elementsof__(z)];
    ++static int y[__nelementsof__(z)];
     +
     +void
     +automatic(void)
     +{
    -+  __elementsof__ (w);
    ++  __nelementsof__ (w);
     +}
     +
     +void
     +incomplete (int p[])
     +{
    -+  __elementsof__ (x);  /* { dg-error "incomplete" } */
    ++  __nelementsof__ (x);  /* { dg-error "incomplete" } */
     +
     +  /* We want to support the following one in the future,
     +     but for now it should fail.  */
    -+  __elementsof__ (p);  /* { dg-error "invalid" } */
    ++  __nelementsof__ (p);  /* { dg-error "invalid" } */
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/elementsof-compile.c (new)
     +    int fam[];
     +  } s;
     +
    -+  __elementsof__ (s.fam); /* { dg-error "incomplete" } */
    ++  __nelementsof__ (s.fam); /* { dg-error "incomplete" } */
     +}
     +
    -+void fix_fix (int i, char (*a)[3][5], int (*x)[__elementsof__ (*a)]);
    -+void fix_var (int i, char (*a)[3][i], int (*x)[__elementsof__ (*a)]);
    -+void fix_uns (int i, char (*a)[3][*], int (*x)[__elementsof__ (*a)]);
    ++void fix_fix (int i, char (*a)[3][5], int (*x)[__nelementsof__ (*a)]);
    ++void fix_var (int i, char (*a)[3][i], int (*x)[__nelementsof__ (*a)]);
    ++void fix_uns (int i, char (*a)[3][*], int (*x)[__nelementsof__ (*a)]);
     +
     +void
     +func (void)
    @@ gcc/testsuite/gcc.dg/elementsof-compile.c (new)
     +    int x[3];
     +  } s;
     +
    -+  __elementsof__ (x); /* { dg-error "invalid" } */
    -+  __elementsof__ (int); /* { dg-error "invalid" } */
    -+  __elementsof__ (s); /* { dg-error "invalid" } */
    -+  __elementsof__ (struct s); /* { dg-error "invalid" } */
    -+  __elementsof__ (&x); /* { dg-error "invalid" } */
    -+  __elementsof__ (p); /* { dg-error "invalid" } */
    -+  __elementsof__ (int *); /* { dg-error "invalid" } */
    -+  __elementsof__ (&s.x); /* { dg-error "invalid" } */
    -+  __elementsof__ (int (*)[3]); /* { dg-error "invalid" } */
    ++  __nelementsof__ (x); /* { dg-error "invalid" } */
    ++  __nelementsof__ (int); /* { dg-error "invalid" } */
    ++  __nelementsof__ (s); /* { dg-error "invalid" } */
    ++  __nelementsof__ (struct s); /* { dg-error "invalid" } */
    ++  __nelementsof__ (&x); /* { dg-error "invalid" } */
    ++  __nelementsof__ (p); /* { dg-error "invalid" } */
    ++  __nelementsof__ (int *); /* { dg-error "invalid" } */
    ++  __nelementsof__ (&s.x); /* { dg-error "invalid" } */
    ++  __nelementsof__ (int (*)[3]); /* { dg-error "invalid" } */
     +}
     +
     +static int f1();
    @@ gcc/testsuite/gcc.dg/elementsof-compile.c (new)
     +{
     +  int b[n][n];
     +
    -+  __elementsof__ (a[f1()]);
    -+  __elementsof__ (b[f2()]);
    ++  __nelementsof__ (a[f1()]);
    ++  __nelementsof__ (b[f2()]);
     +}
     +
     +void
     +no_parens(void)
     +{
    -+  __elementsof__ a;
    -+  __elementsof__ *a;
    -+  __elementsof__ (int [3]) {};
    ++  __nelementsof__ a;
    ++  __nelementsof__ *a;
    ++  __nelementsof__ (int [3]) {};
     +
    -+  __elementsof__ int [3]; /* { dg-error "expected expression before" } */
    ++  __nelementsof__ int [3]; /* { dg-error "expected expression before" } */
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/elementsof-compile.c (new)
     +{
     +  int n = 7;
     +
    -+  _Static_assert (__elementsof__ (int [3][n]) == 3);
    -+  _Static_assert (__elementsof__ (int [n][3]) == 7); /* { dg-error "not constant" } */
    -+  _Static_assert (__elementsof__ (int [0][3]) == 0);
    -+  _Static_assert (__elementsof__ (int [0]) == 0);
    ++  _Static_assert (__nelementsof__ (int [3][n]) == 3);
    ++  _Static_assert (__nelementsof__ (int [n][3]) == 7); /* { dg-error "not constant" } */
    ++  _Static_assert (__nelementsof__ (int [0][3]) == 0);
    ++  _Static_assert (__nelementsof__ (int [0]) == 0);
     +
    -+  /* FIXME: elementsof(int [0][n]) should result in a constant expression.  */
    -+  _Static_assert (__elementsof__ (int [0][n]) == 0); /* { dg-error "not constant" } */
    ++  /* FIXME: nelementsof(int [0][n]) should result in a constant expression.  */
    ++  _Static_assert (__nelementsof__ (int [0][n]) == 0); /* { dg-error "not constant" } */
     +}
     
    - ## gcc/testsuite/gcc.dg/elementsof-vla.c (new) ##
    + ## gcc/testsuite/gcc.dg/nelementsof-vla.c (new) ##
     @@
     +/* { dg-do compile } */
     +/* { dg-options "-Wno-pedantic -Wvla-parameter" } */
     +
     +void fix_fix (int i,
     +	      char (*a)[3][5],
    -+	      int (*x)[__elementsof__ (*a)]);
    ++	      int (*x)[__nelementsof__ (*a)]);
     +void fix_var (int i,
     +	      char (*a)[3][i], /* dg-warn "variable" */
    -+	      int (*x)[__elementsof__ (*a)]);
    ++	      int (*x)[__nelementsof__ (*a)]);
     +void fix_uns (int i,
     +	      char (*a)[3][*],
    -+	      int (*x)[__elementsof__ (*a)]);
    ++	      int (*x)[__nelementsof__ (*a)]);
     +
     +void zro_fix (int i,
     +	      char (*a)[0][5],
    -+	      int (*x)[__elementsof__ (*a)]);
    ++	      int (*x)[__nelementsof__ (*a)]);
     +void zro_var (int i,
     +	      char (*a)[0][i], /* dg-warn "variable" */
    -+	      int (*x)[__elementsof__ (*a)]);
    ++	      int (*x)[__nelementsof__ (*a)]);
     +void zro_uns (int i,
     +	      char (*a)[0][*],
    -+	      int (*x)[__elementsof__ (*a)]);
    ++	      int (*x)[__nelementsof__ (*a)]);
     +
     +void var_fix (int i,
     +	      char (*a)[i][5], /* dg-warn "variable" */
    -+	      int (*x)[__elementsof__ (*a)]); /* dg-warn "variable" */
    ++	      int (*x)[__nelementsof__ (*a)]); /* dg-warn "variable" */
     +void var_var (int i,
     +	      char (*a)[i][i], /* dg-warn "variable" */
    -+	      int (*x)[__elementsof__ (*a)]); /* dg-warn "variable" */
    ++	      int (*x)[__nelementsof__ (*a)]); /* dg-warn "variable" */
     +void var_uns (int i,
     +	      char (*a)[i][*], /* dg-warn "variable" */
    -+	      int (*x)[__elementsof__ (*a)]); /* dg-warn "variable" */
    ++	      int (*x)[__nelementsof__ (*a)]); /* dg-warn "variable" */
     +
     +void uns_fix (int i,
     +	      char (*a)[*][5],
    -+	      int (*x)[__elementsof__ (*a)]);
    ++	      int (*x)[__nelementsof__ (*a)]);
     +void uns_var (int i,
     +	      char (*a)[*][i], /* dg-warn "variable" */
    -+	      int (*x)[__elementsof__ (*a)]);
    ++	      int (*x)[__nelementsof__ (*a)]);
     +void uns_uns (int i,
     +	      char (*a)[*][*],
    -+	      int (*x)[__elementsof__ (*a)]);
    ++	      int (*x)[__nelementsof__ (*a)]);
     +
     +// Can't test due to bug: <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116284>
     +//static int z2[0];
    -+//static int y2[__elementsof__(z2)];
    ++//static int y2[__nelementsof__(z2)];
     
    - ## gcc/testsuite/gcc.dg/elementsof.c (new) ##
    + ## gcc/testsuite/gcc.dg/nelementsof.c (new) ##
     @@
     +/* { dg-do run } */
     +/* { dg-options "-Wno-declaration-after-statement -Wno-pedantic -Wno-vla" } */
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +{
     +  short a[7];
     +
    -+  static_assert (__elementsof__ (a) == 7);
    -+  static_assert (__elementsof__ (long [0]) == 0);
    -+  static_assert (__elementsof__ (unsigned [99]) == 99);
    ++  static_assert (__nelementsof__ (a) == 7);
    ++  static_assert (__nelementsof__ (long [0]) == 0);
    ++  static_assert (__nelementsof__ (unsigned [99]) == 99);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +  int a[] = {1, 2, 3};
     +  int z[] = {};
     +
    -+  static_assert (__elementsof__ (a) == 3);
    -+  static_assert (__elementsof__ (z) == 0);
    ++  static_assert (__nelementsof__ (a) == 3);
    ++  static_assert (__nelementsof__ (z) == 0);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +  unsigned n;
     +
     +  n = 99;
    -+  assert (__elementsof__ (short [n - 10]) == 99 - 10);
    ++  assert (__nelementsof__ (short [n - 10]) == 99 - 10);
     +
     +  int v[n / 2];
    -+  assert (__elementsof__ (v) == 99 / 2);
    ++  assert (__nelementsof__ (v) == 99 / 2);
     +
     +  n = 0;
     +  int z[n];
    -+  assert (__elementsof__ (z) == 0);
    ++  assert (__nelementsof__ (z) == 0);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +    int a[8];
     +  } s;
     +
    -+  static_assert (__elementsof__ (s.a) == 8);
    ++  static_assert (__nelementsof__ (s.a) == 8);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +  int i;
     +
     +  i = 7;
    -+  assert (__elementsof__ (struct {int x;}[i++]) == 7);
    ++  assert (__nelementsof__ (struct {int x;}[i++]) == 7);
     +  assert (i == 7 + 1);
     +
     +  int v[i];
     +  int (*p)[i];
     +  p = &v;
    -+  assert (__elementsof__ (*p++) == i);
    ++  assert (__nelementsof__ (*p++) == i);
     +  assert (p - 1 == &v);
     +}
     +
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +  int i;
     +
     +  i = 3;
    -+  static_assert (__elementsof__ (struct {int x[i++];}[3]) == 3);
    ++  static_assert (__nelementsof__ (struct {int x[i++];}[3]) == 3);
     +  assert (i == 3);
     +}
     +
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +array_noeval (void)
     +{
     +  long a[5];
    -+  long (*p)[__elementsof__ (a)];
    ++  long (*p)[__nelementsof__ (a)];
     +
     +  p = &a;
    -+  static_assert (__elementsof__ (*p++) == 5);
    ++  static_assert (__nelementsof__ (*p++) == 5);
     +  assert (p == &a);
     +}
     +
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +{
     +  int i;
     +
    -+  static_assert (__elementsof__ (int [0][4]) == 0);
    ++  static_assert (__nelementsof__ (int [0][4]) == 0);
     +  i = 3;
    -+  assert (__elementsof__ (int [0][i]) == 0);
    ++  assert (__nelementsof__ (int [0][i]) == 0);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +{
     +  int i;
     +
    -+  static_assert (__elementsof__ (int [7][4]) == 7);
    ++  static_assert (__nelementsof__ (int [7][4]) == 7);
     +  i = 3;
    -+  static_assert (__elementsof__ (int [7][i]) == 7);
    ++  static_assert (__nelementsof__ (int [7][i]) == 7);
     +}
     +
     +void
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +  int i, j;
     +
     +  i = 7;
    -+  assert (__elementsof__ (int [i++][4]) == 7);
    ++  assert (__nelementsof__ (int [i++][4]) == 7);
     +  assert (i == 7 + 1);
     +
     +  i = 9;
     +  j = 3;
    -+  assert (__elementsof__ (int [i++][j]) == 9);
    ++  assert (__nelementsof__ (int [i++][j]) == 9);
     +  assert (i == 9 + 1);
     +}
     +
    @@ gcc/testsuite/gcc.dg/elementsof.c (new)
     +  int a[7];
     +  int v[n];
     +
    -+  static_assert (__elementsof__ a == 7); 
    -+  assert (__elementsof__ v == 3); 
    ++  static_assert (__nelementsof__ a == 7); 
    ++  assert (__nelementsof__ v == 3); 
     +}
     +
     +int

base-commit: 4d44f3fc387815eb232d7757352857993a1d21d9