[comp.lang.c] ANSI C -- static forward references

minow@decvax.UUCP (Martin Minow) (12/14/86)

This is one of a collection of comments on the Draft Standard, posted to
comp.lang.c for discussion before I mail a final draft to the Ansi C
committee.  Each message discusses one problem I have found with the Draft
Standard that I feel warrants a "no" vote.  Note that this message is my
personal opinion, and does not reflect on the opinions of my employer.

---- Problem:

Page 19, line 42, page 49, line 43, page 70, line 22. "static" should
be permitted only for definitions. "extern" should be the only declarator
that specifies external linkage (whether file or program).

Page 19, line 42ff.  The formulation as given here does not allow the
common use of "extern" to forward reference a variable or function that
is actually declared "static."  This breaks existing programs with sequences
such as the following:

	char *
	func()
	{
		extern char	*subr();
		...
		return (subr());
	}
	...
	static char *
	subr()
	{
		...
	}

A subsequent definition should be allowed to declare an object or function
"static," overriding the previous linkage specification.  The storage class
specifier "static" (section 3.5.1, page 49) should be permitted only on
definitions.

Page 70, line 22.  As noted above, "static" should not be used to declare
external functions with file scope.  The module referring to the function]
does not need to ``know'' that the actual function definition is in the same
file -- indeed, it should not.  If only "extern" were used to specify linkage,
the programmer would be free to move function definitions between files
as required by the application.  In the current standard, the *user* of
a function must know whether the function is defined in the current file.

The only time this use of "static" would be useful would be to permit
one-pass compilers to produce smaller code segments for function calls.
Given the current direction of the computer industry, there is no need
for such limitations.

----

Martin Minow
decvax!minow

gwyn@brl-smoke.ARPA (Doug Gwyn ) (12/15/86)

In article <108@decvax.UUCP> minow@decvax.UUCP (Martin Minow) writes
about the usage of "static" and "extern".

The interplay between "static" and "extern" was carefully worked out
to maximally support existing practice, while permitting one-pass
compilation implementations.  Martin's code example falls into the
"undefined" category, which means that an implementation is free to
provide the semantics that Martin wants, but that a portable
application cannot count on them.  I believe an editorial
clarification of this is in the works.

My feeling is that it would take a very strong argument to convince
X3J11 to change this aspect of the standard.

minow@decvax.UUCP (Martin Minow) (12/17/86)

Discussing my suggestion to change the interaction between "static"
and "extern", Doug Gwyn (@ brl-smoke) notes that the Draft Standard's
semantics were designed to permit one-pass compilation.

Are there still true one-pass compilers being written today?  My impression
is that the minimum hardware being sold today is something on the order
of a Macintosh or Atari ST, both of which support full-featured, multi-pass
compilers.  I would much prefer the Standard be written for the current
(and future) "minimum" computer, rather than for some very limited
10-year old microcomputer.

Martin Minow
decvax!minow

braner@batcomputer.tn.cornell.edu (braner) (12/17/86)

[]

> [claims that one-pass compilers only belong on obsolete hardware...]

I use the Megamax C compiler on the Atari ST.  It is a one-pass compiler,
vintage 1986.  I love it, because it is VERY fast!  Just because the machine
has 1 Meg RAM and a 68000 does NOT mean efficiency doesn't pay!

A note to compiler writers: One-pass, two-pass, I don't care what the
algorithm is, but please use RAM, not the disk, for "temporary files" stuff.
It's all that disk-grinding that is obsolete!

- Moshe Braner

bobr@zeus.UUCP (Robert Reed) (12/17/86)

