Search code examples
cz80

Operands are not assignment compatible


I have the following code:

struct something {
  char *(*choices)[2];
};
char* arr[2] = {"foo", "bar"};

int main(void) {
  struct something obj;
  obj.choices = &arr;
  return 0;
}

When I compile this using a normal C compiler (gcc), I get no errors. However, I am compiling for the Z80, and it raises a ERROR (152) Operands are not assignment compatible, which is described as:

An attempt was made to assign a value whose type cannot be promoted to the type of the destination.

I fail to understand how the types of &arr and char *(*choices)[2] could differ. What can I do to fix this ?

(I'm using the Zilog z80 compiler, which is part of ZDS 5.2.0)


Solution

  • Zilog support has claimed that it is in fact not a compiler bug, and that the original code does not compile as it is not strictly ANSI C. It is accepted by GCC because the compiler is "lenient" and has added a few more syntax rules that go beyond ANSI C specification. Here is the full response:

    The GCC compiler, while very good, is not necessarily a perfect implementation of the C Standard. Over the years I have seen a few cases where widely used compilers, like MSVC++ and, less frequently, GCC accept syntax that is not strictly ANSI C when that syntax seems to be a harmless quasi-extension of standard C and there’s no danger of its being interpreted in some alternate, legitimate meaning. This may be another instance of that.

    There’s a fine point of C syntax involved and here is my understanding of that point, along with why perhaps GCC allows the customer’s original syntax. Once a function pointer, e.g. a properly defined variable fnPtr, has acquired a definition, it is allowed to invoke it without the preceding * indirection operator through an expression like

    result = fnPtr(x); // This is legal syntax…

    result = (*fnPtr) (x); // … even though this is “more correct”

    The reason why the 1st syntax shown above is allowed is that the parentheses enclosing parameter x are regarded as a C operator whose type is “pointer to function”. So the presence of those parentheses makes the indirection operator unnecessary when the function pointer is actually used for making a function call. However, in a case like this customer code where you are just using the function pointer in an assignment statement, this doesn’t come into play and so those operands are not, in fact, strictly assignment compatible. However, a user who is not an expert language wrangler can hardly be blamed for expecting that if the function pointer can be used without the * in one place, it should also be acceptable in other contexts. This might be why the GCC developers have apparently decided to accept the user’s syntax.

    Here is an alternate version that compiles:

    struct something {
      char *(*choices[2]);
    };
    
    char* arr[2] = {"foo", "bar"};
    
    int main(void) {
      struct something obj;
      *obj.choices = &arr;
      return 0;
    }