[cfe-commits] r93316 - in /cfe/trunk: lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaExprCXX.cpp lib/Sema/SemaOverload.cpp lib/Sema/SemaOverload.h test/SemaCXX/overload-call.cpp test/SemaCXX/overload-member-call.cpp

John McCall rjmccall at apple.com
Wed Jan 13 01:16:55 PST 2010


Author: rjmccall
Date: Wed Jan 13 03:16:55 2010
New Revision: 93316

URL: http://llvm.org/viewvc/llvm-project?rev=93316&view=rev
Log:
Record some basic information about bad conversion sequences.  Use that
information to feed diagnostics instead of regenerating it.  Much room for
improvement here, but fixes some unfortunate problems reporting on method calls.


Modified:
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaOverload.h
    cfe/trunk/test/SemaCXX/overload-call.cpp
    cfe/trunk/test/SemaCXX/overload-member-call.cpp

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=93316&r1=93315&r2=93316&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Jan 13 03:16:55 2010
@@ -4389,7 +4389,10 @@
     = CompareReferenceRelationship(DeclLoc, T1, T2, DerivedToBase);
 
   // Most paths end in a failed conversion.
-  if (ICS) ICS->setBad();
+  if (ICS) {
+    ICS->setBad();
+    ICS->Bad.init(BadConversionSequence::no_conversion, Init, DeclType);
+  }
 
   // C++ [dcl.init.ref]p5:
   //   A reference to type "cv1 T1" is initialized by an expression

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=93316&r1=93315&r2=93316&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Wed Jan 13 03:16:55 2010
@@ -1076,6 +1076,7 @@
                                 bool Elidable,
                                 ImplicitConversionSequence& ICS) {
   ICS.setBad();
+  ICS.Bad.init(BadConversionSequence::no_conversion, From, ToType);
   if (Elidable && getLangOptions().CPlusPlus0x) {
     ICS = TryImplicitConversion(From, ToType,
                                 /*SuppressUserConversions=*/false,
@@ -1942,7 +1943,7 @@
 
   ImplicitConversionSequence E1ToC2, E2ToC2;
   E1ToC2.setBad();
-  E2ToC2.setBad();
+  E2ToC2.setBad();  
   if (Context.getCanonicalType(Composite1) !=
       Context.getCanonicalType(Composite2)) {
     E1ToC2 = TryImplicitConversion(E1, Composite2,

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=93316&r1=93315&r2=93316&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Wed Jan 13 03:16:55 2010
@@ -489,8 +489,10 @@
     //   of a class copy-initialization, or by 13.3.1.4, 13.3.1.5, or
     //   13.3.1.6 in all cases, only standard conversion sequences and
     //   ellipsis conversion sequences are allowed.
-    if (SuppressUserConversions && ICS.isUserDefined())
+    if (SuppressUserConversions && ICS.isUserDefined()) {
       ICS.setBad();
+      ICS.Bad.init(BadConversionSequence::suppressed_user, From, ToType);
+    }
   } else if (UserDefResult == OR_Ambiguous) {
     ICS.setAmbiguous();
     ICS.Ambiguous.setFromType(From->getType());
@@ -501,6 +503,7 @@
         ICS.Ambiguous.addConversion(Cand->Function);
   } else {
     ICS.setBad();
+    ICS.Bad.init(BadConversionSequence::no_conversion, From, ToType);
   }
 
   return ICS;
@@ -2129,6 +2132,7 @@
                             bool InOverloadResolution) {
   if (ToType->isReferenceType()) {
     ImplicitConversionSequence ICS;
+    ICS.Bad.init(BadConversionSequence::no_conversion, From, ToType);
     CheckReferenceInit(From, ToType,
                        /*FIXME:*/From->getLocStart(),
                        SuppressUserConversions,
@@ -2223,8 +2227,10 @@
   QualType FromTypeCanon = Context.getCanonicalType(FromType);
   if (ImplicitParamType.getCVRQualifiers() 
                                     != FromTypeCanon.getLocalCVRQualifiers() &&
-      !ImplicitParamType.isAtLeastAsQualifiedAs(FromTypeCanon))
+      !ImplicitParamType.isAtLeastAsQualifiedAs(FromTypeCanon)) {
+    ICS.Bad.init(BadConversionSequence::bad_qualifiers, FromType, ImplicitParamType);
     return ICS;
+  }
 
   // Check that we have either the same type or a derived type. It
   // affects the conversion rank.
@@ -2233,8 +2239,10 @@
     ICS.Standard.Second = ICK_Identity;
   else if (IsDerivedFrom(FromType, ClassType))
     ICS.Standard.Second = ICK_Derived_To_Base;
-  else
+  else {
+    ICS.Bad.init(BadConversionSequence::unrelated_class, FromType, ImplicitParamType);
     return ICS;
+  }
 
   // Success. Mark this as a reference binding.
   ICS.setStandard();
@@ -2385,6 +2393,7 @@
   if ((NumArgs + (PartialOverloading && NumArgs)) > NumArgsInProto && 
       !Proto->isVariadic()) {
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_too_many_arguments;
     return;
   }
 
@@ -2397,6 +2406,7 @@
   if (NumArgs < MinRequiredArgs && !PartialOverloading) {
     // Not enough arguments.
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_too_few_arguments;
     return;
   }
 
@@ -2416,6 +2426,7 @@
                                 /*InOverloadResolution=*/true);
       if (Candidate.Conversions[ArgIdx].isBad()) {
         Candidate.Viable = false;
+        Candidate.FailureKind = ovl_fail_bad_conversion;
         break;
       }
     } else {
@@ -2532,6 +2543,7 @@
   // list (8.3.5).
   if (NumArgs > NumArgsInProto && !Proto->isVariadic()) {
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_too_many_arguments;
     return;
   }
 
@@ -2544,6 +2556,7 @@
   if (NumArgs < MinRequiredArgs) {
     // Not enough arguments.
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_too_few_arguments;
     return;
   }
 
@@ -2560,6 +2573,7 @@
       = TryObjectArgumentInitialization(ObjectType, Method, ActingContext);
     if (Candidate.Conversions[0].isBad()) {
       Candidate.Viable = false;
+      Candidate.FailureKind = ovl_fail_bad_conversion;
       return;
     }
   }
@@ -2579,6 +2593,7 @@
                                 /*InOverloadResolution=*/true);
       if (Candidate.Conversions[ArgIdx + 1].isBad()) {
         Candidate.Viable = false;
+        Candidate.FailureKind = ovl_fail_bad_conversion;
         break;
       }
     } else {
@@ -2670,6 +2685,7 @@
     OverloadCandidate &Candidate = CandidateSet.back();
     Candidate.Function = FunctionTemplate->getTemplatedDecl();
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_bad_deduction;
     Candidate.IsSurrogate = false;
     Candidate.IgnoreObjectArgument = false;
     return;
@@ -2726,6 +2742,7 @@
     Candidate.Conversions[0].Standard.Second = ICK_Identity;
   if (Candidate.Conversions[0].isBad()) {
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_bad_conversion;
     return;
   }
   
@@ -2737,6 +2754,7 @@
   QualType ToCanon = Context.getCanonicalType(ToType).getUnqualifiedType();
   if (FromCanon == ToCanon || IsDerivedFrom(FromCanon, ToCanon)) {
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_bad_conversion;
     return;
   }
   
@@ -2774,6 +2792,7 @@
 
   case ImplicitConversionSequence::BadConversion:
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_bad_conversion;
     break;
 
   default:
@@ -2847,6 +2866,7 @@
     = TryObjectArgumentInitialization(ObjectType, Conversion, ActingContext);
   if (ObjectInit.isBad()) {
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_bad_conversion;
     return;
   }
 
@@ -2869,6 +2889,7 @@
   // list (8.3.5).
   if (NumArgs > NumArgsInProto && !Proto->isVariadic()) {
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_too_many_arguments;
     return;
   }
 
@@ -2877,6 +2898,7 @@
   if (NumArgs < NumArgsInProto) {
     // Not enough arguments.
     Candidate.Viable = false;
+    Candidate.FailureKind = ovl_fail_too_few_arguments;
     return;
   }
 
@@ -2896,6 +2918,7 @@
                                 /*InOverloadResolution=*/false);
       if (Candidate.Conversions[ArgIdx + 1].isBad()) {
         Candidate.Viable = false;
+        Candidate.FailureKind = ovl_fail_bad_conversion;
         break;
       }
     } else {
@@ -3038,6 +3061,7 @@
     }
     if (Candidate.Conversions[ArgIdx].isBad()) {
       Candidate.Viable = false;
+      Candidate.FailureKind = ovl_fail_bad_conversion;
       break;
     }
   }
