Variadic builtin strangeness (and an odd vfprintf!)

Hello everyone,

I'm trying to sort out --
but struggling.

For the particular case of __builtin_isnan (the C99 math macros in
this section are strange too), it is defined as,

BUILTIN(__builtin_isnan, "i.", "nc")

That's how it should look. Unfortunately, ASTContext::GetBuiltinType
treats an argument list with "..." as its sole member specially,

  if (ArgTypes.empty() && Variadic) return
    getFunctionNoProtoType(ResType, EI);

Which essentially generates a declaration like,

int __builtin_isnan();

That works for double and long double (but not float (default
promotions...)) in C, and in C++
it's a very strange declaration for this builtin!

Once you fix this and make Clang generate the correct (magic)
prototype of

int __builtin_isnan(...);

which Sema::SemaBuiltinFPClassification expects it to take, the bug in
PR20958 goes away, we can declare,

int __builtin_isnan(T);

for T in (double, long double, float).

Unfortunately, we get some interesting test errors:


error: cannot pass object of non-POD type 'A' through variadic
function; call will abort at runtime
  return __noop(A());

The documentation for __noop says that the argument list should be
parsed but no code should be generated. This is where my knowledge of
cfe breaks down -- it seems like the implementation of this intrinsic
as as a variadic builtin is wrong, but I would appreciate pointers on
how to fix that, or indeed if I'm barking up the wrong tree.

The other test errors uncovered a strange implementation of vfprintf,

LIBBUILTIN(vfprintf, "i.", "fP:1:", "stdio.h", ALL_LANGUAGES)

That should of course be,

LIBBUILTIN(vfprintf, "iP*cC*a", "fP:0:", "stdio.h", ALL_LANGUAGES)

but the reason for doing it this way is because of autoconf weirdness,

I'm not sure about the rationale for the above commit. Can anyone
with context help me understand it?

I note that in GCC's own builtins.def file (you'd think they would
get autoconf right!), the type of vfprintf is int ()(FILE*, const
char *, va_list) as I expect. Maybe GCC has workarounds elsewhere.

Clues? Thanks for reading!

- Charlie.

I have a patch attached for this issue that allows you do declare

Apologies for the previous message, I hit send by accident.

I have a patch attached now that allows you to declare,

int __builtin_isnan(T);

for T in (double, long double, float).

In C++ mode, but not in C. Where as before this patch you could declare

int __builtin_isnan(T);

for T in (double, long double), but not for T=float in C before, now
you can declare any of these prototypes.

So I suppose I have just moved the problem from C++ to C. I have
cleaned up the strange behavior of builtins taking only "..." to be
declared as taking "()".

The root problem here is that there does not appear to be a way to
declare type generic builtins that work across C and C++.

The most rigorous way to define the __builtin_isnan function would be
to add the "t" attribute in Builtins.def and then write a custom
argument checking function in SemaChecking that makes sure the
arguments are "real-floating", as defined in C99.

That seems pretty unpleasant, so before I attempt such work, I would
like to ask for advice as to how best to fix this problem.

Thank you for your time,

0001-PR20958-Allow-__builtin_isnan-to-be-declared.patch (4.46 KB)