john said:
K&R/2 is a little vague on the following question: under what
circumstances is it legal to declare using new-style syntax a
separately compiled function that was compiled with an old-style
definition.
Is it sufficient to use only promoted arguments in the new-style
declaration?
The relevant paragraph is 6.7.5.3p15. Note in particular the
second-to-last sentence.
For two function types to be compatible, both shall specify
compatible return types(127). 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 the
determination of type compatibility and of a composite type, each
parameter declared with function or array type is taken as having
the adjusted type and each parameter declared with qualified type
is taken as having the unqualified version of its declared type.)
with footnote (127):
If both function types are ``old style'', parameter types are not
compared.
Conceivably, the whole calling sequence for old style and new style
definitions could differ.
Under ISO C they cannot. As long as the declaration (with prototype)
is compatible with the type at the definition -- regardless of whether
it is "old style" or "new style" -- the declaration is legal and calls
matching the declaration must work, no matter how the function is
defined, and since the compiler doesn't know how it's defined, the
calling sequences for the two cases must be the same.
Is it legal for the compiler to choose
completely incompatible calling sequences for old-style and new-style
declarations?
Only in the sense that it's possible to jigger up all sorts of strange
things under the "as if" rule. Practically speaking, no compiler
would ever go to such lengths, because calls in the two cases must
behave identically. Not only that, but taking the address of a
function using '&' produces a pointer-to-function that must behave
identically for the two cases. There is just no reasonable way to
do this in a standard compilation environment where the function in
question is defined in another translation unit.
It would certainly be desirable, since new-style
declarations seem to allow for significant optimizations that aren't
possible with old-style declarations.
I guess it's possible that this is right, but it's the wrong question.
The question is, if we have a new style _declaration_, does it make
sense to use different calling sequences for a new style _definition_
vs an old style _definition_. The new style _declaration_ gives us
all the information we need to do any possible optimizations in both
cases. At the point of _definition_, there is enough information
available to construct a new style _declaration_, and that allows
all the optimizations that would be possible if the definition were
written using a prototype.
Remember: functions defined with prototypes (assuming they meet
certain criteria as regards which types are present, use of ellipsis,
etc) must be callable using either new style or old style declarations.
Because we don't know (in those cases that meet the criteria) whether
the declaration style matches the definition style, and all four cases
must work the same way, there's no useful way to differentiate in
general.
This is a frequent problem if you have old library binaries and
sources but want the extra type checking from an ANSI compiler.
If you're compiling the (old style) sources, a good sanity check
is to write new-style declarations (ie the same ones that you're
using to compile the other code) and arrange to have those be
present in the source before the old style function definitions.
(Can be done as compilations "on the side" if necessary.) This
way, if the prototypes don't match the definitions, the compiler
will squawk, and you'll know the prototype is no good. But if
the old style definition is accepted with a prototype declared
before it, then the old style definition is going to work when
called using that prototype declaration.