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