[net.lang.c] enum function bug?

tps@sdchem.UUCP (Tom Stockfisch) (09/11/86)

I want to pass a
	pointer to function returning "bool"
to a function, but lint says I'm not doing it right.  I think I am, aren't I?
Sample code follows with messages from lint inserted.  Is this a bug?


typedef enum { FALSE = 0, TRUE = 1 }	bool;

bool	truth();
void	f(), g();

main()
{
/*###8 [lint] f arg. 1 used inconsistently testbool.c(14) :: testbool.c(8)%%%*/
	f( truth );
}

void
f( ft )
	bool	(*ft)();
/*###14 [lint] f arg. 1 used inconsistently testbool.c(14) :: testbool.c(8)%%%*/
{
/*###15 [lint] g arg. 1 used inconsistently testbool.c(21) :: testbool.c(15)%%%*/
	g( ft );
}

void
g( gt )
/*###20 [lint] warning argument gt unused in function g%%%*/
	bool	(*gt)();
/*###21 [lint] g arg. 1 used inconsistently testbool.c(21) :: testbool.c(15)%%%*/
{
}

bool
truth()
{
	return	TRUE;
}
-- 

-- Tom Stockfisch, UCSD Chemistry

guy@sun.uucp (Guy Harris) (09/12/86)

> I want to pass a
> 	pointer to function returning "bool"
> to a function, but lint says I'm not doing it right.  I think I am,
> aren't I? ... Is this a bug?


Yes, and yes.  PCC converts "enum"s into "char"s, "short"s, or "int"s at a
fairly early stage in its processing.  Unfortunately, this means "lint" does
also.  As such, "pointer to function returning enum" gets converted to
"pointer to function returning char/short/int", which collides with the
formal argument of "f".  In this particular case, I tried it and it gave the
errors listed; I changed the function pointer declarations to "int (*xx)()"
and it passed.

The fix is not to convert "enum"s in that fashion in "lint", and fix pass 2
to check that enum formal arguments match the actual arguments used.

You can tell the parts of the language that were added later; the front end
of PCC doesn't really handle them right.
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)

karl@haddock (09/17/86)

sdchem!tps (Tom Stockfisch) writes:
>I want to pass a pointer to function returning [enum bool] to a function,
>but lint says I'm not doing it right.

Cc and lint disagree about what "enum" really means.  Lint calls it a new
datatype, but cc thinks it's just a synonym for "int" in some contexts.
Apparently the distinction is lost somewhere between lint pass 1 and pass 2.
Yes, it's a bug.

