t.p.northover created this revision.
Herald added a subscriber: mcrosier.

This has a couple of changes to how Clang determines whether a floating-point 
promotion has occurred for C++ (and `__attribute__((overloadable))`) purposes.

First, I think the special rules for C are based on a misconception. C99 
referred to promotions to "long double" but it actually meant conversions, and 
the only real promotions that happened were the "float -> double" default 
argument promotion for varargs (and obviously nothing to do with overload 
resolution). C11 fixed this wording to remove all mention of promotions. So 
this patch makes all overload resolution follow C++ rules, which seems like the 
least surprising behaviour for __attribute__((overloadable)).

Second, this allows "half -> double" promotion unconditionally. Since half is a 
non-standard type there's a bit of guesswork here, but it seems like the most 
consistent extension of C++'s wording to that type (with special mention going 
to allowing "half -> float" too). It definitely seems reasonable that the 
decision should not depend on NativeHalfTypes.

Does the change look reasonable to others?


Repository:
  rL LLVM

https://reviews.llvm.org/D34779

Files:
  clang/lib/Sema/SemaOverload.cpp
  clang/test/CodeGenCXX/fp16-overload.cpp
  clang/test/Sema/overloadable-complex.c
  clang/test/SemaCXX/floating-point-promotion.cpp

Index: clang/test/SemaCXX/floating-point-promotion.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/floating-point-promotion.cpp
@@ -0,0 +1,110 @@
+// RUN: %clang_cc1 -fsyntax-only -fallow-half-arguments-and-returns -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fallow-half-arguments-and-returns -fnative-half-type -verify %s
+
+#include <stdint.h>
+
+typedef __fp16 half;
+
+// expected-n...@floating-point-promotion.cpp:* 1+ {{candidate function}}
+
+extern void half_to_int_or_float(int x);
+extern void half_to_int_or_float(float x);
+void test1(half h) {
+  half_to_int_or_float(h);  // expected-error {{call to 'half_to_int_or_float' is ambiguous}}
+}
+
+extern void half_to_int_or_double(int x);
+extern void half_to_int_or_double(double x);
+void test2(half h) {
+  half_to_int_or_double(h);
+}
+
+extern void half_to_int_or_long_double(int x);
+extern void half_to_int_or_long_double(long double x);
+void test3(half h) {
+  half_to_int_or_long_double(h);  // expected-error {{call to 'half_to_int_or_long_double' is ambiguous}}
+}
+
+extern void half_to_float_or_double(float x);
+extern void half_to_float_or_double(double x);
+void test4(half h) {
+  half_to_float_or_double(h);
+}
+
+extern void half_to_float_or_long_double(float x);
+extern void half_to_float_or_long_double(long double x);
+void test5(half h) {
+  half_to_float_or_long_double(h);  // expected-error {{call to 'half_to_float_or_long_double' is ambiguous}}
+}
+
+extern void float_to_int_or_half(int x);
+extern void float_to_int_or_half(half x);
+void test6(float f) {
+  float_to_int_or_half(f);  // expected-error {{call to 'float_to_int_or_half' is ambiguous}}
+}
+
+extern void float_to_int_or_double(int x);
+extern void float_to_int_or_double(double x);
+void test7(float f) {
+  float_to_int_or_double(f);
+}
+
+extern void float_to_int_or_long_double(int x);
+extern void float_to_int_or_long_double(long double x);
+void test8(float f) {
+  float_to_int_or_long_double(f);  // expected-error {{call to 'float_to_int_or_long_double' is ambiguous}}
+}
+
+extern void float_to_half_or_double(half x);
+extern void float_to_half_or_double(double x);
+void test9(float f) {
+  float_to_half_or_double(f);
+}
+
+extern void float_to_half_or_long_double(half x);
+extern void float_to_half_or_long_double(long double x);
+void test10(float f) {
+  float_to_half_or_long_double(f);  // expected-error {{call to 'float_to_half_or_long_double' is ambiguous}}
+}
+
+extern void double_to_int_or_half(int x);
+extern void double_to_int_or_half(half x);
+void test11(double d) {
+  double_to_int_or_half(d);  // expected-error {{call to 'double_to_int_or_half' is ambiguous}}
+}
+
+extern void double_to_int_or_float(int x);
+extern void double_to_int_or_float(float x);
+void test12(double d) {
+  double_to_int_or_float(d);  // expected-error {{call to 'double_to_int_or_float' is ambiguous}}
+}
+
+extern void double_to_int_or_long_double(int x);
+extern void double_to_int_or_long_double(long double x);
+void test13(double d) {
+  double_to_int_or_long_double(d);  // expected-error {{call to 'double_to_int_or_long_double' is ambiguous}}
+}
+
+extern void double_to_half_or_float(half x);
+extern void double_to_half_or_float(float x);
+void test14(double d) {
+  double_to_half_or_float(d);  // expected-error {{call to 'double_to_half_or_float' is ambiguous}}
+}
+
+extern void double_to_half_or_long_double(half x);
+extern void double_to_half_or_long_double(long double x);
+void test15(double d) {
+  double_to_half_or_long_double(d);  // expected-error {{call to 'double_to_half_or_long_double' is ambiguous}}
+}
+
+extern void long_double_to_half_or_float(half x);
+extern void long_double_to_half_or_float(float x);
+void test16(long double ld) {
+  double_to_half_or_float(ld);  // expected-error {{call to 'double_to_half_or_float' is ambiguous}}
+}
+
+extern void long_double_to_float_or_double(float x);
+extern void long_double_to_float_or_double(double x);
+void test17(long double ld) {
+  long_double_to_float_or_double(ld);  // expected-error {{call to 'long_double_to_float_or_double' is ambiguous}}
+}
Index: clang/test/Sema/overloadable-complex.c
===================================================================
--- clang/test/Sema/overloadable-complex.c
+++ clang/test/Sema/overloadable-complex.c
@@ -27,12 +27,12 @@
   long *lp = foo(dc);
 }
 
