[comp.std.c] Scope of incomplete types

wmm@charis.UUCP (William M. Miller) (09/06/90)

As far as I can tell, X3.159-1989 does not define what happens in the
following situation:
 
                struct X* xp1;
                {
                   struct X* xp2;
                   }
 
The question is whether xp1 and xp2 have the same type.  On the affirmative
side, there doesn't appear to be anything in the Standard to indicate any
difference in the way the scope rules apply to incomplete types.  The
following wording from 3.5.2.3 would seem to allow (but perhaps not to
require?) this interpretation:
 
        A declaration of the form
 
                struct-or-union identifier ;
 
        specifies a structure or union type and declares a tag, both visible
        only within the scope in which the declaration occurs.  It specifies
        a new type distinct from any type with the same tag in an enclosing
        scope (if any).
 
If "any type with the same tag" includes incomplete types, the implication
is that an incomplete type with the same tag in an enclosing type *would* be
visible, apart from such a declaration.
 
The commentary on one of the examples in 3.5.2.3 appears to be conclusive:
 
        ...the declarations
 
                struct s1 { struct s2 *s2p; /*...*/ }; /* D1 */
                struct s2 { struct s1 *s1p; /*...*/ }; /* D2 */
 
        specify a pair of structures that contain pointers to each other.
        Note, however, that if s2 were already declared as a tag in an
        enclosing scope, the declaration D1 would refer to *it*, not to the
        tag s2 declared in D2.
 
Since the declaration of xp1 declares X as a tag in the enclosing block,
this wording would indicate that the declaration of xp2 refers to the same
tag and that, therefore, xp1 and xp2 have the same type.
 
There are basically two factors supporting the opposite conclusion.  The
first is the wording used in 3.5.2.3 of the Rationale: "if struct y is
already *defined* [emphasis mine] in a containing block..."  This wording
would tend to indicate that a full definition, not simply an incomplete
type, is required in the containing block for the type to be visible.
 
To illustrate the second consideration, consider the following slightly
expanded version of the example with which I opened this posting:
 
                struct X* xp1;
                {
                   struct X* xp2;
                   }
                struct X { /*...*/ };   /* D1 */
 
3.5.2.3 says about incomplete struct-or-union types,
 
        If the type is to be completed, another declaration of the tag in
        the same scope (but not in an enclosed block, which declares a new
        type known only within that block) shall define the content.
 
If xp1 and xp2 have the same type, and the declaration D1 completes the type
introduced by the declaration of xp1, then it also completes the type in the
declaration of xp2, in violation of the above-quoted rule requiring such
completion to be in the same scope.
 
Does anyone out there in netland have anything to say about this question?
 
------------------------------------------------------------------------------
William M. Miller, Glockenspiel, Inc.; P. O. Box 366, Sudbury, MA 01776-0003
wmmiller@cup.portal.com         BIX: wmiller            CI$: 72105,1744
-- 
Non-disclaimer:  My boss and I always see eye-to-eye (every time I look in
the mirror).

wmm@sdti.sdti.com

gwyn@smoke.BRL.MIL (Doug Gwyn) (09/07/90)

In article <1990Sep5.191221.5118@charis.UUCP> wmmiller@cup.portal.com writes:
>                struct X* xp1;
>                {
>                   struct X* xp2;
>                   }

Neither of these declares a struct X.  That is a crucial point to
understand in conjunction with the example from 3.5.2.3 that you cited.

>... there doesn't appear to be anything in the Standard to indicate any
>difference in the way the scope rules apply to incomplete types.

That's correct; visibility of an identifier (scope) is not affected by
incompleteness of its type.

>        If the type is to be completed, another declaration of the tag in
>        the same scope (but not in an enclosed block, which declares a new
>        type known only within that block) shall define the content.

Yes, basically one of the interesting things X3J11 figured out at the
first interpretation-phase meeting was that the standard insists that
type information is not propagated outward from a block.  That has all
sorts of subtle ramifications, fortunately not affecting programmers
who use sensible coding style in the first place.

