diff mbox series

[RFC,v1,2/2] c: Add _Lengthof() operator

Message ID 20240728141547.302478-3-alx@kernel.org
State New
Headers show
Series c: Add _Lengthof operator | expand

Commit Message

Alejandro Colomar July 28, 2024, 2:16 p.m. UTC
This operator is similar to sizeof() but can only be applied to an
array, and returns its length (number of elements).

FUTURE DIRECTIONS:

	We could make it work with array parameters to functions, and
	somehow magically return the length designator of the array,
	regardless of it being really a pointer.

Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2529.pdf>
Cc: Gabriel Ravier <gabravier@gmail.com>
Cc: Martin Uecker <uecker@tugraz.at>
Cc: Joseph Myers <josmyers@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 gcc/Makefile.in           |  1 +
 gcc/c-family/c-common.cc  | 20 ++++++++++
 gcc/c-family/c-common.def |  4 ++
 gcc/c-family/c-common.h   |  2 +
 gcc/c/c-parser.cc         | 35 ++++++++++++----
 gcc/c/c-tree.h            |  4 ++
 gcc/c/c-typeck.cc         | 84 +++++++++++++++++++++++++++++++++++++++
 gcc/cp/operators.def      |  1 +
 gcc/ginclude/stdlength.h  | 35 ++++++++++++++++
 gcc/target.h              |  3 ++
 10 files changed, 181 insertions(+), 8 deletions(-)
 create mode 100644 gcc/ginclude/stdlength.h
diff mbox series

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index f4bb4a88cf3..88496edbdc8 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -477,6 +477,7 @@  USER_H = $(srcdir)/ginclude/float.h \
 	 $(srcdir)/ginclude/stdalign.h \
 	 $(srcdir)/ginclude/stdatomic.h \
 	 $(srcdir)/ginclude/stdckdint.h \
+	 $(srcdir)/ginclude/stdlength.h \
 	 $(EXTRA_HEADERS)
 
 USER_H_INC_NEXT_PRE = @user_headers_inc_next_pre@
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index e7e371fd26f..c9020a7faef 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -407,6 +407,7 @@  const struct c_common_resword c_common_reswords[] =
   { "_Decimal64",       RID_DFLOAT64,  D_CONLY },
   { "_Decimal128",      RID_DFLOAT128, D_CONLY },
   { "_Fract",           RID_FRACT,     D_CONLY | D_EXT },
+  { "_Lengthof",        RID_LENGTHOF,  D_CONLY | D_EXT },
   { "_Accum",           RID_ACCUM,     D_CONLY | D_EXT },
   { "_Sat",             RID_SAT,       D_CONLY | D_EXT },
   { "_Static_assert",   RID_STATIC_ASSERT, D_CONLY },
@@ -523,6 +524,7 @@  const struct c_common_resword c_common_reswords[] =
   { "if",		RID_IF,		0 },
   { "inline",		RID_INLINE,	D_EXT89 },
   { "int",		RID_INT,	0 },
+  { "lengthof",		RID_LENGTHOF,	D_EXT },
   { "long",		RID_LONG,	0 },
   { "mutable",		RID_MUTABLE,	D_CXXONLY | D_CXXWARN },
   { "namespace",	RID_NAMESPACE,	D_CXXONLY | D_CXXWARN },
@@ -4070,6 +4072,24 @@  c_alignof_expr (location_t loc, tree expr)
 
   return fold_convert_loc (loc, size_type_node, t);
 }
+
+/* Implement the _Lengthof keyword: Return the length of an array,
+   that is, the number of elements in the array.  */
+
+tree
+c_lengthof_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);
+      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 5de96e5d4a8..46e128072b5 100644
--- a/gcc/c-family/c-common.def
+++ b/gcc/c-family/c-common.def
@@ -50,6 +50,10 @@  DEFTREECODE (EXCESS_PRECISION_EXPR, "excess_precision_expr", tcc_expression, 1)
    number.  */
 DEFTREECODE (USERDEF_LITERAL, "userdef_literal", tcc_exceptional, 3)
 
+/* Represents a 'sizeof' expression during C++ template expansion,
+   or for the purpose of -Wsizeof-pointer-memaccess warning.  */
+DEFTREECODE (LENGTHOF_EXPR, "lengthof_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 ccaea27c2b9..f815a4cf3bc 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_LENGTHOF,
   RID_VA_ARG,
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,    RID_CHOOSE_EXPR,
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,	   RID_BUILTIN_SHUFFLE,
@@ -885,6 +886,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_lengthof_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-parser.cc b/gcc/c/c-parser.cc
index 12c5ed5d92c..eb1d8b932fd 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_lengthof_expression (parser, RID_SIZEOF)                 \
+)
 
