Re: Why is this not an error in Visual C?



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.

This is like not fastening a seatbelt or putting on a life jacket, when these
safety devices are available.

State-of-the-art statically typed languages go out of the way to infer type
information in the absence of declarations, yet here it is being thrown away.

Presumably because "existing code is important," as it says in
the Rationale, even if that code is not an exemplar of cleanliness.
Similar considerations probably underlay the decision to make string
literals char[] instead of const char[], and to retain gets().

--
Eric.Sosman@xxxxxxx
.



Relevant Pages

  • Re: Why is this not an error in Visual C?
    ... says that when an old-style function definition is processed, ... That is, I think, `int fint x; ... Concerning the compatibility of function ... All declarations in the same scope that refer to the same object or function ...
    (comp.lang.c)
  • Re: Why is this not an error in Visual C?
    ... int foo ... What we're looking for to describe MSVC as broken is a constraint ... involve declarations of incompatible types. ... compatibility and of a composite type, ...
    (comp.lang.c)
  • Re: Why is this not an error in Visual C?
    ... extern int foo; ... involve declarations of incompatible types. ... This is not a constraint so there is no requirement on an ... type compatibility, with no exception for functions that are not ...
    (comp.lang.c)
  • Re: range vs precisionC
    ... volatile int a; ... in this case that is volatile. ... and we have declarations like: ... What is needed now, I think, is to realize that subtypes (and all those ad ...
    (comp.lang.c)
  • Re: typedef Used to Cast Function Pointers
    ... >> the result shall compare equal to the original pointer. ... integer type, usually int. ... The more complicated cases are compatibility between or with ... Both prototyped and unprototyped declarations of a ...
    (comp.lang.c)