george.burgess.iv created this revision.
george.burgess.iv added a reviewer: rsmith.
george.burgess.iv added a subscriber: cfe-commits.

In C, we allow pointer conversions like:

```
void foo(int *a);
long b;
void runFoo() { foo(&b); }
```

...But not when overloading:

```
int bar(char *a) __attribute__((overloadable));
int bar(struct{}) __attribute__((overloadable));
unsigned char d;
void runBar() { bar(&d); } // error: no matching function for call to 'bar'
```

This patch makes it so `bar(&d)` isn't an error.

A few notes:

- We'll still emit a warning about the conversion, assuming the user hasn't 
opted out of said warnings.
- Every such conversion is ranked as the worst possible thing. So, if there's 
any ambiguity at all (e.g. if bar had a `void(signed char*)` overload, as 
well), we'll still emit an error.
- We consider conversions that drop qualifiers to be equally as bad as 
converting between incompatible pointer types. I'm happy to make conversions 
that only drop qualifiers preferred over incompatible pointer types, if that 
would be better.

https://reviews.llvm.org/D24113

Files:
  include/clang/Basic/AttrDocs.td
  include/clang/Sema/Overload.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaOverload.cpp
  test/CodeGen/builtins-systemz-zvector-error.c
  test/CodeGen/overloadable.c
  test/Sema/overloadable.c
  test/Sema/pass-object-size.c
  test/SemaOpenCL/event_t_overload.cl

Index: test/SemaOpenCL/event_t_overload.cl
===================================================================
--- test/SemaOpenCL/event_t_overload.cl
+++ test/SemaOpenCL/event_t_overload.cl
@@ -1,11 +1,11 @@
 // RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only
 
-void __attribute__((overloadable)) foo(event_t, __local char *); // expected-note {{candidate function not viable: no known conversion from '__global int *' to '__local char *' for 2nd argument}}
-void __attribute__((overloadable)) foo(event_t, __local float *); // expected-note {{candidate function not viable: no known conversion from '__global int *' to '__local float *' for 2nd argument}}
+void __attribute__((overloadable)) foo(event_t, __local char *); // expected-note {{candidate function}}
+void __attribute__((overloadable)) foo(event_t, __local float *); // expected-note {{candidate function}}
 
 void kernel ker(__local char *src1, __local float *src2, __global int *src3) {
   event_t evt;
   foo(evt, src1);
   foo(0, src2);
-  foo(evt, src3); // expected-error {{no matching function for call to 'foo'}}
+  foo(evt, src3); // expected-error {{call to 'foo' is ambiguous}}
 }
Index: test/Sema/pass-object-size.c
===================================================================
--- test/Sema/pass-object-size.c
+++ test/Sema/pass-object-size.c
@@ -52,5 +52,5 @@
 
   int P;
   (&NotOverloaded)(&P); //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}}
-  (&IsOverloaded)(&P); //expected-error{{no matching function}} expected-note@35{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@36{{candidate function not viable: no known conversion from 'int *' to 'char *' for 1st argument}}
+  (&IsOverloaded)(&P); //expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'char *'}} expected-note@36{{passing argument to parameter 'p' here}}
 }
Index: test/Sema/overloadable.c
===================================================================
--- test/Sema/overloadable.c
+++ test/Sema/overloadable.c
@@ -23,7 +23,7 @@
 void test_funcptr(int (*f1)(int, double),
                   int (*f2)(int, float)) {
   float *fp = accept_funcptr(f1);
-  accept_funcptr(f2); // expected-error{{no matching function for call to 'accept_funcptr'}}
+  accept_funcptr(f2); // expected-error{{call to 'accept_funcptr' is ambiguous}}
 }
 
 struct X { int x; float y; };
@@ -122,3 +122,32 @@
 
   void *specific_disabled = &disabled;
 }
