1998 vintage C code now fails to compile under gcc
Asked Answered
P

2

6

I have ~16k lines of 1998 vintage C code (~50 main progs) which built flawlessly under gcc at that time but now fails with many "lvalue required as left operand of assignment" errors in the first routine, "stutter.c". I'm not enough of a C programmer to find the problem and can't seem to search out an answer on the internet specific to my problem (with this rather generic error message).

Here are the specifics:

Compile line from the (vintage) Makefile:

gcc -O3 -Wall -D__dest_os=unix -I/usr/X11/include -DPLOTX11 -c -o stutter.o ../src/stutter.c

Failing statement examples:

    cell_car(cell) = free_list;
    cell_type(cell) = type;
    cell_name(atom) = strdup(name);
    cell_cdr(list) = binding_list;
    cell_cdr(cell_car(current)) = b;

... (and many similar)

preceded by:

    typedef enum CELL_ENUM {
      CELL_LAMBDA, CELL_SFUNC, CELL_VFUNC, CELL_LIST, CELL_ATOM
    } CELL_TYPE;

    typedef struct CELL_STRUCT {
    void *car, *cdr;
    unsigned type : 7;
    unsigned mark : 1;
    char empty[3];
    } CELL;

and:

    #define cell_car(c)      ((CELL *)(c)->car)
    #define cell_cdr(c)      ((CELL *)(c)->cdr)        
    #define cell_name(c)     ((char *)(c)->car)
    #define cell_func(c)     ((CELL *(*)())(c)->car)
    #define cell_type(c)     ((CELL_TYPE)(c)->type)
    #define cell_mark(c)     ((c)->mark)

More code particulars available if needed. Is there some obvious deprecated feature here which explains this error??

I have many years of experience as a scientific Fortran programmer but retired before learning enough of C to completely make the switch. Haven't found anything helpful about legacy code at http://gcc.gnu.org/bugs/. I'd appreciate any help that would save me having to complete my C, C++ and gcc education before I get these routines working under linux for my current project. Thanks much!

Phosgene answered 14/4, 2014 at 20:9 Comment(6)
@dyp, hard to tell without knowing the type of free_list... @sambledsoe, can you try to compile with clang instead of gcc in case you would get more informative error messages?Pelargonium
Was it in the past legal in C to say (some_variable) = 123? Because that's what you're doing here.Ordovician
@EricLippert it's still legal...Pelargonium
@Maxime: I learn something new every day. :-)Ordovician
@Maxime A cast never yields an lvalue (in C). So whatever the type of free_list is, the macro can't be on the lhs of an assignment.Labannah
car (contents of the address register) and cdr (contents of the data register). Hmmm - I smell Lisp.Heim
N
13

gcc is no longer allowing you to assign to a cast.

i.e.

((CELL *)(cell)->car) = free_list;

is no longer legal. Instead of casting the lhs to match the rhs, it would rather you cast the rhs to match the lhs. One way around this is to take the address of the lvalue, cast it as a pointer, and then dereference that pointer, so the assignment is to a pointer dereference instead of a cast.

i.e.

*((CELL **)&(cell)->car) = free_list;

This can be handled by updating the macros, so it should be quite painless...

i.e.

#define cell_car(c)      (*((CELL **)&(c)->car))

etc...

This macro can then be used as either an lvalue or an rvalue.

Newcastle answered 14/4, 2014 at 20:18 Comment(8)
clang would raise the following error: "error: assignment to cast is illegal, lvalue casts are not supported".Pelargonium
@Maxime, yes, that error message is more informative than gcc's, "lvalue required as left operand of assignment".Newcastle
I think it's this extension The documentation is from 2001, though (didn't find an older one).Labannah
Lvalue casts appear to be a GCC extension that is now deprecatedNewcastle
Yes, I think they've deprecated them around 3.3.6 (2004), they don't appear in the 4.X docs.Labannah
The "cast-as-lvalue" C language extension (deprecated in 3.3.4) was removed in GCC 4.0 (2005): gcc.gnu.org/gcc-4.0/changes.htmlCyaneous
@pat, thanks (and all the others). I understand the problem now and your mods seemed to work for the -car, -cdr and -name macros. I more or less guessed (the multiple pointer, address-of and de-references have me totally befuddled) at the -type and -func re-defines: ((CELL ()())(c)->car) became (((CELL *(*)())&(c)->car)) and ((CELL_TYPE)(c)->type) became (*((CELL_TYPE *)(c)->type)) ?? --stutter compiles with only "cast to pointer from integer of different size" warnings (many) but gives seg-fault on execution. --lots of work to do!Phosgene
That style of pointer cast unfortunately tends to cause Undefined Behavior under C99 and later versions unless one can disable the Strict Aliasing Rule.Spokane
S
0

In addition to changing the code to take an address, cast the pointer, and dereference, it will likely also be necessary to employ the "no_strict_aliasing" option. C99 added some rules, with the intention of facilitating optimization, which negate C's model of the system state as being a bunch of unsigned char[]. The most infamous of these is the Strict Aliasing Rule. While code which casts pointers as a means of saying "interpret the thing pointed to by this pointer as a X" may compile, writing to a pointer using one type and reading using another will generally yield Undefined Behavior, and some compilers will interpret that as license to behave in rather strange and bizarre ways unconstrained by laws of time and causality.

Spokane answered 30/7, 2015 at 19:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.