]> granicus.if.org Git - clang/commitdiff
[Sema] Compare bad conversions in overload resolution.
authorGeorge Burgess IV <george.burgess.iv@gmail.com>
Wed, 7 Sep 2016 20:03:19 +0000 (20:03 +0000)
committerGeorge Burgess IV <george.burgess.iv@gmail.com>
Wed, 7 Sep 2016 20:03:19 +0000 (20:03 +0000)
r280553 introduced an issue where we'd emit ambiguity errors for code
like:

```
void foo(int *, int);
void foo(unsigned int *, unsigned int);

void callFoo() {
  unsigned int i;
  foo(&i, 0); // ambiguous: int->unsigned int is worse than int->int,
              // but unsigned int*->unsigned int* is better than
              // int*->int*.
}
```

This patch fixes this issue by changing how we handle ill-formed (but
valid) implicit conversions. Candidates with said conversions now always
rank worse than candidates without them, and two candidates are
considered to be equally bad if they both have these conversions for
the same argument.

Additionally, this fixes a case in C++11 where we'd complain about an
ambiguity in a case like:

```
void f(char *, int);
void f(const char *, unsigned);
void g() { f("abc", 0); }
```

...Since conversion to char* from a string literal is considered
ill-formed in C++11 (and deprecated in C++03), but we accept it as an
extension.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@280847 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/AttrDocs.td
lib/Sema/SemaOverload.cpp
test/CodeGen/overloadable.c
test/SemaCXX/overload-call.cpp

index 3b439594bb00181f2b4cf3b668a21c9d37ada26b..804a33431cb97029ef97a93f9fa95d04a0547c61 100644 (file)
@@ -470,10 +470,11 @@ semantics:
 * 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.
+* If no viable candidates are otherwise available, we allow a conversion from a
+  pointer of type ``T*`` to a pointer of type ``U*``, where ``T`` and ``U`` are
+  incompatible. This conversion 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
index 183fdf4272edad9daaae5ff508d3b6637b3d330f..10aa997745951dd2e7e7479e0a904c3763e305cc 100644 (file)
@@ -8600,13 +8600,40 @@ bool clang::isBetterOverloadCandidate(Sema &S, const OverloadCandidate &Cand1,
   if (Cand1.IgnoreObjectArgument || Cand2.IgnoreObjectArgument)
     StartArg = 1;
 
+  auto IsIllFormedConversion = [&](const ImplicitConversionSequence &ICS) {
+    // We don't allow incompatible pointer conversions in C++.
+    if (!S.getLangOpts().CPlusPlus)
+      return ICS.isStandard() &&
+             ICS.Standard.Second == ICK_Incompatible_Pointer_Conversion;
+
+    // The only ill-formed conversion we allow in C++ is the string literal to
+    // char* conversion, which is only considered ill-formed after C++11.
+    return S.getLangOpts().CPlusPlus11 && !S.getLangOpts().WritableStrings &&
+           hasDeprecatedStringLiteralToCharPtrConversion(ICS);
+  };
+
+  // Define functions that don't require ill-formed conversions for a given
+  // argument to be better candidates than functions that do.
+  unsigned NumArgs = Cand1.NumConversions;
+  assert(Cand2.NumConversions == NumArgs && "Overload candidate mismatch");
+  bool HasBetterConversion = false;
+  for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) {
+    bool Cand1Bad = IsIllFormedConversion(Cand1.Conversions[ArgIdx]);
+    bool Cand2Bad = IsIllFormedConversion(Cand2.Conversions[ArgIdx]);
+    if (Cand1Bad != Cand2Bad) {
+      if (Cand1Bad)
+        return false;
+      HasBetterConversion = true;
+    }
+  }
+
+  if (HasBetterConversion)
+    return true;
+
   // C++ [over.match.best]p1:
   //   A viable function F1 is defined to be a better function than another
   //   viable function F2 if for all arguments i, ICSi(F1) is not a worse
   //   conversion sequence than ICSi(F2), and then...
-  unsigned NumArgs = Cand1.NumConversions;
-  assert(Cand2.NumConversions == NumArgs && "Overload candidate mismatch");
-  bool HasBetterConversion = false;
   for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) {
     switch (CompareImplicitConversionSequences(S, Loc,
                                                Cand1.Conversions[ArgIdx],
index d9769c21f11a5b64e10105b970cc56de1d9e146d..2ec6fe445e7a66cbd5c422b14fd09d5adda5df49 100644 (file)
@@ -74,3 +74,23 @@ void bar() {
   // CHECK: call void @_Z7ovl_barPc
   ovl_bar(ucharbuf);
 }
+
+// CHECK-LABEL: define void @baz
+void ovl_baz(int *, int) __attribute__((overloadable));
+void ovl_baz(unsigned int *, unsigned int) __attribute__((overloadable));
+void ovl_baz2(int, int *) __attribute__((overloadable));
+void ovl_baz2(unsigned int, unsigned int *) __attribute__((overloadable));
+void baz() {
+  unsigned int j;
+  // Initial rules for incompatible pointer conversions made this overload
+  // ambiguous.
+  // CHECK: call void @_Z7ovl_bazPjj
+  ovl_baz(&j, 0);
+  // CHECK: call void @_Z7ovl_bazPjj
+  ovl_baz(&j, 0u);
+
+  // CHECK: call void @_Z8ovl_baz2jPj
+  ovl_baz2(0, &j);
+  // CHECK: call void @_Z8ovl_baz2jPj
+  ovl_baz2(0u, &j);
+}
index 7eaf98b601c181ef1ac4a8de5bc12d05b082be26..3a01bf24b31afd78f325d8deea881076313bdb4b 100644 (file)
@@ -647,3 +647,14 @@ namespace PR20218 {
     g(y); // expected-error {{ambiguous}}
   }
 }
+
+namespace StringLiteralToCharAmbiguity {
+  void f(char *, int);
+  void f(const char *, unsigned);
+  void g() { f("foo", 0); }
+#if __cplusplus <= 199711L
+  // expected-error@-2 {{call to 'f' is ambiguous}}
+  // expected-note@-5 {{candidate function}}
+  // expected-note@-5 {{candidate function}}
+#endif
+}