This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c/15052] gcc frontend accepts mismatched function declaration/defintion
- From: "hozelda at yahoo dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: 21 Jul 2004 18:11:47 -0000
- Subject: [Bug c/15052] gcc frontend accepts mismatched function declaration/defintion
- References: <20040421182423.15052.tjruwase@stanfordalumni.org>
- Reply-to: gcc-bugzilla at gcc dot gnu dot org
------- Additional Comments From hozelda at yahoo dot com 2004-07-21 18:11 -------
I have some things to report.
I tested the following program using -std=c99 (gcc 3.2.2) and got error
messages. I believe this is A-OK. I don't think gcc is at all broken with respect
to the foo examples (and later an 'f' example) that were given.. at least not in
c99 mode.
If there are errors or inconsistencies in non-c99 mode, I really don't know about
that or what is proper. This post is only about c99 mode and how gcc is NOT
failing.
Let me show the code I ran and the compiler output:
>> int f30(void); //line 502
>> const int f30(void) {return 0;} //line 503
-->>
:: practice.c:503: conflicting types for `f30'
:: practice.c:502: previous declaration of `f30'
[I used 'f30' instead of 'foo' and I jumped to line 500 but that's just the way I am.
Don't mind me.]
[Not shown are identical results (same gcc output) when the second line was a
simple declaration much like the first (instead of a definition).]
For the rest of this post I will argue that gcc does need to produce
errors/warnings. Why go to the trouble just to conclude that everything is ok?
..for several reasons:
-- there seems to be some confusion and some people have asked about
legalese, so I will provide some to hopefully clear things a bit.
-- while I think c99 mode works, the other modes may not (someone mentioned
some inconsistencies with gcc that depend on whether or not the second
declaration is a definition), and what I will argue may help since enough of it
may apply to c90 or other.
-- I wouldn't want someone to "fix" gcc in c99 mode.
-- I originally wrote this whole analysis thing out under the assumption that gcc
was broken. Only afterward (of course) did I decide to code, compile, and
check.. and was surprised (pleased though) to find that gcc worked. I am
rewriting the post, but I will not throw away the main result of the prior effort.
[I repeat, gcc worked by spewing diagnostics in both cases (both, when the
second declaration was a definition and when it wasn't). I only tried 2 of the 6
combinations: i,ci vs ci,i and dcl/dcl, dcl/def, def/dcl, which of course just
tested 'int' .. oh, I did test both at block (implicit extern) and at file scopes... so
really, with 4 full cases covered, only inf-4 to go ... to be really sure.]
Before getting into the meaty stuff, some assumptions: [the assumptions are
things I don't want to bother to try to "prove" with quotes from the Standard,
mostly because it would distract and take too much quoting and I may not do
much convincing. The assumptions are all relatively easy to believe, if perhaps
a bxxx to confirm without resorting to a lot of reading or quoting... If that is the
case then, I'd rather skip out on the quoting and let the rest of you do the
voluminous reading.]
An assumption is that the example declarations given earlier in this bug report
(and reworked using 'f30') do refer to the __same exact function. 6.2.1 and
6.2.2, identifiers, scope, and linkage, in particular (..and wherever else
identifier syntax is revealed) are mandatory reading.
Another assumption is to accept that the foo/f/f30, as coded in this bug report,
is a function, that is, it is an identifier with function type. 6.2.5#1 partitions types
into object, function, and incomplete. There are other ways to categorize types,
but the Standard uses the words "object types," "function types," and
"incomplete types," when referring to any type, to always mean that the type
falls into one and only one of these three categories (partitions). Other earlier
parts of 6.2 and elsewhere discuss identifiers in greater detail -- must reads
also.
For the record, let me also state that the Standard uses a number of other
words to mean very precise things, too. An example of this, which is significant
to this post, is in the usage of "declaration" and "definition." A definition is
always a declaration but not vise-versa. See 6.9#4 and related syntax; see
6.7.5.3#3, which makes "int foo(a);" illegal while allowing "int foo(a)..{..}", but
this isn't clear unless definition and declaration are understood to refer to
specific but distinct concepts; see 6.7.5.3#12.. etc.
The quoted material makes references to "declaration" and not "definition". The
result of this is that what is shown is that the 2 __declarations__ of the function
having different qualifiers for the return type are not legal c99. This result,
hence, automatically includes the more specific cases where any of the 2
declarations (or both, if that were allowed) happen to be a definition.
And now the story...
6.7#4: [Constraints]
All declarations in the same scope that refer to the
same object or function shall specify compatible types.
[OK, so, to begin, I have these two declarations of the same function 'foo'. How
do I know whether or not these types are compatible? ..if they aren't compatible,
then I see we have a constraint violation.]
6.7.5.3#15 [Semantics]
For two function types to be compatible, both shall
specify compatible return types. .... [plus other param related stuff]
[OK, so I am getting a better picture of what it means for two function types (i.e.
the types of these 2 function declarations) to be compatible... Essentially, we
can just look at the types that each returns and judge from that. If the return
types ARE compatible, then I may have to look further, at the parameters and at
the rest of the stuff that wasn't quoted, but if the return types AREN'T
compatible, then I will have my answer. It will be that these functions AREN'T
compatible and gcc better catch that, as per the earlier 6.7#4. So, let's move
on. Let's figure out all the possible types that cam be compatible with other
types, and, in particular, let's see if 'int' is compatible with 'const int'.]
6.2.7#1:
Two types have compatible type if their types are the
same. Additional rules for determining whether two types
are compatible are described in 6.7.2 for type specifiers,
in 6.7.3 for type qualifiers, and in 6.7.5 for
declarators.46) Moreover .... [the moreover concerns struct/union declarations
across translation units]
[I now know a little bit more, that same types are compatible types, but I still
don't know what it means to be same types, and there are still many
combinations to consider to know what is and isn't compatible with what. So is
the 'int' the same type as 'const int'? Or, more fundamentally, is the 'int' type
compatible with 'const int' type?]
6.2.5#25:
Any type so far mentioned is an unqualified type. Each
unqualified type has several qualified versions of its
type,38) corresponding to the combinations of one, two, or
all three of the const, volatile, and restrict qualifiers.
The qualified or unqualified versions of a type are distinct
types .... [Footnote 38 refers us to 6.7.3 for more details on qualified arrays
and functions.]
[Very well, 'int' is not the same type as 'const int', but there is still so much
ground to cover to know if they are compatible, after all, only one case has
been eliminated [..we know they can't be compatible solely by virtue of them
being the same type (because we now know they are not the same types)]. So
are they compatible on some other ground? Let's pay attention to footnote 38
(and to 6.2.7#1 from earlier) and just see what 6.7.3 has to say.]
6.7.3#9: [Semantics]
For two qualified types to be compatible, both shall
have the identically qualified version of a compatible type;
.... [it then mentions how the order of the qualifiers is irrelevant]
Hmm. OK. First I note that the above uses the word "shall." This means that the
__only way__ that two qualified types can be compatible is .... This statement is
simply a filter. It doesn't implicitly enumerate all of the compatibility cases. What
it does is to say, two types that would be compatible were we to ignore (outer)
qualifier issues, now must pass the qualifier test which is that these two
otherwise compatible types must ALSO be identically qualified. If they aren't
identically qualified, then these two types are not compatible (although they
would have been if they each had had the same exact qualifiers).
In general, to determine whether two types are compatible or not, there are a
number of questions we can ask. This particular question if of the type that
when its prerequisites are met (we have 2 types whose unqualified versions are
surely compatible), can determine once and for all whether the two types are
compatible or not. Not all test are of this nature. Some give a lot less
information or can only either eliminate or pass. To generalize beyond the
specific case shown of 'foo' could involve very different lines of reasoning,
especially near the bottom. Of course, I added dialog along the way to try to
give a feel for that. The fact is that if the last test had resulted in the affirmative
or was inconclusive, we could easily find ourselves backtracking to dig more
dirt on compatibility issues.
So, I'll repeat myself a bit. We got to this stage by considering 'int' and 'const
int' specifically. In our case, the answer of "no, these types aren't compatible"
allows us (and gcc) to finish the analysis started by the very first test at the top
(6.7#4). Let me trace through that logic in reverse quickly: 'int' is the same type
as 'int'; hence, 'int' is compatible with 'int'; however, applying this last test
(6.7.3#9), 'int' is not compatible with 'const int'; hence, a function type returning
'int' is not compatible with a function type returning 'const int'; hence, a
declaration (or definition) of a particular function followed by a second
declaration (or definition), within the same scope, of that same function, but
which is not compatible with the first (such as the 'const int' vs 'int' case),
violates the 6.7#4 constraint which burdens gcc with the task of providing
diagnostics (5.1.1.3 mainly).
This is the specific conclusion to the examples provided within the current bug
report. My gcc did exactly what I just claimed gcc should have done, though I
used the std=c99 flag (notice I was quoting c99 stuff and not x147 stuff).
At this point I cannot tell if gcc has a problem, but if it had a problem with
something more complex than const int and int, the possible "proof" could be
very different and certainly more involved (it could also potentially be easier).
Someone may take all of the input to this bug report to try produce something
more thorough. But that is not a quick task and it may not be very necessary if
gcc has been coded correctly or mostly correctly.
I have come to the end, but I have some miscellaneous concerns.
Is there a mailing list or group I can join where I can ask questions or at least
be able to learn about the code base quicker.
I have started to look at the main gcc subdir. I imagine that c.parse.y is the
main file to concentrate on. I imagine I will have to know all of the (at least
main) tree functions. I have also taken a look at the gcc internals guide... but it
may be a while before I can contribute code.. or not.
Anyway, anyone have suggestions? I'd like to participate, ultimately beyond
providing quotations.
I also would like to know when/if I should be using -pedantic or -std=c99
-pedantic or whatever. Having spent a good bit of time reading the Standard, I
would like to focus in the c99 area for the time being.
ps: It must be such a trick to maintain a compiler (suite!) that is so compatible
with so many different standards. One reason I would like to help with the code
is because that is such a challenge. Anyway, I haven't read the prior postings
about 'volatile const' and such very carefully. I will try that now.
Also, don't anyone be offended. Because I was detailed only so as to cover as
wide of an audience as possible since what might be obvious to some is nit
picky to someone else, yet I also want that someone else to make sense of the
arguments.
And also, I welcome critique. For example, someone may want to focus on the
Semantics/Constraints/Other considerations to see if gcc must really do
anything.
--
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=15052