gcc prototype oddity

A

Allin Cottrell

Thomas Heinz wrote (in re. gcc compilation of this erroneous
program):

$ cat test.c
int f(int);
int f();
int f() {return 0;}

int main (void) { return 0; }
The only point which remains concerns the error statement for
the program ... which is:

test.c: In function »f«:
test.c:3: error: number of arguments doesn't match prototype
test.c:2: error: prototype declaration

Here, it would be nice if the prototype declaration (3rd line of
error statement) refers to the strictest declaration (in the
example line 1).

Interesting: that message does seem wrong, since there's no
inconsistency between lines 2 and 3, only between 1 and 3.

I'm cc'ing comp.lang.c.

Allin Cottrell.
 
E

E. Robert Tisdale

Allin said:
Thomas Heinz wrote:

$ cat test.c
1 int f(int);
2 int f();
3 int f() { return 0; }
4
5 int main(void) { return 0; }


Interesting: that message does seem wrong,
since there's no inconsistency
between lines 2 and 3, only between 1 and 3.

No, the message is correct.
The declaration

int f();

is a redeclaration of

int f(int);

and far as the compiler can tell.
> cat main.c
int f();
int f(int);
int f() { return 0; }

int main(void) { return 0; }
> gcc -Wall -std=c99 -pedantic -o main main.c
main.c: In function `f':
main.c:3: error: number of arguments doesn't match prototype
main.c:2: error: prototype declaration

The compiler is *not* clairvoyant.
It does not know any more than I do what you *intended* to write.

Since the type specifier appears in the declaration

int f(int);

the mistake may be that the declaration

int f(void);

was intended where you wrote

int f();

in which case you should also have written

int f(void) { return 0; }
 
L

Larry I Smith

Allin said:
Thomas Heinz wrote (in re. gcc compilation of this erroneous
program):

$ cat test.c
int f(int);
int f();
int f() {return 0;}

int main (void) { return 0; }



Interesting: that message does seem wrong, since there's no
inconsistency between lines 2 and 3, only between 1 and 3.

I'm cc'ing comp.lang.c.

Allin Cottrell.

The compiler's error messages are correct.

Line 1 is ok - it defines the prototype for f().

Line 2 is an error - it attempts to redefine the prototype
for f() that was already defined by line 1.

Line 3 is an error - it does not match the prototype for
f() that was defined by line 1.

In 'C' there can only be one prototype for a given
function name. The first one encountered in the
compilation is used (by most compilers).

Now if it was a C++ program you could have multiple
overloaded functions of the same name - each with
a different arg list.

Larry
 
S

Stephen Sprunk

Larry I Smith said:
....
The compiler's error messages are correct.

Line 1 is ok - it defines the prototype for f().

Line 2 is an error - it attempts to redefine the prototype
for f() that was already defined by line 1.

Line 3 is an error - it does not match the prototype for
f() that was defined by line 1.

In 'C' there can only be one prototype for a given
function name. The first one encountered in the
compilation is used (by most compilers).

Isn't line 2 a definition sans prototype? This appears to be benign.

Interestingly, if you reverse lines 1 and 2, you get the exact same errors
(and line numbers). gcc is apparently accepting both definitions, and the
error on line 3 just refers to the last definition, not the one to which
it's actually comparing the prototype.

S
 
P

Peter Ammon

Allin said:
Thomas Heinz wrote (in re. gcc compilation of this erroneous
program):

$ cat test.c
int f(int);
int f();
int f() {return 0;}

int main (void) { return 0; }



Interesting: that message does seem wrong, since there's no
inconsistency between lines 2 and 3, only between 1 and 3.

I'm cc'ing comp.lang.c.

Allin Cottrell.

Here's my take, FWIW.

Line 1 declares a function f() returning int and taking one parameter of
type int.

Line 2 declares a function f(). It is not a prototype (I think Larry
Smith is wrong on this issue). Since the return types are the same, the
declarations are compatible. Notice if you change the return type to
float, you get an error.

Line 3 declares a function f(). This declaration is not a prototype, so
no problem. Line 3 also defines a function int f(void). The definition
does not match the prototype in line 1, and hence the error.

Notice you can do weird stuff with this declaration/definition disconnect.

void foo() {
foo(5);
foo(3.5);
foo("hello");
}

should compile as C, even though it's obviously illegal. But change it to

void foo() {
void foo(int);
}

and you'll get an error.
 
A

Allin Cottrell

Peter said:
Here's my take, FWIW.

Line 1 declares a function f() returning int and taking one parameter of
type int.

Line 2 declares a function f(). It is not a prototype (I think Larry
Smith is wrong on this issue). Since the return types are the same, the
declarations are compatible. Notice if you change the return type to
float, you get an error.
Agreed.

Line 3 declares a function f(). This declaration is not a prototype, so
no problem. Line 3 also defines a function int f(void). The definition
does not match the prototype in line 1, and hence the error.

Again, agreed. The issue raised by Thomas is just this: the
disagreement is between the prototype on line 1 and the function
definition on line 3, yet the gcc error message refers to a
"prototype" on line 2.
 
A

Arthur J. O'Dwyer

Thomas Heinz wrote (in re. gcc compilation of this erroneous
program):

$ cat test.c
int f(int);
int f();
int f() {return 0;}

int main (void) { return 0; }


Interesting: that message does seem wrong, since there's no
inconsistency between lines 2 and 3, only between 1 and 3.

I'm cc'ing comp.lang.c.

Come not to clc for counsel, for they will say both "duuuh, what?"
and "that's off-topic."

Larry Smith's answer is wrong, though at least partially correct
for The Language That C Is Not. Tisdale's answer is flat wrong,
perhaps due to a misreading of the original program. Peter Ammon's
answer to your question is correct AFAICT (but I'm curious about
the 'void foo()' examples at the end of his post... chapter and
verse, Peter?).

1 int f(int);
2 int f();
3 int f() {return 0;}

As far as the Standard is concerned, and AFAICT, line 2 is fine
(it's just a redeclaration of the already-prototyped function).
Line 3 is invalid (conflicts with previous prototype), and thus
requires a diagnostic. GCC provides a diagnostic. Thus GCC is
perfectly conforming in this regard.

Now certainly GCC's diagnostic is not the most user-friendly; it
ought to point the programmer to the two lines which are in conflict,
rather than to the two most recent declarations of 'f'. I don't know
the internals of GCC, but it certainly *seems* like a ten-line fix
at most. ;) I would consider this a minor bug in GCC, deserving of
a bug report. I don't think comp.lang.c really needed to be dragged
into it. :)

-Arthur
 
D

Dan Pop

In said:
Thomas Heinz wrote (in re. gcc compilation of this erroneous
program):

$ cat test.c
int f(int);
int f();
int f() {return 0;}

int main (void) { return 0; }


Interesting: that message does seem wrong, since there's no
inconsistency between lines 2 and 3, only between 1 and 3.

The message about line 2 *is* wrong. Note, however, that, if I fix line 3
the message about line 2 goes away:

fangorn:~/tmp 48> cat test.c
int f(int);
int f();
int f(int i) {return 0;}
fangorn:~/tmp 49> gcc -c test.c
fangorn:~/tmp 50>

So, we have no conformance issue: the original code *required* at least
one diagnostic and got it. The correct code was correctly translated.

As for why the message about line 2 is wrong, here is the chapter and
verse from the C standard:

15 For two function types to be compatible, both shall specify
compatible return types. 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...

Which means that

int f(int);
int f();

is OK, but

int f(float);
int f();

is not.

Dan
 
A

Allin Cottrell

Dan said:
The message about line 2 *is* wrong. Note, however, that, if I fix line 3
the message about line 2 goes away:

fangorn:~/tmp 48> cat test.c
int f(int);
int f();
int f(int i) {return 0;}
fangorn:~/tmp 49> gcc -c test.c
fangorn:~/tmp 50>

Fine, I don't find that surprising.
So, we have no conformance issue: the original code *required* at least
one diagnostic and got it. The correct code was correctly translated.

I guess my question is, where do conformance issues end and QoI issues
begin? You say, "the original code *required* at least one diagnostic
and got it". True, but what if the diagnostic had been:

test.c:666: error: elephants are trampling your begonias

The point that Thomas Heinz observed is that

test.c:2: error: prototype declaration

is not much better (though I accept the first diagnostic line given
by gcc was perfectly correct).

Allin Cottrell
 
N

Nick Austin

I guess my question is, where do conformance issues end and QoI issues
begin? You say, "the original code *required* at least one diagnostic
and got it". True, but what if the diagnostic had been:

This is a simple case of one correct error message followed by an
incorrect message.

I find that compilers do that quite often. Not something to
be unduly concerned about.

Nick.
 
E

E. Robert Tisdale

Allin said:
Again, agreed. The issue raised by Thomas is just this:
the disagreement is between the prototype on line 1
and the function definition on line 3,
yet the gcc error message refers to a "prototype" on line 2.

You are thinking about this all wrong.
The declaration on line #2

int f();

is a re-declaration of the declaration on line #1

int f(int);

The definition on line #3

int f(void) { return 0; }

conflicts with *both* of the previous declarations.
The compiler cannot read the programmer's mind.
It *cannot* know the programmer's intent.
 
L

Larry I Smith

Dan said:
The message about line 2 *is* wrong. Note, however, that, if I fix line 3
the message about line 2 goes away:

fangorn:~/tmp 48> cat test.c
int f(int);
int f();
int f(int i) {return 0;}
fangorn:~/tmp 49> gcc -c test.c
fangorn:~/tmp 50>

So, we have no conformance issue: the original code *required* at least
one diagnostic and got it. The correct code was correctly translated.

As for why the message about line 2 is wrong, here is the chapter and
verse from the C standard:

15 For two function types to be compatible, both shall specify
compatible return types. 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...

Which means that

int f(int);
int f();

is OK, but

int f(float);
int f();

is not.

Dan

Well, ok Dan, you are correct.

But in my defense, I was taught that this was
a bad programming practice (2 conflicting prototypes
for the same function).

The GNU WEB site states that once the C99 spec is
fully supported by GCC that it (C99) will become the
default mode for GCC. Once that is done, the
'int promotion' assumption will no longer be
supported and f() will no longer be assumed to
be the same as f(int). Per the CGG 'checklist'
of C99 conformance, some of this has already been
done. So, what we may be seeing here is partial
implementation of the C99 spec in the error handling
code. This is also mentioned in 6.11.6 and 6.11.7
of the C99 spec.

In the final analysis, while it may not be an
error per C89, and it may be an error per C99,
it's probably not something we need to be too
concerned about.

http://gcc.gnu.org/onlinedocs/gcc-3.4.0/gcc/Standards.html#Standards

Regards,
Larry
 
E

E. Robert Tisdale

Dan said:
As for why the message about line 2 is wrong, here is the chapter and
verse from the C standard:

15 For two function types to be compatible, both shall specify
compatible return types. 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...

Which means that

int f(int);
int f();

is OK, but

int f(float);
int f();

is not.
> nl main.c
1 int f(float);
2 int f();
3 int f(float x) { return 0; }
4
5 int main (void) { return 0; }
6
> gcc -Wall -std=c99 -pedantic -o main main.c
main.c:2: error: conflicting types for `f'
main.c:2: error: an argument type that has a default promotion can't
match an empty parameter name list declaration
main.c:1: error: previous declaration of `f'
main.c:3: error: conflicting types for `f'
main.c:3: error: an argument type that has a default promotion can't
match an empty parameter name list declaration
main.c:2: error: previous declaration of `f'
 
D

Dan Pop

In said:
Fine, I don't find that surprising.

I do: changing line 3 didn't affect the relatioship between line 1 and 2
in *any* way, did it?
I guess my question is, where do conformance issues end and QoI issues
begin? You say, "the original code *required* at least one diagnostic
and got it". True, but what if the diagnostic had been:

test.c:666: error: elephants are trampling your begonias

The point that Thomas Heinz observed is that

test.c:2: error: prototype declaration

is not much better (though I accept the first diagnostic line given
by gcc was perfectly correct).

OTOH, if, after fixing line 3, gcc still treated line 2 as an error (i.e.
no object code was generated), there would have been a conformance
problem. There are enough loopholes in the standard for any language
lawyer to "solve" it, but gcc users would have had a valid reason for
complaining.

Dan
 
A

Arthur J. O'Dwyer

[correctly produces an error on gcc, but said error has weird contents]
[...Dan quotes Standard...]
Well, ok Dan, you are correct.

But in my defense, I was taught that this was
a bad programming practice (2 conflicting prototypes
for the same function).

Two conflicting prototypes for the same function is not merely a
"bad programming practice"; it is an ERROR. (From N869: A function
prototype is a declaration of a function that declares the types of
its parameters.)
The GNU WEB site

(By the way, I'm fairly certain GNU does not have a WEB site. WEB
is Knuth territory. GNU may very well have a website, or a site on
the WWW. ;)
states that once the C99 spec is
fully supported by GCC that it (C99) will become the
default mode for GCC. Once that is done, the
'int promotion' assumption will no longer be
supported

I think you are referring to "implicit int." (This is just
a pet rule of thumb, and I wouldn't force it on you, but I only
use quote marks when I'm actually quoting something. "int
promotion" isn't quoted from anywhere, least of all the GCC
website, so it was a little weird.)
"Implicit int" is the rule that allows in C90 programs

main()
{
auto i;
...

This is ruled out (AFAICT) by N869 6.7.2#2, "At least one type
specifier shall be given in the declaration specifiers in each
declaration...," where a 'type-specifier' is for example 'int'
or 'long'.
and f() will no longer be assumed to
be the same as f(int).

'f()' and 'f(int)' have never been even remotely similar in any
flavor of C, except that you can put them both in the context
'int ~;' and get (different) valid function declarations.
Per the CGG 'checklist'
of C99 conformance, some of this has already been
done. So, what we may be seeing here is partial
implementation of the C99 spec in the error handling
code. This is also mentioned in 6.11.6 and 6.11.7
of the C99 spec.

For my own selfish benefit: Is this 6.11.4 and 6.11.5 of
the N869 draft? The former talks about function declarators
such as 'int f();' in the original example, and the latter
talks about function definitions such as 'int f(f) int f; {}'.
In the final analysis, while it may not be an
error per C89,

It is.
and it may be an error per C99,

It is.
it's probably not something we need to be too
concerned about.

But it is (read: appears to be) so easy to fix, there's no
reason *not* to fix it right away.

-Arthur
 
L

Larry I Smith

Arthur said:
But it is (read: appears to be) so easy to fix, there's no
reason *not* to fix it right away.

-Arthur

I meant that there's no need to be too concerned about
this being a GCC internal bug; since that was the OP
question that started this thread.

Dan says the double prototype is not an error (and includes
text from the spec to support his position).
Yet, you say it is always an error.
Which position is correct? No wonder some of us get confused.....

Regards,
Larry
 
D

Dan Pop

In said:
Dan says the double prototype is not an error (and includes
text from the spec to support his position).

I merely said that, in this particular case, a prototype and a
non-prototype declaration for the same function are not an error. I have
also provided an example where they are an error.

I didn't address any double prototype issue.

Dan
 
L

Larry I Smith

Dan said:
I merely said that, in this particular case, a prototype and a
non-prototype declaration for the same function are not an error. I have
also provided an example where they are an error.

I didn't address any double prototype issue.

Dan

Ok, I get it. My error in assumption was that I interpreted
both lines (1 & 2) as prototypes; that's the way I was
trained - sorry.

I think we've beaten this horse enough....

Regards,
Larry
 
P

Peter Ammon

Allin said:
Again, agreed. The issue raised by Thomas is just this: the
disagreement is between the prototype on line 1 and the function
definition on line 3, yet the gcc error message refers to a
"prototype" on line 2.

Sorry, I missed that point. I agree that the compiler's error message
is wrong (in the sense that its explanation is wrong; the standard
permits wrong explanations AFAIK).
 
P

Peter Ammon

Arthur J. O'Dwyer wrote:

[...]

Peter Ammon's
answer to your question is correct AFAICT (but I'm curious about
the 'void foo()' examples at the end of his post... chapter and
verse, Peter?).

[..]

My examples are reproduced below:

--
Notice you can do weird stuff with this declaration/definition disconnect.

void foo() {
foo(5);
foo(3.5);
foo("hello");
}

should compile as C, even though it's obviously illegal. But change it to

void foo() {
void foo(int);
}

and you'll get an error.
--

I'm afraid I'm empty handed regarding chapter and verse. I think I got
the impression that this code must compile from an old clc post (which I
cannot locate) by Chris Torek, though in the case I'm wrong I certainly
don't mean to incriminate him. Heck, he's named in the headers in my OS.

-Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,049
Latest member
Allen00Reed

Latest Threads

Top