xor@aix01.aix.rpi.edu (Joseph Schwartz) (05/02/91)
Must an ANSI C compiler accept pointers to incomplete types in a function prototype? More specifically, if struct bar has not been defined, is an ANSI compiler allowed to reject the following prototype: extern void foo(struct bar *); According to 3.1.2.5, struct bar is an incomplete type in this case, and struct bar * is a pointer to an incomplete type. I couldn't find anything in 3.5.4.3 that allows or forbids incomplete types, or pointers to them, in function prototypes. Can anyone give me more conclusive information? Our HP "ANSI" C compiler is complaining about the above prototype, and I want some ammunition before I report it to HP as a problem. Thanks in advance for any help you can provide. -- Joe Schwartz Internet: xor@mts.rpi.edu Bitnet: userez3n@rpitsmts
lrg7030@uxa.cso.uiuc.edu (Loren J. Rittle) (05/02/91)
In article <w!2g7+c@rpi.edu> xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: >Must an ANSI C compiler accept pointers to incomplete types in a >function prototype? According to K&R's 2nd edition, in a word, yes. See page 212 (quoted here): ``Objects with an incomplete structure or union type may be mentioned in contexts where their size is not needed, for example in declarations (not definitions), for specifying a pointer, or for creating a typedef, but not otherwise.'' >More specifically, if struct bar has not been defined, is an ANSI >compiler allowed to reject the following prototype: > > extern void foo(struct bar *); As the size of bar is not needed in the prototype for foo, the above should be accepted by an ANSI compiler even if bar is incomplete. >According to 3.1.2.5, struct bar is an incomplete type in this case, >and struct bar * is a pointer to an incomplete type. > >I couldn't find anything in 3.5.4.3 that allows or forbids incomplete >types, or pointers to them, in function prototypes. Can anyone give >me more conclusive information? Our HP "ANSI" C compiler is complaining >about the above prototype, and I want some ammunition before I report >it to HP as a problem. As an aside, the SAS/C 5.1 (more or less) ANSI compiler accepts the above line of code no problem. Good luck getting HP to upgrade their compiler. May the big guns be with you. >Joe Schwartz Loren J. Rittle l-rittle@uiuc.edu -- ``NewTek stated that the Toaster *would* *not* be made to directly support the Mac, at this point Sculley stormed out of the booth...'' --- A scene at the recent MacExpo. Gee, you wouldn't think that an Apple Exec would be so worried about one little Amiga device... Loren J. Rittle l-rittle@uiuc.edu
steve@taumet.com (Stephen Clamage) (05/02/91)
xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: >More specifically, if struct bar has not been defined, is an ANSI >compiler allowed to reject the following prototype: > > extern void foo(struct bar *); >Our HP "ANSI" C compiler is complaining >about the above prototype, and I want some ammunition before I report >it to HP as a problem. There was another answer posted to this question which was not complete. As shown, the prototype declares "struct bar" to be an incomplete type *local to the prototype*, and hence unavailable outside the prototype. It is then impossible to call this function, since no object of type "struct bar" (or pointer to one) could ever exist. (Any later "struct bar" is a new type in a different scope.) The compiler may have been complaining about this. If you precede the declaration with an incomplete decl for bar, the prototype now refers to that decl, and all is well. Example: struct bar; /* defined later */ extern void foo(struct bar*); /* refers to previous bar */ You didn't say whether there was a prior incomplete decl. -- Steve Clamage, TauMetric Corp, steve@taumet.com
lrg7030@uxa.cso.uiuc.edu (Loren J. Rittle) (05/02/91)
In article <700@taumet.com> steve@taumet.com (Stephen Clamage) writes: >xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: > >>More specifically, if struct bar has not been defined, is an ANSI >>compiler allowed to reject the following prototype: >> >> extern void foo(struct bar *); > >>Our HP "ANSI" C compiler is complaining >>about the above prototype, and I want some ammunition before I report >>it to HP as a problem. > >There was another answer posted to this question which was not complete. >As shown, the prototype declares "struct bar" to be an incomplete type >*local to the prototype*, and hence unavailable outside the prototype. >It is then impossible to call this function, since no object of type >"struct bar" (or pointer to one) could ever exist. (Any later "struct >bar" is a new type in a different scope.) The compiler may have been >complaining about this. What are you talking about? This is not true, see below. >If you precede the declaration with an incomplete decl for bar, the >prototype now refers to that decl, and all is well. Example: > struct bar; /* defined later */ > extern void foo(struct bar*); /* refers to previous bar */ > >You didn't say whether there was a prior incomplete decl. It does not matter. According to my understanding of the standard and according to my ANSI C compiler the following is valid code: struct bar *getbar(int); void changebar(struct bar *, int); void main(void) { struct bar *mybar; mybar = getbar (1); if (mybar) changebar (mybar, 2); } This is very nice and allows data hiding, etc. Steve, if you are right then I better get on the phone to my C compiler vendor. Can one of the standard writers, please enlighten us? >Steve Clamage, TauMetric Corp, steve@taumet.com Loren J. Rittle -- ``NewTek stated that the Toaster *would* *not* be made to directly support the Mac, at this point Sculley stormed out of the booth...'' --- A scene at the recent MacExpo. Gee, you wouldn't think that an Apple Exec would be so worried about one little Amiga device... Loren J. Rittle l-rittle@uiuc.edu
baxter@clipper.ingr.com (Bill Baxter) (05/03/91)
In article <w!2g7+c@rpi.edu>, xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: |> Must an ANSI C compiler accept pointers to incomplete types in a |> function prototype? |> |> More specifically, if struct bar has not been defined, is an ANSI |> compiler allowed to reject the following prototype: |> |> extern void foo(struct bar *); No, the compiler should not reject it on the grounds that it is an incomplete type. First, 'bar' may have been defined previously with file scope, hence this is an instance of that type. Second, incomplete pointer types are perfectly legitimate if all you are interested in is the pointer itself (i.e., you will not attempt to dereference what it points to without casting to a known type). Our compiler allows this construct. Good luck with HP! A more twisted case, and probably totally useless, is when you have the following compilation module: -----start f.c------- int foo (struct s s) { struct s { int i; }; return s.i; } -----end f.c--------- Since the identifiers in the prototype definition have function scope, should this be legal ANSI C? I can find no convincing verbiage in the standard that would rule-out this case. Our compiler currently does not accept this, but I fear it should and we are currently seeking convincing arguments for why we shouldn't have to implement it. |> |> According to 3.1.2.5, struct bar is an incomplete type in this case, |> and struct bar * is a pointer to an incomplete type. |> |> I couldn't find anything in 3.5.4.3 that allows or forbids incomplete |> types, or pointers to them, in function prototypes. Can anyone give |> me more conclusive information? Our HP "ANSI" C compiler is complaining |> about the above prototype, and I want some ammunition before I report |> it to HP as a problem. I think you have a legitimate gripe. At the prototype declaration site, you may have no need for the complete structure type. Consider a module that is manipulating pointers only, without regards to what is being pointed to. Therefore, it would be sufficient to have an incomplete type. You might be pulling pointers out of a data structure passing them to this function that might understand what is being pointed to. |> |> Thanks in advance for any help you can provide. |> |> |> -- |> Joe Schwartz |> Internet: xor@mts.rpi.edu |> Bitnet: userez3n@rpitsmts -- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Bill Baxter Compiler Group: Advanced Processor Division (415)852-2333 E-MAIL: {apple|pyramid}!clipper!baxter USPS: 2400 Geng Rd., Palo Alto, CA 94303 _STD_DISC: INGR has their opinions. I have mine. Believe them if conflicting.
gwyn@smoke.brl.mil (Doug Gwyn) (05/03/91)
In article <1991May2.164028.10641@ux1.cso.uiuc.edu> l-rittle@uiuc.edu (Loren J. Rittle) writes: -In article <700@taumet.com> steve@taumet.com (Stephen Clamage) writes: ->As shown, the prototype declares "struct bar" to be an incomplete type ->*local to the prototype*, and hence unavailable outside the prototype. -What are you talking about? This is not true, see below. Steve was correct.
jimp@cognos.UUCP (Jim Patterson) (05/03/91)
In article <700@taumet.com> steve@taumet.com (Stephen Clamage) writes: >xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: > >>More specifically, if struct bar has not been defined, is an ANSI >>compiler allowed to reject the following prototype: >> >> extern void foo(struct bar *); > >>Our HP "ANSI" C compiler is complaining >>about the above prototype, and I want some ammunition before I report >>it to HP as a problem. > >There was another answer posted to this question which was not complete. >As shown, the prototype declares "struct bar" to be an incomplete type >*local to the prototype*, and hence unavailable outside the prototype. >It is then impossible to call this function, since no object of type >"struct bar" (or pointer to one) could ever exist. That in itself should not be considered a reason to issue an error. We have this situation a lot, because we use prototype headers in all of our files and in quite a few instances there are missing struct declarators for functions we don't call in that module. If you do try to call a function with an incomplete struct type, then you get a pointer mismatch error which is unavoidable if you don't first declare the struct type no matter how you try to cast it. Actually, I'm a bit confused by this whole thing, because we also have a HP system (HP 9000/835 running HP/UX 7.0), and the problem doesn't occur with our compiler. When I compiled extern void foo(struct bar *); it compiled fine. We have had a similar problem, but only where there is a typedef involved, e.g.: extern void foo(bar *); where 'bar' is intended to be a typedef, e.g. what's missing is typedef struct Bar bar; Here, you quite legitimately get an error because it doesn't know what 'bar' is (it assumes its a parameter name, and gives an "unexpected symbol" error at "*"). This is however not the same problem that was reported by Joseph Schwarz. You also get an error if you declare foo twice because the two definitions aren't compatible (struct bar in the first declaration is not the same as struct bar in the second, unless again you declare struct bar before the first occurrence of foo's prototype). The error given is 'Inconsistent parameter list declaration for "foo"'. I think this is also a legitimate error, though a confusing one. -- Jim Patterson Cognos Incorporated UUCP:uunet!mitel!cunews!cognos!jimp P.O. BOX 9707 PHONE:(613)738-1440 x6112 3755 Riverside Drive Ottawa, Ont K1G 3Z4
xor@aix01.aix.rpi.edu (Joseph Schwartz) (05/03/91)
In article <700@taumet.com> steve@taumet.com (Stephen Clamage) writes: > >There was another answer posted to this question which was not complete. >As shown, the prototype declares "struct bar" to be an incomplete type >*local to the prototype*, and hence unavailable outside the prototype. >It is then impossible to call this function, since no object of type >"struct bar" (or pointer to one) could ever exist. (Any later "struct >bar" is a new type in a different scope.) The compiler may have been >complaining about this. > >If you precede the declaration with an incomplete decl for bar, the >prototype now refers to that decl, and all is well. Example: > struct bar; /* defined later */ > extern void foo(struct bar*); /* refers to previous bar */ > >You didn't say whether there was a prior incomplete decl. No, there was no prior incomplete declaration of struct bar. Your claim that struct bar has local scope to the prototype is pretty scary. In many of our cases, a function foo is declared with a prototype in an included header file, and then a struct bar is defined in a second included header file, and finally the function foo is called from the .c file that's been doing the #including. Now, if I understand you correctly, I should never be allowed to call function foo, because the struct bar * that I pass to it is incompatible with the struct bar * that it was declared to take as a parameter. Can you please direct me to the section of the Standard that supports this? Specifically, I want to know where it says that struct bar has scope local only to the prototype. I'm aware that if I said: extern void foo(struct bar *stool); then stool would be local to the prototype. I'm skeptical that this also holds for struct/union tags. -- Joe Schwartz Internet: xor@mts.rpi.edu Bitnet: userez3n@rpitsmts
john@acorn.co.uk (John Bowler) (05/03/91)
In article <700@taumet.com> steve@taumet.com (Stephen Clamage) writes: >xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: > >>More specifically, if struct bar has not been defined, is an ANSI >>compiler allowed to reject the following prototype: >> >> extern void foo(struct bar *); >>... >There was another answer posted to this question which was not complete. >As shown, the prototype declares "struct bar" to be an incomplete type >*local to the prototype*, and hence unavailable outside the prototype. >It is then impossible to call this function, since no object of type >"struct bar" (or pointer to one) could ever exist. (Any later "struct >bar" is a new type in a different scope.) This is incorrect; consider the following (the example is the complete compilation unit...):- extern void foo(struct bar *ptr); void bar(void *ptr) { foo(ptr); } Or the code fragment:- ... foo(0); ... You might get a warning from the latter, but it is valid ANSI C. Further it *is* possible for the argument to ``bar'' to genuinely be of the type ``struct bar *'' declared in the parameter list to foo:- struct bar {int i;}; void foo(struct bar *ptr) { if (!ptr) { struct bar mine; bar(&mine); } else ptr->i = 0; } Or how about the compilation unit:- void foo(struct bar *ptr) { /* struct bar incomplete */ struct bar { int i; } mine; /* Now it's complete. */ if (!ptr) bar(&mine); else ptr->i = 0; } But what about the following (the compiler I have rejects it; it would seem that the completion of the structure definition goes out of scope, even though the (incomplete) structure definition is still in scope - is this correct???) void foo(struct bar *ptr) { /* struct bar incomplete */ if (!ptr) { struct bar { int i; } mine;/* Now it's complete. */ bar(&mine); } else ptr->i = 0; /* It's now incomplete??? */ } John Bowler (jbowler@acorn.co.uk)
steve@taumet.com (Stephen Clamage) (05/03/91)
lrg7030@uxa.cso.uiuc.edu (Loren J. Rittle) writes: >In article <700@taumet.com> steve@taumet.com (Stephen Clamage) writes: >>xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: >>> extern void foo(struct bar *); >> >>As shown, the prototype declares "struct bar" to be an incomplete type >>*local to the prototype*, and hence unavailable outside the prototype. >>If you precede the declaration with an incomplete decl for bar, the >>prototype now refers to that decl, and all is well. Example: >> struct bar; /* defined later */ >> extern void foo(struct bar*); /* refers to previous bar */ >It does not matter. According to my understanding of the standard and >according to my ANSI C compiler the following is valid code: >struct bar *getbar(int); >void changebar(struct bar *, int); The two cases are not the same. The example you give is valid, because the first 'struct bar' does not appear inside the parameter list in the first prototype; it has file scope (assuming this declaration is at the file level). Section 3.2.1.1 of the Standard defines scopes of identifiers. It says in part "If the declarator or type specifier that declares the identifier appears within the list of parameter declarations in a function prototype (not part of a function definition), the identifier has *function prototype scope*, which terminates at the end of the function declarator." [italics in the Standard] -- Steve Clamage, TauMetric Corp, steve@taumet.com
steve@taumet.com (Stephen Clamage) (05/04/91)
xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: >In article <700@taumet.com> steve@taumet.com (Stephen Clamage) writes: >>As shown, the prototype declares "struct bar" to be an incomplete type >>*local to the prototype*, and hence unavailable outside the prototype. >Your claim that struct bar has local scope to the prototype is pretty >scary. In many of our cases, a function foo is declared with a prototype >in an included header file, and then a struct bar is defined in a second >included header file, and finally the function foo is called from the >.c file that's been doing the #including. As I quoted in another posting, this is explicit in the Standard, in section 3.1.2.1, scary or not. What you cite seems to be currently common in Unix implementations which are converting to ANSI-style prototypes. I have found many headers which are incomplete in this respect, having missing prototypes (which isn't always important for C, but is devastating in C++), and exactly this problem with structs being first declared in prototype scope. The thing to do is to report these to the vendor as bugs, and hope they get fixed in the next release. In the mean time, you can edit the system headers, or supply corrected versions in a local directory which is searched first. >I'm aware that if I said: > extern void foo(struct bar *stool); >then stool would be local to the prototype. I'm skeptical that this >also holds for struct/union tags. There is nothing necessarily wrong with being skeptical, but in this case, the C Standard is explicit as it can be, leaving no room for doubt. -- Steve Clamage, TauMetric Corp, steve@taumet.com
xor@aix01.aix.rpi.edu (Joseph Schwartz) (05/04/91)
In article <cl3gg!n@rpi.edu> I wrote: >In article <700@taumet.com> steve@taumet.com (Stephen Clamage) writes: >> >>There was another answer posted to this question which was not complete. >>As shown, the prototype declares "struct bar" to be an incomplete type >>*local to the prototype*, and hence unavailable outside the prototype. >>It is then impossible to call this function, since no object of type >>"struct bar" (or pointer to one) could ever exist. (Any later "struct >>bar" is a new type in a different scope.) The compiler may have been >>complaining about this. >> >>If you precede the declaration with an incomplete decl for bar, the >>prototype now refers to that decl, and all is well. Example: >> struct bar; /* defined later */ >> extern void foo(struct bar*); /* refers to previous bar */ >> >>You didn't say whether there was a prior incomplete decl. > >No, there was no prior incomplete declaration of struct bar. > >Your claim that struct bar has local scope to the prototype is pretty >scary. In many of our cases, a function foo is declared with a prototype >in an included header file, and then a struct bar is defined in a second >included header file, and finally the function foo is called from the >.c file that's been doing the #including. > >Now, if I understand you correctly, I should never be allowed to call >function foo, because the struct bar * that I pass to it is incompatible >with the struct bar * that it was declared to take as a parameter. >Can you please direct me to the section of the Standard that supports >this? Specifically, I want to know where it says that struct bar has >scope local only to the prototype. > >I'm aware that if I said: > > extern void foo(struct bar *stool); > >then stool would be local to the prototype. I'm skeptical that this >also holds for struct/union tags. Okay, I've done some research and staightened out a few misunderstandings. First things first. Our compiler was NOT reporting an error when it encountered the prototype. It was reporting a warning, essentially saying exactly what Stephen said above: it warned that struct bar had scope local to the prototype. The compiler WAS reporting an error when it encountered a call to function foo, again supporting Stephen 100%: it reported that the pointer being passed as a parameter was incompatible with the pointer used in the prototype. I apologize for not Reading The F***ing Error Messages. I'm researching and reporting this problem on behalf of a co-worker, and I misunderstood his description of the problem. Stephen, it DID help to put the vacuous declaration "struct bar;" before the prototype. Everything worked. However, this is not a completely acceptable solution, and I'm still not convinced that the behavior of this compiler is a correct interpretation of the Standard. The paragraph in question is 3.1.2.1, lines 32-37: "If the declarator or type specifier that declares the identifier appears within the list of parameter declarations in a function prototype (not part of a function definition), the identifier has FUNCTION PROTOTYPE SCOPE [emphasis theirs], which terminates at the end of the function declarator. If an outer declaration of a lexically identical identifier exists in the same name space, it is hidden until the current scope terminates, after which it again becomes visible." As I said earlier, I believe this applies to the identifier "stool" in: extern void foo(struct bar *stool); I could also believe that it applies to complete structure declarations, as in: extern void foo(struct bar { int a; int b; } *); But I still have a hard time believing that it applies to struct bar in: extern void foo(struct bar *); Making bar have local scope in this case is of questionable usefulness, and it would appear to break code that existed before the Standard. But let's assume that the correct interpretation of the Standard is to give struct bar local scope in my last example above. Then why does this: struct bar; extern void foo(struct bar *); solve the scoping problems? According to the paragraph that I quoted above, the vacuous declaration of struct bar is HIDDEN inside the prototype. That is, the local struct bar inside the prototype will override the one that's already declared. Once the prototype ends, its struct bar goes out of scope, and the previous declaration is unhidden. Any subsequent calls to foo (or declarations of struct bar) will use a struct bar compatible with the vacuous declaration, but not compatible with the struct bar local to the prototype. How can adding the vacuous declaration prevent the prototype from giving struct bar local scope? -- Joe Schwartz Internet: xor@mts.rpi.edu Bitnet: userez3n@rpitsmts
kre@cs.mu.oz.au (Robert Elz) (05/04/91)
Disclaimer: I know absolutely nothing about ANSI C ... I can't see how its possible for type foo(struct bar *arg); to possibly be considered as any kind of a definition of the struct tag "bar". Whether "bar" is defined elsewhere or not, this is (and can only be) a reference to "bar". Struct tags are (should be, surely) only defined in a context as struct bar { ... }; The question of the scope inside the function prototype can only possibly be relevant to names defined there, of which "bar" is not one in the above example. It would be in type foo(struct bar { ... } *arg); which would *always* be a meaningless prototype, regardless of whether bar was defined elsewhere or not. It seems as if the compiler involved is treating the reference to bar in the first prototype above as a definition, as it hadn't seen a definition before. That is surely a bug. Would all those people who have blindly been quoting the standard on these issue please turn to the paragraph that defines struct tag definition, and please quote form there as well. kre
steve@taumet.com (Stephen Clamage) (05/05/91)
john@acorn.co.uk (John Bowler) writes: >In article <700@taumet.com> steve@taumet.com (Stephen Clamage) writes: >>As shown, the prototype declares "struct bar" to be an incomplete type >>*local to the prototype*, and hence unavailable outside the prototype. >>It is then impossible to call this function, since no object of type >>"struct bar" (or pointer to one) could ever exist. (Any later "struct >>bar" is a new type in a different scope.) >This is incorrect; consider the following (the example is the complete >compilation unit...):- >extern void foo(struct bar *ptr); >void bar(void *ptr) { > foo(ptr); >} True enough. If you later define struct bar at, say, the file level, you could call foo() by casting a pointer to void* first. >Further it *is* possible for the argument to ``bar'' to genuinely be >of the type ``struct bar *'' declared in the parameter list to foo:- >struct bar {int i;}; >void foo(struct bar *ptr) { >... >} Of course. This is exactly what I said. >Or how about the compilation unit:- >void foo(struct bar *ptr) { /* struct bar incomplete */ > struct bar { int i; } mine; /* Now it's complete. */ >... >} This does not solve the problem of there being no struct bar outside function foo, as in the original example. The struct bar INSIDE the function is not the same type as any struct bar OUTSIDE the function, either lexically before or after the function. In fact, given the sequence struct bar... /* complete or incomplete */ void foo(struct bar* p) { struct bar { ... } ... } The struct bar inside the function is not the same type as that the parameter p points to. >But what about the following (the compiler I have rejects it;... As it should. It is illegal. >void foo(struct bar *ptr) { /* struct bar incomplete */ > if (!ptr) { > struct bar { int i; } mine;/* Now it's complete. */ > bar(&mine); > } else > ptr->i = 0; /* It's now incomplete??? */ >} You cannot complete an incomplete declaration in an outer scope inside an inner scope. If this is the compilation unit, there is no complete declaration for what the parameter ptr points to. You need to study a good C text on the subject of scopes. -- Steve Clamage, TauMetric Corp, steve@taumet.com
steve@taumet.com (Stephen Clamage) (05/05/91)
xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: >But I still have a hard time believing that it applies to struct bar in: > extern void foo(struct bar *); >Making bar have local scope in this case is of questionable usefulness, >and it would appear to break code that existed before the Standard. I agree it is not useful to write code this way. But code before the standard did not use prototypes -- they were introduced by the standard (borrowing from C++). So what previously-correct code can be broken? The only alternative is to promote the declaration in a function prototype to some outer scope. To which scope? Always to file scope? Just to the enclosing scope? Consider: void f1() { extern void f2( void (*)(struct bar*) ); ... } Here, f2 takes a parameter which is a pointer to a function which takes a parameter of type struct bar. If no declaration for struct bar appears before this, what is the scope of struct bar? The ANSI rules provide a completely consistent interpretation of these issues. In the absence of a corresponding declaration in an enclosing scope, the scope of a declaration in a prototype parameter list ends with the prototype. If you mean for it to refer to a declaration in an enclosing scope, the declaration must be visible. This too is a uniform rule: you must declare a type before you can use it. >Then why does this: > struct bar; > extern void foo(struct bar *); >solve the scoping problems? Because of the uniform scoping rules throughout the standard. The first declaration of struct bar is in a scope which encloses the prototype scope, and there is no other declaration to hide it. Therefore, the struct bar in the prototype must refer to the same type as the first. The point you seem to be missing is that the parameter list in a function prototype not part of a function definition has its own scope. -- Steve Clamage, TauMetric Corp, steve@taumet.com
torek@elf.ee.lbl.gov (Chris Torek) (05/05/91)
(This is worth a try, anyway....) The ANSI scoping rules are actually quite simple, but they do produce surprising results sometimes. Here is one way (I believe correct :-) ) to work out what happens. Item: file scope is called `level 0'. File scope ends only at the end of a `source unit' (the source file). Item: braces (the characters `{' and `}') delimit scopes. An open brace introduces a new scope; this scope ends at the corresponding close brace. In effect, `{' increments the current scope and `}' decrements the current scope. Item: function parameters in all forms of declaration and definition appear at scope level 1. Variables (and `goto' labels) that appear inside the function are at level 2 or higher. This is necessary, among other reasons, because f(p) char *p; { int p = 3; ... } is legal (if a bit peculiar). Item: `extern' declarations are inserted at the current scope level (this differs from pcc, in which extern declarations are inserted in scope 0, regardless of the current scope). Goto labels are inserted in scope 2 (so that you can jump across braces). Now, inner scope declarations (higher numbers) of some name may give it a type that differs from an outer (lower number) scope declaration ---for instance, in the f(p) example above, the `int' p is not at all the same as the `char *' p. To disambiguate these, you should keep a mental `stack of paper' by your left, on which there is one sheet per scope, and one very large sheet on your right. (You could share the level 0 page for this but it is easier to imagine a separate sheet). Here is how you work them. (The following ignores name space separation---variables, structure tags, and goto labels all get their own pairs of left-pile,right-page---but is good enough for illustration.) Whenever you come across a *declaration* for a name, you search for that name on the top page on your left (the highest numbered scope). If it appears, you probably have a redeclaration error (e.g., `int k; int k;') (but see below). If not, however, you: 1. Write the name on the right sheet of paper. Append a number. The number can be any that you have not written on the right before, but it is easiest to start at 0 or 1 and increase. Thus, given `int k;' at scope 0 with both pages blank, you would write: k<0> on the right page. 2. Write the name and the same number on the topmost sheet on the left---here, k<0>. This counts as the declaration. It may be `incomplete' if this is a struct or union, in which case we need one more declaration to `complete' it. (This is what `struct foo;', with no structure contents and no variable names, is for. It is a special case.) Whenever you come across a *reference* to a name, you search for that name in *all* the pages on your left, starting at the top (the highest numbered scope). If it appears, you take its number; this is the `real' name for that identifier. If it does *not* appear, this may be an error (e.g., `return foo;' where `foo' is undefined) or it may count as an `incomplete' declaration (e.g., `struct glorp *' where `struct glorp' is undefined). If it is an incomplete declaration, it works just as described above. A declaration that fills out an incomplete type occurs only when it happens on the same piece of paper on the left. Whenever you open a new scope, you add a blank sheet of paper on the left, on top of the pile. Whenever you close it, you throw away the top sheet. The sheet on the right remains `active' for the whole file. (There is a special case for `typedef': when you see `typedef foo bar;' rather than looking for bar, not finding it, and giving it a *new* number, you look for bar, do not find it, and give it the *same* number you found for foo. typedefs for `base' types [int, char, etc] can be written as `bar<int>', if you like. But never mind that.) Two `struct' types are the same ONLY IF THEIR NUMBERS MATCH. Okay, so now what happens with incomplete structure types that appear in various scope levels? Suppose we have void f(p) struct a *p; { struct a { int a; }; ... Working only the `struct' declarations, we start with two blank sheets. Between `f(p)' and the first `{' we add a new scope---a new blank sheet ---on the left, and we look for `struct a' on both left-hand pages (because this pointer refers to struct a, i.e., this is a reference to, not a definition of, `struct a'). It is not there, so we add an incomplete definition, writing struct a<0> on the right and copying it to the left. (We still do not know what `struct a' is.) Then we take the open brace, which adds a new scope, so we put a third sheet of paper on the left. Now we come across a definition for a `struct a'---but there is no `struct a' on the top page on the left, so we add a new one on the right: struct a<1> and copy that to the left. The result is that `p' points to a `struct a<0>' but the only `struct a' we know about is a `struct a<1>'. p is thus largely useless. When we reach the final `}' closing function f, we throw away the top two left-hand sheets, going back to our blank one, and so if we declare another `struct a' it is a `struct a<2>'. This works the same whether f is written as void f(p) struct a *p; { or void f(struct a *p) { or even void f(struct a *p); Now consider what happens if we have: struct a; void f(struct a *p); This time we write `struct a<0>' on the right and copy to the left before we add any more sheets of paper. This gives us an incomplete declaration of the structure `a'. Next, we put down a new blank sheet. We then see a reference to `struct a'. We search through both sheets on the left and, voila!, find `struct a<0>'. p thus points to a `struct a<0>'. The level-1 scope (top page) disappears after the second semicolon, and if we encounter a struct a { ... }; definition, this `fleshes out' the struct a<0>. [Now that I have done all this, it occurs to me that it might be simpler to tag each declaration with its scope level, rather than a global unique number, at least for discussion. The BSD debugging symbol table format uses global unique numbers, which is why I did it this way. The initial number is not 0, however; the first few numbers are assigned to the `base types'. This is what the strings in a `.stabs' directive are all about.] -- In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427) Berkeley, CA Domain: torek@ee.lbl.gov
henry@zoo.toronto.edu (Henry Spencer) (05/05/91)
In article <12818@dog.ee.lbl.gov> torek@elf.ee.lbl.gov (Chris Torek) writes: > f(p) char *p; { int p = 3; ... } > >is legal (if a bit peculiar). 3.7.1, footnote 81: "A parameter is in effect declared at the head of the compound statement that constitutes the function body, and therefore may not be redeclared in the function body (except in an enclosed block)." This isn't exactly obvious from 3.7.1, but a careful reading of the scope rules in 3.1.2.1 confirms it: a function definition sets up *one* scope, not two, for variables. -- And the bean-counter replied, | Henry Spencer @ U of Toronto Zoology "beans are more important". | henry@zoo.toronto.edu utzoo!henry
henry@zoo.toronto.edu (Henry Spencer) (05/05/91)
In article <12818@dog.ee.lbl.gov> torek@elf.ee.lbl.gov (Chris Torek) writes: >... Goto labels are inserted in >scope 2 (so that you can jump across braces). Nope, goto labels have "function scope", which covers the whole function body regardless of braces. -- And the bean-counter replied, | Henry Spencer @ U of Toronto Zoology "beans are more important". | henry@zoo.toronto.edu utzoo!henry
scs@adam.mit.edu (Steve Summit) (05/06/91)
People are having a hard time understanding how
extern blort(struct piffle *);
struct piffle { int cazart; };
creates two *different* struct piffle's, i.e. the struct
definition on the second line does *not* complete the incomplete
struct within the prototype.
For those who prefer concrete examples, I will describe how
structures are typically implemented within compilers. (I hasten
to point out that such an excursion should not, strictly speaking,
be necessary -- we are supposed to be able to answer these
questions by reading the documentation, without peeking at the
source code. In this case, the Standard is unambiguous, but
understanding how its requirements map to the compiler internals
may put a few minds at rest.)
Inside the compiler, we might have a structure which describes a
structure. It might look something like this:
struct structure
{
char *tag;
int flags;
struct symtabent *members;
int nmembers;
};
The tag field obviously records the structure's tag name, or is
NULL for unnamed structures. The members and nmembers fields
record the number, names, and types of the structure's members,
but those details do not concern us here.
The important fact to realize is that if the compiler has to
structures lying around which it wants to test for compatibility,
it does *not* do so by comparing the tag names:
struct structure *sp1, *sp2;
...
if(strcmp(sp1->tag, sp2->tag) != 0) /* WRONG */
error("incompatible structures");
Rather, with one exception [footnote 1], it does so by comparing
the pointers themselves for equality:
if(sp1 != sp2)
error("incompatible structures");
I can't say exactly why the Standard requires compilers to behave
in this way; one reason is obviously that tag comparison can't
work for structures without tags. (I confess that I can't find
explicit language in the Standard which requires behavior such
as I have described, but if you look at section 3.1.2.6 -- "Two
types have compatible type if their types are the same" -- and
section 3.5.2.1 -- "The presence of a struct-declaration-list in
a struct-or-union-specifier declares a new type, within a
translation unit" -- it's clear that tag comparison is not used.
Section 3.5.2.3 is devoted to tags, which we'll now explore.)
The second thing to understand is the way that scopes nest, and
the way that existing names are looked up in, and new names
inserted into, these nested scopes, particularly when the name is
a structure tag. (Chris Torek has already described this process
in considerable detail; the informal treatment I present here
may be a bit easier to follow.)
When a compiler sees a structure tag without a struct-
declaration-list (the brace-enclosed list of the structure member
names and types), it looks through the current set of nested
scopes for a matching struct tag. (Unlike compatible structure
testing, this search *is* made by string comparison of the tag
names.) If it finds one, then this struct tag is a reference to
an already-declared struct, and that already-declared struct is
used as the type of whatever is being declaring now (via the
standalone struct tag just encountered, at the beginning of this
paragraph). In particular, part of the type of the thing being
declared now is the pointer to the struct structure of the struct
with the matching tag. (Got that :-) ?)
If a matching struct tag is *not* found, the compiler has
encountered an incomplete struct definition. It allocates a new
struct structure, with the given tag and no members (and perhaps
an explicit indication in the flags field that this is an
incomplete struct). This incomplete structure definition must
now be entered (still in its incomplete form) into the scope
list. At which level? At the current one, just like any other
definition. No other choice would be regular, or make much
sense.
When a structure declaration with a struct-declaration-list is
encountered, whether it has a tag or not, it is the definition of
a new struct type. (See section 3.5.2.1, page 61, lines 23-24.)
This new structure definitely gets defined at the current scope
level. If this new structure has a tag, and if there is already
a structure with the same tag at this scope level, and if that
existing struct was incomplete, this declaration completes it.
(The incomplete definition's struct structure is used, so that
anything already declared using the incomplete type will remain
compatible, by the method of pointer comparison.) If the new
structure has a tag, and if there is already a structure with the
same tag at this scope level, and if that existing struct is
*not* incomplete (already has members), it's an error (an
attempt is being made to redefine the structure) [footnote 2].
Finally, given that there is a new, microscopic (but still
nested) scope active within a function prototype that is not part
of a function prototype, we can see why
extern blort(struct piffle *);
struct piffle { int cazart; } x;
blort(&x);
defines two different struct piffles, such that the call to blort
in the third line is in error, while
struct piffle;
extern blort(struct piffle *);
struct piffle { int cazart; } x;
blort(&x);
works as intended. That empty struct piffle on the first line is
just to get an incomplete struct piffle entered at file scope, so
that the incomplete struct piffle in the prototype on line 2 will
reference it rather than creating a new one, and so that the
definition on line 3 will complete the same struct referenced in
the prototype, so that the call on line 4 will use a properly
compatible type.
Steve Summit
scs@adam.mit.edu
Footnote 1. The exception is when structures must be compatible
across translation units. Obviously, if they're compiled
separately, the compiler can't compare pointers to its run-time
data structures. In fact, the compiler isn't going to check
compatibility at all; nor, for that matter, does the linker
usually do so. Section 3.1.2.6 describes when structures are
compatible across translation units; presumably a utility like
lint might make use of it. (Obviously, the programmer must also
be aware of this information, if the programs are to work,
although the common and recommended practice of putting structure
definitions in header files of course ensures compatibility.)
Curiously, section 3.1.2.6 requires that the members have the
same types and be in the same order (obviously) and also that
they have the same names, but *not* that the structures have the
same tags. Presumably this means that two structure types with
different tags (or without tags) but with identical descriptions
would be strictly compatible across translation units.
(Obviously, the code would work correctly, under any conceivable
architecture, in any case, and nothing would be likely to go
wrong if the member names didn't match, either.)
Footnote 2. A few months ago, there was a long discussion about
incomplete types and the precise interpretation of the term "an
enclosing scope." I don't remember if the discussion concerned
structure tags (it might have been about incomplete array types),
but section 3.5.2.3 states explicitly that when a tag is
declared, "Subsequent declarations [with the same tag] shall omit
the bracketed list."
rfg@NCD.COM (Ron Guilmette) (05/06/91)
In article <cl3gg!n@rpi.edu> xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: > >Now, if I understand you correctly, I should never be allowed to call >function foo, because the struct bar * that I pass to it is incompatible >with the struct bar * that it was declared to take as a parameter. >Can you please direct me to the section of the Standard that supports >this? Specifically, I want to know where it says that struct bar has >scope local only to the prototype. Ok boys and girls. I've been reading this thread about structure tags declared first (and perhaps only) in function prototypes and it seems to me that people are working under the assumption that the following two possibilities are mutually exclusive: The tag has "function prototype" scope. The function is "callable". For what it's worth, I just wanted to say that (a) I believe that those folks who are asserting that the tag has function prototype scope are correct about that, and (b) the two possibilities I've listed above are *not* completely mutually exclusive. The following example should justify my second assertion: void foo (struct s *arg); void example () { foo ((void *) 0); } I admit that I make only a minor point here. Sorry if I have digressed from the main point of the discussion. -- // Ron ("Loose Cannon") Guilmette // Internet: rfg@ncd.com uucp: ...uunet!lupine!rfg // New motto: If it ain't broke, try using a bigger hammer.
mev@hpfcso.FC.HP.COM (Mike Vermeulen) (05/07/91)
> extern void foo(struct bar *); > > According to 3.1.2.5, struct bar is an incomplete type in this case, > and struct bar * is a pointer to an incomplete type. > > I couldn't find anything in 3.5.4.3 that allows or forbids incomplete > types, or pointers to them, in function prototypes. Agreed. I don't think there is anything in the standard that forbids such an incomplete type in a parameter list. > Our HP "ANSI" C compiler is complaining about the above prototype The HP ANSI C compiler issues the following warning: warning: struct 'bar' declared in parameter list will have scope limited to this function declaration or definition. I believe section 2.1.1.3 (see also footnote 6) allows a conforming implementation to issue diagnostics such as the warning above, as long as it correctly translates valid programs. The reason for the warning is to help in the diagnosis of a situation such as: /* header file contents */ extern void foo(struct bar *); struct bar { /* fields */ }; /* user code */ #include HEADER struct bar *barp; main(){ foo(barp); } In this program, the compiler will diagnose a mismatch in parameter types at the call to foo(). The reason is that the first "struct bar" in the parameter list goes out of scope at the end of the declaration; so the next declaration of "struct bar" in the header file declares a new (incompatible) structure type. While the standard allows incomplete types to be declared, and go out of scope at the end of the parameter list; this is usually not what the user desires. The warning was added to ease in diagnosing this problem. > I want some ammunition before I report it to HP as a problem. Consider it unofficially reported. Contact me if you have further questions or concerns about the HP ANSI C compiler. --mev, mev@hpcomet.hp.com Standard disclaimer: speaking for myself and not HP.
diamond@jit533.swstokyo.dec.com (Norman Diamond) (05/07/91)
In article <683g+p#@rpi.edu> xor@aix01.aix.rpi.edu (Joseph Schwartz) writes: >The paragraph in question is 3.1.2.1, lines 32-37: >"If the declarator or type specifier that declares the identifier appears ----------------- >within the list of parameter declarations in a function prototype (not >part of a function definition), the identifier has FUNCTION PROTOTYPE >SCOPE [emphasis theirs], which terminates at the end of the function >declarator. If an outer declaration of a lexically identical identifier >exists in the same name space, it is hidden until the current scope >terminates, after which it again becomes visible." > >As I said earlier, I believe this applies to the identifier "stool" in: > extern void foo(struct bar *stool); >I could also believe that it applies to complete structure declarations, >as in: > extern void foo(struct bar { int a; int b; } *); >But I still have a hard time believing that it applies to struct bar in: > extern void foo(struct bar *); It certainly does. It says "type specifier" with no exceptions. >Making bar have local scope in this case is of questionable usefulness, I agree, but that's not the issue any more. >and it would appear to break code that existed before the Standard. This also isn't the issue at the moment, but just how much PROTOTYPED code do you think existed before the Standard? >But let's assume that the correct interpretation of the Standard is to >give struct bar local scope in my last example above. Then why does this: > struct bar; > extern void foo(struct bar *); >solve the scoping problems? According to the paragraph that I quoted above, >the vacuous declaration of struct bar is HIDDEN inside the prototype. No. A new declaration of "bar" would hide the outer declared "bar". The Standard plays a dirty trick in section 3.5.2.1, page 61 lines 23-34: "The presence of a struct-declaration-list in a struct-or-union-specifier declares a new type, within a translation unit." (Hmm, this doesn't exactly say that this declares the TAG, but presumably an interpretation ruling would say so.) And then section 3.5.2.3, page 63 lines 31-35, give the famous case of a vacuous declaration (which can break old code). Your type-name of "struct bar *" does not meet either of these criteria, so it does not declare a new type. Aside -- something troubles me about the answer that I have just given. A declaration must declare at least a declarator, a tag, or the members of an enumeration, but a type-name need not do so. Furthermore, a type-name does not have the form described in 3.5.2.3, page 63 line 32: " struct-or-union identifier ; " because a type-name declaration in a prototype doesn't have a semicolon. I'm not sure why I'm troubled; perhaps another logician can help out. -- Norman Diamond diamond@tkov50.enet.dec.com If this were the company's opinion, I wouldn't be allowed to post it.
diamond@jit533.swstokyo.dec.com (Norman Diamond) (05/07/91)
In article <kre.673334440@mundamutti.cs.mu.OZ.AU> kre@cs.mu.oz.au (Robert Elz) writes: >Disclaimer: I know absolutely nothing about ANSI C ... Well, a bit better than nothing. I hope that your disclaimer hasn't discouraged some other competent people from reading. >I can't see how its possible for > type foo(struct bar *arg); >to possibly be considered as any kind of a definition of the declaration >struct tag "bar". Whether "bar" is defined elsewhere or not, declared >this is (and can only be) a reference to "bar". The standard might have benefitted from distinguishing between tag declarations and tag definitions, but it did not do so. Anyway, when we adjust this statement to use the standard's words, I think it is correct. >Struct tags are (should be, surely) only [declared] in a context as > struct bar { ... }; As well as (SOMETIMES) in the vacuous case of struct bar; >The question of the scope inside the function prototype can only >possibly be relevant to names defined there, of which "bar" is not >one in the above example. It would be in > type foo(struct bar { ... } *arg); >which would *always* be a meaningless prototype, regardless of >whether bar was [declared] elsewhere or not. Exactly. (No other possibility, because type foo(struct bar;); would be syntactically invalid.) >It seems as if the compiler involved is treating the reference >to bar in the first prototype above as a definition, as it hadn't >seen a definition before. That is surely a bug. Yes. No wonder I was dissatisfied with my previous posting on this issue. Something still seems wrong. -- Norman Diamond diamond@tkov50.enet.dec.com If this were the company's opinion, I wouldn't be allowed to post it.
mev@hpfcso.FC.HP.COM (Mike Vermeulen) (05/07/91)
>>You didn't say whether there was a prior incomplete decl. > It does not matter. According to my understanding of the standard and > according to my ANSI C compiler the following is valid code: > struct bar *getbar(int); > void changebar(struct bar *, int); > > void main(void) > { > struct bar *mybar; > > mybar = getbar (1); > if (mybar) > changebar (mybar, 2); > } In the example above, the line "struct bar *getbar(int);" causes an incomplete declaration of struct bar. An earlier note complained about warning messages from the HP ANSI C compiler. However, in the example above, the HP ANSI C compiler is silent because the incomplete declaration on the first line means that the "struct bar" tag in the next line refers to "struct bar" at file scope and not prototype scope. --mev, mev@hpcomet standard disclaimer: I speak for myself and not HP.
mev@hpfcso.FC.HP.COM (Mike Vermeulen) (05/07/91)
> Actually, I'm a bit confused by this whole thing, because we also have > a HP system (HP 9000/835 running HP/UX 7.0), and the problem doesn't > occur with our compiler. The 7.0 ANSI C compiler on the 835 is not the same as on the hp9000 Series 300. --mev
scs@adam.mit.edu (Steve Summit) (05/08/91)
In article <1991May7.043654.4795@tkou02.enet.dec.com> diamond@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes: >The standard might have benefitted from distinguishing between tag >declarations and tag definitions, but it did not do so. When writing my previous response on this topic, I found myself wondering whether to use "declaring" or "defining" with respect to struct types and tags. Your bringing the issue out in the open like this makes me realize that there is no meaningful distinction. When declaring objects and functions, we commonly (and meaningfully) distinguish between declarations: extern int i; extern double square(double); and definitions: int i = 314; double square(double x) { return x * x; } Note that, in either case, an identifier and some type information are entered into a symbol table. A definition additionally implies space allocation, and perhaps content initialization. However, a struct declaration, with or without a tag and with or without a struct-declaration-list, only enters identifier and type information into symbol tables. We could make an arbitrary distinction and use the term "struct definition" for all struct declarations which include struct-declaration-lists, but this is a very different distinction than that which is made between object and function declarations and definitions, so it is perhaps wise (and deliberate) that section 3.5.2.1 says that "The presence of a struct-declaration-list in a struct-or-union- specifier *declares* a new type, within a translation unit" [emphasis mine]. >In article <kre.673334440@mundamutti.cs.mu.OZ.AU> kre@cs.mu.oz.au (Robert Elz) writes: >>It seems as if the compiler involved is treating the reference >>to bar in... >[ type foo(struct bar *arg); >] >...as a definition, as it hadn't >>seen a definition before. That is surely a bug. > >Yes. No wonder I was dissatisfied with my previous posting on this >issue. Something still seems wrong. There's nothing wrong here. In the absence of any previous mention of a struct bar, type foo(struct bar *arg); is as much of a struct "definition" as is type foo(struct bar { ... } *arg); It can't be a "reference;" what (previous) struct bar could it be referring to? If mentioning struct bar * didn't suffice to "define" a struct bar (assuming it wasn't already "defined"), then the one-line translation unit struct bar *bp; would have to give some kind of an error, much like the "warning: struct bar never defined" which lint issues under the -h option. (This is not usually a useful error message; I use grep -v to eliminate it when I'm using -h with lint.) The sole problem with extern foo(struct bar *arg); at the top of some translation unit is that any call to foo later in that translation unit can't pass a compatible pointer-to- struct-bar. (As has been noted, it could successfully pass a null pointer.) The problem is not so much that the (strictly speaking) incompatible pointer-to-struct-bar that could be passed would fail to work correctly over in the translation unit where foo() is defined. The problem is just that it is incompatible, according to the rules. If we had the two translation units main.c: foo.c: extern foo(struct bar *); struct bar {int i; }; main() foo(struct bar *bp) { { struct bar {int i;} b; bp->i = 3; foo(&b); return 0; return 0; } } , then the struct bar with which x is defined in main.c *is* compatible (by the special language in section 3.1.2.6) with the struct bar which foo(), over in foo.c, accepts a pointer to. x's struct bar is however *not* compatible with the struct bar which the prototype at the top of main.c says foo() accepts a pointer to. The compiler is obliged to issue a diagnostic for the call to foo() in main.c, whether we think it's meaningful or not. (I admit that it's dubious from an intuitive point of view, but the compiler is just following the rules.) Therefore, if a struct type is "defined" (either because of the presence of a struct-declarator-list, or the first appearance of a particular tag in the translation unit) inside of a prototype declaration, it is immediately discernible as "odd," and a lint-like warning is appropriate. (I believe it was just such a diagnostic which started this thread. Whether compilers should be issuing lint-like messages is of course another issue.) I thought there was a footnote or a sentence in the Rationale specifically mentioning the uselessness of struct "definitions" in prototypes, but I can't find it now. (Finally, we might note that the additional confusion evidently generated by this particular lint-like message might be worse than the confusion it was supposed to prevent, when the type passed to foo() is later diagnosed as incompatible.) Steve Summit scs@adam.mit.edu
diamond@jit533.swstokyo.dec.com (Norman Diamond) (05/08/91)
In article <1991May8.043353.28983@athena.mit.edu> scs@adam.mit.edu writes: >In article <1991May7.043654.4795@tkou02.enet.dec.com> diamond@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes: >>The standard might have benefitted from distinguishing between tag >>declarations and tag definitions, but it did not do so. >Your bringing the issue out in the >open like this makes me realize that there is no meaningful distinction. Sorry, there is. >A definition additionally implies space allocation, and perhaps content >initialization. For variables and functions, yes. >However, a struct declaration, with or without a tag and with or >without a struct-declaration-list, only enters identifier and >type information into symbol tables. And scope and storage class information. If definitions were distinguished from declarations, then the difference would be that declarations don't enter type information into the table, and the scope issue might be cloudy. >>In article <kre.673334440@mundamutti.cs.mu.OZ.AU> kre@cs.mu.oz.au (Robert Elz) writes: >>>It seems as if the compiler involved is treating the reference to bar in... > type foo(struct bar *arg); >>>as a definition, as it hadn't seen a definition before. >>>That is surely a bug. >> >>Yes. No wonder I was dissatisfied with my previous posting on this >>issue. Something still seems wrong. > >There's nothing wrong here. In the absence of any previous >mention of a struct bar, > type foo(struct bar *arg); >is as much of a struct "definition" as is > type foo(struct bar { ... } *arg); No. The standard says that the presence of "{ ... }" declares the a type. No part of the standard says that "struct bar *" declares a new type. "struct bar *" must be a reference. Now: The standard allows forward references for structure tags that haven't been declared/[defined] yet. It also allows references to structure tags that are declared in outer scopes. It does not explicitly say that a single reference may take advantage of both of these features simultaneously. But I think it must be allowed. There is still no other meaning. This answer runs counter to my previous beliefs and to everyone else's posted beliefs, but it is the only one that is consistent with the standard. >It can't be a "reference;" what (previous) struct bar could it be >referring to? A later one. The standard allows forward references of structure tags. >If mentioning > struct bar * >didn't suffice to "define" a struct bar (assuming it wasn't already "defined"), You betcha! It doesn't! RTFS. >then the one-line translation unit > struct bar *bp; >would have to give some kind of an error, much like the "warning: >struct bar never defined" which lint issues under the -h option. I think this might be true. (Sorry, I'm not in the mood to look it up at the moment.) >The sole problem with > extern foo(struct bar *arg); >at the top of some translation unit is that any call to foo later >in that translation unit can't pass a compatible pointer-to-struct-bar. It can complete the declaration of struct bar at a later time. -- Norman Diamond diamond@tkov50.enet.dec.com If this were the company's opinion, I wouldn't be allowed to post it.
diamond@jit533.swstokyo.dec.com (Norman Diamond) (05/10/91)
In article <7330008@hpfcso.FC.HP.COM> mev@hpfcso.FC.HP.COM (Mike Vermeulen) writes: >> extern void foo(struct bar *); >> According to 3.1.2.5, struct bar is an incomplete type in this case, >> and struct bar * is a pointer to an incomplete type. >> I couldn't find anything in 3.5.4.3 that allows or forbids incomplete >> types, or pointers to them, in function prototypes. > >Agreed. I don't think there is anything in the standard that forbids such >an incomplete type in a parameter list. >The HP ANSI C compiler issues the following warning: > warning: struct 'bar' declared in parameter list will have scope > limited to this function declaration or definition. The problem, as someone else pointed out, is that struct 'bar' IS NOT DECLARED in the parameter list. It is REFERENCED in the parameter list. If the declaration were extern void foo(struct bar { /* fields */ } *); then the HP ANSI C compiler's warning would be correct. >The reason for the warning is to help in the diagnosis of a situation such as: > /* header file contents */ > extern void foo(struct bar *); > struct bar { /* fields */ }; > /* user code */ > #include HEADER > struct bar *barp; > main(){ > foo(barp); > } >In this program, the compiler will diagnose a mismatch in parameter types at >the call to foo(). The reason is that the first "struct bar" in the parameter >list goes out of scope at the end of the declaration; so the next declaration >of "struct bar" in the header file declares a new (incompatible) structure >type. Sorry, no. There is only one declaration of a type "struct bar" in that code, and it is referenced several times. There is no compatibility problem. -- Norman Diamond diamond@tkov50.enet.dec.com If this were the company's opinion, I wouldn't be allowed to post it. Permission is granted to feel this signature, but not to look at it.
scs@adam.mit.edu (Steve Summit) (05/10/91)
In article <1991May8.072112.20866@tkou02.enet.dec.com> diamond@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes: >In article <1991May8.043353.28983@athena.mit.edu> scs@adam.mit.edu writes: >>...there is no meaningful distinction [between struct type >>"declarations" and "definitions]." >>...a struct declaration, with or without a tag and with or >>without a struct-declaration-list, only enters identifier and >>type information into symbol tables. > >And scope and storage class information. If definitions were distinguished >from declarations, then the difference would be that declarations don't >enter type information into the table, and the scope issue might be cloudy. Structure type definitions/declarations don't have a storage class. "Scope information," to my view, is the implicit property of which (nested) symbol table a given symbol is declared/defined in, which is of course the whole issue here. >>There's nothing wrong here. In the absence of any previous >>mention of a struct bar, >> type foo(struct bar *arg); >>is as much of a struct "definition" as is >> type foo(struct bar { ... } *arg); > >No. The standard says that the presence of "{ ... }" declares the a type. Well, I hope this doesn't sound like overly-persnickety verbal nitpicking, but the standard also says things like "A string literal is a primary expression." I don't conclude that the presence of "{...}" is necessarily the only way of declaring a new type. >No part of the standard says that "struct bar *" declares a new type. >"struct bar *" must be a reference. > >Now: The standard allows forward references for structure tags that >haven't been declared/[defined] yet. It also allows references to structure >tags that are declared in outer scopes. It does not explicitly say that a >single reference may take advantage of both of these features simultaneously. >But I think it must be allowed. There is still no other meaning. I sort of agree, to the extent that neither analysis can be conclusively disproved. To make things clear for everybody, consider struct a *aptr; struct a {int a;}; struct b *bptr; { struct b {int b;}; } { struct c *cptr; } struct c {int c;}; The declaration of aptr is clearly legal; the following line completes the incomplete struct a type. Section 3.5.2.3 discusses this case. The incomplete struct b type with which bptr is declared is clearly not completed two lines down. This, too, is explicitly stated in section 3.5.2.3 . (The wording here talks about "defining the content," so it was not quite correct of me to suggest that the standard makes no distinction between "declaring" and "defining" structures. I'm still not convinced it's a formal distinction, however.) The sticky case is the third one. I don't think that a struct type can be completed in an outer scope, but the standard doesn't explicitly say that it can't. Nor does it say that it can. >>If mentioning >> struct bar * >>didn't suffice to "define" a struct bar (assuming it wasn't already "defined"), > >You betcha! It doesn't! RTFS. > >>then the one-line translation unit >> struct bar *bp; >>would have to give some kind of an error, much like the "warning: >>struct bar never defined" which lint issues under the -h option. > >I think this might be true. (Sorry, I'm not in the mood to look it up >at the moment.) Well, I was, and I did, and from (still in section 3.5.2.3): If a type specifier of the form struct-or-union identifier occurs prior to the declaration that defines the content, the structure or union is an incomplete type. It declares a tag that specifies a type that may be used only when the size of an object of the specified type is not needed. If the type is to be completed, another declaration of the tag *in the same scope* ... shall define the content [emphasis mine]. I concluded that the incomplete declaration, when not followed by another declaration of the tag in the same scope, was sufficient to declare a type, as long as that type's size was not needed. That's why I think that the one-line translation unit struct bar *bp; is conforming, and need elicit no diagnostic. Since section 3.5.2.3 specifically mentions that a later declaration in the same scope can complete a struct type (and the same point is made in the paragraph on incomplete types in section 3.1.2.5), I am led to believe that it was not the intention that a declaration in an outer scope be able to do so. I admit that there may be some ambiguity here. I had realized that an interpretation like Norm's was possible while composing my first reply, and almost mentioned it. (I hadn't realized until now that Norm was advancing this interpretation as the way out of the dissatisfaction.) To repeat, though, I think that both interpretations are viable in that neither is explicitly ruled out. I leaned the way I did (inner-scope incomplete struct types aren't completed in outer scopes) because of the "in the same scope" language, and because I think that's the way compilers have tended to do it. (The ones I've written have, anyway, but I just checked pcc and it seems to, too.) Steve Summit scs@adam.mit.edu
diamond@jit533.swstokyo.dec.com (Norman Diamond) (05/10/91)
In article <1991May10.021509.9862@athena.mit.edu> scs@adam.mit.edu writes: >In article <1991May8.072112.20866@tkou02.enet.dec.com> diamond@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes: >>In article <1991May8.043353.28983@athena.mit.edu> scs@adam.mit.edu writes: >"Scope information," to my view, is the implicit property >of which (nested) symbol table a given symbol is declared/defined >in, which is of course the whole issue here. Yes. >>>In the absence of any previous mention of a struct bar, >>> type foo(struct bar *arg); >>>is as much of a struct "definition" as is >>> type foo(struct bar { ... } *arg); >>No. The standard says that the presence of "{ ... }" declares a type. > >Well, I hope this doesn't sound like overly-persnickety verbal >nitpicking, but the standard also says things like "A string >literal is a primary expression." I don't conclude that the >presence of "{...}" is necessarily the only way of declaring a >new type. Sorry, it is. How would you like a compiler to arbitrarily decide that some of your references to variables are really declarations rather than references (because the standard doesn't say that they aren't declarations), and then reject your program for having illegal declarations? Sorry, I think we have to accept that the standard DOES say what a declaration is, and that references are not declarations. However, when I reread the section that you quote below, type foo(struct bar *arg); declares the TAG bar, though it does not declare the type. And the tag goes out of scope at the end of the function prototype. OK, so Mr. Summitt and I both got lost in the rathole, and the HP ANSI C compiler was right after all. However, its warning was an optional one, not a required one. >>No part of the standard says that "struct bar *" declares a new type. >>"struct bar *" must be a reference. I was half right; a reference to a type which "shall" (but this is in a Semantics section, not Contraints) be declared in the same scope. >>Now: The standard allows forward references for structure tags that >>haven't been declared/[defined] yet. It also allows references to structure >>tags that are declared in outer scopes. It does not explicitly say that a >>single reference may take advantage of both of these features simultaneously. >>But I think it must be allowed. There is still no other meaning. > >I sort of agree, to the extent that neither analysis can be >conclusively disproved. We're both wrong here. >>>If mentioning >>> struct bar * >>>didn't suffice to "define" a struct bar (assuming it wasn't already "defined"), >> >>You betcha! It doesn't! RTFS. Uh, you should have told me to RTFS. It declares the tag bar. >> >>>then the one-line translation unit >>> struct bar *bp; >>>would have to give some kind of an error, much like the "warning: >>>struct bar never defined" which lint issues under the -h option. >> >>I think this might be true. (Sorry, I'm not in the mood to look it up >>at the moment.) Ah, it wouldn't be true, because Semantics violations don't require diagnostics. >Well, I was, and I did, and from (still in section 3.5.2.3): > If a type specifier of the form > struct-or-union identifier > occurs prior to the declaration that defines the content, > the structure or union is an incomplete type. It > declares a tag that specifies a type that may be used > only when the size of an object of the specified type is > not needed. If the type is to be completed, another > declaration of the tag *in the same scope* ... shall > define the content [emphasis mine]. >Since section 3.5.2.3 specifically mentions that a later >declaration in the same scope can complete a struct type (and >the same point is made in the paragraph on incomplete types in >section 3.1.2.5), I am led to believe that it was not the >intention that a declaration in an outer scope be able to do so. Section 3.5.2.3 explicitly "bars" inner scopes from completing the type, but does not "bar" apparently-outer scopes. Section 3.1.2.1 accomplishes that (indirectly). >I admit that there may be some ambiguity here. It's gone, I think. I think we don't even need an interpretation ruling. -- Norman Diamond diamond@tkov50.enet.dec.com If this were the company's opinion, I wouldn't be allowed to post it. Permission is granted to feel this signature, but not to look at it.
gwyn@smoke.brl.mil (Doug Gwyn) (05/11/91)
In article <1991May10.011038.6781@tkou02.enet.dec.com> diamond@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes: >The problem, as someone else pointed out, is that struct 'bar' >IS NOT DECLARED in the parameter list. It is REFERENCED in the >parameter list. No, you're making distinctions of your own that the standard does not support. Use of "bar" in that context certainly does declare an identifier "bar" to be a structure tag, according to the syntax of section 3.5.2.1, which is a subset of the declaration syntax. A structure can be declared (incompletely or completely) "in passing" within a "fatter" declaration; note the use of the plural for "entities", "declarators", and "identfiers" in the second paragraph of 3.5 Semantics. Absence of the "{ struct-declaration-list }" portion in the syntactic form used in the source code merely means that the third paragraph in the Semantics subsection is not relevant. It does not mean that the declaration does NOT declare a new type; that would be asserting the converse, which is a logical fallacy.
gwyn@smoke.brl.mil (Doug Gwyn) (05/11/91)
In article <1991May10.021509.9862@athena.mit.edu> scs@adam.mit.edu writes: >That's why I think that the one-line translation unit > struct bar *bp; >is conforming, and need elicit no diagnostic. Of course it is. Sheesh, you guys have gotten yourselves confused. >I admit that there may be some ambiguity here. X3J11 has already ruled that type information is not "exported" from an inner scope to an outer one. It's pretty clear that the type of a local variable has nothing to do with the type of a global one whose identifier is temporarily "hidden" by the one in the local scope.
diamond@jit533.swstokyo.dec.com (Norman Diamond) (05/13/91)
In article <16108@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes: >In article <1991May10.011038.6781@tkou02.enet.dec.com> diamond@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes: >>The problem, as someone else pointed out, is that struct 'bar' >>IS NOT DECLARED in the parameter list. It is REFERENCED in the >>parameter list. (This was wrong. However, Mr. Gwyn proceeds to make a different mistake.) >Absence of the "{ struct-declaration-list }" portion in the syntactic form >used in the source code merely means that the third paragraph in the >Semantics subsection is not relevant. It does not mean that the >declaration does NOT declare a new type; that would be asserting the >converse, which is a logical fallacy. Misuse of converses does lead to logical fallacies, but I do think that converses apply in certain situations. If we allow the compiler to say that lots of things are declarations, when the standard does not explicitly prohibit them from being declarations, then the compiler could reject lots of programs that should be legal. Although the standard's rules should be interpreted mathematically as "if (A) then (Z); if (B) then (Z); etc." rules, we'd better interpret them technologically as "(Z) if-and-only-if (A) or (B) or etc." in order to have any use at all. -- Norman Diamond diamond@tkov50.enet.dec.com If this were the company's opinion, I wouldn't be allowed to post it. Permission is granted to feel this signature, but not to look at it.
gwyn@smoke.brl.mil (Doug Gwyn) (05/13/91)
In article <1991May13.004120.26457@tkou02.enet.dec.com> diamond@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes: >If we allow the compiler to say that lots of things are declarations, when >the standard does not explicitly prohibit them from being declarations, then >the compiler could reject lots of programs that should be legal. This seems to me totally irrelevant to the topic under discussion, which I would summarize as "a declaration can specify things about more than one identifier at a time".