On Tue, Aug 06, 2024 at 02:22:38PM GMT, Alejandro Colomar 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

Self-correction: s/2/4/

>    was questioned the value of this operator.  Below is a rationale to
>    defend it.
> -  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.  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.  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?
> 
> -  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
> 



-- 
<https://www.alejandro-colomar.es/>

Attachment: signature.asc
Description: PGP signature

Reply via email to