@@ -4336,36 +4360,69 @@
 
 namespace {
 
-void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I,
-                           Expr **Args, unsigned NumArgs) {
+void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) {
+  const ImplicitConversionSequence &Conv = Cand->Conversions[I];
+  assert(Conv.isBad());
   assert(Cand->Function && "for now, candidate must be a function");
   FunctionDecl *Fn = Cand->Function;
 
   // There's a conversion slot for the object argument if this is a
   // non-constructor method.  Note that 'I' corresponds the
   // conversion-slot index.
+  bool isObjectArgument = false;
   if (isa<CXXMethodDecl>(Fn) && !isa<CXXConstructorDecl>(Fn)) {
-    // FIXME: talk usefully about bad conversions for object arguments.
-    if (I == 0) return S.NoteOverloadCandidate(Fn);
-    else I--;
+    if (I == 0)
+      isObjectArgument = true;
+    else
+      I--;
   }
 
-  // FIXME: can we have a bad conversion on an ellipsis parameter?
-  assert(I < NumArgs && "index exceeds number of formal arguments");
-  assert(I < Fn->getType()->getAs<FunctionProtoType>()->getNumArgs() &&
-         "index exceeds number of formal parameters");
-
   std::string FnDesc;
   OverloadCandidateKind FnKind = ClassifyOverloadCandidate(S, Fn, FnDesc);
 
-  QualType FromTy = Args[I]->getType();
-  QualType ToTy = Fn->getType()->getAs<FunctionProtoType>()->getArgType(I);
+  Expr *FromExpr = Conv.Bad.FromExpr;
+  QualType FromTy = Conv.Bad.getFromType();
+  QualType ToTy = Conv.Bad.getToType();
 
   // TODO: specialize based on the kind of mismatch
   S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_conv)
     << (unsigned) FnKind << FnDesc
