[RFC,v3,0/3] c: Add __lengthof__ operator

Message ID 20240803231702.89150-1-alx@kernel.org
Headers
Series c: Add __lengthof__ operator |

Message

Alejandro Colomar Aug. 3, 2024, 11:17 p.m. UTC
  Hi,

v3:

-  Add some documentation.
-  Reject incomplete types.
-  Rename array_type_nelts()=>array_type_nelts_minus_one().  (However, I
   included that patch in this set only for completeness purposes; that
   patch is also sent standalone, and I would like it to be merged in
   the separate discussion for it.)

(Below is a range diff against v2.)

Below is a test program I'm using for implementing this feature:

	$ cat len.c 
	#include <stdalign.h>
	#include <stdio.h>

	#define memberof(T, member)  ((T){}.member)

	struct s {
		int x;
		int y[8];
		int z[];
	};

	extern int x[];

	int
	main(int argc, char *argv[argc + 1])
	{
		short   a[42];
		size_t  n;

		(void) argv;

		// Wishlist:
		//n = __lengthof__(argv);
		//printf("lengthof(argv) == %zu\n", n);

		n = __lengthof__(a);
		printf("lengthof(a):\t %zu\n", n);

		// Expected error:
		//n = __lengthof__(x);
		//printf("lengthof(x):\t %zu\n", n);

		n = __lengthof__(long [0]);
		printf("lengthof(long [0]):\t %zu\n", n);

		n = __lengthof__(long [99]);
		printf("lengthof(long [99]):\t %zu\n", n);

		n = __lengthof__(short [n - 10]);
		printf("lengthof(short [n - 10]):\t %zu\n", n);

		int  b[n / 2];
		n = __lengthof__(b);
		printf("lengthof(b):\t %zu\n", n);

		puts("");
	// X(memberof(struct s, y))
		n = __lengthof__(memberof(struct s, y));
		printf("lengthof(memberof(struct s, y)):\t %zu\n", n);

		n = sizeof(memberof(struct s, y));
		printf("sizeof(memberof(struct s, y)):\t %zu\n", n);

		n = alignof(memberof(struct s, y));
		printf("alignof(memberof(struct s, y)):\t %zu\n", n);

		puts("");
	// X(memberof(struct s, z))
		// Expected error:
		//n = __lengthof__(memberof(struct s, z));
		//printf("lengthof(memberof(struct s, z)):\t %zu\n", n);

		// Expected error:
		//n = sizeof(memberof(struct s, z));
		//printf("sizeof(memberof(struct s, z)):\t %zu\n", n);

		n = alignof(memberof(struct s, z));
		printf("alignof(memberof(struct s, z)):\t %zu\n", n);

		puts("");
	// X(struct {int x;}[i++])
		int  i = 4;
		n = __lengthof__(struct {int x;}[i++]);
		printf("lengthof(struct {int x;}[i++]):\t %zu;  i: %d\n", n, i);

		i = 4;
		n = sizeof(struct {int x;}[i++]);
		printf("sizeof(struct {int x;}[i++]):\t %zu; i: %d\n", n, i);

		i = 4;
		n = alignof(struct {int x;}[i++]);
		printf("alignof(struct {int x;}[i++]):\t %zu;  i: %d\n", n, i);

		i = 4;
		typeof(struct {int x;}[i++])  z1;
		printf("typeof(struct {int x;}[i++]);\t     i: %d\n", i);

		puts("");
	// X(struct {int x[i++];}[3])
		i = 4;
		n = __lengthof__(struct {int x[i++];}[3]);
		printf("lengthof(struct {int x[i++];}[3]):\t %zu;  i: %d\n", n, i);

		i = 4;
		n = sizeof(struct {int x[i++];}[3]);
		printf("sizeof(struct {int x[i++];}[3]):\t %zu; i: %d\n", n, i);

		i = 4;
		n = alignof(struct {int x[i++];}[3]);
		printf("alignof(struct {int x[i++];}[3]):\t %zu;  i: %d\n", n, i);

		i = 4;
		typeof(struct {int x[i++];}[3])  z2;
		printf("typeof(struct {int x[i++];}[3]);\t     i: %d\n", i);

		puts("");
	// X(struct {int x[(i++, 2)];}[3])
		i = 4;
		n = __lengthof__(struct {int x[(i++, 2)];}[3]);
		printf("lengthof(struct {int x[(i++, 2)];}[3]):\t %zu;  i: %d\n", n, i);

		i = 4;
		n = sizeof(struct {int x[(i++, 2)];}[3]);
		printf("sizeof(struct {int x[(i++, 2)];}[3]):\t %zu; i: %d\n", n, i);

		i = 4;
		n = alignof(struct {int x[(i++, 2)];}[3]);
		printf("alignof(struct {int x[(i++, 2)];}[3]):\t %zu;  i: %d\n", n, i);

		i = 4;
		typeof(struct {int x[(i++, 2)];}[3])  z3;
		printf("typeof(struct {int x[(i++, 2)];}[3]);\t     i: %d\n", i);

		puts("");
	// X(*p++)
		short  (*p)[42];
		p = &a;
		n = __lengthof__(*p++);
		printf("lengthof(*p++):\t %zu; p: %p\n", n, p);

		p = &a;
		n = sizeof(*p++);
		printf("lengthof(*p++):\t %zu; p: %p\n", n, p);

		p = &a;
		n = alignof(*p++);
		printf("alignof(*p++):\t %zu;  p: %p\n", n, p);

		p = &a;
		typeof(*p++)  z4;
		printf("typeof(*p++);\t     p: %p\n", p);

		puts("");
	// X(*q++)
		short  (*q)[__lengthof__(b)];
		q = &a;
		n = __lengthof__(*q++);
		printf("lengthof(*q++):\t %zu; p: %p\n", n, q);

		q = &a;
		n = sizeof(*q++);
		printf("lengthof(*q++):\t %zu; p: %p\n", n, q);

		q = &a;
		n = alignof(*q++);
		printf("alignof(*q++):\t %zu;  p: %p\n", n, q);

		q = &a;
		typeof(*q++)  z5;
		printf("typeof(*q++);\t     p: %p\n", q);
	}

	$ /opt/local/gnu/gcc/lengthof/bin/gcc len.c 
	$ ./a.out 
	lengthof(a):	 42
	lengthof(long [0]):	 0
	lengthof(long [99]):	 99
	lengthof(short [n - 10]):	 89
	lengthof(b):	 44

	lengthof(memberof(struct s, y)):	 8
	sizeof(memberof(struct s, y)):	 32
	alignof(memberof(struct s, y)):	 4

	alignof(memberof(struct s, z)):	 4

	lengthof(struct {int x;}[i++]):	 4;  i: 5
	sizeof(struct {int x;}[i++]):	 16; i: 5
	alignof(struct {int x;}[i++]):	 4;  i: 4
	typeof(struct {int x;}[i++]);	     i: 5

	lengthof(struct {int x[i++];}[3]):	 3;  i: 5
	sizeof(struct {int x[i++];}[3]):	 48; i: 5
	alignof(struct {int x[i++];}[3]):	 4;  i: 4
	typeof(struct {int x[i++];}[3]);	     i: 5

	lengthof(struct {int x[(i++, 2)];}[3]):	 3;  i: 5
	sizeof(struct {int x[(i++, 2)];}[3]):	 24; i: 5
	alignof(struct {int x[(i++, 2)];}[3]):	 4;  i: 4
	typeof(struct {int x[(i++, 2)];}[3]);	     i: 5

	lengthof(*p++):	 42; p: 0x7fffe52379f0
	lengthof(*p++):	 84; p: 0x7fffe52379f0
	alignof(*p++):	 2;  p: 0x7fffe52379f0
	typeof(*p++);	     p: 0x7fffe52379f0

	lengthof(*q++):	 44; p: 0x7fffe5237a48
	lengthof(*q++):	 88; p: 0x7fffe5237a48
	alignof(*q++):	 2;  p: 0x7fffe52379f0
	typeof(*q++);	     p: 0x7fffe5237a48