Given that relying on the interplay between "static" and "extern" falls into
the cracks between what should be in the standard and what is conventional
practise, it is reasonable to ask:  Does the standard provide a mechanism
for forward referencing functions?  (Not having a copy of the standard, I
can't answer this myself).  I know I can predeclare functions in most
conventional implementations but having to insert two declarations which
include type information, e.g.:

    static char *foo();

    biz()
    {
    	foo();
    }

    static char *foo()
    {

    }

seems to be a potential source of errors because of the duplicated type
declaration.  Though the static/extern technique may be a hack, Martin's
need seems reasonable.
-- 
Robert Reed, Tektronix CAE Systems Division, bobr@zeus.TEK

jdb@mordor.s1.gov (John Bruner) (12/18/86)

The last time I looked, PCC2 was a one-pass compiler.
-- 
  John Bruner (S-1 Project, Lawrence Livermore National Laboratory)
  MILNET: jdb@mordor.s1.gov		(415) 422-0758
  UUCP: ...!ucbvax!decwrl!mordor!jdb 	...!seismo!mordor!jdb

greg@utcsri.UUCP (Gregory Smith) (12/19/86)

In article <114@decvax.UUCP> minow@decvax.UUCP (Martin Minow) writes:
>Discussing my suggestion to change the interaction between "static"
>and "extern", Doug Gwyn (@ brl-smoke) notes that the Draft Standard's
>semantics were designed to permit one-pass compilation.
>
>Are there still true one-pass compilers being written today?  My impression
>is that the minimum hardware being sold today is something on the order
>of a Macintosh or Atari ST, both of which support full-featured, multi-pass
>compilers.  I would much prefer the Standard be written for the current
>(and future) "minimum" computer, rather than for some very limited
>10-year old microcomputer.

In article <264@mordor.s1.gov> jdb@mordor.UUCP (John Bruner) writes:
>The last time I looked, PCC2 was a one-pass compiler.

I think there is some confusion here. C compilers are normally 'One-pass'
in the sense that they can generate code for X without requiring any
information about the source which appears after X. Assemblers (normal
assemblers anyway) cannot be one-pass in this sense, since they
need to resolve forward references.

A 'multi-pass' compiler is usually a bunch of programs which pipe
their data from one to the other (The PCC2 can be considered 2-pass in this
sense since it reads the output of the preprocessor). However, the
entire system acts as a one-pass compiler, again, since code (assembler)
is generated for X without knowing what comes after X.
To compile stuff on this machine, e.g. 'cc -o foo foo.c', foo.c
is run thru /bin/cpp, /bin/ccom, [ and /bin/c2 if you say -O ]. Then
the assembler makes two passes on the output, and the linker probably
makes two passes on the object module. Now do cpp and ccom count
as two passes? Some pcc implementations split ccom into two stages,
each of which is essentially a filter. Am I the only one who thinks
there is a terminology problem here? I would like to see the word 'stage'
used for a section of a pipe, while 'pass' refers to the number of
times a module must read its input stream. So a compiler could
be referred to as a '3-stage, 1-pass' compiler.

Of course, the distinction applies whether pipes or intermediate files
are used between stages. The use of temp files to delay such things as
string constants is not really an issue here, since it has such a minor
impact on the compiler design (Strictly speaking, when a pcc reopens
this temp file in order to spool its contents onto the end of the .s
file, it is making a second pass, since it is 'backing up' and
re-examining information gleaned from the first 'pass').


-- 
----------------------------------------------------------------------
Greg Smith     University of Toronto      UUCP: ..utzoo!utcsri!greg
Have vAX, will hack...

tanner@ki4pv.UUCP (Tanner Andrews) (12/21/86)

While no doubt many people consider it good and fine to say something
like the following example fragment, I find that the assorted
compilers for our assorted machines all work just as well or better
if you omit the storage class on the forward reference.

That "extern" just isn't needed.  Why not leave it off.  Makes the
code more accurate (seeing that "blunge" really ISN'T extern).  Also,
no harm results if "blunge" moves to another module!  All in all,
better to just leave off the "extern" entirely!

 /*  start of example */
extern char *blunge();			/* declare blunge */
char *gork() { return(blunge(69)); }	/* use blunge */
static char *blunge(arg) int	arg; { /* body of blunge here */ }
 /*  end of example */
-- 
<std dsclm, copies upon request>	   Tanner Andrews