-char *promote_or_convert(double _Complex) __attribute__((__overloadable__));  // expected-note 2 {{candidate function}}
-int *promote_or_convert(long double _Complex) __attribute__((__overloadable__)); // expected-note 2 {{candidate function}} 
+char *promote_or_convert(double _Complex) __attribute__((__overloadable__));  // expected-note {{candidate function}}
+int *promote_or_convert(long double _Complex) __attribute__((__overloadable__));  // expected-note {{candidate function}}
 
 void test_promote_or_convert(float f, float _Complex fc) {
-  char *cp = promote_or_convert(fc); // expected-error{{call to 'promote_or_convert' is ambiguous}}
-  int *ip2 = promote_or_convert(f); // expected-error{{call to 'promote_or_convert' is ambiguous}}
+  char *cp = promote_or_convert(fc);
+  char *ip2 = promote_or_convert(f);  // expected-error{{call to 'promote_or_convert' is ambiguous}}
 }
 
 char *promote_or_convert2(float) __attribute__((__overloadable__));
@@ -48,3 +48,11 @@
 void test_promote_or_convert3(short _Complex sc) {
   char *cp = promote_or_convert3(sc);
 }
+
+char *promote_or_convert4(float _Complex) __attribute__((__overloadable__));  // expected-note 2 {{candidate function}}
+int *promote_or_convert4(long double _Complex) __attribute__((__overloadable__));  // expected-note 2 {{candidate function}}
+
+void test_promote_or_convert4(double d, double _Complex dc) {
+  char *cp = promote_or_convert4(dc);  // expected-error{{call to 'promote_or_convert4' is ambiguous}}
+  int *ip2 = promote_or_convert4(d);  // expected-error{{call to 'promote_or_convert4' is ambiguous}}
+}
Index: clang/test/CodeGenCXX/fp16-overload.cpp
===================================================================
--- clang/test/CodeGenCXX/fp16-overload.cpp
+++ clang/test/CodeGenCXX/fp16-overload.cpp
@@ -1,10 +1,33 @@
 // RUN: %clang_cc1 -emit-llvm -o - -triple arm-none-linux-gnueabi %s | FileCheck %s