There are some things to consider:

-  Error handling could be incomplete; there are a few things I still
   don't understand.
-  I'd like to implement it so that only top-level VLAs trigger
   evaluation, but I still don't understand that well.

Other than that, it starts looking good.


Have a lovely night!
Alex


Alejandro Colomar (3):
  gcc/: Rename array_type_nelts() => array_type_nelts_minus_one()
  Merge definitions of array_type_nelts_top()
  c: Add __lengthof__() 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               | 30 +++++++----
 gcc/c/c-fold.cc               |  7 +--
 gcc/c/c-parser.cc             | 61 ++++++++++++++++------
 gcc/c/c-tree.h                |  4 ++
 gcc/c/c-typeck.cc             | 95 +++++++++++++++++++++++++++++++++--
 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           | 12 +++++
 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/tree.cc                   | 17 ++++++-
 gcc/tree.h                    |  3 +-
 25 files changed, 245 insertions(+), 79 deletions(-)

Range-diff against v2:
-:  ----------- > 1:  73010cb4af6 gcc/: Rename array_type_nelts() => array_type_nelts_minus_one()
1:  507f5a51e17 ! 2:  2bb966a0a89 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 _Lengthof().  Merge them, and
    +    where they are needed for implementing __lengthof__().  Merge them, and
         provide the single definition in gcc/tree.{h,cc}, where it's available
    -    for _Lengthof().
    +    for __lengthof__().
     
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
    @@ gcc/cp/tree.cc: cxx_print_statistics (void)
     -{
     -  return fold_build2_loc (input_location,
     -		      PLUS_EXPR, sizetype,
    --		      array_type_nelts (type),
    +-		      array_type_nelts_minus_one (type),
     -		      size_one_node);
     -}
     -
    @@ gcc/rust/backend/rust-tree.cc: is_empty_class (tree type)
     -array_type_nelts_top (tree type)
     -{
     -  return fold_build2_loc (input_location, PLUS_EXPR, sizetype,
    --			  array_type_nelts (type), size_one_node);
    +-			  array_type_nelts_minus_one (type), size_one_node);
     -}
     -
      // forked from gcc/cp/tree.cc builtin_valid_in_constant_expr_p
    @@ gcc/rust/backend/rust-tree.h: extern location_t rs_expr_location (const_tree);
      
     
      ## gcc/tree.cc ##
    -@@ gcc/tree.cc: array_type_nelts (const_tree type)
    +@@ gcc/tree.cc: array_type_nelts_minus_one (const_tree type)
      	  ? max
      	  : fold_build2 (MINUS_EXPR, TREE_TYPE (max), max, min));
      }
    @@ gcc/tree.cc: array_type_nelts (const_tree type)
     +{
     +  return fold_build2_loc (input_location,
     +		      PLUS_EXPR, sizetype,
    -+		      array_type_nelts (type),
    ++		      array_type_nelts_minus_one (type),
     +		      size_one_node);
     +}
      
    @@ gcc/tree.h
     @@ gcc/tree.h: extern tree build_method_type (tree, tree);
      extern tree build_offset_type (tree, tree);
      extern tree build_complex_type (tree, bool named = false);
    - extern tree array_type_nelts (const_tree);
    + extern tree array_type_nelts_minus_one (const_tree);
     +extern tree array_type_nelts_top (tree);
      
      extern tree value_member (tree, tree);