-    << Args[I]->getSourceRange() << FromTy << ToTy
-    << I+1;
+    << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
+    << FromTy << ToTy << I+1;
+}
+
+void DiagnoseArityMismatch(Sema &S, OverloadCandidate *Cand,
+                           unsigned NumFormalArgs) {
+  // TODO: treat calls to a missing default constructor as a special case
+
+  FunctionDecl *Fn = Cand->Function;
+  const FunctionProtoType *FnTy = Fn->getType()->getAs<FunctionProtoType>();
+
+  unsigned MinParams = Fn->getMinRequiredArguments();
+  
+  // at least / at most / exactly
+  unsigned mode, modeCount;
+  if (NumFormalArgs < MinParams) {
+    assert(Cand->FailureKind == ovl_fail_too_few_arguments);
+    if (MinParams != FnTy->getNumArgs() || FnTy->isVariadic())
+      mode = 0; // "at least"
+    else
+      mode = 2; // "exactly"
+    modeCount = MinParams;
+  } else {
+    assert(Cand->FailureKind == ovl_fail_too_many_arguments);
+    if (MinParams != FnTy->getNumArgs())
+      mode = 1; // "at most"
+    else
+      mode = 2; // "exactly"
+    modeCount = FnTy->getNumArgs();
+  }
+
+  std::string Description;
+  OverloadCandidateKind FnKind = ClassifyOverloadCandidate(S, Fn, Description);
+
+  S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity)
+    << (unsigned) FnKind << Description << mode << modeCount << NumFormalArgs;
 }
 
 void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
@@ -4388,52 +4445,24 @@
     return;
   }
 
