From: John McCall Date: Thu, 14 Jan 2010 03:28:57 +0000 (+0000) Subject: Improve overload diagnostics some more by calling out qualifier mismatches X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=651f3eec130099fb46e2ce2c8aee9d01531dc3b5;p=clang Improve overload diagnostics some more by calling out qualifier mismatches for special diagnostics. Unfortunately, the non-overload diagnostics are not this good. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@93420 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d3b27c08e9..7e59f88fce 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -925,6 +925,30 @@ def note_ovl_candidate_bad_conv : Note<"candidate " "function (the implicit copy assignment operator)}0%1" " not viable: no known conversion from %2 to %3 for " "%select{%ordinal5 argument|object argument}4">; +def note_ovl_candidate_bad_addrspace : Note<"candidate " + "%select{function|function|constructor|" + "function |function |constructor |" + "constructor (the implicit default constructor)|" + "constructor (the implicit copy constructor)|" + "function (the implicit copy assignment operator)}0%1 not viable: " + "%select{%ordinal6|'this'}5 argument (%2) is in " + "address space %3, but parameter must be in address space %4">; +def note_ovl_candidate_bad_cvr_this : Note<"candidate " + "%select{|function|||function||||" + "function (the implicit copy assignment operator)}0 not viable: " + "'this' argument has type %2, but method is not marked " + "%select{const|volatile|const or volatile|restrict|const or restrict|" + "volatile or restrict|const, volatile, or restrict}3">; +def note_ovl_candidate_bad_cvr : Note<"candidate " + "%select{function|function|constructor|" + "function |function |constructor |" + "constructor (the implicit default constructor)|" + "constructor (the implicit copy constructor)|" + "function (the implicit copy assignment operator)}0%1 not viable: " + "%ordinal4 argument (%2) would lose " + "%select{const|volatile|const and volatile|restrict|const and restrict|" + "volatile and restrict|const, volatile, and restrict}3 qualifier" + "%select{||s||s|s|s}3">; def note_ambiguous_type_conversion: Note< "because of ambiguity in conversion of %0 to %1">; def note_ovl_builtin_binary_candidate : Note< diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 9111cf46b5..6ec4d1b600 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2191,7 +2191,7 @@ bool Sema::PerformCopyInitialization(Expr *&From, QualType ToType, /// parameter of the given member function (@c Method) from the /// expression @p From. ImplicitConversionSequence -Sema::TryObjectArgumentInitialization(QualType FromType, +Sema::TryObjectArgumentInitialization(QualType OrigFromType, CXXMethodDecl *Method, CXXRecordDecl *ActingContext) { QualType ClassType = Context.getTypeDeclType(ActingContext); @@ -2208,6 +2208,7 @@ Sema::TryObjectArgumentInitialization(QualType FromType, ICS.setBad(); // We need to have an object of class type. + QualType FromType = OrigFromType; if (const PointerType *PT = FromType->getAs()) FromType = PT->getPointeeType(); @@ -2228,7 +2229,8 @@ Sema::TryObjectArgumentInitialization(QualType FromType, if (ImplicitParamType.getCVRQualifiers() != FromTypeCanon.getLocalCVRQualifiers() && !ImplicitParamType.isAtLeastAsQualifiedAs(FromTypeCanon)) { - ICS.Bad.init(BadConversionSequence::bad_qualifiers, FromType, ImplicitParamType); + ICS.Bad.init(BadConversionSequence::bad_qualifiers, + OrigFromType, ImplicitParamType); return ICS; } @@ -4384,7 +4386,57 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) { QualType FromTy = Conv.Bad.getFromType(); QualType ToTy = Conv.Bad.getToType(); - // TODO: specialize based on the kind of mismatch + // Do some hand-waving analysis to see if the non-viability is due to a + CanQualType CFromTy = S.Context.getCanonicalType(FromTy); + CanQualType CToTy = S.Context.getCanonicalType(ToTy); + if (CanQual RT = CToTy->getAs()) + CToTy = RT->getPointeeType(); + else { + // TODO: detect and diagnose the full richness of const mismatches. + if (CanQual FromPT = CFromTy->getAs()) + if (CanQual ToPT = CToTy->getAs()) + CFromTy = FromPT->getPointeeType(), CToTy = ToPT->getPointeeType(); + } + + if (CToTy.getUnqualifiedType() == CFromTy.getUnqualifiedType() && + !CToTy.isAtLeastAsQualifiedAs(CFromTy)) { + // It is dumb that we have to do this here. + while (isa(CFromTy)) + CFromTy = CFromTy->getAs()->getElementType(); + while (isa(CToTy)) + CToTy = CFromTy->getAs()->getElementType(); + + Qualifiers FromQs = CFromTy.getQualifiers(); + Qualifiers ToQs = CToTy.getQualifiers(); + + if (FromQs.getAddressSpace() != ToQs.getAddressSpace()) { + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_addrspace) + << (unsigned) FnKind << FnDesc + << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) + << FromTy + << FromQs.getAddressSpace() << ToQs.getAddressSpace() + << (unsigned) isObjectArgument << I+1; + return; + } + + unsigned CVR = FromQs.getCVRQualifiers() & ~ToQs.getCVRQualifiers(); + assert(CVR && "unexpected qualifiers mismatch"); + + if (isObjectArgument) { + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_cvr_this) + << (unsigned) FnKind << FnDesc + << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) + << FromTy << (CVR - 1); + } else { + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_cvr) + << (unsigned) FnKind << FnDesc + << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) + << FromTy << (CVR - 1) << I+1; + } + return; + } + + // TODO: specialize more based on the kind of mismatch S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_conv) << (unsigned) FnKind << FnDesc << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) diff --git a/test/SemaCXX/overload-member-call.cpp b/test/SemaCXX/overload-member-call.cpp index 700e6d8bc6..22416f3ea4 100644 --- a/test/SemaCXX/overload-member-call.cpp +++ b/test/SemaCXX/overload-member-call.cpp @@ -78,8 +78,11 @@ namespace test1 { 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 bar(double d); //expected-note {{candidate function not viable: no known conversion from 'class test1::A const' to 'class test1::A' for object argument}} - void bar(int i); //expected-note {{candidate function not viable: no known conversion from 'class test1::A const' to 'class test1::A' for object argument}} + void bar(double d); //expected-note {{candidate function not viable: 'this' argument has type 'class test1::A const', but method is not marked const}} + void bar(int i); //expected-note {{candidate function not viable: 'this' argument has type 'class test1::A const', but method is not marked const}} + + void baz(A &d); // expected-note {{candidate function not viable: 1st argument ('class test1::A const') would lose const qualifier}} + void baz(int i); // expected-note {{candidate function not viable: no known conversion from 'class test1::A const' to 'int' for 1st argument}} }; void test() { @@ -88,6 +91,8 @@ namespace test1 { const A b; b.bar(0); //expected-error {{no matching member function for call to 'bar'}} + + a.baz(b); //expected-error {{no matching member function for call to 'baz'}} } }