[comp.std.c] Pointers to Incomplete Types in Prototypes

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