+// RUN: %clang_cc1 -DWITH_AMBIGUOUS -verify %s
+// RUN: %clang_cc1 -DWITH_AMBIGUOUS -fnative-half-type -fallow-half-arguments-and-returns -verify %s
 
-extern int foo(float x);
-extern int foo(double x);
+typedef __fp16 half;
+half x;
 
-__fp16 a;
+void half_to_float_or_double(float x);
+void half_to_float_or_double(double x);
 
-// CHECK: call i32 @_Z3foof
-// CHECK-NOT: call i32 @_Z3food
-int bar (void) { return foo(a); }
+// CHECK:     call void @_Z23half_to_float_or_doubled
+// CHECK-NOT: call void @_Z23half_to_float_or_doublef
+void test1() {
+  half_to_float_or_double(x);
+}
+
+extern void half_to_int_or_double(int x);
+extern void half_to_int_or_double(double x);
+
+// CHECK:     call void @_Z21half_to_int_or_doubled
+// CHECK-NOT: call void @_Z21half_to_int_or_doublei
+void test2() {
+  half_to_int_or_double(x);
+}
+
+#ifdef WITH_AMBIGUOUS
+extern void half_to_int_or_float(int x);    // expected-note {{candidate function}}
+extern void half_to_int_or_float(float x);  // expected-note {{candidate function}}
+
+void test3() {
+  half_to_int_or_float(x);  // expected-error {{call to 'half_to_int_or_float' is ambiguous}}
+}
+#endif
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -1915,7 +1915,7 @@
 /// IsIntegralPromotion - Determines whether the conversion from the
 /// expression From (whose potentially-adjusted type is FromType) to
 /// ToType is an integral promotion (C++ 4.5). If so, returns true and
-/// sets PromotedType to the promoted type.
+/// sets From to the promoted type.
 bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType) {
   const BuiltinType *To = ToType->getAs<BuiltinType>();
   // All integers are built-in.
@@ -2063,7 +2063,7 @@
 
 /// IsFloatingPointPromotion - Determines whether the conversion from
 /// FromType to ToType is a floating point promotion (C++ 4.6). If so,
-/// returns true and sets PromotedType to the promoted type.
+/// returns true.
 bool Sema::IsFloatingPointPromotion(QualType FromType, QualType ToType) {
   if (const BuiltinType *FromBuiltin = FromType->getAs<BuiltinType>())
     if (const BuiltinType *ToBuiltin = ToType->getAs<BuiltinType>()) {
@@ -2073,20 +2073,14 @@
           ToBuiltin->getKind() == BuiltinType::Double)
         return true;
 
-      // C99 6.3.1.5p1:
-      //   When a float is promoted to double or long double, or a
-      //   double is promoted to long double [...].
-      if (!getLangOpts().CPlusPlus &&
-          (FromBuiltin->getKind() == BuiltinType::Float ||
-           FromBuiltin->getKind() == BuiltinType::Double) &&
-          (ToBuiltin->getKind() == BuiltinType::LongDouble ||
-           ToBuiltin->getKind() == BuiltinType::Float128))
-        return true;
-
-      // Half can be promoted to float.
-      if (!getLangOpts().NativeHalfType &&
-           FromBuiltin->getKind() == BuiltinType::Half &&
-          ToBuiltin->getKind() == BuiltinType::Float)
+      /// Behave as if C++ 4.6 was modified by:
+      /// - Inserting a paragraph:
+      ///     "A prvalue of type half can be converted to a prvalue of type
+      ///      double. The value is unchanged."
+      /// - Updating paragraph 2 to:
+      ///     "These conversions are called floating point promotions."
+      if (FromBuiltin->getKind() == BuiltinType::Half &&
+          ToBuiltin->getKind() == BuiltinType::Double)
         return true;
     }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to