This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug c/15052] gcc frontend accepts mismatched function declaration/defintion


------- 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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]