For your example, basically I would say that not declaring what a struct X
really is before declaring pointers to it is a poor practice except in the
case of mutually-referential structures as in the 3.5.2.3 example, and even
there one should as a matter of safety predeclare an incomplete type like:
	struct s2;	/* forces s2 to refer to the one in the current scope */
	struct s1 { struct s2 *s2p; /*...*/ };
	struct s2 { struct s1 *s1p; /*...*/ };

wmm@charis.UUCP (William M. Miller) (09/10/90)

In article <13765@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes:
> In article <1990Sep5.191221.5118@charis.UUCP> wmmiller@cup.portal.com writes:
> >                struct X* xp1
> >                {
> >                   struct X* xp2;
> >                   }
>
> Neither of these declares a struct X.  That is a crucial point to
> understand in conjunction with the example from 3.5.2.3 that you cited.
 
What do you mean by "declaring a struct X?"  The exact wording of 3.5.2.3 is
 
        If a type specifier of the form
 
                struct-or-union identifier
 
        occurs prior to the declaration that defines the content, the
        structure of 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.
 
When it says "declares a tag that specifies a type," I understood that to be
equivalent to declaring a type "struct X."  If you're drawing a distinction
with the standalone declaration
 
                struct X;
 
I don't see it, since the verbiage for this declaration ("specifies a
structure or union type and declares a tag") seems essentially identical.
If you're simply stating that neither declaration defines the contents,
that's obviously true, but my question essentially boils down to whether
such a defining declaration is needed.
 
Besides, in the example, the issue is not whether a struct X is declared;
the wording is, "if s2 were already declared as a tag in an enclosing
scope," and it's clear that "struct X* p1" *does* declare X as a tag.
 
> >... there doesn't appear to be anything in the Standard to indicate any
> >difference in the way the scope rules apply to incomplete types.
>
> That's correct; visibility of an identifier (scope) is not affected by
> incompleteness of its type.
 
So the tag X from the outer block is visible in the inner block.
 
> >        If the type is to be completed, another declaration of the tag in
> >        the same scope (but not in an enclosed block, which declares a new
> >        type known only within that block) shall define the conte7t.
>
> Yes, basically one of the interesting things X3J11 figured out at the
> first interpretation-phase meeting was that the standard insists that
> type information is not propagated outward from a block.  That has all
> sorts of subtle ramifications, fortunately not affecting programmers
> who use sensible coding style in the first place.
 
