Wednesday, February 7, 2024

[SOLVED] Why does enclosing the function name in parenthesis disable implicit declaration?

Issue

This is the construct in question:

int main (void)
{
    int a = 5;
    
    (func)(a);

    return 0;
}

Compiled with GNU GCC C99 the following will happen:

  • If func was defined as a function before main() the function will be executed.
  • If func was not defined as a function before main the compiler issues "func undeclared" error

However, if I remove the braces around func the function will be implicitly declared so even if it is defined in another translation unit, it will execute, which is the behavior I would always expect.

A recursive-descent parser identifies a function call expression (also according to the standard) by the presence of (arguments) which is why code like 3(a); would issue an error: "called object is not a function or function pointer". And just like other similar operations such as the suffix increment, what comes before (arguments) is expected to be a subexpression - in this case the constraint demands an operand that is a function or a function pointer. With every other operation you can freely add as many brackets as you like: ((a))++ and that wouldn't change a thing, however with function calls it does not look quite like that.

Why would that code behave differently, is it a language feature that I didn't know of, or a result from some carefully crafted parsing design choice?


Solution

This is because of how implicit declarations were defined in C89. Only a bare identifier qualifies as an implicit declaration.

This is spelled out in section 3.3.2.2 of the C89 standard:

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

     extern int  identifier();

appeared.

Since in this case:

(func)(a);

The expression preceding the parenthesized arguments is not just an identifier, it does not qualify as an implicit declaration.

As for why gcc still seems to allow this even in C99 and later mode with -pedantic, I'd chalk that up to either an extension or a bug.



Answered By - dbush
Answer Checked By - Willingham (WPSolving Volunteer)