+
+void incompatible_pointer_type_conversions() {
+  char charbuf[1];
+  unsigned char ucharbuf[1];
+  int intbuf[1];
+
+  void foo(char *c) __attribute__((overloadable));
+  void foo(short *c) __attribute__((overloadable));
+  foo(charbuf);
+  foo(ucharbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@131{{candidate function}} expected-note@132{{candidate function}}
+  foo(intbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@131{{candidate function}} expected-note@132{{candidate function}}
+
+  void bar(unsigned char *c) __attribute__((overloadable));
+  void bar(signed char *c) __attribute__((overloadable));
+  bar(charbuf); // expected-error{{call to 'bar' is ambiguous}} expected-note@137{{candidate function}} expected-note@138{{candidate function}}
+  bar(ucharbuf);
+  bar(intbuf); // expected-error{{call to 'bar' is ambiguous}} expected-note@137{{candidate function}} expected-note@138{{candidate function}}
+}
+
+void dropping_qualifiers_is_incompatible() {
+  const char ccharbuf[1];
+  volatile char vcharbuf[1];
+
+  void foo(char *c) __attribute__((overloadable));
+  void foo(const volatile unsigned char *c) __attribute__((overloadable));
+
+  foo(ccharbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@148{{candidate function}} expected-note@149{{candidate function}}
+  foo(vcharbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@148{{candidate function}} expected-note@149{{candidate function}}
+}
Index: test/CodeGen/overloadable.c
===================================================================
--- test/CodeGen/overloadable.c
+++ test/CodeGen/overloadable.c
@@ -59,3 +59,18 @@
   // CHECK: @_Z13addrof_singlePc
   void *vp3 = &addrof_single;
 }
+
+
+void ovl_bar(char *) __attribute__((overloadable));
+void ovl_bar(int) __attribute__((overloadable));
+
+// CHECK-LABEL: define void @bar
+void bar() {
+  char charbuf[1];
+  unsigned char ucharbuf[1];
+
+  // CHECK: call void @_Z7ovl_barPc
+  ovl_bar(charbuf);
+  // CHECK: call void @_Z7ovl_barPc
+  ovl_bar(ucharbuf);
+}
Index: test/CodeGen/builtins-systemz-zvector-error.c
===================================================================
--- test/CodeGen/builtins-systemz-zvector-error.c
+++ test/CodeGen/builtins-systemz-zvector-error.c
@@ -233,38 +233,27 @@
                                              // expected-note@vecintrin.h:* 1 {{must be a constant integer from 0 to 1}}
 
   vsc = vec_load_bndry(cptrsc, idx);   // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
   vsc = vec_load_bndry(cptrsc, 200);   // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
   vsc = vec_load_bndry(cptrsc, 32);    // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
   vsc = vec_load_bndry(cptrsc, 8192);  // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
   vuc = vec_load_bndry(cptruc, idx);   // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
   vss = vec_load_bndry(cptrss, idx);   // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
   vus = vec_load_bndry(cptrus, idx);   // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
   vsi = vec_load_bndry(cptrsi, idx);   // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
   vui = vec_load_bndry(cptrui, idx);   // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
   vsl = vec_load_bndry(cptrsl, idx);   // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
   vul = vec_load_bndry(cptrul, idx);   // expected-error {{no matching function}}
-                                       // expected-note@vecintrin.h:* 8 {{candidate function not viable}}
-                                       // expected-note@vecintrin.h:* 1 {{must be a constant power of 2 from 64 to 4096}}
+                                       // expected-note@vecintrin.h:* 9 {{must be a constant power of 2 from 64 to 4096}}
 
   vuc = vec_genmask(idx);  // expected-error {{no matching function}}
                            // expected-note@vecintrin.h:* {{must be a constant integer}}
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -136,7 +136,8 @@
     ICR_Exact_Match, // NOTE(gbiv): This may not be completely right --
                      // it was omitted by the patch that added
                      // ICK_Zero_Event_Conversion
-    ICR_C_Conversion
+    ICR_C_Conversion,
+    ICR_C_Conversion_Extension
   };
   return Rank[(int)Kind];
 }
@@ -170,7 +171,8 @@
     "Transparent Union Conversion",
     "Writeback conversion",
     "OpenCL Zero Event Conversion",
-    "C specific type conversion"
+    "C specific type conversion",
+    "Incompatible pointer conversion"
   };
   return Name[Kind];
 }
@@ -1783,22 +1785,37 @@
     return false;
 
   ExprResult ER = ExprResult{From};
