i found some ambiguous wording in the c89(/c90) standard, where gcc and clang disagree on the interpretation. it concerns the behavior of implicit function declarations, which were removed in c99, so this was never disambiguated.
for those unaware: c89 has a cool "feature" where, if you try to call a function which doesn't exist, rather than erroring out, the function is implicitly declared as a function with unspecified parameters returning int.
more specifically: if the function in a call expression is a non-parenthesized identifier which isn't in scope, it's inserted into scope as an extern int ().
(a funny thing about this is that it means that seemingly superfluous parentheses around expressions affect semantics: f() inserts f into scope, but (f)() doesn't)
anyways, i'm gonna show you a fun edge case with this feature. but to build up to it, let's start simple. here's a declaration whose declarator declares itself:
int f(int [sizeof(f())]);
this declares a function with one parameter, which is an array (decayed to a pointer).
identifiers are inserted into scope after the declarator is completed. so when f() is called inside the array declarator, it isn't yet in scope, so it's declared as int (). the declarator then finishes, and f is redeclared with the compatible type int (int *).
i'm now going to change the declaration by adding one character.
int f(int f[sizeof(f())]);
ok cool so like what the fuck.
is this legal?
no, really, is this legal?
i'll spoil it: clang compiles this just fine; gcc errors out. so as always whenever compilers disagree on semantics, we consult the holy script:
If the expression that precedes the parenthesized argument list in a function call consists solely of an identifier, and if no declaration is visible for this identifier, the identifier is implicitly declared exactly as if, in the innermost block containing the function call, the declaration
appeared.
extern int identifier();
the key phrase here is "innermost block". the standard never elaborates on what this means.
intuitively, you would think this means block scope (i.e. compound statement, pretty much), so the code should be legal. but the declaration can also appear in file scope, outside of any block. implicit declarations in file scope are never explicitly disallowed, and gcc and clang both allow them.
so another interpretation is that it actually means innermost scope. in that case, then f() would be declared in function prototype scope, inside of the function declarator. if this is the case, then the parameter f would be an invalid redeclaration (redeclaring int () as int), so the code should be illegal.
one hint that this may not be the correct interpretation is that the implicit declaration is specified to have the form extern int (). the extern specifier isn't allowed inside function prototype scope, so maybe it's meant to be excluded here. maybe "innermost block" actually means innermost block scope or file scope, and so the code really is legal.
here's another example, which confirms that the divergence in behavior comes down to which scope the implicit declaration goes in. here, clang errors and gcc succeeds (the opposite of the previous example):
int main(void)
{
int f(int [sizeof(x())]);
int x;
}
also note that, in gcc, the implicit declaration in function prototype scope doesn't decay to a pointer, even though function types in function prototype scope are normally impossible. gcc errors out when trying to compile the following, because f is redeclared as int (*)():
int f(int [sizeof(f())], int f());
this is yet another hint that clang's behavior may be more "correct".
because the problematic behavior was removed from the standard over 27 years ago, we'll never have closure on what the intended interpretation is.
p.s. i am begging people to stop using c89. please. c11 was finalized over 15 years ago, it's been enough time, i promise you it will be ok
"but i'm writing code for a niche microprocessor made by a company that went bankrupt 25 years ago and it's only supported by this one proprietary c89 compiler and the machine will self-destruct if you attempt to use anything else" look if you have a reason why you genuinely have no other option then you know who you are. most people have no excuse.
unrelated footnote