Hi, Alex,
I noticed that all your 4 versions of the patches and the corresponding 
discussion are all in the same email thread, it’s very inconvenient to read. 
Can you start a new email thread for each of the new version of the patch? 
(i.e, Please not reply to the previous version when you have a new version of 
the patch).
Some more questions and comments below:

On Aug 6, 2024, at 08:22, Alejandro Colomar <a...@kernel.org> wrote:

Hi!

v4:

-  Only evaluate the operand if the top array is VLA.  Inner VLAs are
  ignored.  [Joseph, Martin]
  This proved very useful for compile-time diagnostics, since we have
  more cases that are constant expressions.
-  Document the evaluation rules, which are unique to this operator
  (similar to sizeof, but we ignore inner VLAs).
-  Add tests to the testsuite.  [Joseph]
-  Swap diagnostic cases preference, to give more meaningful
  diagnostics.  [Martin]
-  Document that Xavier was the first one to suggest this feature, and
  provide a link to the mail thread where that happened.
  BTW, while reading that discussion from 2 years ago, I see that it
  was questioned the value of this operator.  Below is a rationale to
  defend it.
I briefly read the two links you provided as the background of your patch:

https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2529.pdf

This is a proposal submitted on 6/4/2020, do you know the current status of 
this proposal?

https://inbox.sourceware.org/gcc/m8s4oqy--...@tutanota.com/T/

This is some discussion within GCC community on this proposal around the same 
time (the end of May of 2020, before the submission date of the proposal).

From the discussion, I didn’t see a consistent positive opinion on the proposal 
itself.

So, I am wondering whether you have any new background information since then? 
What’s the major motivation to bring up this proposal again this time after 4 
years?

-  Document that Martin's help has been crucial for implementing this,
  with 'Co-developed-by'.  Would you mind confirming that I can use
  that tag?
-  CC += Kees, Qing, Jens

Rationale:

-  While compiler extensions already allow implementing ARRAY_SIZE()
  (<https://stackoverflow.com/a/57537491/6872717>), there's still no
  way to get the length of a function parameter which uses array
  notation.


Is this one of major benefits from this new __lenghth__ operator?
If so, any rough idea now on how to implement this (i.e, the length of a 
function parameter array).


 While this first implementation doesn't support those yet
  (because there are some issues that need to be fixed first), the plan
  is to add support to those.


What kind of issues are? What’s the plan to resolve those issues?


 This would be a huge step towards arrays
  being first-class citizens in C.  In those cases, it would reduce the
  chance of programmer errors.  See for example
  <https://lkml.org/lkml/2015/9/3/428>.  That entire class of bugs
  would be over, _and_ programs would become simpler.

Some specific questions or concerns:

-  The tests seem to work as expected if I compile them manually, and
  run (the one that should be run) as a normal program.  The one that
  should not be run also gives the expected diagnostics.
  Can anyone give advice of why it's not running well under the test
  suite?

You might want to check some existing testing cases in GCC’s testsuite first to 
see what kind of directives you are missing in your test case. (For example, 
any testing case in gcc/testsuite/gcc.dg/).

The documentation of the test suite is here:
https://gcc.gnu.org/onlinedocs/gccint/Testsuites.html

Adding testing case correctly into GCC’s testing suite is very important for 
any patch. And adding them in the beginning of the development also is very 
important and will save you a lot of time.

Qing


-  I don't like the fact that [*][n] is internally implemented exactly
  like [0][n], which makes them indistinguishable.  All other cases of
  [0] return a constent expression of value 0, but [0][n] must return a
  variable 0, to keep support for [*][n].
  Could you please change the way [*][n] (and thus [*]) is represented
  internally so that it can be differentiated from [0]?
  Do you have in mind any other way that would be a viable
  implementation of [*] that would allow distinguishing [0][n] and
  [*][n]?  Maybe making it to have one node instead of zero and mark
  that node specially?

At the bottom of this email is a range-diff against v3.

And below is a test program I used while developing the feature.  It is
quite similar to what's on the test suite (patch 4/4), since those are
based on this one.

It has comments where I'd like more diagnostics, but those are not
responsibility of this feature.  Some are fault of the representation
for [*], and others are already being worked on by Martin.  There are
also comments on code that causes compile-time errors as expected
(wanted).  Some assertions about evaluation of the operand are commented
out because due to the problems with [*][n] and [0][n] we have more
evaluation than I'd like.  However, those are only with [0], which is
not yet well supported by GCC, so we don't need to worry much for now.

The program below also compares with sizeof and alignof, which the
test-suite tests do not.

Have a lovely day!
Alex

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


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


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


struct s2 {
int x;
int z[] __attribute__((counted_by(x)));
};


extern int x[];


void array(void);
void incomplete_err(int inc[]);
void unspecified_err(void);
void vla(void);
void member_array(void);
void fam_err(void);
void vla_eval(void);
void in_vla_noeval(void);
void in_vla_noeval2(void);
void array_noeval(void);
void vla_eval2(void);
void matrix_0(void);
void matrix_fixed(void);
void matrix_vla(void);
void f_fixed(void);
void f_zero(void);
void f_vla(void);
void f_star(void);


int
main(int argc, char *argv[argc + 1])
{
(void) argv;

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

array();
incomplete_err(&argc);
unspecified_err();
vla();
member_array();
fam_err();
vla_eval();
in_vla_noeval();
in_vla_noeval2();
array_noeval();
vla_eval2();
matrix_0();
matrix_fixed();
matrix_vla();
f_fixed();
f_zero();
f_vla();
f_star();
}

void
array(void)
{
short   a[42];
size_t  n;

puts("");

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

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

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

void
incomplete_err(int inc[])
{
//size_t  n;

puts("");

// error: invalid application of ‘lengthof’ to incomplete type ‘int[]’
//n = lengthof(x);
//printf("lengthof(x):\t %zu\n", n);

// error:
//n = lengthof(inc);
//printf("lengthof(inc):\t %zu\n", n);
}

void
unspecified_err(void)
{
puts("");

// error:
//n = lengthof(int [*]);
//printf("lengthof(int [*])\t %zu\n", n);
}

void
vla(void)
{
size_t  n;

n = 99;
puts("");

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

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

n = 0;
int  z[n];
n = __lengthof__(z);
printf("lengthof(z):\t %zu\n", n);
assert(n == 0);
}

void
member_array(void)
{
size_t  n;

puts("");

n = __lengthof__(memberof(struct s, y));
printf("lengthof(memberof(struct s, y)):\t %zu\n", n);
assert(n == 8);
}

void
fam_err(void)
{
size_t  n;

puts("");

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

// 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);
}

void
vla_eval(void)
{
int     i;
size_t  n;

puts("");

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

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

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

void
in_vla_noeval(void)
{
int     i;
size_t  n;

puts("");

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

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

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

void
in_vla_noeval2(void)
{
int     i;
size_t  n;

puts("");

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);
assert(i == 4);
assert(n == 3);

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);
assert(i == 5);

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);
assert(i == 4);
}

void
array_noeval(void)
{
short   a[42];
short   (*p)[42];
size_t  n;

puts("");

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

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

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

void
vla_eval2(void)
{
size_t  n;

n = 33;

int  v[n / 2];
int  (*q)[__lengthof__(v)];

puts("");

q = &v;
n = __lengthof__(*q++);
printf("lengthof(*q++):\t %zu; q: %p\n", n, q);
assert(q - 1 == &v);
assert(n == 33 / 2);

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

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

void
matrix_0(void)
{
int     i;
size_t  n;

puts("");

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

i = 5;
n = __lengthof__(int [0][i++]);
printf("lengthof(int [0][i++]):\t %zu;  i: %d\n", n, i);
//assert(i == 5);
assert(n == 0);

// error: ‘[*]’ not allowed in other than function prototype scope
//n = lengthof(int [0][*]);
//printf("lengthof(int [0][*]):\t %zu\n", n);
//assert(n == 0);
}

void
matrix_fixed(void)
{
int     i;
size_t  n;


n = __lengthof__(int [7][4]);
printf("lengthof(int [7][4]):\t %zu\n", n);
assert(n == 7);

i = 5;
n = __lengthof__(int [7][i++]);
printf("lengthof(int [7][i++]):\t %zu;  i: %d\n", n, i);
assert(i == 5);
assert(n == 7);

// error: ‘[*]’ not allowed in other than function prototype scope
//n = lengthof(int [7][*]);
//printf("lengthof(int [7][*]):\t %zu\n", n);
//assert(n == 7);
}

void
matrix_vla(void)
{
int     i;
size_t  n;


i = 7;
n = __lengthof__(int [i++][4]);
printf("lengthof(int [i++][4]):\t %zu;  i: %d\n", n, i);
assert(i == 8);
assert(n == 7);

n = __lengthof__(int [i++][n]);
printf("lengthof(int [i++][n]):\t %zu;  i: %d\n", n, i);
assert(i == 9);
assert(n == 8);

// error: ‘[*]’ not allowed in other than function prototype scope
//n = lengthof(int [i++][*]);
//printf("lengthof(int [i++][*]):\t %zu;  i: %d\n", n, i);
//assert(i == 10);
//assert(n == 9);
}

void l_fixed_1(int i, char (*a)[3][5], int (*x)[__lengthof__(*a)]);
void l_fixed_2(int i, char (*a)[3][i], int (*x)[__lengthof__(*a)]);
void l_fixed_3(int i, char (*a)[3][*], int (*x)[__lengthof__(*a)]);

void s_fixed_1(int i, char (*a)[5][3], int (*x)[sizeof(**a)]);
void s_fixed_2(int i, char (*a)[i][3], int (*x)[sizeof(**a)]);
void s_fixed_3(int i, char (*a)[*][3], int (*x)[sizeof(**a)]);

void
f_fixed(void)
{
int  i3[3];
int  i5[5];
char c35[3][5];
char c53[5][3];

sizeof(l_fixed_1(5, &c35, &i3));
//sizeof(l_fixed_1(5, &c35, &i5));  // -Wincompatible-pointer-types

sizeof(l_fixed_2(5, &c35, &i3));
//sizeof(l_fixed_2(5, &c35, &i5));  // -Wincompatible-pointer-types

sizeof(l_fixed_3(5, &c35, &i3));
//sizeof(l_fixed_3(5, &c35, &i5));  // -Wincompatible-pointer-types

sizeof(s_fixed_1(5, &c53, &i3));
//sizeof(s_fixed_1(5, &c53, &i5));  // -Wincompatible-pointer-types

sizeof(s_fixed_2(5, &c53, &i3));
//sizeof(s_fixed_2(5, &c53, &i5));  // -Wincompatible-pointer-types

sizeof(s_fixed_3(5, &c53, &i3));
//sizeof(s_fixed_3(5, &c53, &i5));  // -Wincompatible-pointer-types
}

void l_zero_1(int i, char (*a)[0][5], int (*x)[__lengthof__(*a)]);
void l_zero_2(int i, char (*a)[0][i], int (*x)[__lengthof__(*a)]);
void l_zero_3(int i, char (*a)[0][*], int (*x)[__lengthof__(*a)]);

void s_zero_1(int i, char (*a)[5][0], int (*x)[sizeof(**a)]);
void s_zero_2(int i, char (*a)[i][0], int (*x)[sizeof(**a)]);
void s_zero_3(int i, char (*a)[*][0], int (*x)[sizeof(**a)]);

void
f_zero(void)
{
int  i0[0];
int  i5[5];
char c05[0][5];
char c50[5][0];

sizeof(l_zero_1(5, &c05, &i0));
//sizeof(l_zero_1(5, &c05, &i5));  // -Wincompatible-pointer-types

sizeof(l_zero_2(5, &c05, &i0));
sizeof(l_zero_2(5, &c05, &i5));  // Wantfail

sizeof(l_zero_3(5, &c05, &i0));
sizeof(l_zero_3(5, &c05, &i5));  // Wantfail

sizeof(s_zero_1(5, &c50, &i0));
sizeof(s_zero_1(5, &c50, &i5));  // Wantfail

sizeof(s_zero_2(5, &c50, &i0));
sizeof(s_zero_2(5, &c50, &i5));  // Wantfail

sizeof(s_zero_3(5, &c50, &i0));
sizeof(s_zero_3(5, &c50, &i5));  // Wantfail
}

void l_vla_1(int i, int j, char (*a)[i][5], int (*x)[__lengthof__(*a)]);
void l_vla_2(int i, int j, char (*a)[i][j], int (*x)[__lengthof__(*a)]);
void l_vla_3(int i, int j, char (*a)[i][*], int (*x)[__lengthof__(*a)]);

void s_vla_1(int i, int j, char (*a)[5][j], int (*x)[sizeof(**a)]);
void s_vla_2(int i, int j, char (*a)[i][j], int (*x)[sizeof(**a)]);
void s_vla_3(int i, int j, char (*a)[*][j], int (*x)[sizeof(**a)]);

void
f_vla(void)
{
int  i3[3];
int  i5[5];
char c35[3][5];
char c53[5][3];

sizeof(l_vla_1(3, 5, &c35, &i3));
sizeof(l_vla_1(3, 5, &c35, &i5));  // Wantwarn

sizeof(l_vla_2(3, 5, &c35, &i3));
sizeof(l_vla_2(3, 5, &c35, &i5));  // Wantwarn

sizeof(l_vla_3(3, 5, &c35, &i3));
sizeof(l_vla_3(3, 5, &c35, &i5));  // Wantwarn

sizeof(s_vla_1(5, 3, &c53, &i3));
sizeof(s_vla_1(5, 3, &c53, &i5));  // Wantwarn

sizeof(s_vla_2(5, 3, &c53, &i3));
sizeof(s_vla_2(5, 3, &c53, &i5));  // Wantwarn

sizeof(s_vla_3(5, 3, &c53, &i3));
sizeof(s_vla_3(5, 3, &c53, &i5));  // Wantwarn
}

void l_star_1(int i, char (*a)[*][5], int (*x)[__lengthof__(*a)]);
void l_star_2(int i, char (*a)[*][i], int (*x)[__lengthof__(*a)]);
void l_star_3(int i, char (*a)[*][*], int (*x)[__lengthof__(*a)]);

void s_star_1(int i, char (*a)[5][*], int (*x)[sizeof(**a)]);
void s_star_2(int i, char (*a)[i][*], int (*x)[sizeof(**a)]);
void s_star_3(int i, char (*a)[*][*], int (*x)[sizeof(**a)]);

void
f_star(void)
{
int  i3[3];
int  i5[5];
char c35[3][5];
char c53[5][3];

sizeof(l_star_1(5, &c35, &i3));
sizeof(l_star_1(5, &c35, &i5));  // Wantwarn

sizeof(l_star_2(5, &c35, &i3));
sizeof(l_star_2(5, &c35, &i5));  // Wantwarn

sizeof(l_star_3(5, &c35, &i3));
sizeof(l_star_3(5, &c35, &i5));  // Wantwarn

sizeof(s_star_1(5, &c53, &i3));
sizeof(s_star_1(5, &c53, &i5));  // Wantwarn

sizeof(s_star_2(5, &c53, &i3));
sizeof(s_star_2(5, &c53, &i5));  // Wantwarn

sizeof(s_star_3(5, &c53, &i3));
sizeof(s_star_3(5, &c53, &i5));  // Wantwarn
}

And here's how it runs:

$ /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(v): 44
lengthof(z): 0

lengthof(memberof(struct s, y)): 8

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

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

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

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

lengthof(*q++): 16; q: 0x7ffd18a52b60
lengthof(*q++): 64; q: 0x7ffd18a52b60
alignof(*q++): 4;  q: 0x7ffd18a52b20

lengthof(int [0][4]): 0
lengthof(int [0][i++]): 0;  i: 6
lengthof(int [7][4]): 7
lengthof(int [7][i++]): 7;  i: 5
lengthof(int [i++][4]): 7;  i: 8
lengthof(int [i++][n]): 8;  i: 9



Alejandro Colomar (4):
 gcc/: Rename array_type_nelts() => array_type_nelts_minus_one()
 Merge definitions of array_type_nelts_top()
 c: Add __lengthof__() operator (n2529)
 testsuite: Add tests for __lengthof__

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                       | 114 ++++++++++++++++++++-
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                     |  27 +++++
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/lengthof-compile.c |  48 +++++++++
gcc/testsuite/gcc.dg/lengthof.c         | 126 ++++++++++++++++++++++++
gcc/tree.cc                             |  17 +++-
gcc/tree.h                              |   3 +-
27 files changed, 453 insertions(+), 79 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/lengthof-compile.c
create mode 100644 gcc/testsuite/gcc.dg/lengthof.c

Range-diff against v3:
1:  73010cb4af6 = 1:  73010cb4af6 gcc/: Rename array_type_nelts() => 
array_type_nelts_minus_one()
2:  2bb966a0a89 = 2:  2bb966a0a89 Merge definitions of array_type_nelts_top()
3:  d22b5e1c015 ! 3:  e2dbfc43b14 c: Add __lengthof__() operator
   @@ Metadata
    Author: Alejandro Colomar <a...@kernel.org>

     ## Commit message ##
   -    c: Add __lengthof__() operator
   +    c: Add __lengthof__() operator (n2529)

        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....@tutanota.com>
   +    Link: <https://inbox.sourceware.org/gcc/m8s4oqy--...@tutanota.com/T/>
   +    Suggested-by: Xavier Del Campo Romero <xavi....@tutanota.com>
   +    Co-developed-by: Martin Uecker <uec...@tugraz.at>
        Cc: Gabriel Ravier <gabrav...@gmail.com>
   -    Cc: Martin Uecker <uec...@tugraz.at>
        Cc: Joseph Myers <josmy...@redhat.com>
        Cc: Jakub Jelinek <ja...@redhat.com>
   +    Cc: Kees Cook <keesc...@chromium.org>
   +    Cc: Qing Zhao <qing.z...@oracle.com>
   +    Cc: Jens Gustedt <jens.gust...@inria.fr>
        Signed-off-by: Alejandro Colomar <a...@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 (type_code != ARRAY_TYPE)
   ++    {
   ++      error_at (loc, "invalid application of %<lengthof%> to type %qT", 
type);
   ++      return error_mark_node;
   ++    }
    +  if (!COMPLETE_TYPE_P (type))
    +    {
    +      error_at (loc,
   @@ gcc/c-family/c-common.cc: c_alignof_expr (location_t loc, tree expr)
    + type);
    +      return error_mark_node;
    +    }
   -+  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);
    +}
   @@ gcc/c/c-typeck.cc: c_expr_sizeof_type (location_t loc, struct c_type_name 
*t)
       return ret;
     }

   ++static bool
   ++is_top_array_vla (tree type)
   ++{
   ++  bool zero, var;
   ++  tree d;
   ++
   ++  if (TREE_CODE (type) != ARRAY_TYPE)
   ++    return false;
   ++  if (!COMPLETE_TYPE_P (type))
   ++    return false;
   ++
   ++  d = TYPE_DOMAIN (type);
   ++  zero = !TYPE_MAX_VALUE (d);
   ++  var = (!zero
   ++ && (TREE_CODE (TYPE_MIN_VALUE (d)) != INTEGER_CST
   ++     || TREE_CODE (TYPE_MAX_VALUE (d)) != INTEGER_CST));
   ++  var = var || (zero && C_TYPE_VARIABLE_SIZE (type));
   ++  return var;
   ++}
   ++
    +/* Return the result of lengthof applied to EXPR.  */
    +
    +struct c_expr
   @@ gcc/c/c-typeck.cc: c_expr_sizeof_type (location_t loc, struct c_type_name 
*t)
    +      ret.original_code = LENGTHOF_EXPR;
    +      ret.original_type = NULL;
    +      ret.m_decimal = 0;
   -+      if (C_TYPE_VARIABLE_SIZE (TREE_TYPE (folded_expr)))
   ++      if (is_top_array_vla (TREE_TYPE (folded_expr)))
    + {
    +  /* lengthof is evaluated when given a vla.  */
    +  ret.value = build2 (C_MAYBE_CONST_EXPR, TREE_TYPE (ret.value),
   @@ gcc/c/c-typeck.cc: c_expr_sizeof_type (location_t loc, struct c_type_name 
*t)
    +  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)));
   ++      pop_maybe_used (is_top_array_vla (TREE_TYPE (folded_expr)));
    +    }
    +  return ret;
    +}
   @@ gcc/c/c-typeck.cc: c_expr_sizeof_type (location_t loc, struct c_type_name 
*t)
    +    }
    +  else
    +  if ((type_expr || TREE_CODE (ret.value) == INTEGER_CST)
   -+      && C_TYPE_VARIABLE_SIZE (type))
   ++      && is_top_array_vla (type))
    +    {
    +      /* If the type is a [*] array, it is a VLA but is represented as
    + having a size of zero.  In such a case we must ensure that
   @@ gcc/c/c-typeck.cc: c_expr_sizeof_type (location_t loc, struct c_type_name 
*t)
    +  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);
   ++  pop_maybe_used (type != error_mark_node ? is_top_array_vla (type) : 
false);
    +  return ret;
    +}
    +
   @@ gcc/doc/extend.texi: If the operand of the @code{__alignof__} expression 
is a fu

    +@node Length
    +@section Determining the Length of Arrays
   ++@cindex lengthof
    +@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.)
   ++Its syntax is just like @code{sizeof}.
   ++The operand must be a complete array type.
   ++The operand is not evaluated
   ++if the top-level length designator is an integer constant expression;
   ++and it is evaluated
   ++if the top-level length designator is not an integer constant expression.
   ++
   ++XXX: Do we want to document the following?  I think so.
   ++XXX: It would prevent users from relying on __lengthof__
   ++XXX: for distinguishing arrays from pointers.
   ++XXX: I don't want users to complain in the future
   ++XXX: if this doesn't report errors on function parameters anymore
   ++XXX: and that breaks their assumptions.
   ++In the future,
   ++it might also accept a function parameter with array notation,
   ++an incomplete array whose length is specified by other means,
   ++such as attributes,
   ++or other similar cases.
    +
     @node Inline
     @section An Inline Function is As Fast As a Macro
-:  ----------- > 4:  9a691f7f208 testsuite: Add tests for __lengthof__
--
2.45.2


Reply via email to