Are these compatible function types in C?
c, c11, function, language-lawyer, pointers
Solution
These two defect reports address your issue:
- Defect Report #316
- Defect Report #317
Defect report 316 says (emphasis mine going forward):
The rules for compatibility of function types in 6.7.5.3#15 do not define when a function type is "specified by a function definition that contains a (possibly empty) identifier list", [...]
and it has a similar example to the one you give:
void f(a)int a;{}
void (*h)(int, int, int) = f;
and it goes on to say:
I believe the intent of the standard is that a type is specified by a function definition only for the purposes of checking compatibility of multiple declarations of the same function; when as here the name of the function appears in an expression, its type is determined by its return type and contains no trace of the parameter types. However, implementation interpretations vary.
Question 2: Is the above translation unit valid?
and the answer from the committee was:
The Committee believe the answers to Q1 & 2 are yes
This was between C99 and C11 but the committee adds:
We have no intention of fixing the old style rules. However, the observations made in this document seem to be generally correct.
and as far a I can tell C99 and C11 do not differ greatly in the sections you have quoted in the question. If we further look into defect report 317 we can see that it says:
I believe the intent of C is that old-style function definitions with empty parentheses do not give the function a type including a prototype for the rest of the translation unit. For example:
void f(){}
void g(){if(0)f(1);}
Question 1: Does such a function definition give the function a type including a prototype for the rest of the translation unit?
Question 2: Is the above translation unit valid?
and the committees response was:
The answer to question #1 is NO, and to question #2 is YES. There are no constraint violations, however, if the function call were executed it would have undefined behavior. See 6.5.2.2;p6.
This seems to hinge on the fact that it is underspecified whether a function definition defines a type or a prototype and therefore means there is no compatibility checking requirements. This was originally the intent with old style function definitions and the committee will not clarify further probably because it is deprecated.
The committee points out that just because the translation unit is valid does not mean there is no undefined behavior.
Problem
Consider the following C program: ``` int f() { return 9; } int main() { int (*h1)(int); h1 = f; // why is this allowed? return h1(7); } ``` According to the C11 Standard, Sec. 6.5.16.1, in a simple assignment, "one of the following shall hold", and the only relevant one in the list is the following: the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right; Moreover, this is a "constraint", meaning, a conforming implementation must report a diagnostic message if it is violated. It seems to me that this constraint is violated in the assignment in the program above. Both sides of the assignment are function pointers. So the question is, are the two function types compatible? This is answered in Sec. 6.7.6.3: For two function types to be compatible, both shall specify compatible return types.146) Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types. If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. In this case, one of the types, that of h1, has a parameter type list; the other, f, does not. Hence the last sentence in the quote above applies: in particular, "both shall agree in the number of parameters". Clearly h1 takes one parameter. What about f? The following point occurs just before the above: An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. So clearly f takes 0 parameters. So the two types do not agree in the number of parameters, the two function types are incompatible, and the assignment violates a constraint, and a diagnostic should be issued. However, both gcc 4.8 and Clang emit no warnings when compiling the program: ``` tmp$ gcc-mp-4.8 -std=c11 -Wall tmp4.c tmp$ cc -std=c11 -Wall tmp4.c tmp$ ``` By the way, both compilers do issue warnings if f is declared "int f(void) ...", but this should not be necessary based on my reading of the Standard above. The questions: Q1: Does the assignment "h1=f;" in the program above violate the constraint "both operands are pointers to qualified or unqualified versions of compatible types"? Specifically: Q2: The type of h1 in the expression "h1=f" is pointer-to-T1 for some function type T1. What exactly is T1? Q3: The type of f in the expression "h1=f" is pointer-to-T2 for some function type T2. What exactly is T2? Q4: Are T1 and T2 compatible types? (Please quote appropriate sections of the Standard or other documents to support the answer.) Q1', Q2', Q3', Q4': Now suppose the declaration of f is changed to "int f(void) { return 9; }". Answer questions 1-4 again for this program.