[clang] 2519554 - When diagnosing the lack of a viable conversion function, also list

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 9 15:15:21 PST 2020


Author: Richard Smith
Date: 2020-01-09T15:15:02-08:00
New Revision: 25195541349b1d6dfc03bf7511483110bda69b29

URL: https://github.com/llvm/llvm-project/commit/25195541349b1d6dfc03bf7511483110bda69b29
DIFF: https://github.com/llvm/llvm-project/commit/25195541349b1d6dfc03bf7511483110bda69b29.diff

LOG: When diagnosing the lack of a viable conversion function, also list
explicit functions that are not candidates.

It's not always obvious that the reason a conversion was not possible is
because the function you wanted to call is 'explicit', so explicitly say
if that's the case.

It would be nice to rank the explicit candidates higher in the
diagnostic if an implicit conversion sequence exists for their
arguments, but unfortunately we can't determine that without potentially
triggering non-immediate-context errors that we're not permitted to
produce.

Added: 
    

Modified: 
    clang/include/clang/AST/DeclCXX.h
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Overload.h
    clang/lib/Sema/SemaInit.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp
    clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp
    clang/test/CXX/dcl.decl/dcl.init/p14-0x.cpp
    clang/test/CXX/drs/dr15xx.cpp
    clang/test/CXX/drs/dr1xx.cpp
    clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp
    clang/test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp
    clang/test/CXX/special/class.inhctor/p3.cpp
    clang/test/PCH/cxx-explicit-specifier.cpp
    clang/test/SemaCXX/conversion-function.cpp
    clang/test/SemaCXX/convert-to-bool.cpp
    clang/test/SemaCXX/converting-constructor.cpp
    clang/test/SemaCXX/copy-initialization.cpp
    clang/test/SemaCXX/cxx2a-explicit-bool.cpp
    clang/test/SemaCXX/default1.cpp
    clang/test/SemaCXX/explicit.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index aba33e383976..73b4fb94a494 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1783,7 +1783,7 @@ class CXXRecordDecl : public RecordDecl {
 };
 
 /// Store information needed for an explicit specifier.
