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.
-  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

Attachment: signature.asc
Description: PGP signature

Reply via email to