Am Dienstag, dem 18.03.2025 um 14:03 -0700 schrieb Yeoul Na via Gcc: > > > On Mar 18, 2025, at 12:48 PM, Martin Uecker <uec...@tugraz.at> wrote: > > > > Am Dienstag, dem 18.03.2025 um 09:52 -0700 schrieb Yeoul Na via Gcc: > > > > > > > On Mar 18, 2025, at 12:16 AM, Martin Uecker <uec...@tugraz.at> wrote: > > > > > > > > When xp->ptr is accessed, the size expression > > > > is evaluated and the lvalue which is formed at this point in > > > > time gets the type int(*)[xp->count], similar to how the size > > > > expression of function arguments are evaluated when the > > > > function is entered. The type of this expression > > > > then does not change anymore. You could implement it > > > > by rewriting it to the following. > > > > > > Yes, that’s effectively similar to how the size of bounds annotations > > > work too. > > > > > > Though my question was, VLA types currently don't work this way. > > > > > > The size is evaluated when it’s declared as you all discussed earlier. > > > > this is the case only for automatic variables. For function arguments > > they are evaluated at function entry. > > Well, I think saying this way sounds a bit misleading. This works this > way because function parameters are automatic variables of the function > and their declarations are reached at the entry of the function.
There is no control flow which goes through the declarations of the arguments in the function prototype, which would define exactly when in what order these "declarations are reached", while this is clear for function-scope declarations which are actually reached by control flow. The C standard just explicitely states that on function entry those size expressions are evaluated. > And by “only for automatic variables’, then you mean global > and static variables are not or won't be working in the same > way as the automatic variables in the future? I should have said objects defined at function-scope. static variables already work like this. > For global and static variables, I meant “pointers" to VLA types. > > Also, what about a pointer to pointer to an array? Should it > be fixed to be evaluated at the time of use? According to the C standard, the size expression for the example below is evaluated on function entry. We try to not break existing code in C without a very good reason, so I do not think this can be changed (but it also seems just fine to me how it is). > > int foo(int len, int (*((*inout_buf)[len])) { > // use *inout_buf > // ... > > int new_len = 10; > *out_buf = malloc(new_len); // should it be fixed to be evaluated at the > time of use? > > return new_len; > } > > > > > > > So do you mean you want to fix the current behavior or make the > > > > I think the current behavior is fine, and changing it > > would also be problematic for existing code. > > > > > behavior inconsistent between if they are struct members, > > > indirect pointers vs. local variables like below? > > > > I think the nature of the problem requires that the size > > expression must be evaluated at different time points > > depending on the scenario. > > > > For automatic variables, this is when the declaration is > > reached in the control flow and for function arguments when > > the function is entered. For structure members where the size > > expression refers to other members, they need to be > > evaluated when the member is accessed. > > > > > > > > void test(struct X *xp) { > > > int len = 10; > > > int (*ptr)[len] = malloc(sizeof(int) * len); // (1) int (*ptr)[len] is > > > evaluated here and fixed. > > > > > > xp->count = 100; > > > xp->ptr = malloc(sizeof(int) * 100); // size of xp->ptr is dynamically > > > evaluated with xp->count, hence, this works fine. > > > > > > len = 5; // (2) > > > ptr = malloc(sizeof(int) * len); // size of ptr is still fixed to be > > > (1), so this traps at run time. > > > } > > > > Yes. > > I think the fact that the behavior diverges in this subtle > way will make things very confusing. > I use it in this way (using a macro for access) and think it works very nicely. > Not sure adding “dot” actually helps that much with the users > comprehending this code. > Based on my humble experience, I would say that it helps people to avoid mistakes when things that do behave differently do not look identically. > That’s why I was quite skeptical if VLA in C can ever be evolved > to model the size with a struct member safely, given that VLA made > a wrong choice from the beginning. I don't think a wrong choice was made. The alternative would be to have the type of a variable change whenever a variable in the size expression changes. I think this would be much harder to understand and cause more errors. For a structure members there is no alternative. But it is also less problematic than for other variables, because we need to restrict the expressions which we allow in this context anyhow. I also like things to be consistent, but different things also need to be treated differently. We could try to make things more restrictive, e.g. require these expressions to be "const" in some safety profile, which would make such differences go a way. Martin > That said this is just my own impression and whether we can make the usable > model this way may need more study. > > At least now I understand what is your vision. Thanks. > > > > > For GNU C (but not ISO C) there could be size expression in > > the declaration referring to automatic variables. > > > > int foo() > > { > > int n = 1; > > struct { > > char buf[n]; // evaluated when the declaration is reached > > } x; > > }; > > > > This is something which can not currently happen for ISO C where > > this can only refer to named constants and where the size is > > evaluated at compile time. > > > > int foo() > > { > > enum { N = 1 }; > > struct { > > char buf[N]; > > } x; > > } > > > > Still, I think this is another reason why it would be much > > better to have new syntax for this. We also need to restrict > > the size expressions in all three scenarios in different ways. > > > > int foo() > > { > > int n = 1; > > struct { > > > > int m; > > char buf1[n]; // evaluated when the declaration is reached > > char buf2[.m]; // evaluated on access to buf2, restricted syntax > > } x { .m = 10 }; > > } > > > > > > Martin > > Yeoul >