-/// used by CXXDeductionGuideDecl, CXXConstructorDecl and CXXConversionDecl.
+/// Used by CXXDeductionGuideDecl, CXXConstructorDecl and CXXConversionDecl.
 class ExplicitSpecifier {
   llvm::PointerIntPair<Expr *, 2, ExplicitSpecKind> ExplicitSpec{
       nullptr, ExplicitSpecKind::ResolvedFalse};
@@ -1796,20 +1796,22 @@ class ExplicitSpecifier {
   const Expr *getExpr() const { return ExplicitSpec.getPointer(); }
   Expr *getExpr() { return ExplicitSpec.getPointer(); }
 
-  /// Return true if the ExplicitSpecifier isn't defaulted.
+  /// Determine if the declaration had an explicit specifier of any kind.
   bool isSpecified() const {
     return ExplicitSpec.getInt() != ExplicitSpecKind::ResolvedFalse ||
            ExplicitSpec.getPointer();
   }
 
-  /// Check for Equivalence of explicit specifiers.
-  /// Return True if the explicit specifier are equivalent false otherwise.
+  /// Check for equivalence of explicit specifiers.
+  /// \return true if the explicit specifier are equivalent, false otherwise.
   bool isEquivalent(const ExplicitSpecifier Other) const;
-  /// Return true if the explicit specifier is already resolved to be explicit.
+  /// Determine whether this specifier is known to correspond to an explicit
+  /// declaration. Returns false if the specifier is absent or has an
+  /// expression that is value-dependent or evaluates to false.
   bool isExplicit() const {
     return ExplicitSpec.getInt() == ExplicitSpecKind::ResolvedTrue;
   }
-  /// Return true if the ExplicitSpecifier isn't valid.
+  /// Determine if the explicit specifier is invalid.
   /// This state occurs after a substitution failures.
   bool isInvalid() const {
     return ExplicitSpec.getInt() == ExplicitSpecKind::Unresolved &&
@@ -1817,9 +1819,7 @@ class ExplicitSpecifier {
   }
   void setKind(ExplicitSpecKind Kind) { ExplicitSpec.setInt(Kind); }
   void setExpr(Expr *E) { ExplicitSpec.setPointer(E); }
-  // getFromDecl - retrieve the explicit specifier in the given declaration.
-  // if the given declaration has no explicit. the returned explicit specifier
-  // is defaulted. .isSpecified() will be false.
+  // Retrieve the explicit specifier in the given declaration, if any.
   static ExplicitSpecifier getFromDecl(FunctionDecl *Function);
   static const ExplicitSpecifier getFromDecl(const FunctionDecl *Function) {
     return getFromDecl(const_cast<FunctionDecl *>(Function));

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e71bddc06b57..53d4571fa8ad 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3871,10 +3871,9 @@ def note_ovl_candidate : Note<
     "| has 
diff erent qualifiers (expected %5 but found %6)"
     "| has 
diff erent exception specification}4">;
 
-def note_ovl_candidate_explicit_forbidden : Note<
-    "candidate %0 ignored: cannot be explicit">;
-def note_explicit_bool_resolved_to_true : Note<
-    "explicit(bool) specifier resolved to true">;
+def note_ovl_candidate_explicit : Note<
+    "explicit %select{constructor|conversion function|deduction guide}0 "
+    "is not a candidate%select{| (explicit specifier evaluates to true)}1">;
 def note_ovl_candidate_inherited_constructor : Note<
     "constructor from base class %0 inherited here">;
 def note_ovl_candidate_inherited_constructor_slice : Note<

diff  --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 0ccb658c6a77..1394c6236965 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -732,10 +732,9 @@ class Sema;
     /// attribute disabled it.
     ovl_fail_enable_if,
 
-    /// This candidate constructor or conversion fonction
-    /// is used implicitly but the explicit(bool) specifier
-    /// was resolved to true
-    ovl_fail_explicit_resolved,
+    /// This candidate constructor or conversion function is explicit but
+    /// the context doesn't permit explicit functions.
+    ovl_fail_explicit,
 
     /// This candidate was not viable because its address could not be taken.
     ovl_fail_addr_not_available,

diff  --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 94d524a63f5a..d89d37d84995 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -3877,9 +3877,6 @@ ResolveConstructorOverload(Sema &S, SourceLocation DeclLoc,
     if (!Info.Constructor || Info.Constructor->isInvalidDecl())
       continue;
 
-    if (!AllowExplicit && Info.Constructor->isExplicit())
-      continue;
-
     if (OnlyListConstructors && !S.isInitListConstructor(Info.Constructor))
       continue;
 
@@ -3951,18 +3948,16 @@ ResolveConstructorOverload(Sema &S, SourceLocation DeclLoc,
         else
           Conv = cast<CXXConversionDecl>(D);
 
-        if (AllowExplicit || !Conv->isExplicit()) {
-          if (ConvTemplate)
-            S.AddTemplateConversionCandidate(
-                ConvTemplate, I.getPair(), ActingDC, Initializer, DestType,
-                CandidateSet, AllowExplicit, AllowExplicit,
-                /*AllowResultConversion*/ false);
-          else
-            S.AddConversionCandidate(Conv, I.getPair(), ActingDC, Initializer,
-                                     DestType, CandidateSet, AllowExplicit,
-                                     AllowExplicit,
-                                     /*AllowResultConversion*/ false);
-        }
+        if (ConvTemplate)
+          S.AddTemplateConversionCandidate(
+              ConvTemplate, I.getPair(), ActingDC, Initializer, DestType,
+              CandidateSet, AllowExplicit, AllowExplicit,
+              /*AllowResultConversion*/ false);
+        else
+          S.AddConversionCandidate(Conv, I.getPair(), ActingDC, Initializer,
+                                   DestType, CandidateSet, AllowExplicit,
+                                   AllowExplicit,
+                                   /*AllowResultConversion*/ false);
       }
     }
   }
@@ -4495,7 +4490,7 @@ static OverloadingResult TryRefInitWithConversionFunction(
         continue;
 
       if (!Info.Constructor->isInvalidDecl() &&
-          Info.Constructor->isConvertingConstructor(AllowExplicitCtors)) {
+          Info.Constructor->isConvertingConstructor(/*AllowExplicit*/true)) {
         if (Info.ConstructorTmpl)
           S.AddTemplateOverloadCandidate(
               Info.ConstructorTmpl, Info.FoundDecl,
@@ -4540,8 +4535,7 @@ static OverloadingResult TryRefInitWithConversionFunction(
       // FIXME: Do we need to make sure that we only consider conversion
       // candidates with reference-compatible results? That might be needed to
       // break recursion.
-      if ((AllowExplicitConvs || !Conv->isExplicit()) &&
-          (AllowRValues ||
+      if ((AllowRValues ||
            Conv->getConversionType()->isLValueReferenceType())) {
         if (ConvTemplate)
           S.AddTemplateConversionCandidate(
@@ -5153,7 +5147,7 @@ static void TryUserDefinedConversion(Sema &S,
           continue;
 
         if (!Info.Constructor->isInvalidDecl() &&
-            Info.Constructor->isConvertingConstructor(AllowExplicit)) {
+            Info.Constructor->isConvertingConstructor(/*AllowExplicit*/true)) {
           if (Info.ConstructorTmpl)
             S.AddTemplateOverloadCandidate(
                 Info.ConstructorTmpl, Info.FoundDecl,
@@ -5197,16 +5191,14 @@ static void TryUserDefinedConversion(Sema &S,
         else
           Conv = cast<CXXConversionDecl>(D);
 
-        if (AllowExplicit || !Conv->isExplicit()) {
-          if (ConvTemplate)
-            S.AddTemplateConversionCandidate(
-                ConvTemplate, I.getPair(), ActingDC, Initializer, DestType,
-                CandidateSet, AllowExplicit, AllowExplicit);
-          else
-            S.AddConversionCandidate(Conv, I.getPair(), ActingDC, Initializer,
-                                     DestType, CandidateSet, AllowExplicit,
-                                     AllowExplicit);
-        }
+        if (ConvTemplate)
+          S.AddTemplateConversionCandidate(
+              ConvTemplate, I.getPair(), ActingDC, Initializer, DestType,
+              CandidateSet, AllowExplicit, AllowExplicit);
+        else
+          S.AddConversionCandidate(Conv, I.getPair(), ActingDC, Initializer,
+                                   DestType, CandidateSet, AllowExplicit,
+                                   AllowExplicit);
       }
     }
   }
@@ -9778,9 +9770,8 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
       // C++ [over.match.copy]p1: (non-list copy-initialization from class)
       //   The converting constructors of T are candidate functions.
       if (!AllowExplicit) {
-        // Only consider converting constructors.
-        if (GD->isExplicit())
-          continue;
+        // Overload resolution checks whether the deduction guide is declared
+        // explicit for us.
 
         // When looking for a converting constructor, deduction guides that
         // could never be called with one argument are not interesting to

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index fa811ee2bd25..69609986be4d 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -3320,8 +3320,7 @@ IsInitializerListConstructorConversion(Sema &S, Expr *From, QualType ToType,
       continue;
 
     bool Usable = !Info.Constructor->isInvalidDecl() &&
-                  S.isInitListConstructor(Info.Constructor) &&
-                  (AllowExplicit || !Info.Constructor->isExplicit());
+                  S.isInitListConstructor(Info.Constructor);
     if (Usable) {
       // If the first argument is (a reference to) the target type,
       // suppress conversions.
@@ -3443,11 +3442,9 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
           continue;
 
         bool Usable = !Info.Constructor->isInvalidDecl();
-        if (ListInitializing)
-          Usable = Usable && (AllowExplicit || !Info.Constructor->isExplicit());
-        else
-          Usable = Usable &&
-                   Info.Constructor->isConvertingConstructor(AllowExplicit);
+        if (!ListInitializing)
+          Usable = Usable && Info.Constructor->isConvertingConstructor(
+                                 /*AllowExplicit*/ true);
         if (Usable) {
           bool SuppressUserConversions = !ConstructorsOnly;
           if (SuppressUserConversions && ListInitializing) {
@@ -3501,16 +3498,14 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
         else
           Conv = cast<CXXConversionDecl>(D);
 
-        if (AllowExplicit || !Conv->isExplicit()) {
-          if (ConvTemplate)
-            S.AddTemplateConversionCandidate(
-                ConvTemplate, FoundDecl, ActingContext, From, ToType,
-                CandidateSet, AllowObjCConversionOnExplicit, AllowExplicit);
-          else
-            S.AddConversionCandidate(
-                Conv, FoundDecl, ActingContext, From, ToType, CandidateSet,
-                AllowObjCConversionOnExplicit, AllowExplicit);
-        }
+        if (ConvTemplate)
+          S.AddTemplateConversionCandidate(
+              ConvTemplate, FoundDecl, ActingContext, From, ToType,
+              CandidateSet, AllowObjCConversionOnExplicit, AllowExplicit);
+        else
+          S.AddConversionCandidate(
+              Conv, FoundDecl, ActingContext, From, ToType, CandidateSet,
+              AllowObjCConversionOnExplicit, AllowExplicit);
       }
     }
   }
@@ -4543,11 +4538,6 @@ FindConversionForRefInit(Sema &S, ImplicitConversionSequence &ICS,
     else
       Conv = cast<CXXConversionDecl>(D);
 
-    // If this is an explicit conversion, and we're not allowed to consider
-    // explicit conversions, skip it.
-    if (!AllowExplicit && Conv->isExplicit())
-      continue;
-
     if (AllowRvalues) {
       // If we are initializing an rvalue reference, don't permit conversion
       // functions that return lvalues.
@@ -6183,6 +6173,15 @@ void Sema::AddOverloadCandidate(
   Candidate.IgnoreObjectArgument = false;
   Candidate.ExplicitCallArguments = Args.size();
 
+  // Explicit functions are not actually candidates at all if we're not
+  // allowing them in this context, but keep them around so we can point
+  // to them in diagnostics.
+  if (!AllowExplicit && ExplicitSpecifier::getFromDecl(Function).isExplicit()) {
+    Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_explicit;
+    return;
+  }
+
   if (Function->isMultiVersion() && Function->hasAttr<TargetAttr>() &&
       !Function->getAttr<TargetAttr>()->isDefaultVersion()) {
     Candidate.Viable = false;
@@ -6316,15 +6315,6 @@ void Sema::AddOverloadCandidate(
     }
   }
 
-  if (!AllowExplicit) {
-    ExplicitSpecifier ES = ExplicitSpecifier::getFromDecl(Function);
-    if (ES.getKind() != ExplicitSpecKind::ResolvedFalse) {
-      Candidate.Viable = false;
-      Candidate.FailureKind = ovl_fail_explicit_resolved;
-      return;
-    }
-  }
-
   if (EnableIfAttr *FailedAttr = CheckEnableIf(Function, Args)) {
     Candidate.Viable = false;
     Candidate.FailureKind = ovl_fail_enable_if;
@@ -6923,6 +6913,12 @@ void Sema::AddMethodTemplateCandidate(
                      Conversions, PO);
 }
 
+/// Determine whether a given function template has a simple explicit specifier
+/// or a non-value-dependent explicit-specification that evaluates to true.
+static bool isNonDependentlyExplicit(FunctionTemplateDecl *FTD) {
+  return ExplicitSpecifier::getFromDecl(FTD->getTemplatedDecl()).isExplicit();
+}
+
 /// Add a C++ function template specialization as a candidate
 /// in the candidate set, using template argument deduction to produce
 /// an appropriate function template specialization.
@@ -6935,6 +6931,18 @@ void Sema::AddTemplateOverloadCandidate(
   if (!CandidateSet.isNewCandidate(FunctionTemplate, PO))
     return;
 
+  // If the function template has a non-dependent explicit specification,
+  // exclude it now if appropriate; we are not permitted to perform deduction
+  // and substitution in this case.
+  if (!AllowExplicit && isNonDependentlyExplicit(FunctionTemplate)) {
+    OverloadCandidate &Candidate = CandidateSet.addCandidate();
+    Candidate.FoundDecl = FoundDecl;
+    Candidate.Function = FunctionTemplate->getTemplatedDecl();
+    Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_explicit;
+    return;
+  }
+
   // C++ [over.match.funcs]p7:
   //   In each case where a candidate is a function template, candidate
   //   function template specializations are generated using template argument
@@ -7122,6 +7130,9 @@ void Sema::AddConversionCandidate(
   // Per C++ [over.match.conv]p1, [over.match.ref]p1, an explicit conversion
   // operator is only a candidate if its return type is the target type or
   // can be converted to the target type with a qualification conversion.
+  //
+  // FIXME: Include such functions in the candidate list and explain why we
+  // can't select them.
   if (Conversion->isExplicit() &&
       !isAllowableExplicitConversion(*this, ConvType, ToType,
                                      AllowObjCConversionOnExplicit))
@@ -7143,6 +7154,15 @@ void Sema::AddConversionCandidate(
   Candidate.Viable = true;
   Candidate.ExplicitCallArguments = 1;
 
+  // Explicit functions are not actually candidates at all if we're not
+  // allowing them in this context, but keep them around so we can point
+  // to them in diagnostics.
+  if (!AllowExplicit && Conversion->isExplicit()) {
+    Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_explicit;
+    return;
+  }
+
   // C++ [over.match.funcs]p4:
   //   For conversion functions, the function is considered to be a member of
   //   the class of the implicit implied object argument for the purpose of
@@ -7267,13 +7287,6 @@ void Sema::AddConversionCandidate(
            "Can only end up with a standard conversion sequence or failure");
   }
 
-  if (!AllowExplicit && Conversion->getExplicitSpecifier().getKind() !=
-                            ExplicitSpecKind::ResolvedFalse) {
-    Candidate.Viable = false;
-    Candidate.FailureKind = ovl_fail_explicit_resolved;
-    return;
-  }
-
   if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, None)) {
     Candidate.Viable = false;
     Candidate.FailureKind = ovl_fail_enable_if;
@@ -7304,6 +7317,18 @@ void Sema::AddTemplateConversionCandidate(
   if (!CandidateSet.isNewCandidate(FunctionTemplate))
     return;
 
+  // If the function template has a non-dependent explicit specification,
+  // exclude it now if appropriate; we are not permitted to perform deduction
+  // and substitution in this case.
+  if (!AllowExplicit && isNonDependentlyExplicit(FunctionTemplate)) {
+    OverloadCandidate &Candidate = CandidateSet.addCandidate();
+    Candidate.FoundDecl = FoundDecl;
+    Candidate.Function = FunctionTemplate->getTemplatedDecl();
+    Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_explicit;
+    return;
+  }
+
   TemplateDeductionInfo Info(CandidateSet.getLocation());
   CXXConversionDecl *Specialization = nullptr;
   if (TemplateDeductionResult Result
@@ -10764,30 +10789,36 @@ static void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) {
 }
 
 static void DiagnoseFailedExplicitSpec(Sema &S, OverloadCandidate *Cand) {
-  ExplicitSpecifier ES;
-  const char *DeclName;
+  ExplicitSpecifier ES = ExplicitSpecifier::getFromDecl(Cand->Function);
+  assert(ES.isExplicit() && "not an explicit candidate");
+
+  unsigned Kind;
   switch (Cand->Function->getDeclKind()) {
   case Decl::Kind::CXXConstructor:
-    ES = cast<CXXConstructorDecl>(Cand->Function)->getExplicitSpecifier();
-    DeclName = "constructor";
+    Kind = 0;
     break;
   case Decl::Kind::CXXConversion:
-    ES = cast<CXXConversionDecl>(Cand->Function)->getExplicitSpecifier();
-    DeclName = "conversion operator";
+    Kind = 1;
     break;
   case Decl::Kind::CXXDeductionGuide:
-    ES = cast<CXXDeductionGuideDecl>(Cand->Function)->getExplicitSpecifier();
-    DeclName = "deductiong guide";
+    Kind = Cand->Function->isImplicit() ? 0 : 2;
     break;
   default:
     llvm_unreachable("invalid Decl");
   }
-  assert(ES.getExpr() && "null expression should be handled before");
-  S.Diag(Cand->Function->getLocation(),
-         diag::note_ovl_candidate_explicit_forbidden)
-      << DeclName;
-  S.Diag(ES.getExpr()->getBeginLoc(),
-         diag::note_explicit_bool_resolved_to_true);
+
+  // Note the location of the first (in-class) declaration; a redeclaration
+  // (particularly an out-of-class definition) will typically lack the
+  // 'explicit' specifier.
+  // FIXME: This is probably a good thing to do for all 'candidate' notes.
+  FunctionDecl *First = Cand->Function->getFirstDecl();
+  if (FunctionDecl *Pattern = First->getTemplateInstantiationPattern())
+    First = Pattern->getFirstDecl();
+
+  S.Diag(First->getLocation(),
+         diag::note_ovl_candidate_explicit)
+      << Kind << (ES.getExpr() ? 1 : 0)
+      << (ES.getExpr() ? ES.getExpr()->getSourceRange() : SourceRange());
 }
 
 static void DiagnoseOpenCLExtensionDisabled(Sema &S, OverloadCandidate *Cand) {
@@ -10888,7 +10919,7 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
   case ovl_fail_enable_if:
     return DiagnoseFailedEnableIfAttr(S, Cand);
 
-  case ovl_fail_explicit_resolved:
+  case ovl_fail_explicit:
     return DiagnoseFailedExplicitSpec(S, Cand);
 
   case ovl_fail_ext_disabled:
@@ -11053,6 +11084,23 @@ struct CompareOverloadCandidatesForDisplay {
       OverloadCandidateSet::CandidateSetKind CSK)
       : S(S), NumArgs(NArgs), CSK(CSK) {}
 
+  OverloadFailureKind EffectiveFailureKind(const OverloadCandidate *C) const {
+    // If there are too many or too few arguments, that's the high-order bit we
+    // want to sort by, even if the immediate failure kind was something else.
+    if (C->FailureKind == ovl_fail_too_many_arguments ||
+        C->FailureKind == ovl_fail_too_few_arguments)
+      return static_cast<OverloadFailureKind>(C->FailureKind);
+
+    if (C->Function) {
+      if (NumArgs > C->Function->getNumParams() && !C->Function->isVariadic())
+        return ovl_fail_too_many_arguments;
+      if (NumArgs < C->Function->getMinRequiredArguments())
+        return ovl_fail_too_few_arguments;
+    }
+
+    return static_cast<OverloadFailureKind>(C->FailureKind);
+  }
+
   bool operator()(const OverloadCandidate *L,
                   const OverloadCandidate *R) {
     // Fast-path this check.
@@ -11076,34 +11124,37 @@ struct CompareOverloadCandidatesForDisplay {
 
     // Criteria by which we can sort non-viable candidates:
     if (!L->Viable) {
+      OverloadFailureKind LFailureKind = EffectiveFailureKind(L);
+      OverloadFailureKind RFailureKind = EffectiveFailureKind(R);
+
       // 1. Arity mismatches come after other candidates.
-      if (L->FailureKind == ovl_fail_too_many_arguments ||
-          L->FailureKind == ovl_fail_too_few_arguments) {
-        if (R->FailureKind == ovl_fail_too_many_arguments ||
-            R->FailureKind == ovl_fail_too_few_arguments) {
+      if (LFailureKind == ovl_fail_too_many_arguments ||
+          LFailureKind == ovl_fail_too_few_arguments) {
+        if (RFailureKind == ovl_fail_too_many_arguments ||
+            RFailureKind == ovl_fail_too_few_arguments) {
           int LDist = std::abs((int)L->getNumParams() - (int)NumArgs);
           int RDist = std::abs((int)R->getNumParams() - (int)NumArgs);
           if (LDist == RDist) {
-            if (L->FailureKind == R->FailureKind)
+            if (LFailureKind == RFailureKind)
               // Sort non-surrogates before surrogates.
               return !L->IsSurrogate && R->IsSurrogate;
             // Sort candidates requiring fewer parameters than there were
             // arguments given after candidates requiring more parameters
             // than there were arguments given.
-            return L->FailureKind == ovl_fail_too_many_arguments;
+            return LFailureKind == ovl_fail_too_many_arguments;
           }
           return LDist < RDist;
         }
         return false;
       }
-      if (R->FailureKind == ovl_fail_too_many_arguments ||
-          R->FailureKind == ovl_fail_too_few_arguments)
+      if (RFailureKind == ovl_fail_too_many_arguments ||
+          RFailureKind == ovl_fail_too_few_arguments)
         return true;
 
       // 2. Bad conversions come first and are ordered by the number
       // of bad conversions and quality of good conversions.
-      if (L->FailureKind == ovl_fail_bad_conversion) {
-        if (R->FailureKind != ovl_fail_bad_conversion)
+      if (LFailureKind == ovl_fail_bad_conversion) {
+        if (RFailureKind != ovl_fail_bad_conversion)
           return true;
 
         // The conversion that can be fixed with a smaller number of changes,
@@ -11141,17 +11192,17 @@ struct CompareOverloadCandidatesForDisplay {
         if (leftBetter > 0) return true;
         if (leftBetter < 0) return false;
 
-      } else if (R->FailureKind == ovl_fail_bad_conversion)
+      } else if (RFailureKind == ovl_fail_bad_conversion)
         return false;
 
-      if (L->FailureKind == ovl_fail_bad_deduction) {
-        if (R->FailureKind != ovl_fail_bad_deduction)
+      if (LFailureKind == ovl_fail_bad_deduction) {
+        if (RFailureKind != ovl_fail_bad_deduction)
           return true;
 
         if (L->DeductionFailure.Result != R->DeductionFailure.Result)
           return RankDeductionFailure(L->DeductionFailure)
                < RankDeductionFailure(R->DeductionFailure);
-      } else if (R->FailureKind == ovl_fail_bad_deduction)
+      } else if (RFailureKind == ovl_fail_bad_deduction)
         return false;
 
       // TODO: others?
@@ -11180,7 +11231,8 @@ CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
   assert(!Cand->Viable);
 
   // Don't do anything on failures other than bad conversion.
-  if (Cand->FailureKind != ovl_fail_bad_conversion) return;
+  if (Cand->FailureKind != ovl_fail_bad_conversion)
+    return;
 
   // We only want the FixIts if all the arguments can be corrected.
   bool Unfixable = false;

diff  --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp
index ed6c6c0bccf1..f4e8521093fc 100644
--- a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp
@@ -38,7 +38,7 @@ namespace Numbers {
   // expected-note at -2 2 {{candidate constructor (the implicit move constructor) not viable}}
 #endif
 
-    explicit Number(double d) : d(d) {}
+    explicit Number(double d) : d(d) {} // expected-note 2{{explicit constructor is not a candidate}}
     double d;
   };
   Number zero(0.0f);

diff  --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp
index 869fc4f01493..3e8f18c93077 100644
--- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp
@@ -65,7 +65,7 @@ namespace test3 {
 namespace explicit_ctor {
   struct A {};
   struct B { // expected-note 2{{candidate}}
-    explicit B(const A&);
+    explicit B(const A&); // expected-note {{explicit constructor is not a candidate}}
   };
   A a;
   const B &b(a); // expected-error {{no viable conversion}}

diff  --git a/clang/test/CXX/dcl.decl/dcl.init/p14-0x.cpp b/clang/test/CXX/dcl.decl/dcl.init/p14-0x.cpp
index 419f2bff1282..e7f501352168 100644
--- a/clang/test/CXX/dcl.decl/dcl.init/p14-0x.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.init/p14-0x.cpp
@@ -5,7 +5,7 @@ struct NoDefault {
   NoDefault(int);
 };
 struct Explicit { // expected-note 2 {{candidate}} expected-note {{here}}
-  explicit Explicit(int);
+  explicit Explicit(int); // expected-note {{not a candidate}}
 };
 struct NoCopy {
   NoCopy();

diff  --git a/clang/test/CXX/drs/dr15xx.cpp b/clang/test/CXX/drs/dr15xx.cpp
index 80a27e7863c9..55d838e45a57 100644
--- a/clang/test/CXX/drs/dr15xx.cpp
+++ b/clang/test/CXX/drs/dr15xx.cpp
@@ -142,7 +142,7 @@ struct Z0 { // expected-note 0+ {{candidate}}
 };
 struct Z { // expected-note 0+ {{candidate}}
   explicit Z(); // expected-note 0+ {{here}}
-  explicit Z(int);
+  explicit Z(int); // expected-note {{not a candidate}}
   explicit Z(int, int); // expected-note 0+ {{here}}
 };
 template <class T> int Eat(T); // expected-note 0+ {{candidate}}

diff  --git a/clang/test/CXX/drs/dr1xx.cpp b/clang/test/CXX/drs/dr1xx.cpp
index 26ab67d54d31..1e78c03be8ab 100644
--- a/clang/test/CXX/drs/dr1xx.cpp
+++ b/clang/test/CXX/drs/dr1xx.cpp
@@ -600,7 +600,7 @@ namespace dr151 { // dr151: yes
 namespace dr152 { // dr152: yes
   struct A {
     A(); // expected-note 0-2{{not viable}}
-    explicit A(const A&);
+    explicit A(const A&); // expected-note 1-2{{not a candidate}}
   };
   A a1 = A();
 #if __cplusplus <= 201402L

diff  --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp
index 63e51a761449..7fd94b658f46 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp
@@ -27,7 +27,7 @@ void hiding() {
 
 struct ExplicitCopy {
   ExplicitCopy(); // expected-note 2{{not viable}}
-  explicit ExplicitCopy(const ExplicitCopy&);
+  explicit ExplicitCopy(const ExplicitCopy&); // expected-note 2{{not a candidate}}
 };
 auto init_kind_1 = [ec(ExplicitCopy())] {};
 auto init_kind_2 = [ec = ExplicitCopy()] {}; // expected-error {{no matching constructor}}

diff  --git a/clang/test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp b/clang/test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp
index 31a679f1ebc0..c63f30971539 100644
--- a/clang/test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp
+++ b/clang/test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp
@@ -4,7 +4,7 @@ namespace ExplicitConv {
   struct X { }; // expected-note 2{{candidate constructor}}
 
   struct Y {
-    explicit operator X() const;
+    explicit operator X() const; // expected-note {{not a candidate}}
   };
 
   void test(const Y& y) {
@@ -18,8 +18,8 @@ namespace DR899 {
   struct C { }; // expected-note 2 {{candidate constructor}}
 
   struct A {
-    explicit operator int() const;
-    explicit operator C() const;
+    explicit operator int() const; // expected-note {{not a candidate}}
+    explicit operator C() const; // expected-note {{not a candidate}}
   };
 
   struct B {

diff  --git a/clang/test/CXX/special/class.inhctor/p3.cpp b/clang/test/CXX/special/class.inhctor/p3.cpp
index 19f15ebe0f0b..9d50e0c05cee 100644
--- a/clang/test/CXX/special/class.inhctor/p3.cpp
+++ b/clang/test/CXX/special/class.inhctor/p3.cpp
@@ -15,7 +15,7 @@ D1 d1a(1), d1b(1, 1);
 D1 fd1() { return 1; }
 
 struct B2 {
-  explicit B2(int, int = 0, int = 0);
+  explicit B2(int, int = 0, int = 0); // expected-note {{not a candidate}}
 };
 struct D2 : B2 { // expected-note 2{{candidate constructor}}
   using B2::B2;

diff  --git a/clang/test/PCH/cxx-explicit-specifier.cpp b/clang/test/PCH/cxx-explicit-specifier.cpp
index a800cfc9d50b..9c7b0560e5bf 100644
--- a/clang/test/PCH/cxx-explicit-specifier.cpp
+++ b/clang/test/PCH/cxx-explicit-specifier.cpp
@@ -40,7 +40,9 @@ struct A {
 #else
 //expected-note at -6+ {{candidate constructor}}
 //expected-note at -9+ {{candidate constructor}}
-//expected-note at -6+ {{candidate function}}
+//expected-note-re at -7+ {{explicit constructor is not a candidate{{$}}}}
+//expected-note at -7+ {{candidate function}}
+//expected-note at -7+ {{explicit conversion function is not a candidate (explicit specifier evaluates to true)}}
 
 //CHECK: explicit{{ +}}A(
 //CHECK-NEXT: explicit(false){{ +}}operator
@@ -73,12 +75,10 @@ B<true> b_true;
 B<false> b_false;
 #else
 //expected-note at -8 {{candidate template ignored}}
-//expected-note at -8+ {{explicit constructor}}
+//expected-note at -8 {{explicit constructor declared here}}
 //expected-note at -15+ {{candidate constructor}}
-//expected-note at -8+ {{candidate conversion operator ignored}}
-//expected-note at -9+ {{explicit(bool) specifier resolved to true}}
-//expected-note at -12 {{explicit(bool) specifier resolved to true}}
-//expected-note at -13+ {{candidate deductiong guide ignored}}
+//expected-note at -8+ {{explicit conversion function is not a candidate (explicit specifier}}
+//expected-note at -11 {{explicit constructor is not a candidate (explicit specifier}}
 
 //CHECK: explicit(b){{ +}}A
 //CHECK: explicit(b{{ +}}^{{ +}}T::value){{ +}}operator

diff  --git a/clang/test/SemaCXX/conversion-function.cpp b/clang/test/SemaCXX/conversion-function.cpp
index 1d3593d8ed1b..1486abea8c60 100644
--- a/clang/test/SemaCXX/conversion-function.cpp
+++ b/clang/test/SemaCXX/conversion-function.cpp
@@ -211,7 +211,7 @@ namespace smart_ptr {
   // expected-note at -2 {{candidate constructor (the implicit move constructor) not}}
 #endif
 
-    explicit X(Y);
+    explicit X(Y); // expected-note {{not a candidate}}
   };
 
   Y make_Y();

diff  --git a/clang/test/SemaCXX/convert-to-bool.cpp b/clang/test/SemaCXX/convert-to-bool.cpp
index 117b50899b7e..94d994143922 100644
--- a/clang/test/SemaCXX/convert-to-bool.cpp
+++ b/clang/test/SemaCXX/convert-to-bool.cpp
@@ -11,7 +11,7 @@ struct ConvToInt {
 };
 
 struct ExplicitConvToBool {
-  explicit operator bool();
+  explicit operator bool(); // expected-note {{explicit}}
 #if __cplusplus <= 199711L // C++03 or earlier modes
   // expected-warning at -2{{explicit conversion functions are a C++11 extension}}
 #endif
@@ -45,7 +45,7 @@ void test_conv_to_bool(ConvToBool ctb, ConvToInt cti, ExplicitConvToBool ecb) {
 void accepts_bool(bool) { } // expected-note{{candidate function}}
 
 struct ExplicitConvToRef {
-  explicit operator int&();
+  explicit operator int&(); // expected-note {{explicit}}
 #if (__cplusplus <= 199711L) // C++03 or earlier modes
   // expected-warning at -2{{explicit conversion functions are a C++11 extension}}
 #endif
@@ -58,14 +58,14 @@ void test_explicit_bool(ExplicitConvToBool ecb) {
 }
 
 void test_explicit_conv_to_ref(ExplicitConvToRef ecr) {
-  int& i1 = ecr; // expected-error{{non-const lvalue reference to type 'int' cannot bind to a value of unrelated type 'ExplicitConvToRef'}}
+  int& i1 = ecr; // expected-error{{no viable conversion from 'ExplicitConvToRef' to 'int'}}
   int& i2(ecr); // okay
 }
 
 struct A { };
 struct B { };
 struct C {
-  explicit operator A&(); 
+  explicit operator A&();  // expected-note {{explicit}}
 #if __cplusplus <= 199711L // C++03 or earlier modes
 // expected-warning at -2{{explicit conversion functions are a C++11 extension}}
 #endif

diff  --git a/clang/test/SemaCXX/converting-constructor.cpp b/clang/test/SemaCXX/converting-constructor.cpp
index 75002fa585b8..d66e09e9c4b0 100644
--- a/clang/test/SemaCXX/converting-constructor.cpp
+++ b/clang/test/SemaCXX/converting-constructor.cpp
@@ -36,7 +36,7 @@ class FromShortExplicitly { // expected-note{{candidate constructor (the implici
 #endif
 
 public:
-  explicit FromShortExplicitly(short s);
+  explicit FromShortExplicitly(short s); // expected-note {{explicit constructor is not a candidate}}
 };
 
 void explicit_constructor(short s) {

diff  --git a/clang/test/SemaCXX/copy-initialization.cpp b/clang/test/SemaCXX/copy-initialization.cpp
index 8866fe70db86..29c91ba85635 100644
--- a/clang/test/SemaCXX/copy-initialization.cpp
+++ b/clang/test/SemaCXX/copy-initialization.cpp
@@ -4,9 +4,9 @@
 
 class X {
 public:
-  explicit X(const X&);
+  explicit X(const X&); // expected-note 2{{not a candidate}}
   X(int*); // expected-note 3{{candidate constructor}}
-  explicit X(float*); // expected-note {{candidate constructor}}
+  explicit X(float*); // expected-note {{candidate constructor}} expected-note 2{{not a candidate}}
 };
 
 class Y : public X { };

diff  --git a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp
index 56fa76f0a8bd..df776b390548 100644
--- a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp
+++ b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp
@@ -55,8 +55,8 @@ template<int a>
   // expected-note at -1 {{candidate constructor}} expected-note at -1 {{candidate constructor}}
   // expected-note at -2 {{candidate constructor}} expected-note at -2 {{candidate constructor}}
   explicit(a == 0)
-C(int),
-C(double);
+C(int), // expected-note 2{{not a candidate}}
+C(double); // expected-note 2{{not a candidate}}
 };
 
 C<0> c0 = 0.0; // expected-error {{no viable conversion}}
@@ -105,7 +105,7 @@ template<bool b>
   struct A {
     // expected-note at -1+ {{candidate constructor}}
     // expected-note at -2+ {{candidate function}}
-    explicit(b) A(int, int = 0);
+    explicit(b) A(int, int = 0); // expected-note {{not a candidate}}
   // expected-note at -1+ {{explicit constructor declared here}}
 };
 
@@ -154,10 +154,9 @@ struct A {
   // expected-note at -2 {{candidate constructor}} expected-note at -2 {{candidate constructor}}
   template<typename T2>
   explicit(a ^ is_same<T1, T2>::value)
-  // expected-note at -1+ {{explicit(bool) specifier resolved to true}}
   A(T2) {}
   // expected-note at -1+ {{explicit constructor declared here}}
-  // expected-note at -2+ {{candidate constructor ignored}}
+  // expected-note at -2+ {{not a candidate}}
 };
 
 A<true, int> a0 = 0.0; // expected-error {{no viable conversion}}
@@ -196,17 +195,15 @@ struct A {
   // expected-note at -1+ {{candidate constructor}}
   template<typename T>
   explicit(enable_ifv<is_same<int, T>::value, a>::value)
-  //expected-note at -1 {{explicit(bool) specifier resolved to true}}
   A(T) {}
   // expected-note at -1+ {{substitution failure}}
-  // expected-note at -2 {{candidate constructor ignored}}
+  // expected-note at -2 {{not a candidate}}
   // expected-note at -3 {{explicit constructor declared here}}
   template<typename T, bool c = true>
   explicit(enable_ifv<is_same<bool, T>::value, a>::value)
-  //expected-note at -1 {{explicit(bool) specifier resolved to true}}
   A(T) {}
   // expected-note at -1+ {{substitution failure}}
-  // expected-note at -2 {{candidate constructor ignored}}
+  // expected-note at -2 {{not a candidate}}
   // expected-note at -3 {{explicit constructor declared here}}
 };
 
@@ -236,7 +233,7 @@ namespace conversion {
 
 template<bool a>
 struct A {
-  explicit(a) operator int ();
+  explicit(a) operator int (); // expected-note+ {{not a candidate}}
 };
 
 template<bool a>
@@ -293,10 +290,9 @@ template<bool a>
 struct A {
   template<typename T2>
   explicit(enable_ifv<is_same<B, T2>::value, a>::value)
-  // expected-note at -1+ {{explicit(bool) specifier resolved to true}}
   operator T2() { return T2(); };
   // expected-note at -1+ {{substitution failure}}
-  // expected-note at -2+ {{candidate conversion}}
+  // expected-note at -2+ {{not a candidate}}
 };
 
 A<false> A_false;
@@ -348,9 +344,8 @@ struct A {
   // expected-note at -2+ {{candidate function}}
   template<typename ... Ts>
   explicit((is_same<T, Ts>::value && ...))
-  // expected-note at -1 {{explicit(bool) specifier resolved to true}}
   A(Ts...);
-  // expected-note at -1 {{candidate constructor}}
+  // expected-note at -1 {{not a candidate}}
   // expected-note at -2 {{explicit constructor}}
 };
 
@@ -517,8 +512,8 @@ class Y { }; // expected-note+ {{candidate constructor (the implicit}}
 template<bool b>
 struct Z {
   explicit(b) operator X() const;
-  explicit(b) operator Y() const;
-  explicit(b) operator int() const;
+  explicit(b) operator Y() const; // expected-note 2{{not a candidate}}
+  explicit(b) operator int() const; // expected-note {{not a candidate}}
 };
 
 void testExplicit()
@@ -559,9 +554,7 @@ template<typename T1>
 struct C {
   template<typename T>
   explicit(!is_same<T1, T>::value)
-  // expected-note at -1+ {{explicit(bool) specifier resolved to true}}
-  operator T();
-  // expected-note at -1+ {{candidate conversion operator ignored}}
+  operator T(); // expected-note+ {{explicit conversion function is not a candidate}}
 };
 
 using Bool = C<bool>;
@@ -684,10 +677,9 @@ template<typename T1 = int, typename T2 = int>
 struct A {
   // expected-note at -1+ {{candidate template ignored}}
   explicit(!is_same<T1, T2>::value)
-  // expected-note at -1+ {{explicit(bool) specifier resolved to true}}
   A(T1 = 0, T2 = 0) {}
-  // expected-note at -1 {{explicit constructor}}
-  // expected-note at -2+ {{candidate deductiong guide ignored}}
+  // expected-note at -1 {{explicit constructor declared here}}
+  // expected-note at -2 2{{explicit constructor is not a candidate}}
 };
 
 A a0 = 0;
@@ -723,15 +715,14 @@ using size_t = decltype(sizeof(0));
 
 struct Str {// expected-note+ {{candidate constructor}}
   template <size_t N>
-  explicit(N > 7)// expected-note {{resolved to true}}
-  Str(char const (&str)[N]);
+  explicit(N > 7)
+  Str(char const (&str)[N]); // expected-note {{explicit constructor is not a candidate}}
 };
 
 template <size_t N>
 Str::Str(char const(&str)[N]) { }
-// expected-note at -1 {{candidate constructor}}
 
 Str a = "short";
 Str b = "not so short";// expected-error {{no viable conversion}}
 
-}
\ No newline at end of file
+}

diff  --git a/clang/test/SemaCXX/default1.cpp b/clang/test/SemaCXX/default1.cpp
index 3bc6f832b686..457cada13bda 100644
--- a/clang/test/SemaCXX/default1.cpp
+++ b/clang/test/SemaCXX/default1.cpp
@@ -31,7 +31,7 @@ struct Y { // expected-note 2{{candidate constructor (the implicit copy construc
 // expected-note at -2 2 {{candidate constructor (the implicit move constructor) not viable}}
 #endif
 
-  explicit Y(int);
+  explicit Y(int); // expected-note 2{{explicit constructor is not a candidate}}
 };
 
 void k(Y y = 17); // expected-error{{no viable conversion}} \

diff  --git a/clang/test/SemaCXX/explicit.cpp b/clang/test/SemaCXX/explicit.cpp
index 58760d347f0d..ba2c36d99daf 100644
--- a/clang/test/SemaCXX/explicit.cpp
+++ b/clang/test/SemaCXX/explicit.cpp
@@ -7,7 +7,7 @@ struct A {
 };
 
 struct B { // expected-note+ {{candidate}}
-  explicit B(int);
+  explicit B(int); // expected-note {{not a candidate}}
 };
 
 B::B(int) { } // expected-note+ {{here}}
@@ -78,8 +78,8 @@ namespace Conversion {
 
     struct Z {
       explicit operator X() const;
-      explicit operator Y() const;
-      explicit operator int() const;
+      explicit operator Y() const; // expected-note 2{{not a candidate}}
+      explicit operator int() const; // expected-note {{not a candidate}}
     };
     
     Z z;
@@ -118,7 +118,7 @@ namespace Conversion {
     };
 
     struct NotBool {
-      explicit operator bool(); // expected-note {{conversion to integral type 'bool'}}
+      explicit operator bool(); // expected-note {{conversion to integral type 'bool'}} expected-note 4{{explicit conversion function is not a candidate}}
     };
     Bool    b;
     NotBool n;


        


More information about the cfe-commits mailing list