0

Consider the following test code:

#include <tgmath.h>

void test()
{
    double x=cos(4.5);
}

Compiling it as with

arm-none-eabi-gcc test.c -c

on Ubuntu 18.04 (gcc 6.3.1, newlib 2.4.0) works fine, but on Ubuntu 20.04 (gcc 9.2.1, newlib 3.3.0) I get the following errors:

In file included from test.c:1:
test.c: In function 'test':
test.c:5:14: error: 'ccosl' undeclared (first use in this function); did you mean 'ccosh'?
    5 |     double x=cos(4.5);
      |              ^~~
test.c:5:14: note: each undeclared identifier is reported only once for each function it appears in
test.c:5:14: error: argument 6 of '__builtin_tgmath' is not a function pointer

Apparently, the definition of cos has somehow changed, so that it now mentions ccosl which is not declared anywhere.

If I change from tgmath.h to math.h, the error no longer appears. This is of course just a workaround, not a fix, since this way I lose the type genericity for float vs double.

My question is: how do I make it work properly? Do I have to add some compilation option, or is it just a bug in the toolchain?

Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • The [`` header file](https://en.cppreference.com/w/c/numeric/tgmath) redefines many common [``](https://en.cppreference.com/w/c/numeric/math) functions. – Some programmer dude Aug 28 '20 at 12:01
  • @Someprogrammerdude yes, that's the point of using it – Ruslan Aug 28 '20 at 12:06
  • [C11 7.25p1](http://port70.net/~nsz/c/c11/n1570.html#7.25) "The header `` includes the headers `` and `` and defines several type-generic macros." – pmg Aug 28 '20 at 12:10
  • @pmg so that means... what? – Ruslan Aug 28 '20 at 12:12
  • Just a link to the Standard, no other meaning. – pmg Aug 28 '20 at 12:14
  • From your question: "Apparently, the definition of `cos` has somehow changed, so that it now mentions `ccosl`" From that it seems you were confused as to why `cos` is redefined as `ccosl`, but that's the whole point of using the `` header file (as you mentioned), that it redefines common math functions. – Some programmer dude Aug 28 '20 at 12:22
  • @Someprogrammerdude well, I realize that it should have mentioned `ccosl` all the time (ideally), but it might not have done it in the older version due to possibly unimplemented `long double` versions of functions or whatever. – Ruslan Aug 28 '20 at 12:26
  • Does it work if you declare `long double complex ccosl(long double complex);` in your "test.c" and can you then link it with `-lm`? – Ian Abbott Aug 28 '20 at 14:50
  • 1
    @IanAbbott yes, it compiles and links. The resulting ELF doesn't contain `ccosl` symbol, of course, since this function doesn't get called. – Ruslan Aug 28 '20 at 15:00

1 Answers1

0

It appears that the difference between the versions of the toolchains is in the GCC implementation of tgmath cos macro in different versions. Namely, compiling with -E option for gcc yields the following (cleaned up) expansion of double x=cos(4.5) in 6.3.1:

double x=__builtin_choose_expr(__builtin_classify_type(4.5) == 9,
                               __builtin_choose_expr(__builtin_types_compatible_p(__typeof__(__real__(4.5)), long double),
                                                     ccosl(4.5),
                                                     __builtin_choose_expr(__builtin_types_compatible_p(__typeof__(__real__(4.5)), double) || __builtin_classify_type(__real__(4.5)) == 1,
                                                                           ccos(4.5),
                                                                           ccosf(4.5))
                                                    ),
                               __builtin_choose_expr(__builtin_types_compatible_p(__typeof__(4.5), long double),
                                                     cosl(4.5),
                                                     __builtin_choose_expr(__builtin_types_compatible_p(__typeof__(4.5), double) || __builtin_classify_type(4.5) == 1,
                                                                           cos(4.5),
                                                                           cosf(4.5))
                                                    )
                              );

while in GCC 9.3.0 the expansion is as a simple function call:

double x=__builtin_tgmath (cosf, cos, cosl, ccosf, ccos, ccosl, 4.5);

The main difference between the two is that __builtin_choose_expr doesn't evaluate the expression that is not chosen (as said in the docs), while __builtin_tgmath is a function, which needs all the arguments to be valid. And it looks like newlib has never had ccosl in its complex.h, so it appears incompatible with the newer version of GCC.

Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • The same sort of failure occurs on Debian "testing" with arm-none-eabi-gcc 8.3.1 and newlib 3.3.0. I wonder if it is including the wrong "tgmath.h" because there is one in "/usr/include/newlib/" and one in "/usr/lib/gcc/arm-none-eabi/8.3.1/include/t" and they are quite different, but "complex.h" and "math.h" only appear in "/usr/include/newlib/". – Ian Abbott Aug 28 '20 at 14:18
  • Re: my above comment:- Experimentally renaming "/usr/lib/gcc/arm-none-eabi/8.3.1/include/tgmath.h" to something else resulted in a different error during compilation: "error: first argument to '__builtin_choose_expr' not a constant". – Ian Abbott Aug 28 '20 at 14:24
  • All the newlib "libm.a" files for the different ARM flavours do seem to have a `ccosl` function, so the problem seems to be the header files. "/usr/include/newlib/complex.h" conditionally declares the `long double complex` functions only if `__CYGWIN__` is defined. – Ian Abbott Aug 28 '20 at 14:48