From: Richard Trieu Date: Wed, 23 Nov 2011 22:32:32 +0000 (+0000) Subject: Add feature to diagnostics that will provide more information on function X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6efd4c55a1a481d92966a91141c03e8145234cf6;p=clang Add feature to diagnostics that will provide more information on function pointer mismatch. Cases covered are: initialization, assignment, and function arguments. Additional text will give the extra information about the nature of the mismatch: different classes for member functions, wrong number of parameters, different parameter type, different return type, and function qualifier mismatch. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@145114 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c8b138f2c7..4de2351914 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1014,7 +1014,16 @@ def err_init_conversion_failed : Error< "cannot initialize %select{a variable|a parameter|return object|an " "exception object|a member subobject|an array element|a new value|a value|a " "base class|a constructor delegation|a vector element}0 of type %1 with an " - "%select{rvalue|lvalue}2 of type %3">; + "%select{rvalue|lvalue}2 of type %3" + "%select{|: different classes (%5 vs %6)" + "|: different number of parameters (%5 vs %6)" + "|: type mismatch in %ordinal5 parameter (%6 vs %7)" + "|: different return type (%5 vs %6)" + "|: different qualifiers (" + "%select{none|const|restrict|const and restrict|volatile|const and volatile|" + "volatile and restrict|const, volatile, and restrict}5 vs " + "%select{none|const|restrict|const and restrict|volatile|const and volatile|" + "volatile and restrict|const, volatile, and restrict}6)}4">; def err_lvalue_to_rvalue_ref : Error<"rvalue reference to type %0 cannot bind " "to lvalue of type %1">; @@ -1823,7 +1832,16 @@ def note_ovl_candidate : Note<"candidate " "is the implicit move constructor|" "is the implicit copy assignment operator|" "is the implicit move assignment operator|" - "is an inherited constructor}0%1">; + "is an inherited constructor}0%1" + "%select{| has different class (expected %3 but has %4)" + "| has different number of parameters (expected %3 but has %4)" + "| has type mismatch in %ordinal3 parameter (expected %4 but has %5)" + "| has different return type (%3 expected but has %4)" + "| has different qualifiers (expected " + "%select{none|const|restrict|const and restrict|volatile|const and volatile" + "|volatile and restrict|const, volatile, and restrict}3 but found " + "%select{none|const|restrict|const and restrict|volatile|const and volatile" + "|volatile and restrict|const, volatile, and restrict}4)}2">; def note_ovl_candidate_inherited_constructor : Note<"inherited from here">; def note_ovl_candidate_bad_deduction : Note< @@ -4022,11 +4040,21 @@ def err_typecheck_convert_incompatible : Error< "%select{from incompatible type|to parameter of incompatible type|" "from a function with incompatible result type|to incompatible type|" "with an expression of incompatible type|to parameter of incompatible type|" - "to incompatible type}2 %1; " - "%select{|dereference with *|" - "take the address with &|" - "remove *|" - "remove &}3">; + "to incompatible type}2 %1" + "%select{|; dereference with *|" + "; take the address with &|" + "; remove *|" + "; remove &}3" + "%select{|: different classes (%5 vs %6)" + "|: different number of parameters (%5 vs %6)" + "|: type mismatch at %ordinal5 parameter (%6 vs %7)" + "|: different return type (%5 vs %6)" + "|: different qualifiers (" + "%select{none|const|restrict|const and restrict|volatile|const and volatile|" + "volatile and restrict|const, volatile, and restrict}5 vs " + "%select{none|const|restrict|const and restrict|volatile|const and volatile|" + "volatile and restrict|const, volatile, and restrict}6)}4">; + def warn_incompatible_qualified_id : Warning< "%select{assigning to|passing|returning|converting|initializing|sending|casting}2" " %0 " diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index d424ef49db..da17b248a1 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1399,7 +1399,10 @@ public: bool IsBlockPointerConversion(QualType FromType, QualType ToType, QualType& ConvertedType); bool FunctionArgTypesAreEqual(const FunctionProtoType *OldType, - const FunctionProtoType *NewType); + const FunctionProtoType *NewType, + unsigned *ArgPos = 0); + void HandleFunctionTypeMismatch(PartialDiagnostic &PDiag, + QualType FromType, QualType ToType); CastKind PrepareCastToObjCObjectPointer(ExprResult &E); bool CheckPointerConversion(Expr *From, QualType ToType, @@ -1542,11 +1545,11 @@ public: bool StdNamespaceIsAssociated = false); // Emit as a 'note' the specific overload candidate - void NoteOverloadCandidate(FunctionDecl *Fn); + void NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType = QualType()); // Emit as a series of 'note's all template and non-templates // identified by the expression Expr - void NoteAllOverloadCandidates(Expr* E); + void NoteAllOverloadCandidates(Expr* E, QualType DestType = QualType()); // [PossiblyAFunctionType] --> [Return] // NonFunctionType --> NonFunctionType @@ -4642,7 +4645,8 @@ public: const PartialDiagnostic &NoneDiag, const PartialDiagnostic &AmbigDiag, const PartialDiagnostic &CandidateDiag, - bool Complain = true); + bool Complain = true, + QualType TargetType = QualType()); ClassTemplatePartialSpecializationDecl * getMoreSpecializedPartialSpecialization( diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index ba9dbedd8d..ef9d787fe4 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9028,6 +9028,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, FixItHint Hint; ConversionFixItGenerator ConvHints; bool MayHaveConvFixit = false; + bool MayHaveFunctionDiff = false; switch (ConvTy) { default: llvm_unreachable("Unknown conversion type"); @@ -9117,6 +9118,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); MayHaveConvFixit = true; isInvalid = true; + MayHaveFunctionDiff = true; break; } @@ -9155,8 +9157,15 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, } if (MayHaveConvFixit) { FDiag << (unsigned) (ConvHints.Kind); } + if (MayHaveFunctionDiff) + HandleFunctionTypeMismatch(FDiag, SecondType, FirstType); + Diag(Loc, FDiag); + if (SecondType == Context.OverloadTy) + NoteAllOverloadCandidates(OverloadExpr::find(SrcExpr).Expression, + FirstType); + if (CheckInferredResultType) EmitRelatedResultTypeNote(SrcExpr); diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 85cb76f8a0..9480f63712 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -5118,12 +5118,14 @@ bool InitializationSequence::Diagnose(Sema &S, case FK_ConversionFailed: { QualType FromType = Args[0]->getType(); - S.Diag(Kind.getLocation(), diag::err_init_conversion_failed) + PartialDiagnostic PDiag = S.PDiag(diag::err_init_conversion_failed) << (int)Entity.getKind() << DestType << Args[0]->isLValue() << FromType << Args[0]->getSourceRange(); + S.HandleFunctionTypeMismatch(PDiag, FromType, DestType); + S.Diag(Kind.getLocation(), PDiag); if (DestType.getNonReferenceType()->isObjCObjectPointerType() && Args[0]->getType()->isObjCObjectPointerType()) S.EmitRelatedResultTypeNote(Args[0]); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 11b10a581a..9b605c9319 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2163,22 +2163,126 @@ bool Sema::IsBlockPointerConversion(QualType FromType, QualType ToType, return true; } +enum { + ft_default, + ft_different_class, + ft_parameter_arity, + ft_parameter_mismatch, + ft_return_type, + ft_qualifer_mismatch +}; + +/// HandleFunctionTypeMismatch - Gives diagnostic information for differeing +/// function types. Catches different number of parameter, mismatch in +/// parameter types, and different return types. +void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag, + QualType FromType, QualType ToType) { + // Get the function type from the pointers. + if (FromType->isMemberPointerType() && ToType->isMemberPointerType()) { + const MemberPointerType *FromMember = FromType->getAs(), + *ToMember = ToType->getAs(); + if (FromMember->getClass() != ToMember->getClass()) { + PDiag << ft_different_class << QualType(ToMember->getClass(), 0) + << QualType(FromMember->getClass(), 0); + return; + } + FromType = FromMember->getPointeeType(); + ToType = ToMember->getPointeeType(); + } else if (FromType->isPointerType() && ToType->isPointerType()) { + FromType = FromType->getPointeeType(); + ToType = ToType->getPointeeType(); + } else { + PDiag << ft_default; + return; + } + + FromType = FromType.getNonReferenceType(); + ToType = ToType.getNonReferenceType(); + + // If either type is not valid, of the types are the same, no extra info. + if (FromType.isNull() || ToType.isNull() || + Context.hasSameType(FromType, ToType)) { + PDiag << ft_default; + return; + } + + // Don't print extra info for non-specialized template functions. + if (FromType->isInstantiationDependentType() && + !FromType->getAs()) { + PDiag << ft_default; + return; + } + + const FunctionProtoType *FromFunction = FromType->getAs(), + *ToFunction = ToType->getAs(); + + // Both types need to be function types. + if (!FromFunction || !ToFunction) { + PDiag << ft_default; + return; + } + + if (FromFunction->getNumArgs() != ToFunction->getNumArgs()) { + PDiag << ft_parameter_arity << ToFunction->getNumArgs() + << FromFunction->getNumArgs(); + return; + } + + // Handle different parameter types. + unsigned ArgPos; + if (!FunctionArgTypesAreEqual(FromFunction, ToFunction, &ArgPos)) { + PDiag << ft_parameter_mismatch << ArgPos + 1 + << ToFunction->getArgType(ArgPos) + << FromFunction->getArgType(ArgPos); + return; + } + + // Handle different return type. + if (!Context.hasSameType(FromFunction->getResultType(), + ToFunction->getResultType())) { + PDiag << ft_return_type << ToFunction->getResultType() + << FromFunction->getResultType(); + return; + } + + unsigned FromQuals = FromFunction->getTypeQuals(), + ToQuals = ToFunction->getTypeQuals(); + if (FromQuals != ToQuals) { + PDiag << ft_qualifer_mismatch << ToQuals << FromQuals; + return; + } + + // Unable to find a difference, so add no extra info. + PDiag << ft_default; +} + /// FunctionArgTypesAreEqual - This routine checks two function proto types /// for equlity of their argument types. Caller has already checked that /// they have same number of arguments. This routine assumes that Objective-C /// pointer types which only differ in their protocol qualifiers are equal. +/// If the parameters are different, ArgPos will have the the parameter index +/// of the first different parameter. bool Sema::FunctionArgTypesAreEqual(const FunctionProtoType *OldType, - const FunctionProtoType *NewType) { - if (!getLangOptions().ObjC1) - return std::equal(OldType->arg_type_begin(), OldType->arg_type_end(), - NewType->arg_type_begin()); + const FunctionProtoType *NewType, + unsigned *ArgPos) { + if (!getLangOptions().ObjC1) { + for (FunctionProtoType::arg_type_iterator O = OldType->arg_type_begin(), + N = NewType->arg_type_begin(), + E = OldType->arg_type_end(); O && (O != E); ++O, ++N) { + if (!Context.hasSameType(*O, *N)) { + if (ArgPos) *ArgPos = O - OldType->arg_type_begin(); + return false; + } + } + return true; + } for (FunctionProtoType::arg_type_iterator O = OldType->arg_type_begin(), N = NewType->arg_type_begin(), E = OldType->arg_type_end(); O && (O != E); ++O, ++N) { QualType ToType = (*O); QualType FromType = (*N); - if (ToType != FromType) { + if (!Context.hasSameType(ToType, FromType)) { if (const PointerType *PTTo = ToType->getAs()) { if (const PointerType *PTFr = FromType->getAs()) if ((PTTo->getPointeeType()->isObjCQualifiedIdType() && @@ -2194,6 +2298,7 @@ bool Sema::FunctionArgTypesAreEqual(const FunctionProtoType *OldType, if (PTTo->getInterfaceDecl() == PTFr->getInterfaceDecl()) continue; } + if (ArgPos) *ArgPos = O - OldType->arg_type_begin(); return false; } } @@ -7015,17 +7120,19 @@ void MaybeEmitInheritedConstructorNote(Sema &S, FunctionDecl *Fn) { } // end anonymous namespace // Notes the location of an overload candidate. -void Sema::NoteOverloadCandidate(FunctionDecl *Fn) { +void Sema::NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType) { std::string FnDesc; OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Fn, FnDesc); - Diag(Fn->getLocation(), diag::note_ovl_candidate) - << (unsigned) K << FnDesc; + PartialDiagnostic PD = PDiag(diag::note_ovl_candidate) + << (unsigned) K << FnDesc; + HandleFunctionTypeMismatch(PD, Fn->getType(), DestType); + Diag(Fn->getLocation(), PD); MaybeEmitInheritedConstructorNote(*this, Fn); } //Notes the location of all overload candidates designated through // OverloadedExpr -void Sema::NoteAllOverloadCandidates(Expr* OverloadedExpr) { +void Sema::NoteAllOverloadCandidates(Expr* OverloadedExpr, QualType DestType) { assert(OverloadedExpr->getType() == Context.OverloadTy); OverloadExpr::FindResult Ovl = OverloadExpr::find(OverloadedExpr); @@ -7036,10 +7143,10 @@ void Sema::NoteAllOverloadCandidates(Expr* OverloadedExpr) { I != IEnd; ++I) { if (FunctionTemplateDecl *FunTmpl = dyn_cast((*I)->getUnderlyingDecl()) ) { - NoteOverloadCandidate(FunTmpl->getTemplatedDecl()); + NoteOverloadCandidate(FunTmpl->getTemplatedDecl(), DestType); } else if (FunctionDecl *Fun = dyn_cast((*I)->getUnderlyingDecl()) ) { - NoteOverloadCandidate(Fun); + NoteOverloadCandidate(Fun, DestType); } } } @@ -8132,7 +8239,7 @@ private: << Matches[0].second->getDeclName(), S.PDiag(diag::note_ovl_candidate) << (unsigned) oc_function_template, - Complain); + Complain, TargetFunctionType); if (Result != MatchesCopy.end()) { // Make it the first and only element @@ -8161,7 +8268,7 @@ public: S.Diag(OvlExpr->getLocStart(), diag::err_addr_ovl_no_viable) << OvlExpr->getName() << TargetFunctionType << OvlExpr->getSourceRange(); - S.NoteAllOverloadCandidates(OvlExpr); + S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType); } bool IsInvalidFormOfPointerToMemberFunction() const { @@ -8187,7 +8294,7 @@ public: S.Diag(OvlExpr->getLocStart(), diag::err_addr_ovl_ambiguous) << OvlExpr->getName() << OvlExpr->getSourceRange(); - S.NoteAllOverloadCandidates(OvlExpr); + S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType); } bool hadMultipleCandidates() const { return (OvlExpr->getNumDecls() > 1); } diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 17987da16a..28f489a43b 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -3783,7 +3783,8 @@ Sema::getMostSpecialized(UnresolvedSetIterator SpecBegin, const PartialDiagnostic &NoneDiag, const PartialDiagnostic &AmbigDiag, const PartialDiagnostic &CandidateDiag, - bool Complain) { + bool Complain, + QualType TargetType) { if (SpecBegin == SpecEnd) { if (Complain) Diag(Loc, NoneDiag); @@ -3837,11 +3838,16 @@ Sema::getMostSpecialized(UnresolvedSetIterator SpecBegin, if (Complain) // FIXME: Can we order the candidates in some sane way? - for (UnresolvedSetIterator I = SpecBegin; I != SpecEnd; ++I) - Diag((*I)->getLocation(), CandidateDiag) - << getTemplateArgumentBindingsText( + for (UnresolvedSetIterator I = SpecBegin; I != SpecEnd; ++I) { + PartialDiagnostic PD = CandidateDiag; + PD << getTemplateArgumentBindingsText( cast(*I)->getPrimaryTemplate()->getTemplateParameters(), *cast(*I)->getTemplateSpecializationArgs()); + if (!TargetType.isNull()) + HandleFunctionTypeMismatch(PD, cast(*I)->getType(), + TargetType); + Diag((*I)->getLocation(), PD); + } return SpecEnd; } diff --git a/test/SemaCXX/addr-of-overloaded-function.cpp b/test/SemaCXX/addr-of-overloaded-function.cpp index c3360cc059..af171ec701 100644 --- a/test/SemaCXX/addr-of-overloaded-function.cpp +++ b/test/SemaCXX/addr-of-overloaded-function.cpp @@ -145,3 +145,64 @@ namespace PR7425 { bar2(); } } + +namespace test1 { + void fun(int x) {} + + void parameter_number() { + void (*ptr1)(int, int) = &fun; // expected-error {{cannot initialize a variable of type 'void (*)(int, int)' with an rvalue of type 'void (*)(int)': different number of parameters (2 vs 1)}} + void (*ptr2)(int, int); + ptr2 = &fun; // expected-error {{assigning to 'void (*)(int, int)' from incompatible type 'void (*)(int)': different number of parameters (2 vs 1)}} + } + + void parameter_mismatch() { + void (*ptr1)(double) = &fun; // expected-error {{cannot initialize a variable of type 'void (*)(double)' with an rvalue of type 'void (*)(int)': type mismatch in 1st parameter ('double' vs 'int')}} + void (*ptr2)(double); + ptr2 = &fun; // expected-error {{assigning to 'void (*)(double)' from incompatible type 'void (*)(int)': type mismatch at 1st parameter ('double' vs 'int')}} + } + + void return_type_test() { + int (*ptr1)(int) = &fun; // expected-error {{cannot initialize a variable of type 'int (*)(int)' with an rvalue of type 'void (*)(int)': different return type ('int' vs 'void')}} + int (*ptr2)(int); + ptr2 = &fun; // expected-error {{assigning to 'int (*)(int)' from incompatible type 'void (*)(int)': different return type ('int' vs 'void')}} + } + + int foo(double x, double y) {return 0;} // expected-note {{candidate function has different number of parameters (expected 1 but has 2)}} + int foo(int x, int y) {return 0;} // expected-note {{candidate function has different number of parameters (expected 1 but has 2)}} + int foo(double x) {return 0;} // expected-note {{candidate function has type mismatch at 1st parameter (expected 'int' but has 'double')}} + double foo(float x, float y) {return 0;} // expected-note {{candidate function has different number of parameters (expected 1 but has 2)}} + double foo(int x, float y) {return 0;} // expected-note {{candidate function has different number of parameters (expected 1 but has 2)}} + double foo(float x) {return 0;} // expected-note {{candidate function has type mismatch at 1st parameter (expected 'int' but has 'float')}} + double foo(int x) {return 0;} // expected-note {{candidate function has different return type ('int' expected but has 'double')}} + + int (*ptr)(int) = &foo; // expected-error {{address of overloaded function 'foo' does not match required type 'int (int)'}} + + struct Qualifiers { + void N() {}; + void C() const {}; + void V() volatile {}; + void R() __restrict {}; + void CV() const volatile {}; + void CR() const __restrict {}; + void VR() volatile __restrict {}; + void CVR() const volatile __restrict {}; + }; + + + void QualifierTest() { + void (Qualifiers::*X)(); + X = &Qualifiers::C; // expected-error {{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() const': different qualifiers (none vs const)}} + X = &Qualifiers::V; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() volatile': different qualifiers (none vs volatile)}} + X = &Qualifiers::R; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() restrict': different qualifiers (none vs restrict)}} + X = &Qualifiers::CV; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() const volatile': different qualifiers (none vs const and volatile)}} + X = &Qualifiers::CR; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() const restrict': different qualifiers (none vs const and restrict)}} + X = &Qualifiers::VR; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() volatile restrict': different qualifiers (none vs volatile and restrict)}} + X = &Qualifiers::CVR; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() const volatile restrict': different qualifiers (none vs const, volatile, and restrict)}} + } + + struct Dummy { + void N() {}; + }; + + void (Qualifiers::*X)() = &Dummy::N; // expected-error{{cannot initialize a variable of type 'void (test1::Qualifiers::*)()' with an rvalue of type 'void (test1::Dummy::*)()': different classes ('test1::Qualifiers' vs 'test1::Dummy')}} +}