From: John McCall Date: Wed, 13 Jan 2010 09:16:55 +0000 (+0000) Subject: Record some basic information about bad conversion sequences. Use that X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=adbb8f8aa44216d30d204e843c4a74abab929dae;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@93316 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index e10398af85..edeb7c13e1 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -4389,7 +4389,10 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType, = 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 diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index d6947986a4..d10e11fdb4 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1076,6 +1076,7 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, 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 @@ QualType Sema::FindCompositePointerType(Expr *&E1, Expr *&E2) { ImplicitConversionSequence E1ToC2, E2ToC2; E1ToC2.setBad(); - E2ToC2.setBad(); + E2ToC2.setBad(); if (Context.getCanonicalType(Composite1) != Context.getCanonicalType(Composite2)) { E1ToC2 = TryImplicitConversion(E1, Composite2, diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 4f299ff5b0..ffcf8d4609 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -489,8 +489,10 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType, // 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 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType, ICS.Ambiguous.addConversion(Cand->Function); } else { ICS.setBad(); + ICS.Bad.init(BadConversionSequence::no_conversion, From, ToType); } return ICS; @@ -2129,6 +2132,7 @@ Sema::TryCopyInitialization(Expr *From, QualType ToType, 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 @@ Sema::TryObjectArgumentInitialization(QualType FromType, 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 @@ Sema::TryObjectArgumentInitialization(QualType FromType, 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 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, if ((NumArgs + (PartialOverloading && NumArgs)) > NumArgsInProto && !Proto->isVariadic()) { Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_too_many_arguments; return; } @@ -2397,6 +2406,7 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, if (NumArgs < MinRequiredArgs && !PartialOverloading) { // Not enough arguments. Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_too_few_arguments; return; } @@ -2416,6 +2426,7 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, /*InOverloadResolution=*/true); if (Candidate.Conversions[ArgIdx].isBad()) { Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_bad_conversion; break; } } else { @@ -2532,6 +2543,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, CXXRecordDecl *ActingContext, // list (8.3.5). if (NumArgs > NumArgsInProto && !Proto->isVariadic()) { Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_too_many_arguments; return; } @@ -2544,6 +2556,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, CXXRecordDecl *ActingContext, if (NumArgs < MinRequiredArgs) { // Not enough arguments. Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_too_few_arguments; return; } @@ -2560,6 +2573,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, CXXRecordDecl *ActingContext, = TryObjectArgumentInitialization(ObjectType, Method, ActingContext); if (Candidate.Conversions[0].isBad()) { Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_bad_conversion; return; } } @@ -2579,6 +2593,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, CXXRecordDecl *ActingContext, /*InOverloadResolution=*/true); if (Candidate.Conversions[ArgIdx + 1].isBad()) { Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_bad_conversion; break; } } else { @@ -2670,6 +2685,7 @@ Sema::AddTemplateOverloadCandidate(FunctionTemplateDecl *FunctionTemplate, 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 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, 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 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, 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 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, case ImplicitConversionSequence::BadConversion: Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_bad_conversion; break; default: @@ -2847,6 +2866,7 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, = TryObjectArgumentInitialization(ObjectType, Conversion, ActingContext); if (ObjectInit.isBad()) { Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_bad_conversion; return; } @@ -2869,6 +2889,7 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, // list (8.3.5). if (NumArgs > NumArgsInProto && !Proto->isVariadic()) { Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_too_many_arguments; return; } @@ -2877,6 +2898,7 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, if (NumArgs < NumArgsInProto) { // Not enough arguments. Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_too_few_arguments; return; } @@ -2896,6 +2918,7 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, /*InOverloadResolution=*/false); if (Candidate.Conversions[ArgIdx + 1].isBad()) { Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_bad_conversion; break; } } else { @@ -3038,6 +3061,7 @@ void Sema::AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, } if (Candidate.Conversions[ArgIdx].isBad()) { Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_bad_conversion; break; } } @@ -4336,36 +4360,69 @@ void Sema::DiagnoseAmbiguousConversion(const ImplicitConversionSequence &ICS, 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(Fn) && !isa(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()->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()->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(); + + 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 @@ void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, return; } - // Diagnose arity mismatches. - // TODO: treat calls to a missing default constructor as a special case - unsigned NumFormalArgs = NumArgs; - if (isa(Fn) && !isa(Fn)) - NumFormalArgs--; - const FunctionProtoType *FnTy = Fn->getType()->getAs(); - 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(); - } + switch (Cand->FailureKind) { + case ovl_fail_too_many_arguments: + case ovl_fail_too_few_arguments: + return DiagnoseArityMismatch(S, Cand, NumArgs); - 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; + case ovl_fail_bad_deduction: + return S.NoteOverloadCandidate(Fn); - DiagnoseBadConversion(S, Cand, I, Args, NumArgs); - return; - } + 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) { diff --git a/lib/Sema/SemaOverload.h b/lib/Sema/SemaOverload.h index 78fa05b24c..20add007f7 100644 --- a/lib/Sema/SemaOverload.h +++ b/lib/Sema/SemaOverload.h @@ -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 @@ namespace clang { 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 @@ namespace clang { /// 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 @@ namespace clang { 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 @@ namespace clang { 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 @@ namespace clang { /// 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 diff --git a/test/SemaCXX/overload-call.cpp b/test/SemaCXX/overload-call.cpp index fbd26b2fd8..0a2508d4b8 100644 --- a/test/SemaCXX/overload-call.cpp +++ b/test/SemaCXX/overload-call.cpp @@ -304,10 +304,16 @@ namespace PR5756 { // Tests the exact text used to note the candidates namespace test1 { - template void foo(T t, unsigned N); // expected-note {{candidate function [with T = int]}} - void foo(int n, char N); // expected-note {{candidate function}} + template 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'}} } } + diff --git a/test/SemaCXX/overload-member-call.cpp b/test/SemaCXX/overload-member-call.cpp index 4bb3ff3a54..8eb189850b 100644 --- a/test/SemaCXX/overload-member-call.cpp +++ b/test/SemaCXX/overload-member-call.cpp @@ -66,3 +66,22 @@ void test_X2(X2 *x2p, const X2 *cx2p) { int &ir = x2p->member(); float &fr = cx2p->member(); } + +// Tests the exact text used to note the candidates +namespace test1 { + class A { + template 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'}} + } +} +