-  // Diagnose arity mismatches.
-  // TODO: treat calls to a missing default constructor as a special case
-  unsigned NumFormalArgs = NumArgs;
-  if (isa<CXXMethodDecl>(Fn) && !isa<CXXConstructorDecl>(Fn))
-    NumFormalArgs--;
-  const FunctionProtoType *FnTy = Fn->getType()->getAs<FunctionProtoType>();
-  unsigned MinParams = Fn->getMinRequiredArguments();
-  if (NumFormalArgs < MinParams ||
-      (NumFormalArgs > FnTy->getNumArgs() && !FnTy->isVariadic())) {
-    std::string Description;
-    OverloadCandidateKind FnKind = ClassifyOverloadCandidate(S, Fn, Description);
-
-    // at least / at most / exactly
-    unsigned mode, modeCount;
-    if (NumFormalArgs < MinParams) {
-      if (MinParams != FnTy->getNumArgs())
-        mode = 0; // "at least"
-      else
-        mode = 2; // "exactly"
-      modeCount = MinParams;
-    } else {
-      if (MinParams != FnTy->getNumArgs())
-        mode = 1; // "at most"
-      else
-        mode = 2; // "exactly"
-      modeCount = FnTy->getNumArgs();
-    }
-
-    S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity)
-      << (unsigned) FnKind << Description << mode << modeCount << NumFormalArgs;
-    return;
-  }
-
-  // Look for bad conversions.
-  if (!Cand->Conversions.empty()) {
-    for (unsigned I = 0, N = Cand->Conversions.size(); I != N; ++I) {
-      if (!Cand->Conversions[I].isBad())
-        continue;
-
-      DiagnoseBadConversion(S, Cand, I, Args, NumArgs);
-      return;
-    }
+  switch (Cand->FailureKind) {
+  case ovl_fail_too_many_arguments:
+  case ovl_fail_too_few_arguments:
+    return DiagnoseArityMismatch(S, Cand, NumArgs);
+
+  case ovl_fail_bad_deduction:
+    return S.NoteOverloadCandidate(Fn);
+
+  case ovl_fail_bad_conversion:
+    for (unsigned I = 0, N = Cand->Conversions.size(); I != N; ++I)
+      if (Cand->Conversions[I].isBad())
+        return DiagnoseBadConversion(S, Cand, I);
+    
+    // FIXME: this currently happens when we're called from SemaInit
+    // when user-conversion overload fails.  Figure out how to handle
+    // those conditions and diagnose them well.
+    return S.NoteOverloadCandidate(Fn);
   }
-
-  // Give up and give the generic message.
-  S.NoteOverloadCandidate(Fn);
 }
 
 void NoteSurrogateCandidate(Sema &S, OverloadCandidate *Cand) {

Modified: cfe/trunk/lib/Sema/SemaOverload.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.h?rev=93316&r1=93315&r2=93316&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.h (original)
+++ cfe/trunk/lib/Sema/SemaOverload.h Wed Jan 13 03:16:55 2010
@@ -16,6 +16,7 @@
 #define LLVM_CLANG_SEMA_OVERLOAD_H
 
 #include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/Type.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
@@ -241,6 +242,51 @@
     void copyFrom(const AmbiguousConversionSequence &);
   };
 
+  /// BadConversionSequence - Records information about an invalid
+  /// conversion sequence.
+  struct BadConversionSequence {
+    enum FailureKind {
+      no_conversion,
+      unrelated_class,
+      suppressed_user,
+      bad_qualifiers
+    };
+
+    // This can be null, e.g. for implicit object arguments.
+    Expr *FromExpr;
+
+    FailureKind Kind;
+
+  private:
+    // The type we're converting from (an opaque QualType).
+    void *FromTy;
+
+    // The type we're converting to (an opaque QualType).
+    void *ToTy;
+
+  public:
+    void init(FailureKind K, Expr *From, QualType To) {
+      init(K, From->getType(), To);
+      FromExpr = From;
+    }
+    void init(FailureKind K, QualType From, QualType To) {
+      Kind = K;
+      FromExpr = 0;
+      setFromType(From);
+      setToType(To);
+    }
+
+    QualType getFromType() const { return QualType::getFromOpaquePtr(FromTy); }
+    QualType getToType() const { return QualType::getFromOpaquePtr(ToTy); }
+
+    void setFromExpr(Expr *E) {
+      FromExpr = E;
+      setFromType(E->getType());
+    }
+    void setFromType(QualType T) { FromTy = T.getAsOpaquePtr(); }
+    void setToType(QualType T) { ToTy = T.getAsOpaquePtr(); }
+  };
+
   /// ImplicitConversionSequence - Represents an implicit conversion
   /// sequence, which may be a standard conversion sequence
   /// (C++ 13.3.3.1.1), user-defined conversion sequence (C++ 13.3.3.1.2),
@@ -280,6 +326,10 @@
       /// When ConversionKind == AmbiguousConversion, provides the
       /// details of the ambiguous conversion.
       AmbiguousConversionSequence Ambiguous;
+
+      /// When ConversionKind == BadConversion, provides the details
+      /// of the bad conversion.
+      BadConversionSequence Bad;
     };
 
     ImplicitConversionSequence() : ConversionKind(BadConversion) {}
@@ -294,7 +344,7 @@
       case UserDefinedConversion: UserDefined = Other.UserDefined; break;
       case AmbiguousConversion: Ambiguous.copyFrom(Other.Ambiguous); break;
       case EllipsisConversion: break;
