[comp.unix.aix] C compiler incompatibility

mcuddy@rutabaga.Rational.COM (Mike Cuddy) (12/29/90)

I'm writing macros to deal with linked lists. (circular) and I'm having a spot
of trouble with xlc ( this works fine on a SUN!):


------- cut here -------
typedef struct cll_f {
    struct list *next;
    struct list *prev;
} cll_t;

struct foo {
    cll_t  list;

    int a, b, c;
    /* ... more stuff ... */
};

#define CLL_FOREACH(head,p) \
	for((cll_t *)(p) = (head)->list.next ; \
	    (p) != (head) ; (cll_t *)(p) = (p)->list.next)


void traverse(list)
struct foo *list;
{
    struct foo *p;
    CLL_FOREACH(list,p) {

    /* ... output data ... */

    }
}

------ cut here -------
% cc foo.c
       23 |     for((cll_t *)(p) = (list)->list.next ; (p) != (list) ; (cll_t 
            ........a..................................................b......
a - 1506-025: (S) Operand must be a modifiable lvalue.
b - 1506-025: (S) Operand must be a modifiable lvalue.
            *)(p) = (p)->list.next) {

------ extracted from my xlc.cfg: -------

* standard c compiler aliased as cc
cc:     use        = DEFLT
        crt        = /lib/crt0.o
        mcrt       = /lib/mcrt0.o
        gcrt       = /lib/gcrt0.o
        libraries  = -lc
        proflibs   = -L/lib/profiled,-L/usr/lib/profiled
        options    = -H512,-T512, -qlanglvl=extended, -qnoro


* common definitions
DEFLT:  xlc        = /usr/lpp/xlc/bin/xlcentry
        as         = /bin/as
        ld         = /bin/ld
        options    = -D_IBMR2,-D_AIX,-bhalt:4
        ldopt      = "b:o:e:u:R:H:Y:Z:L:T:A:V:k:j:"

--Mike Cuddy
"...He's a UNIX hack and he's okay, he works all night and he sleeps all day..."

richard@locus.com (Richard M. Mathews) (12/29/90)

mcuddy@rutabaga.Rational.COM (Mike Cuddy) writes:
>I'm writing macros to deal with linked lists. (circular) and I'm having a spot
>of trouble with xlc ( this works fine on a SUN!):

>#define CLL_FOREACH(head,p) \
>	for((cll_t *)(p) = (head)->list.next ; \
>	    (p) != (head) ; (cll_t *)(p) = (p)->list.next)

Your expression is invalid.  Sun is allowing an illegal construct to go
by.  Many old compilers (including PCC) silently discarded these illegal
casts under a variety of circumstances.

ANSI says [all emphasis mine]:
	3.3.16
		An assignment operator stores a value in the OBJECT
		designated by the left operand.
	3.3.2.1
		An LVALUE is an expression ... that designates an
		OBJECT.
	3.3.1
		An identifier is a primary expression, provided it has
		been declared as designating an OBJECT (in which case it
		is an LVALUE) or a function....
	3.3.2.3
		A postfix expression followed by a dot ... is an LVALUE
		if the first expression is an lvalue.
		A postfix expression followed by an arrow ... is an LVALUE.
	3.3.3.2
		If [the unary * operator] points to an object, the result
		is an LVALUE designating the object.
	3.3.2.1
		The definition of the subscript operator [] is that E1[E2]
		is identical to (*(E1+(E2)))  [and thus I conclude that it
		is an LVALUE].

There is no comment in section 3.3.4, Cast Operators, which defines the
result of such an expression to ever be an lvalue.  Footnotes are not
officially part of the standard (according to section 1.4), but there
is an explicit statement in footnote 44 in section 3.3.4:
	44. A cast does not yield an lvalue.

A similar reading of K&R comes to the same conclusion.  The left side
of an assignment must be an lvalue, and cast operators do not create
lvalues.

Why should this be true?  Try creating a meaning for the following:
Assume that int and double are different size objects.  Then do
	int i;
	(double) i = 0;
Does this mean that the bytes past those of the object 'i' should be
modified by this expression?  Does it mean that some subset of the bytes
in (double)0 should be stored in the bytes of the object 'i'?  Which ones?
If you want to do something disgusting like this, you are expected to
use a disgusting construct such as:
	*(double *)&i = 0;
or
	double d = 0;
	i = *(int *)&d;
I know that PCC under BSD4.3 complains about "(double) i = 0" but not
about "(long) i = 0".  This inconsistency is not sanctioned by K&R or
ANSI.

In your case, I'm not sure how I would approach this.  The simplest hack
is to put (void *) on the right side of the assignments.  Much cleaner
would be to avoid all this type punning completely.  Just make sure that
each structure which will use CLL_FOREACH has a "next" and "prev" field
in it, and those fields point to structures of the correct type rather
than "struct list".

Richard M. Mathews			 Freedom for Lithuania
richard@locus.com				Laisve!
lcc!richard@seas.ucla.edu
...!{uunet|ucla-se|turnkey}!lcc!richard