2:  6b48d48ecdd ! 3:  d22b5e1c015 c: Add __lengthof__() operator
    @@ Commit message
         This operator is similar to sizeof() but can only be applied to an
         array, and returns its length (number of elements).
     
    +    TO BE DECIDED BEFORE MERGING:
    +
    +            It would be better to not evaluate the operand if the top-level
    +            array is not a VLA.
    +
         FUTURE DIRECTIONS:
     
                 We could make it work with array parameters to functions, and
    @@ Commit message
                 regardless of it being really a pointer.
     
         Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2529.pdf>
    +    Cc: Xavier Del Campo Romero <xavi.dcr@tutanota.com>
         Cc: Gabriel Ravier <gabravier@gmail.com>
         Cc: Martin Uecker <uecker@tugraz.at>
         Cc: Joseph Myers <josmyers@redhat.com>
    +    Cc: Jakub Jelinek <jakub@redhat.com>
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
      ## gcc/c-family/c-common.cc ##
    @@ gcc/c-family/c-common.cc: c_alignof_expr (location_t loc, tree expr)
     +  enum tree_code type_code;
     +
     +  type_code = TREE_CODE (type);
    ++  if (!COMPLETE_TYPE_P (type))
    ++    {
    ++      error_at (loc,
    ++		"invalid application of %<lengthof%> to incomplete type %qT",
    ++		type);
    ++      return error_mark_node;
    ++    }
     +  if (type_code != ARRAY_TYPE)
     +    {
     +      error_at (loc, "invalid application of %<lengthof%> to type %qT", type);
    @@ gcc/cp/operators.def: DEF_OPERATOR ("co_await", CO_AWAIT_EXPR, "aw", OVL_OP_FLAG
      DEF_OPERATOR ("__real__", REALPART_EXPR, "v18__real__", OVL_OP_FLAG_UNARY)
      
     
    + ## gcc/doc/extend.texi ##
    +@@ gcc/doc/extend.texi: If the operand of the @code{__alignof__} expression is a function,
    + the expression evaluates to the alignment of the function which may
    + be specified by attribute @code{aligned} (@pxref{Common Function Attributes}).
    + 
    ++@node Length
    ++@section Determining the Length of Arrays
    ++@cindex length
    ++@cindex array length
    ++
    ++The keyword @code{__lengthof__} determines the length of an array operand,
    ++that is, the number of elements in the array.
    ++Its syntax is just like @code{sizeof},
    ++and the operand is evaluated following the same rules.
    ++(TODO: We probably want to restrict evaluation to top-level VLAs only.
    ++       This documentation describes the current implementation.)
    ++
    + @node Inline
    + @section An Inline Function is As Fast As a Macro
    + @cindex inline functions
    +
      ## gcc/target.h ##
     @@ gcc/target.h: enum type_context_kind {
        /* Directly measuring the alignment of T.  */