Re: Why is this not an error in Visual C?
- From: Tim Rentsch <txr@xxxxxxxxxxxxxxxxxxx>
- Date: 01 Jun 2009 23:10:37 -0700
Eric Sosman <Eric.Sosman@xxxxxxx> writes:
Kaz Kylheku wrote:
This is not a constraint, but it's a semantic rule. The rule essentially
says that when an old-style function definition is processed, then
the parameter declarations are incorporated into the type info for that
function.
Up to here, I believe you're right. The function is defined,
has a type, and the type comes from the definition.
The function is declared to have a type which includes information
about the number of parameters which it has, and their types.
... but here I think you're wrong. The definition is a declaration,
but I don't think an old-style definition declares the types (or
number) of the parameters. That is, I think, `int f(x) int x; {...}'
furnishes a declaration equivalent to `int f()', not `int f(int)'.
As far as I'm concerned, this full type info constitutes a prototype.
No reasonable, sane distinction can be drawn between the term ``prototype'' as
used in the context of the C language, and ``the state of knowing the number
and types of arguments of a function''.
Footnote 125 to 6.7.5.3p14, although of course not normative,
seems highly suggestive. Concerning the compatibility of function
types, it says "If both function types are ``old style'', parameter
types are not compared." If "the state of knowing the number and
types of arguments of a function" persisted beyond the old-style
definition, it's hard to imagine why this footnote would exist: the
type-and-number information would still be around, and there'd be
no reason not to check. A footnote pointing out that checking is not
performed thus suggests that the information does not persist.
The constraint that is violated is elsewhere, as Han From China pointed
out, namely in 6.7{4}:
All declarations in the same scope that refer to the same object or function
shall specify compatible types.
The *declared* types are compatible, since the old-style
definition declares nothing about the parameters. It turns out
that the new-style declaration describes a parameter list that
is incompatible with the old-style definition, but that's another
kettle of fish.
Given these declarations:
int foo(int);
int foo(x)
double x;
{
}
It's clear that they have incompatible types by 6.7.5.13{15} and that they are
two declarations for the same identifier, in the same scope, which consequently
violates 6.7{4}.
The first part is clear; the part after "and" is murky at best.
Now this second declaration is supposedly not a prototype. What this means is
that even though C implementations are required to record the full type
information for a function definition, even if it is old-style, they must not
use it when translating calls to the function. Doh?
Right. Try it yourself:
int f(x) int x; { return x; }
int main(void) { return f("Hello, world!"); }
For what it's worth, gcc 4.3.2 with "-W -Wall -ansi -pedantic" issues
no diagnostics for this erroneous source. The behavior of a compiler
is non-normative, but again highly suggestive.
If the compiler knows what the parameter types are, it should generate code
which converts the argument expressions to those types, and diagnose any
constraints if the expressions are not assignment-compatible compatible with
the parameter types.
Yes. Since it doesn't do so, "if the compiler knows" is false.
For instance after the definition
int foo(a)
int a;
{
}
the call
foo(3.0);
should behave exactly as if we had:
extern int foo(int);
foo(3.0);
this is because foo was in fact declared to have type ``int (int)''.
The function was defined that way, but not declared that way.
The behavior is undefined because a double is not an int, whereas
if the prototype were around the double would be converted to an
int and all would be well.
FWIW, on my implementation foo(3.0) causes `a' to take on
the value zero.
[...snip...]
There are two distinct questions at issue here, I think it will
be helpful to keep them separate.
Question 1: Are argument types in a function call compared
against parameter types in an old-style function definition
(assuming no other declarations for the function are visible,
in particular no declarations with prototypes)?
Answer: No. This conclusion is made (normatively) explicit in
the section on function calls, specifically 6.5.2.2 p 8:
No other conversions are performed implicitly; in
particular, the number and types of arguments are not
compared with those of the parameters in a function
definition that does not include a function prototype
declarator.
Question 2: If there is a mismatch between parameter types in
a function declaration with prototype and an old-style function
definition, is a diagnostic required?
Answer: Yes. A diagnostic must be issued if the types of
corresponding parameters are not compatible[*]. Paragraph 15
(the entire paragraph) in 6.7.5.3 defines compatible for function
types. The relevant sentence for this situation is this:
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.
The plain text here makes it clear (e.g., "the type of the
corresponding identifier") that the types of the two declarations
are not compatible. The syntax in 6.9 shows that an entire
function definition is a declaration; the Constraint in 6.7p4
says "All /declarations/ [emphasis added] in the same scope that
refer to the same object or function shall specify compatible
types." Because the types aren't compatible, the constraint is
violated, and a diagnostic must be issued.
[*] The corresponding types are compared after adjustment
following the rules for function parameters in the two cases.
What makes the matter murky, I suspect, is thinking about "what
the compiler knows", and expecting that if the compiler "knows
something" in one place then it also knows it later on.
Reasoning based on "what the compiler knows" is following a red
herring; it might be a useful heuristic, but the real test is
text in the Standard. Here the plain text given in the Standard
makes the conclusions clear in each of the two cases.
.
- Prev by Date: Re: The Semantics of 'volatile'
- Next by Date: Re: A Queue implementation of doubly linked list in C
- Previous by thread: Sanity?
- Next by thread: Re: looking for thoughts on the philosophy of optimizing compilers
- Index(es):
Relevant Pages
|