-  auto Conv = S.CheckSingleAssignmentConstraints(ToType, ER,
-                                                 /*Diagnose=*/false,
-                                                 /*DiagnoseCFAudited=*/false,
-                                                 /*ConvertRHS=*/false);
-  if (Conv != Sema::Compatible)
+  Sema::AssignConvertType Conv =
+      S.CheckSingleAssignmentConstraints(ToType, ER,
+                                         /*Diagnose=*/false,
+                                         /*DiagnoseCFAudited=*/false,
+                                         /*ConvertRHS=*/false);
+  ImplicitConversionKind ImplicitConv;
+  switch (Conv) {
+  case Sema::Compatible:
+    ImplicitConv = ICK_C_Only_Conversion;
+    break;
+  // For our purposes, discarding qualifiers is just as bad as using an
+  // incompatible pointer. Note that an IncompatiblePointer conversion can drop
+  // qualifiers, as well.
+  case Sema::CompatiblePointerDiscardsQualifiers:
+  case Sema::IncompatiblePointer:
+  case Sema::IncompatiblePointerSign:
+    ImplicitConv = ICK_Incompatible_Pointer_Conversion;
+    break;
+  default:
     return false;
+  }
 
   SCS.setAllToTypes(ToType);
   // We need to set all three because we want this conversion to rank terribly,
   // and we don't know what conversions it may overlap with.
-  SCS.First = ICK_C_Only_Conversion;
-  SCS.Second = ICK_C_Only_Conversion;
-  SCS.Third = ICK_C_Only_Conversion;
+  SCS.First = ImplicitConv;
+  SCS.Second = ImplicitConv;
+  SCS.Third = ImplicitConv;
   return true;
 }
-  
+
 static bool
 IsTransparentUnionStandardConversion(Sema &S, Expr* From, 
                                      QualType &ToType,
@@ -5105,6 +5122,7 @@
   case ICK_Writeback_Conversion:
   case ICK_Zero_Event_Conversion:
   case ICK_C_Only_Conversion:
+  case ICK_Incompatible_Pointer_Conversion:
     return false;
 
   case ICK_Lvalue_To_Rvalue:
@@ -5906,10 +5924,15 @@
                                 /*AllowObjCWritebackConversion=*/
                                 getLangOpts().ObjCAutoRefCount,
                                 /*AllowExplicit*/false);
-        if (ConversionState.isBad()) {
-          Match = false;
-          break;
-        }
+      // This function looks for a reasonably-exact match, so we consider
+      // incompatible pointer conversions to be a failure here.
+      if (ConversionState.isBad() ||
+          (ConversionState.isStandard() &&
+           ConversionState.Standard.First ==
+               ICK_Incompatible_Pointer_Conversion)) {
+        Match = false;
+        break;
+      }
     }
     // Promote additional arguments to variadic methods.
     if (Match && Method->isVariadic()) {
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -3705,6 +3705,7 @@
   case ICK_Qualification:
   case ICK_Num_Conversion_Kinds:
   case ICK_C_Only_Conversion:
+  case ICK_Incompatible_Pointer_Conversion:
     llvm_unreachable("Improper second standard conversion");
   }
 
Index: include/clang/Sema/Overload.h
===================================================================
--- include/clang/Sema/Overload.h
+++ include/clang/Sema/Overload.h
@@ -84,6 +84,8 @@
     ICK_Writeback_Conversion,  ///< Objective-C ARC writeback conversion
     ICK_Zero_Event_Conversion, ///< Zero constant to event (OpenCL1.2 6.12.10)
     ICK_C_Only_Conversion,     ///< Conversions allowed in C, but not C++
+    ICK_Incompatible_Pointer_Conversion, ///< C-only conversion between pointers
+                                         ///  with incompatible types
     ICK_Num_Conversion_Kinds,  ///< The number of conversion kinds
   };
 
@@ -97,8 +99,10 @@
     ICR_Conversion,              ///< Conversion
     ICR_Complex_Real_Conversion, ///< Complex <-> Real conversion
     ICR_Writeback_Conversion,    ///< ObjC ARC writeback conversion
-    ICR_C_Conversion             ///< Conversion only allowed in the C standard.
+    ICR_C_Conversion,            ///< Conversion only allowed in the C standard.
                                  ///  (e.g. void* to char*)
+    ICR_C_Conversion_Extension   ///< Conversion not allowed by the C standard,
+                                 ///  but that we accept as an extension anyway.
   };
 
   ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind);
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -470,6 +470,11 @@
 * A conversion from type ``T`` to a value of type ``U`` is permitted if ``T``
   and ``U`` are compatible types.  This conversion is given "conversion" rank.
 
+* A conversion from a pointer of type ``T*`` to a pointer of type ``U*``, where
+  ``T`` and ``U`` are incompatible, is allowed, but is ranked below all other
+  types of conversions. Please note: ``U`` lacking qualifiers that are present
+  on ``T`` is sufficient for ``T`` and ``U`` to be incompatible.
+
 The declaration of ``overloadable`` functions is restricted to function
 declarations and definitions.  Most importantly, if any function with a given
 name is given the ``overloadable`` attribute, then all function declarations
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to