-      case BadConversion: break;
+      case BadConversion: Bad = Other.Bad; break;
       }
     }
 
@@ -336,6 +386,13 @@
     void DebugPrint() const;
   };
 
+  enum OverloadFailureKind {
+    ovl_fail_too_many_arguments,
+    ovl_fail_too_few_arguments,
+    ovl_fail_bad_conversion,
+    ovl_fail_bad_deduction
+  };
+
   /// OverloadCandidate - A single candidate in an overload set (C++ 13.3).
   struct OverloadCandidate {
     /// Function - The actual function that this candidate
@@ -376,6 +433,10 @@
     /// object argument.
     bool IgnoreObjectArgument;
 
+    /// FailureKind - The reason why this candidate is not viable.
+    /// Actually an OverloadFailureKind.
+    unsigned char FailureKind;
+
     /// FinalConversion - For a conversion function (where Function is
     /// a CXXConversionDecl), the standard conversion that occurs
     /// after the call to the overload candidate to convert the result

Modified: cfe/trunk/test/SemaCXX/overload-call.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/overload-call.cpp?rev=93316&r1=93315&r2=93316&view=diff

==============================================================================
--- cfe/trunk/test/SemaCXX/overload-call.cpp (original)
+++ cfe/trunk/test/SemaCXX/overload-call.cpp Wed Jan 13 03:16:55 2010
@@ -304,10 +304,16 @@
 
 // Tests the exact text used to note the candidates
 namespace test1 {
-  template <class T> void foo(T t, unsigned N); // expected-note {{candidate function [with T = int]}}
-  void foo(int n, char N); // expected-note {{candidate function}} 
+  template <class T> void foo(T t, unsigned N); // expected-note {{candidate function [with T = int] not viable: no known conversion from 'char const [6]' to 'unsigned int' for argument 2}}
+  void foo(int n, char N); // expected-note {{candidate function not viable: no known conversion from 'char const [6]' to 'char' for argument 2}} 
+  void foo(int n); // expected-note {{candidate function not viable: requires 1 argument, but 2 were provided}}
+  void foo(unsigned n = 10); // expected-note {{candidate function not viable: requires at most 1 argument, but 2 were provided}}
+  void foo(int n, const char *s, int t); // expected-note {{candidate function not viable: requires 3 arguments, but 2 were provided}}
+  void foo(int n, const char *s, int t, ...); // expected-note {{candidate function not viable: requires at least 3 arguments, but 2 were provided}}
+  void foo(int n, const char *s, int t, int u = 0); // expected-note {{candidate function not viable: requires at least 3 arguments, but 2 were provided}}
 
   void test() {
     foo(4, "hello"); //expected-error {{no matching function for call to 'foo'}}
   }
 }
+

Modified: cfe/trunk/test/SemaCXX/overload-member-call.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/overload-member-call.cpp?rev=93316&r1=93315&r2=93316&view=diff

==============================================================================
--- cfe/trunk/test/SemaCXX/overload-member-call.cpp (original)
+++ cfe/trunk/test/SemaCXX/overload-member-call.cpp Wed Jan 13 03:16:55 2010
@@ -66,3 +66,22 @@
   int &ir = x2p->member();
   float &fr = cx2p->member();
 }
+
+// Tests the exact text used to note the candidates
+namespace test1 {
+  class A {
+    template <class T> void foo(T t, unsigned N); // expected-note {{candidate function [with T = int] not viable: no known conversion from 'char const [6]' to 'unsigned int' for argument 2}}
+    void foo(int n, char N); // expected-note {{candidate function not viable: no known conversion from 'char const [6]' to 'char' for argument 2}} 
+    void foo(int n); // expected-note {{candidate function not viable: requires 1 argument, but 2 were provided}}
+    void foo(unsigned n = 10); // expected-note {{candidate function not viable: requires at most 1 argument, but 2 were provided}}
+    void foo(int n, const char *s, int t); // expected-note {{candidate function not viable: requires 3 arguments, but 2 were provided}}
+    void foo(int n, const char *s, int t, ...); // expected-note {{candidate function not viable: requires at least 3 arguments, but 2 were provided}}
+    void foo(int n, const char *s, int t, int u = 0); // expected-note {{candidate function not viable: requires at least 3 arguments, but 2 were provided}}
+  };
+
+  void test() {
+    A a;
+    a.foo(4, "hello"); //expected-error {{no matching member function for call to 'foo'}}
+  }
+}
+





More information about the cfe-commits mailing list