#if SOAPBOX
I think cc should also treat them as distinct types.  (The easy-going days
of "everything is an int" are over, folks.  Egad, now you have to *declare*
functions, even if you're just passing the result to another function!)
If you need an int context, you should cast the enum into an int.  (Though
switch(enum) should still work, provided all the case labels are enums.)  I
suppose an enum actually specifies an *ordered* set, so "++" and "--" are
acceptable, as well as "+" and "-", but they work as with pointers: enum+int
== enum, enum-enum == int.

This proposal would make your "enum bool" less useful, but "typedef int bool"
(which is what I always use) is just as good anyway.

The only addition I'd like to make is to allow one to declare an array to be
subscripted by an enum: "double foo[enum color]; foo[RED] = 1.0; ...".  Such
an array would *not* be subscriptable by int (though of course an integral
expression could be cast into enum color and then used as a subscript).  This
would often obviate the need for a constant NCOLORS, and make the code more
palatable to both cc and lint.  A smart compiler could make a special case
for sparse enums (enum color { RED=0, BLUE=100 }), as long as it's guaranteed
that each valid instance of the enum type may be used as a subscript.

The idea could be extended to allow any datatype, e.g. "int foo[char]" (which
would subscript from -128 to 127 on a pdp11 or vax), but this is less useful.
#endif /* SOAPBOX */

Karl W. Z. Heuer (ima!haddock!karl; karl@haddock.isc.com), The Walking Lint

rcd@nbires.UUCP (Dick Dunn) (09/18/86)

In article <86900054@haddock>, karl@haddock writes:
> Cc and lint disagree about what "enum" really means.  Lint calls it a new
> datatype, but cc thinks it's just a synonym for "int" in some contexts.
>...
> #if SOAPBOX
> I think cc should also treat them as distinct types.  (The easy-going days
> of "everything is an int" are over, folks...

The problem goes a little deeper than this.  Consider that if an enum is
defined in a .h file, and that header file is used in two separate .c
files, you have (by some annoyingly reasonable argument) two separate
types!  [Substitute canonical discussion of name equivalence vs structural
equivalence of types here.]

If lint were to make a name-equivalence type check, you couldn't use enums
across modules.  I suspect that it is not currently equipped to do the
structural-equivalence test, although it's not that hard to do since
there's no nesting or recursive structure in enums.  But even then,
explicit assignment of ordinals to enum elements, where the ordinals might
be constant expressions (yes, bleah, but read on...) adds more worms to the
can.  Consider something silly like:

----------foo.h:
typedef enum {a=XCONST, b, c} etype;

----------gleep.c
#define	YCONST	3
#define	XCONST	YCONST+YCONST
#include "foo.h"
etype xyzzy = b;

----------mumble.c
#define	YCONST	3
#define	XCONST	2*YCONST
#include "foo.h"
extern etype xyzzy;

Do the types of xyzzy in mumble.c and gleep.c match?  They are provably
both enums with the same type name and the same constituents with the same
values, but...

If you admit that name equivalence is inadequate (which I believe) but that
this example carries structural equivalence too far (which I also believe,
I think), then where do you draw the line?
-- 
Dick Dunn    {hao,ucbvax,allegra,seismo}!nbires!rcd	(303)444-5710 x3086
   ...Are you making this up as you go along?

notes@hcx1.UUCP (09/22/86)

On a somewhat related matter, I noticed the following:

AT&T 5.2.2 source seems to accept the following, but Berkeley 4.2 & 4.3beta
give a warning of inconsistent usage.

struct st { int c ; } ; 
int func (b);
   int b; 
{
}
main()
{
   struct st s;
   func (&s);
}

If 's' were a character array, however, AT&T would also complain.

Dave Ray   --  uucp:  {ucf-cs|allegra}!novavax!hrshcx!hcx1!daver

guido@mcvax.uucp (Guido van Rossum) (09/23/86)

In article <572@opus.nbires.UUCP> rcd@nbires.UUCP (Dick Dunn) writes:
>[example with enum {a= 3+3, b, c} in one file and enum {a= 2*3, b, c}
>in another]
>
>If you admit that name equivalence is inadequate (which I believe) but that
>this example carries structural equivalence too far (which I also believe,
>I think), then where do you draw the line?

In my eyes, in the example you give the two types are clearly
structurally equivalent.  After all, the two expressions have to be
constant expressions, which is a well-defined term (in C!).  I propose
the following rule for enum equivalence:

	Two enumerated types are equivalent if they have the same 'tag'
	(or no tags), and define the same set of enumeration constants
	with the same associated value for each constant.

(This will find even enum {a=2, b=1, c=0} and enum {c, b, a} equivalent!)

By the way, I agree with the proposed ANSI standard which defines that
enums are equivalent to ints!  This is the only suitable way to do it
*in C*, even though for any other language, to be designed newly, enums
should be distinct types.  I also agree with Lint checking enum
equivalence -- but there should be a way to turn it off, since I believe
that lint should not issue any errors (warnings are ok) for strictly
ANSI-conforming C programs.
-- 
	Guido van Rossum, CWI, Amsterdam <guido@mcvax.uucp>

colin@vu-vlsi.UUCP (Colin Kelley) (09/23/86)

In article <103@hcx1.UUCP> notes@hcx1.UUCP writes:
>
>struct st { int c ; } ; 
>int func (b);
>   int b; 
>{
>}
>main()
>{
>   struct st s;
>   func (&s);
>}
This is obviously wrong, since ints are not pointers!  But what I thought
you were going to try was

main()
{
	struct st s;
	func (s);
}

That is, call func() passing it the whole structure as if it were an int,
since we all _know_ the structure just consists of one int anyway, right!
PLEASE DON'T DO THIS!!!  Of course it won't get by lint, but quite a few
programmers wind up doing it because it works on so many machines.  Well,
it doesn't work at all on a Pyramid, where structures are passed differently
than ints.

Someone here got bitten by this attempting to port smp (a symbolic math
package) to our Pyramid.  The bozo programmer had declared a union containing
all possible pointers, but then declared the formal parameter just as a pointer
to an int.  "What", you say, "commercial code which doesn't even pass lint?"
That's what I said too!  Yuck...

			-Colin Kelley ..{cbmvax,pyrnj,psuvax1}!vu-vlsi!colin

gbm@galbp.UUCP (Gary McKenney) (09/25/86)

> On a somewhat related matter, I noticed the following:
> 
> AT&T 5.2.2 source seems to accept the following, but Berkeley 4.2 & 4.3beta
> give a warning of inconsistent usage.
> 
> struct st { int c ; } ; 
> int func (b);
>    int b; 
> {
> }
> main()
> {
>    struct st s;
>    func (&s);
> }
> 
> If 's' were a character array, however, AT&T would also complain.
> 
> Dave Ray   --  uucp:  {ucf-cs|allegra}!novavax!hrshcx!hcx1!daver

Why don't you try:

     func (&s.c);

I think this will be excepted to both and it still points to the beginning
of the structure.


gbm

gbm@galbp.UUCP (Gary McKenney) (09/25/86)

> > On a somewhat related matter, I noticed the following:
> > 
> > AT&T 5.2.2 source seems to accept the following, but Berkeley 4.2 & 4.3beta
> > give a warning of inconsistent usage.
> > 
> > struct st { int c ; } ; 
> > int func (b);
> >    int b; 
> > {
> > }
> > main()
> > {
> >    struct st s;
> >    func (&s);
> > }
> > 
> > If 's' were a character array, however, AT&T would also complain.
> > 
> > Dave Ray   --  uucp:  {ucf-cs|allegra}!novavax!hrshcx!hcx1!daver
> 
> Why don't you try:
> 
>      func (&s.c);
> 
> I think this will be excepted to both and it still points to the beginning
> of the structure.
> 
> 
> gbm

One additional comment (and error on my part).  Your function is suppose
to receive an integer, not a pointer to a structure.

if you intend to receive an integer in func() then you should code...

	struct st { int c ; } ; 
	int func (b);
	   int b; 
	{
	}
	main()
	{
	   struct st s;
	   func (s.c);
	}

else if you intend to pass a pointer to the structure then you should code...


	struct st { int c ; } ; 
	int func (b);
	    char *b; 
	{
	}
	main()
	{
	   struct st s;
	   func (&s.c);
	}

stuart@BMS-AT.UUCP (Stuart D. Gathman) (09/29/86)

In article <572@opus.nbires.UUCP>, rcd@nbires.UUCP (Dick Dunn) writes:

> The problem goes a little deeper than this.  Consider that if an enum is
> defined in a .h file, and that header file is used in two separate .c
> files, you have (by some annoyingly reasonable argument) two separate
> types!  [Substitute canonical discussion of name equivalence vs structural

Did you ever wonder how lint handles _structures_ in .h files?  It's really
very simple: anything defined in the same file (even included files)
with the same name is the same.  The same logic works for enum.  

Note that identical structures defined in different files (i.e.
a function returning a structure that is not defined with a .h file)
are considered different types by lint.  I like this because it
encourages defining global structures in only one place (a header file).

Lint keeps track of which file a definition is in with (I believe)
the #line output of cpp.

We have lint in Xenix and CTIX, two very different machines, and the
above is true for both.

I agree: cc should treat enum like structures.  I especially wish that
enum value names did not conflict with variable names.  When using
enum for switch cases or with enum variables, this use is obvious.
Assigning enum values to int's would give an error.  Is there ever
a legitimate reason for doing so?  (Why aren't you using an enum
variable?)  Note that you can still assign (or cast) an enum
variable to an int.
-- 
Stuart D. Gathman	<..!seismo!{vrdxhq|dgis}!BMS-AT!stuart>

gnu@hoptoad.uucp (John Gilmore) (09/29/86)

I think the definitive word on this has been said already:

Date:  8 Nov 1982 0216-PST (Monday)
From: decwrl!decvax!harpo!npoiv!alice!research!dmr
Subject: enums
Newsgroups: net.lang.c

There has been a lot of grousing about the uselessness of the enum type
in C, most of it justified under the circumstances.  The circumstances
are that all versions of PCC that I know of are buggy in their treatment
of this type.

Enums were intended to be entirely equivalent to ints; just a way, really,
of defining names for constants understood by the compiler and subject
to the normal scope rules.

There was a clear choice here: enums as utterly separate collections of atoms,
more or less as in Pascal, or as ways of naming integers.  I chose the
latter after some waffling.  Unfortunately, some of the waffle batter
got mixed in with PCC and has stayed there.

		Dennis Ritchie
-- 
John Gilmore  {sun,ptsfa,lll-crg,ihnp4}!hoptoad!gnu   jgilmore@lll-crg.arpa
		     May the Source be with you!

chris@umcp-cs.UUCP (Chris Torek) (10/01/86)

>In article <572@opus.nbires.UUCP> rcd@nbires.UUCP (Dick Dunn) writes:
>>The problem goes a little deeper than this.  Consider that if an enum is
>>defined in a .h file, and that header file is used in two separate .c
>>files, you have (by some annoyingly reasonable argument) two separate
>>types!  [Substitute canonical discussion of name equivalence vs structural

I do not agree, but I have always been a fan of structural equivalence.
Anyway, onward:

In article <226@BMS-AT.UUCP> stuart@BMS-AT.UUCP (Stuart D. Gathman) replies:
>Did you ever wonder how lint handles _structures_ in .h files?  It's really
>very simple: anything defined in the same file (even included files)
>with the same name is the same.  The same logic works for enum.  

An interesting idea.

>Note that identical structures defined in different files (i.e.
>a function returning a structure that is not defined with a .h file)
>are considered different types by lint.  I like this because it
>encourages defining global structures in only one place (a header file).

Let us try an experiment.  First:

	% more x.c y.c
	::::::::::::::
	x.c
	::::::::::::::
	struct s { int i; };
	proc(p) struct s *p; { p->i = 1; }
	struct s func() { struct s temp; temp.i = 0; return (temp); }
	::::::::::::::
	y.c
	::::::::::::::
	struct s { int i; };
	struct s func();
	main() { struct s temp; temp = func(); proc(&temp); }
	% lint -h x.c y.c
	x.c:
	y.c:
	%

Here the structures are described twice, but are identical in name
and shape.  Lint is happy with this.  Now:

	% more x.c y.c
	::::::::::::::
	x.c
	::::::::::::::
	struct s2 { int i; };
	proc(p) struct s2 *p; { p->i = 1; }
	struct s2 func() { struct s2 temp; temp.i = 0; return (temp); }
	::::::::::::::
	y.c
	::::::::::::::
	struct s { int i; };
	struct s func();
	main() { struct s temp; temp = func(); proc(&temp); }
	% lint -h x.c y.c
	x.c:
	y.c:
	%

Clearly lint does not care about names.  And one last change:

	% more x.c y.c
	% lint -h x.c y.c
	::::::::::::::
	x.c
	::::::::::::::
	struct s2 { int i, j; };
	proc(p) struct s2 *p; { p->i = p->j = 1; }
	struct s2 func() { struct s2 temp; temp.i = temp.j = 0; return (temp); }
	::::::::::::::
	y.c
	::::::::::::::
	struct s { int i; };
	struct s func();
	main() { struct s temp; temp = func(); proc(&temp); }
	x.c:
	y.c:
	func value used inconsistently	x.c(3)  ::  y.c(3)
	proc, arg. 1 used inconsistently	x.c(2)  ::  y.c(3)
	%

My (4.3BSD-beta) lint clearly uses structural conformability, not
names or files.  (As it happens, I prefer this behaviour.)

Ideally, a new strongly-typed language (which would not then be C)
should declare constraints on types, instead of having the compiler
second guessing the programmer.  For example, some enumerations
might be ordered, others not:

	enum color { red ; green ; blue };
		/* pred(green) undefined */
	enum numbers { zero, one, two, many };
		/* succ(two) = many */

If several operations are sensible, make sure all can be implemented
---though not necessarily by embedding them directly in the language.

For now, C enumerated types are rather a mess, and I usually avoid
them entirely.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

stuart@BMS-AT.UUCP (Stuart D. Gathman) (10/04/86)

In article <3635@umcp-cs.UUCP>, chris@umcp-cs.UUCP (Chris Torek) writes:

> 	x.c
> 	::::::::::::::
> 	struct s { int i; };
> 	proc(p) struct s *p; { p->i = 1; }
> 	struct s func() { struct s temp; temp.i = 0; return (temp); }

> 	y.c
> 	::::::::::::::
> 	struct s { int i; };
> 	struct s func();
> 	main() { struct s temp; temp = func(); proc(&temp); }

> 	% lint -h x.c y.c
> 	x.c:
> 	y.c:

Hmmmmm.... On trying this I see that only one of our two versions of lint
uses the algorithm I described (the version with CTIX for the 68010).  
I still think that this example should
generate an error.  It is dangerous to have things defined in more than
one place.  The algorithm I described is safe.  You can't define structures
with the same name twice (in the same scope).  Lint doesn't need to look
at the actual structure definition, and can still flag code that should
be flagged (like the above example).
-- 
Stuart D. Gathman	<..!seismo!{vrdxhq|dgis}!BMS-AT!stuart>