what the fuck is going on with expression evaluation in c
i was nerd sniped in #hare-soc
so, take the declaration `typeof((puts("1"), (int (*)[(puts("2"), 1)])0)) a[(puts("3"), 1)], b[(puts("4"), 1)];`
(the actual declaration sent in #hare-soc is slightly different, but this is a somewhat simplified version)
when this runs, what should be printed?
i still don't know the fucking answer
the c standard makes it so difficult to find an answer to this question that isn't ambiguously worded, if there is any answer at all
here's what i've gathered from my interpretation of the standard:
- full expressions are sequenced with respect to one another
- initializers which aren't part of compound literals are full expressions
- an implicit full expression is created to evaluate the sizes of arrays in a variably modified type
- within a variably modified type, evaluations are unsequenced
- in a declaration, a type consists of the type on the left, as well as the declarator
- the operands of a comma expression are sequenced with respect to one another
- the operand of a typeof expression is evaluated if the type is variably modified
- the type name in a cast expression is "evaluated" whenever the cast expression is evaluated
some of the stuff above i'm still not 100% sure about. specifically:
- the declaration i'm using doesn't have initializers, but i don't see anything about ordering of the "evaluation" of declarators. however, the spec says in a note that "a full declarator for a variably modified type" is a full expression, which, ??? declarators aren't expressions? how can a declarator be a full expression if it isn't even an expression? but either way, this implies that the declarators are evaluated in order
- the thing about types in declarations being the type on the left and the declarator is just inferred by me. i can't find anything that explicitly says that. so idk what it even means to evaluate the implicit full expression of a variably modified type in this context. so i assume that the type on the left should be evaluated twice; once for each declarator? but it's unclear
- this isn't even really relevant, but the standard says "the evaluations of the initialization list expressions are indeterminately sequenced with respect to one another", but it never defines what an "initialization list" is. it defines "initializer list", it only ever uses the term "initialization list" in one place (outside of annex j "portability issues")
the conclusions i can draw from my interpretation of the standard are:
- 3 and 4 are printed once
- 3 is printed before 4
- 1 is printed before 2
- 1 and 2 are printed twice; once for each declarator
- the printing of 1 and 2 is unsequenced with respect to 3 and with respect to 4
so therefore, the order should be one of:
1 2 3 1 2 4
1 2 3 4 1 2
3 1 2 1 2 4
3 1 2 4 1 2
neither gcc nor clang prints *any* of the above though. in fact, both gcc and clang only print "2" once(!!!) but "1" is printed twice! which, like, i can't find any way to justify that, even with all the ambiguities???
clang prints 3 1 2 4 1
gcc prints 1 2 3 1 4
both of these seem reasonable, except for the missing 2. but interestingly, with some minor tweaks to the declaration, gcc instead prints 2 3 1 4 1:
int a[1];
typeof(*(puts("1"), (int (*)[(puts("2"), 1)])&a)) (*b)[(puts("3"), 1)], (*c)[(puts("4"), 1)];
so now 2 is being printed first, before 1! which... idk, i guess if the puts("2") is part of the "type", which is evaluated before the actual typeof expression?? but i feel like that's not right??? because there's still a comma expression, and both operands of the comma expression must be evaluated everytime the comma expression is evaluated, and the first operand must evaluate first. so, like, what the fuck
is there some undefined behavior i'm missing here? i don't think there is?
is this a bug in gcc and/or clang? i don't feel comfortable saying that it is, because i imagine the gcc/clang authors know a lot more than i do, and both gcc and clang behave the same with -O0 as with -O3
wtf is going on here
Introducing for-each loops for Hare
April 2, 2024 by Lorenz (xha) @xha
https://harelang.org/blog/2024-04-01-introducing-for-each-loops-in-hare/