I'm sorry to be obtuse, but I couldn't determine the "bottom line" in your
posting.  Let me give an example and a few possible interpretations, and
perhaps you can tell me which of them is right and why (or give another
interpretation I hadn't thought of).
 
        struct X* p1;
        void f() {
           struct X* p2;
           p2 = p1;             /* A */
           }
        struct X { int i; };    /* B */
 
INTERPRETATION 1: This is a legal compilation unit.  p1 and p2 have the same
type, so assignment A is allowed.  Implication: the wording in the Standard
requiring a completing declaration to be in the same block does not apply to
the declaration inside f() because the type was introduced in the outer
scope.  Implication: the wording in the Rationale mentioning a definition is
excessively restrictive.
 
INTERPRETATION 2: This is an illegal compilation unit.  p1 and p2 have
different types, so assignment A is in error.  Implication: the wording
describing the example in the Standard is incorrect; instead of "if s2 were
already declared as a tag in an enclosing scope," it should say something
like "if s2 were declared as the tag of a structure whose contents have
already been defined in an enclosing scope."  Implication: the tags of
incomplete types are *not* visible across scopes.
 
INTERPRETATION 3: This is an illegal compilation unit.  p1 and p2 have the
same type, so assignment A is allowed; however, declaration B violates the
restriction requiring a completing declaration to be in the same scope,
since it completes the declaration in f() as well as the one in the outer
scope.
 
(BTW, I agree fully with the comment about "a sensible coding style."  I'm
not trying to see what I can get away with; as a member of X3J16, I'm trying
to figure out what it would mean for C++ to be compatible with C on this
point.)
 
------------------------------------------------------------------------------
William M. Miller, Glockenspiel, Inc.; P. O. Box 366, Sudbury, MA 01776-0003
wmmiller@cup.portal.com         BIX: wmiller            CI$: 72105,1744

scjones@thor.UUCP (Larry Jones) (09/10/90)

In article <1990Sep9.194037.346@charis.UUCP>, wmm@charis.UUCP (William M. Miller) writes:
>  
>         struct X* p1;
>         void f() {
>            struct X* p2;
>            p2 = p1;             /* A */
>            }
>         struct X { int i; };    /* B */
>  
> INTERPRETATION 1: This is a legal compilation unit.  p1 and p2 have the same
> type, so assignment A is allowed.  Implication: the wording in the Standard
> requiring a completing declaration to be in the same block does not apply to
> the declaration inside f() because the type was introduced in the outer
> scope.  Implication: the wording in the Rationale mentioning a definition is
> excessively restrictive.

Bingo!

The wording in section 3.5.2.3 could perhaps be clearer, but that
is certainly the intent of the committee.  Remember, the Rationale
is not a part of the standard -- it uses much less formal and
precise language to try to explain >why< the standard says what it
says, not to explain >what< it says.
----
Larry Jones                         UUCP: uunet!sdrc!thor!scjones
SDRC                                      scjones@thor.UUCP
2000 Eastman Dr.                    BIX:  ltl
Milford, OH  45150-2789             AT&T: (513) 576-2070
I think my cerebellum just fused. -- Calvin

gwyn@smoke.BRL.MIL (Doug Gwyn) (09/10/90)

In article <1990Sep9.194037.346@charis.UUCP> wmmiller@cup.portal.com writes:
>        struct X* p1;
>        void f() {
>           struct X* p2;
>           p2 = p1;             /* A */
>           }
>        struct X { int i; };    /* B */

This is strictly conforming, and all three occurrences of "X" refer to
the same structure type, which is complete at comment B.

As an interesting contrast, consider

	struct X* p1;
	void f() {
	   struct X* p2;
	   struct X { char i; };/* C */
	   struct X* p3;
	   p2 = p1;		/* A */
	}			/* D */
	struct X { int i; };	/* B */

In this example, p1 and p2 have the same struct type, as before, but
p3 has a different struct type since the declaration ending at comment C
declares a new type.  At comment A, "struct X" refers to a complete type
like the type of p3, different from the (still incomplete, as it happens)
type of p1 and p2.  Note also that at comment D "struct X" is still an
incomplete type, because the new type at comment C does not propagate
out of the scope within which it was declared (function-body block).

It is also worth noting that the description of the properties of the
special form of declaration
	struct-or-union identifier ;
in 3.5.2.3 is NOT a deduction from other specifications, but specifies
a special case (i.e. "kludge") that was adopted specifically for the
purpose of hiding any declaration of the tag name in an outer scope.
Thus, in the example

	struct X* p1;
	void f() {
	   struct X;		/* E */
	   struct X* p2;
	   struct X { char i; };/* C */
	   struct X* p3;
	   p2 = p1;		/* A */
	}			/* D */
	struct X { int i; };	/* B */

The type of both p2 and p3 will be that of the structure described at
comment C, since the declaration at E starts a new type with tag "X"
unrelated to the tag on the declaration of p1.  (This form is like a
declaration with struct-declarator-list in that it begins a new type,
but unlike that in that it is an incomplete type.)

P.S.  This is how I read the standard and how I recall the discussion
in X3J11; it is possible that an official interpretation could differ.
For an official interpretation, you should send your query to X3J11 via
the CBEMA X3 Secretariat.  X3J11 meets again on the 24th of September,
so if you need an official interpretation ruling soon, you should hurry.