+#define c_parser_lengthof_expression(parser)                                  \
+(                                                                             \
+  c_parser_sizeof_or_lengthof_expression (parser, RID_LENGTHOF)               \
+)
+
 /* 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
@@ -1687,7 +1697,7 @@  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_lengthof_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 *,
@@ -9864,6 +9874,8 @@  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_SIZEOF:
 	  return c_parser_sizeof_expression (parser);
 	case RID_ALIGNOF:
@@ -9903,12 +9915,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_lengthof_expression (c_parser *parser, enum rid rid)
 {
+  const char *op_name = (rid == RID_SIZEOF) ? "sizeof" : "_Lengthof";
   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;
@@ -9952,13 +9965,16 @@  c_parser_sizeof_expression (c_parser *parser)
 	}
       /* 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_LENGTHOF)
+	result = c_expr_lengthof_type (expr_loc, type_name);
+      else
+	result = c_expr_sizeof_type (expr_loc, type_name);
     }
   else
     {
@@ -9971,8 +9987,11 @@  c_parser_sizeof_expression (c_parser *parser)
       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_LENGTHOF)
+	result = c_expr_lengthof_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 15da875a029..102fcfefea6 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -736,6 +736,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_lengthof;
 extern int in_typeof;
 extern bool c_in_omp_for;
 extern bool c_omp_array_section_p;
@@ -786,6 +787,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_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 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 7e0f01ed22b..543254406f5 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -3453,6 +3453,90 @@  c_expr_sizeof_type (location_t loc, struct c_type_name *t)
   return ret;
 }
 
+/* Return the result of _Lengthof applied to EXPR.  */
+
+struct c_expr
+c_expr_lengthof_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_lengthof_type (loc, TREE_TYPE (folded_expr));
+      c_last_sizeof_arg = expr.value;
+      c_last_sizeof_loc = loc;
+      ret.original_code = LENGTHOF_EXPR;
+      ret.original_type = NULL;
+      ret.m_decimal = 0;
+      if (C_TYPE_VARIABLE_SIZE (TREE_TYPE (folded_expr)))
+	{
+	  /* _Lengthof 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 (C_TYPE_VARIABLE_SIZE (TREE_TYPE (folded_expr)));
+    }
+  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
+   location of the original expression.  */
+
+struct c_expr
+c_expr_lengthof_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);
+  c_last_sizeof_arg = type;
+  c_last_sizeof_loc = loc;
+  ret.original_code = LENGTHOF_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)
+      && C_TYPE_VARIABLE_SIZE (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 _Lengthof 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
+	 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
+		  ? C_TYPE_VARIABLE_SIZE (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/cp/operators.def b/gcc/cp/operators.def
index d8878923602..ab1447a3386 100644
--- a/gcc/cp/operators.def
+++ b/gcc/cp/operators.def
@@ -91,6 +91,7 @@  DEF_OPERATOR ("co_await", CO_AWAIT_EXPR, "aw", OVL_OP_FLAG_UNARY)
 
 /* 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 ("__imag__", IMAGPART_EXPR, "v18__imag__", OVL_OP_FLAG_UNARY)
 DEF_OPERATOR ("__real__", REALPART_EXPR, "v18__real__", OVL_OP_FLAG_UNARY)
 
diff --git a/gcc/ginclude/stdlength.h b/gcc/ginclude/stdlength.h
new file mode 100644
index 00000000000..551de92ecb1
--- /dev/null
+++ b/gcc/ginclude/stdlength.h
@@ -0,0 +1,35 @@ 
+/* Copyright (C) 2024 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef _STDLENGTH_H
+#define _STDLENGTH_H
+
+#if (!defined __cplusplus)
+
+#define lengthof _Lengthof
+
+#define __lengthof_is_defined 1
+
+#endif
+
+#endif	/* stdlength.h */
diff --git a/gcc/target.h b/gcc/target.h
index c1f99b97b86..79890ae9944 100644
--- a/gcc/target.h
+++ b/gcc/target.h
@@ -245,6 +245,9 @@  enum type_context_kind {
   /* Directly measuring the alignment of T.  */
   TCTX_ALIGNOF,
 
+  /* Directly measuring the length of array T.  */
+  TCTX_LENGTHOF,
+
   /* Creating objects of type T with static storage duration.  */
   TCTX_STATIC_STORAGE,