Proposed change to __builtin_overload

I've been pondering on tgmath.h, but it is more evil than I thought. As such, I'd like to extend __builtin_overload like this:
http://clang.llvm.org/docs/LanguageExtensions.html#__builtin_overload

The basic problem is that tgmath doesn't follow standard arithmetic promotion rules. For example fmax(int, float) is supposed to call the double version of fmax, not the float version as normal promotion rules would specify.

Because I couldn't find a non-evil approach to handling this (e.g. with builtin_classify_type like GCC does) I just decided to allow a fixed set of programmable promotion rules. This way, we can support any craziness in altivec.h, support the standard promotion rules, and support tgmath.h all in one framework.

I noticed this in altivec.h, which I found pretty amusing/scary:

/* Predicates.
    For C++, we use templates in order to allow non-parenthesized arguments.
    For C, instead, we use macros since non-parenthesized arguments were
    not allowed even in older GCC implementation of AltiVec. */

/* Be very liberal in the pairs we accept. Mistakes such as passing
    a `vector char' and `vector short' will be caught by the middle-end,
    while any attempt to detect them here would produce hard to understand
    error messages involving the implementation details of AltiVec. */

I think the first argument to __builtin_overload will completely fix the ugly hack they are using here (rejecting bugs in codegen instead of in sema).

If this isn't completely revolting to people, I'll implement it in the next few days.

Oh yeah, we also now have a place to document our language extensions, woo.

-Chris

Hi Chris,

Looks reasonable but I see a bug:

The choices "tgmath" and "tgmath1" are too limiting. I suspect that you will ultimately need a way of specifying any combination of the arguments are "generic". "tgmath" is interpreted as "all parameters are generic" and "tgmath1" as "the first parameter is generic." There's no telling what the C committee will throw at you next year. And indeed, they've already bitten you with remquo:

    double remquo(double x, double y, int *quo);

Both x and y are generic, but not quo.

The C++ <cmath> has to implement this same logic, but gets to use C++ to do it. :slight_smile: If it is helpful, see the cayuga <cmath> and <type_traits> for an example C++ implementation, at least for the non-complex parts. I'm not suggesting that clang's <tgmath.h> be implemented in C++. Only that seeing a C++ implementation may spark an idea for a more flexible __builtin_overload.

Or at the very least, you need "tgmath2". :slight_smile:

-Howard

One possible variation:

Change PromotionRuleStr to the form:

     "PromotionRuleArg1, PromotionRuleArg2, ..."

remquo example:

    "tgmath, tgmath, none"

Then you can drop the NumArgs argument because you already have it by counting commas in the PromotionRuleStr.

-Howard

This is a valid question, though, and one I would have asked if I thought the answer might be "yes" :slight_smile:

Should we use C++ function overloading for <tgmath.h>? We could expose the ability to declare overloaded functions in C (say, through some function attribute __overloaded__) and relax a few of our if (getLangOptions().CPlusPlus) conditions to let C take the function-overloading path.

  - Doug

Fwiw the CodeWarrior compiler has/had the ability to switch into C++-mode (and back to C) in the middle of a translation unit. The CodeWarrior <tgmath.h> used this capability and was implemented in C++, using only C++ overloading (plus __typeof__), not templates, as I recall. I.e. it was very close to Doug's suggestion above. It worked well. I don't know if it had any advantages or disadvantages compared to the __builtin_overload approach. It was used because we already had the compiler tools (switching pragma) to do it, and did not already have a __builtin_overload intrinsic, and so it was a quick and easy path that worked.

-Howard

It would take a bit of work to support such a pragma in Clang, because we currently have quite a few places where we make a lot of assumptions about language-specific data structures based on the current language we're parsing (e.g., we assume that all class types are handled by CXXRecordDecl in C++ mode). However, it seems reasonable that we would work toward making Clang #pragma c++'able.

In the shorter term, I think some kind of __overload__ attribute would be relatively easy to implement. Most of the pain would be in making sure that the C++ implicit-conversion rules handle all of the funky C99 types :slight_smile:

  - Doug

I spoke with Doug more about this offline, and I agree. We should add an __attribute__((overloadable)) or something like that. Doug volunteered to tackle this when he gets to a good intermediate point on his template work. I filed PR 3542 to track this.

Thanks Doug! When this is done, I'll do the horrible header hacking stuff to hook it up for tgmath.h to use.

-Chris

I’m curious if this would be useful for a case I’ve hit as a user of compilers.

In Cocoa and CoreFoundation, there are a bunch of types that different as far as the compiler is concerned, but the user knows that they are the same. NSString* and CFStringRef are interchangeable, for example.

I’d love to be able to inform the compiler that certain types are compatible, but short of that, I have an NSToCF macro that does sort of a type-safe cast. If passed an argument of type of NSString*, for example, the result is type CFStringRef. It looks like this:

