Crystal: Crystal forgets to promote numbers when passing to `...` va_args

Created on 11 Sep 2020  ·  7Comments  ·  Source: crystal-lang/crystal

As I found out, when passing through ..., a C compiler will promote incoming args - integers to be no less than int size and floats to be no less than double size. Crystal apparently fails to do that. See here: a basic function accepting varargs and printing the first one as float. When called from C, the float gets promoted to double. But Crystal doesn't promote and the result ends up being zero.

{% `echo '#include <stdio.h>
          #include <stdarg.h>
          void f(const char* fmt, ...) { 
            va_list args;
            va_start(args, fmt);
            printf("!!! %f\\n", va_arg(args, double));
            va_end(args);
          }
          void g() {
            f("unused", 1.23f);
          }
  ' > foo.c &&
    gcc -c foo.c -o /tmp/foo.o` %}

@[Link(ldflags: "/tmp/foo.o")]
lib Lib
  fun f(fmt : LibC::Char*, ...)
  fun g()
end

Lib.f("unused", 1.23f32)
Lib.g()

Output:

!!! 0.000000
!!! 1.230000

Note that it's not a mistake in my example that I read the arg as double.
Output if doing otherwise:

In file included from foo.c:2:
foo.c: In function 'f':
foo.c:6:45: warning: 'float' is promoted to 'double' when passed through '...'
    6 |             printf("!!! %f\n", va_arg(args, float));
      |                                             ^
foo.c:6:45: note: (so you should pass 'double' not 'float' to 'va_arg')
foo.c:6:45: note: if this code is reached, the program will abort
Program received and didn't handle signal ILL (4)
  • that is, there's just no such thing as passing a float through va_args, you have to pass it as a double or otherwise.

All 7 comments

Well, you can't forget something you never knew 😁

Since this observation came to be observed experimentally, I wonder if this is somewhere defined in the ANSI C standard or otherwise? In particular I wonder if the observations made are complete, or there's more things to handle, and whether they're platform or architecture specific or not.

Yes this is part of the standard.

https://stackoverflow.com/questions/1255775/default-argument-promotions-in-c-function-calls

https://unspecified.wordpress.com/2009/08/31/default-argument-types-in-c/

http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

If the expression that denotes the called function has a type that does
not include a prototype, the integer promotions are performed on each
argument, and arguments that have type float are promoted to double.
These are called the default argument promotions.


— An object or expression with an integer type whose integer conversion
rank is less
than or equal to the rank of int and unsigned int.

— A bit-field of type _Bool, int, signed int, or unsigned int.
If an int can represent all values of the original type, the value is
converted to an int;
otherwise, it is converted to an unsigned int. These are called the
integer
promotions.
All other types are unchanged by the integer promotions.

@asterite I'd suggest adding above links as comments in the code.

The code is already linked to these issue. From the issue you can go to the details.

@oprypin Great, thank you for digging that up!

@asterite It's not obvious when you're reading just the code, but hey, _no pasa nada_ :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lbguilherme picture lbguilherme  ·  3Comments

Papierkorb picture Papierkorb  ·  3Comments

TechMagister picture TechMagister  ·  3Comments

RX14 picture RX14  ·  3Comments

pbrusco picture pbrusco  ·  3Comments