[comp.lang.c] Modifying Typedefs

unhd (Anthony Lapadula) (09/15/90)

While implementing a (hopefully soon-to-be-) ANSI-compliant compiler,
we came across the following problem.  Given

   typedef int  INT;
   typedef char CHAR;

which of the following are legal?

   main()
   {
       unsigned INT foo1;
       extern   INT foo2;
       extern   INT (CHAR);
   }

I've read section 3.5.6 of the standard, but with no luck deciding the issue.
Harbison and Steel (1987, p. 116) state that the declaration of ``foo1''
would be illegal, but the declaration of ``foo2'' would be legal.
This seems reasonable.

What about the third declaration?  Seems that it would declare a (new)
variable CHAR to be of type ``int'' and with storage class ``external''.
Is this right?  Is CHAR a candidate to be a variable because it may override
its typedef'ed meaning from the outer block?

It was discussed here (last month?) that implementing typedef-names is
done as follows: lexer get the candidate identifier, asks the symbol table
if it is currently a typedef-name, and returns, e.g., TYPEDEF or IDENTIFIER
to the grammar.

Doesn't the lexer have to know more?  In my third example, the
lexer needs to know that CHAR is a candidate variable, even though it
is currently a typedef-name.  How is this implemented?

As an aside, gcc 1.37.1 rejects my first example
   unsigned INT foo1;
but accepts
   INT unsigned foo1;

Any takers to explain this?

-- Anthony (uunet!unhd!al, al@unh.edu) Lapadula

// Wanted: catchy .sig.

eyal@echo.canberra.edu.au (Eyal Lebedinsky) (09/16/90)

In article <1990Sep14.213822.4828@uunet!unhd> al@uunet!unhd (Anthony Lapadula) writes:
>While implementing a (hopefully soon-to-be-) ANSI-compliant compiler,
>we came across the following problem.  Given
[...]
>       unsigned INT foo1;
3.5.2 clearly states which type-specifier lists are allowed, and this is not
one of those.

>       extern   INT foo2;
This is a storage-class-specifier followed by a type-specifier which is ok
 for 3.5.

>       extern   INT (CHAR);
typedefs and variables share a common namespace to CHAR will hide the
external one.
[...]

>Doesn't the lexer have to know more?  In my third example, the
>lexer needs to know that CHAR is a candidate variable, even though it
>is currently a typedef-name.  How is this implemented?
I found it necessary NOT to have the lexer make the decision for the same
reason.
>
>-- Anthony (uunet!unhd!al, al@unh.edu) Lapadula

I am using the May/88 Draft.

Regards
	Eyal

-- 
Regards
	Eyal

henry@zoo.toronto.edu (Henry Spencer) (09/18/90)

In article <1990Sep14.213822.4828@uunet!unhd> al@uunet!unhd (Anthony Lapadula) writes:
>   typedef int  INT;
>   typedef char CHAR;
>   ... { ...
>       unsigned INT foo1;
>       extern   INT foo2;
>       extern   INT (CHAR);

The 3.5.2 constraints outlaw foo1, but the other two declarations are legal.
Typedef names follow block-structured scope rules and can be hidden by
variable declarations in inner blocks.

Your grammar actually *has* to include the 3.5.2 constraints to some degree;
you can't leave them until later, because

	typedef unsigned U;
	... { ...
		short U = 2;		/* !!! */

is perfectly legal C.  Typedef names don't mix with other type specifiers,
so `!!!' is a perfectly legal declaration of a variable named `U' with
type `signed short int' which is initialized to 2.

Barf.

>... lexer get the candidate identifier, asks the symbol table
>if it is currently a typedef-name, and returns, e.g., TYPEDEF or IDENTIFIER
>to the grammar.
>
>Doesn't the lexer have to know more?  In my third example, the
>lexer needs to know that CHAR is a candidate variable, even though it
>is currently a typedef-name.  How is this implemented?

The lexer really can't know, in these cases, what's wanted.  What you have
to do is mung your parser so that (a) it knows that typedef names don't mix
with the other specifiers like `unsigned' and (b) in some (not all!) places
it takes typedef names as alternatives to identifiers, with later processing
treating them as equivalent.  There is just no other way to cope with the
`!!!' example.

>As an aside, gcc 1.37.1 rejects my first example
>   unsigned INT foo1;
>but accepts
>   INT unsigned foo1;

Sounds like a bug.  (Although the gcc folks have a tendency to call such
things "features" any time they don't like what the standard says.)
-- 
TCP/IP: handling tomorrow's loads today| Henry Spencer at U of Toronto Zoology
OSI: handling yesterday's loads someday|  henry@zoo.toronto.edu   utzoo!henry