#define NSToCF(nsObject) (
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSArray*), (CFArrayRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSAttributedString*), (CFAttributedStringRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSCalendar*), (CFCalendarRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSCharacterSet*), (CFCharacterSetRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSData*), (CFDataRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSDate*), (CFDateRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSDictionary*), (CFDictionaryRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSError*), (CFErrorRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSLocale*), (CFLocaleRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableArray*), (CFMutableArrayRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableAttributedString*), (CFMutableAttributedStringRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableCharacterSet*), (CFMutableCharacterSetRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableData*), (CFMutableDataRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableDictionary*), (CFMutableDictionaryRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableSet*), (CFMutableSetRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableString*), (CFMutableStringRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSNumber*), (CFNumberRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSInputStream*), (CFReadStreamRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSTimer*), (CFRunLoopTimerRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSSet*), (CFSetRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSString*), (CFStringRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSTimeZone*), (CFTimeZoneRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSURL*), (CFURLRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSOutputStream*), (CFWriteStreamRef)(nsObject),
1.0/error on type not found/ )))))))))))))))))))))))))

Would the changes you’re talking about make this less disgusting?

-Ken

It would take a bit of work to support such a pragma in Clang,
because we currently have quite a few places where we make a lot of
assumptions about language-specific data structures based on the
current language we’re parsing (e.g., we assume that all class types
are handled by CXXRecordDecl in C++ mode). However, it seems
reasonable that we would work toward making Clang #pragma c++'able.

In the shorter term, I think some kind of overload attribute
would be relatively easy to implement. Most of the pain would be in
making sure that the C++ implicit-conversion rules handle all of the
funky C99 types :slight_smile:

I spoke with Doug more about this offline, and I agree. We should add
an attribute((overloadable)) or something like that. Doug
volunteered to tackle this when he gets to a good intermediate point
on his template work. I filed PR 3542 to track this.

Thanks Doug! When this is done, I’ll do the horrible header hacking
stuff to hook it up for tgmath.h to use.

-Chris

I’m curious if this would be useful for a case I’ve hit as a user of compilers.

In Cocoa and CoreFoundation, there are a bunch of types that different as far as the compiler is concerned, but the user knows that they are the same. NSString* and CFStringRef are interchangeable, for example.

Interesting.

I’d love to be able to inform the compiler that certain types are compatible, but short of that, I have an NSToCF macro that does sort of a type-safe cast. If passed an argument of type of NSString*, for example, the result is type CFStringRef. It looks like this:

#define NSToCF(nsObject) (
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSArray*), (CFArrayRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSAttributedString*), (CFAttributedStringRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSCalendar*), (CFCalendarRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSCharacterSet*), (CFCharacterSetRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSData*), (CFDataRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSDate*), (CFDateRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSDictionary*), (CFDictionaryRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSError*), (CFErrorRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSLocale*), (CFLocaleRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableArray*), (CFMutableArrayRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableAttributedString*), (CFMutableAttributedStringRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableCharacterSet*), (CFMutableCharacterSetRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableData*), (CFMutableDataRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableDictionary*), (CFMutableDictionaryRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableSet*), (CFMutableSetRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSMutableString*), (CFMutableStringRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSNumber*), (CFNumberRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSInputStream*), (CFReadStreamRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSTimer*), (CFRunLoopTimerRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSSet*), (CFSetRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSString*), (CFStringRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSTimeZone*), (CFTimeZoneRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSURL*), (CFURLRef)(nsObject),
__builtin_choose_expr (__builtin_types_compatible_p (typeof (nsObject), NSOutputStream*), (CFWriteStreamRef)(nsObject),
1.0/error on type not found/ )))))))))))))))))))))))))

Would the changes you’re talking about make this less disgusting?

I believe so. The idea would be to create a bunch of overloaded functions like so:

inline CFArrayRef NSToCF(NSArray *nsObject) attribute((always_inline, overloaded)) { return (CFArrayRef)nsObject; }

inline CFAttributedStringRef NSToCF(NSAttributedString *nsObject) attribute((always_inline, overloaded)) { return (CFAttributedStringRef)nsObject; }

inline CFCalendarRef NSToCF(NSCalendar *nsObject) attribute((always_inline, overloaded)) { return (CFCalendarRef)nsObject; }

inline CFDataRef NSToCF(NSData *nsObject) attribute((always_inline, overloaded)) { return (CFDataRef)nsObject; }
// and so on

It’s better than counting parentheses :slight_smile:

  • Doug

right, it is easy to turn that into a macro. :slight_smile: It is also a lot better in the error case than "1.0/error on type not found/ " :slight_smile:

-Chris

Cool. :slight_smile: Yes, it might be possible to do better than that for the error anyway, that’s just as far as I got. I don’t actually care if this is a macro either. The functions Doug suggested would be fine. Sounds nice.

-Ken