Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"error: ‘_Generic’ specifies two compatible types" but not on some compilers

I have a (complex) code that works on IAR compiler. I took part of that code to make a snippet to improve it. But while moving a part of this code on an online compiler, it would not compile.

The "error: ‘_Generic’ specifies two compatible types"

Because I was using _Generic as this example:

#define  GET_TYPE_(data) _Generic((&data)+0, \
           _Bool * : TYPE_BOOL_, \
           uint8_t* : TYPE_U8_, \
           uint16_t* : TYPE_U16_, \
           uint32_t* : TYPE_U32_, \
           someEnum1_e* : TYPE_BOOL_, \
           someEnum2_e* : TYPE_SPECIAL_A, \
           someEnum3_e* : TYPE_SPECIAL_B, \
           default: TYPE_DEFAULT_ )

With TYPE_XXX being a #define, then I can call

switch(GET_TYPE_(myVariable))
{ 
case TYPE_XXXX: do_something(); break;
...
}

But when I moved it with on the online compiler (online gdb) with gcc, i got the error:

error: ‘_Generic’ specifies two compatible types
   someEnum1_e* : TYPE_BOOL_, \
error: ‘_Generic’ specifies two compatible types
   someEnum2_e* : TYPE_SPECIAL_A, \
error: ‘_Generic’ specifies two compatible types
   someEnum3_e* : TYPE_SPECIAL_B, \

I read that this was a normal behavior and that it should not compile. However, how is this possible to compile on IAR?

(yes, i'm asking you to believe me without a reproducible working example because I won't provide my propretary code)


Edit: On godbolt:

  • x86-64 clang 10 ,: the code works only in c++! and in C produces a similar error:
    • "type 'someEnum1_e*' in generic association compatible with previously specified type 'uint32_t *' (aka 'unsigned int *')"
  • ARM64 GCC 8.2, flag -std=c11: the code produces the error
  • x64 GCC 9.3, flag -std=c11: the code produces the error
  • AVR GCC 5.4, flag -std=c11: the code does not recognize _Generic, nor _Bool:
    • "error: '_Generic' was not declared in this scope"
  • MSP430 GCC 6.2.1, flag -std=c11: the code works! (but not in C++)
  • and on my computer with IAR 8.20.2, flag -std=c11: the code works!

1st bonus question: Could it be a compiler bug? What does the standard says?

2nd bonus question: Is there a way to avoid the error? (for all compilers)

A short snippet to test my sayings

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>

typedef enum
{
    ENUM1_A,
    ENUM1_B,
}someEnum1_e;

#define TYPE_E1_ 0
#define TYPE_U32_ 1

#define  GET_TYPE_(data) _Generic((&data)+0, \
           uint32_t* : TYPE_U32_, \
           someEnum1_e* : TYPE_E1_, \
           default: TYPE_U32_ )

int main (void)
{
    uint32_t foo;
    someEnum1_e bar;
    switch(GET_TYPE_(foo)){default:break;};
    switch(GET_TYPE_(bar)){default:break;};
}
like image 861
Guillaume D Avatar asked Jun 28 '26 20:06

Guillaume D


1 Answers

I found a way to compile and run with all compiler in C11 (that accepts the _Generic keyword):

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>

typedef enum
{
    ENUM1_A=0,
    ENUM1_B,
}someEnum1_e;


typedef union
{
    someEnum1_e value;
}someEnum1_e_t;


void print_uint32_t(uint32_t val)
{
    printf("uint32_t val = %u\n",val);
}

void print_someEnum1_e_t(someEnum1_e_t val)
{
    printf("someEnum1_e_t val = %u\n", val.value);
}

#define  PRINT_(data) _Generic((&data)+0, \
           uint32_t* : print_uint32_t((uint32_t)*((uint32_t*)&data)), \
           someEnum1_e_t* : print_someEnum1_e_t((someEnum1_e_t)*((someEnum1_e_t*)&data)), \
           default: printf("hello\n"))

int main (void)
{
    uint32_t       foo = 32;
    someEnum1_e_t  blah = {ENUM1_A};


    PRINT_(foo);
    PRINT_(blah);
}

For this solution to be working, I don't need the macro trick given by Lundin.

like image 76
Guillaume D Avatar answered Jul 01 '26 15:07

Guillaume D