В стандарте c89(/c90) нашлась неоднозначная формулировка, из-за которой gcc и clang расходятся в трактовке. Дело в неявных объявлениях функций: в c89 можно вызвать необъявленную функцию, и компилятор не выдаст ошибку, а объявит её как extern int () с неопределёнными параметрами. В c99 эту фичу убрали, так что точный смысл так и не уточнили.
Забавно, что семантика зависит от лишних скобок: f() заносит f в область видимости, а (f)() — нет.
Самый простой пример: int f(int [sizeof(f())]); Декларатор объявляет сам себя. Идентификаторы попадают в область видимости после завершения декларатора, поэтому f() внутри массива-параметра видит не саму f, а её неявное объявление int (). Потом f нормально переобъявляется как int (int *). Всё сходится.
Но стоит добавить имя параметра — int f(int f[sizeof(f())]); — и начинается разброд. clang компилирует, gcc ошибается. Вопрос: а так вообще можно? Смотрим в священный текст ANSI X.3-159-1989, раздел 3.3.2.2 Function calls. Там сказано: если вызов состоит из одного идентификатора и для него нет видимого объявления, он неявно объявляется так, будто в innermost block появилось extern int identifier();. Ключевое — «innermost block», и стандарт нигде не объясняет, что это значит.
Интуитивно это «самый внутренний блок» (compound statement), и тогда код легален. Но объявление может быть и на file scope, вне блоков. Неявные объявления на file scope нигде явно не запрещены, и оба компилятора их допускают. Тогда, возможно, имеется в виду innermost scope. В этом случае f() из примера попадает в function prototype scope, и параметр f — невалидное переобъявление int () как int.
Против этой трактовки говорит то, что неявное объявление имеет форму extern int (). Спецификатор extern в function prototype scope запрещён — может, его там и не должно быть. Вот ещё один пример, который показывает, что расхождение именно в области видимости: int main(void) { int f(int [sizeof(x())]); int x; } Здесь clang ругается, а gcc принимает. Вдобавок в gcc неявное объявление в function prototype scope не распадается в указатель, хотя типы функций в такой области видимости обычно не встречаются. gcc выдаёт ошибку на int f(int [sizeof(f())], int f()); потому что f переобъявляется как int (*)(). Это ещё один намёк, что поведение clang ближе к правильному.
Ясности не будет: проблемное поведение убрали из стандарта больше 27 лет назад. Так что остаётся только просьба: хватит использовать c89. c11 приняли более 15 лет назад. Если у вас нет совсем уж жёстких ограничений вроде проприетарного c89-компилятора для микропроцессора от компании, обанкротившейся 25 лет назад (иначе машина самоуничтожится), — просто переходите.