From: John McCall Date: Mon, 14 Feb 2011 18:34:10 +0000 (+0000) Subject: Provide overload diagnostics when explicit casts involving class types fail. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=79ab2c8104ef5df233d271560ccc734836738e56;p=clang Provide overload diagnostics when explicit casts involving class types fail. PR8626. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@125506 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 2cca767360..82ebf0b56d 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1414,6 +1414,17 @@ def note_ovl_builtin_unary_candidate : Note< "built-in candidate %0">; def err_ovl_no_viable_function_in_init : Error< "no matching constructor for initialization of %0">; +def err_ovl_no_conversion_in_cast : Error< + "cannot convert %1 to %2 without a conversion operator">; +def err_ovl_no_viable_conversion_in_cast : Error< + "no matching conversion for %select{|static_cast|reinterpret_cast|" + "dynamic_cast|C-style cast|functional-style cast}0 from %1 to %2">; +def err_ovl_ambiguous_conversion_in_cast : Error< + "ambiguous conversion for %select{|static_cast|reinterpret_cast|" + "dynamic_cast|C-style cast|functional-style cast}0 from %1 to %2">; +def err_ovl_deleted_conversion_in_cast : Error< + "%select{|static_cast|reinterpret_cast|dynamic_cast|C-style cast|" + "functional-style cast}0 from %1 to %2 uses deleted function">; def err_ovl_ambiguous_init : Error<"call to constructor of %0 is ambiguous">; def err_ref_init_ambiguous : Error< "reference initialization of type %0 with initializer of type %1 is ambiguous">; diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index a8ab472427..c191565990 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -791,12 +791,18 @@ public: return FailedCandidateSet; } + /// brief Get the overloading result, for when the initialization + /// sequence failed due to a bad overload. + OverloadingResult getFailedOverloadResult() const { + return FailedOverloadResult; + } + /// \brief Determine why initialization failed. FailureKind getFailureKind() const { assert(getKind() == FailedSequence && "Not an initialization failure!"); return Failure; } - + /// \brief Dump a representation of this initialization sequence to /// the given stream, for debugging purposes. void dump(llvm::raw_ostream &OS) const; diff --git a/lib/Sema/SemaCXXCast.cpp b/lib/Sema/SemaCXXCast.cpp index afbfa17e77..dda7f67d3f 100644 --- a/lib/Sema/SemaCXXCast.cpp +++ b/lib/Sema/SemaCXXCast.cpp @@ -214,6 +214,92 @@ Sema::BuildCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind, return ExprError(); } +/// Try to diagnose a failed overloaded cast. Returns true if +/// diagnostics were emitted. +static bool tryDiagnoseOverloadedCast(Sema &S, CastType CT, + SourceRange range, Expr *src, + QualType destType) { + switch (CT) { + // These cast kinds don't consider user-defined conversions. + case CT_Const: + case CT_Reinterpret: + case CT_Dynamic: + return false; + + // These do. + case CT_Static: + case CT_CStyle: + case CT_Functional: + break; + } + + QualType srcType = src->getType(); + if (!destType->isRecordType() && !srcType->isRecordType()) + return false; + + InitializedEntity entity = InitializedEntity::InitializeTemporary(destType); + InitializationKind initKind + = InitializationKind::CreateCast(/*type range?*/ range, + (CT == CT_CStyle || CT == CT_Functional)); + InitializationSequence sequence(S, entity, initKind, &src, 1); + + assert(sequence.getKind() == InitializationSequence::FailedSequence && + "initialization succeeded on second try?"); + switch (sequence.getFailureKind()) { + default: return false; + + case InitializationSequence::FK_ConstructorOverloadFailed: + case InitializationSequence::FK_UserConversionOverloadFailed: + break; + } + + OverloadCandidateSet &candidates = sequence.getFailedCandidateSet(); + + unsigned msg = 0; + OverloadCandidateDisplayKind howManyCandidates = OCD_AllCandidates; + + switch (sequence.getFailedOverloadResult()) { + case OR_Success: llvm_unreachable("successful failed overload"); + return false; + case OR_No_Viable_Function: + if (candidates.empty()) + msg = diag::err_ovl_no_conversion_in_cast; + else + msg = diag::err_ovl_no_viable_conversion_in_cast; + howManyCandidates = OCD_AllCandidates; + break; + + case OR_Ambiguous: + msg = diag::err_ovl_ambiguous_conversion_in_cast; + howManyCandidates = OCD_ViableCandidates; + break; + + case OR_Deleted: + msg = diag::err_ovl_deleted_conversion_in_cast; + howManyCandidates = OCD_ViableCandidates; + break; + } + + S.Diag(range.getBegin(), msg) + << CT << srcType << destType + << range << src->getSourceRange(); + + candidates.NoteCandidates(S, howManyCandidates, &src, 1); + + return true; +} + +/// Diagnose a failed cast. +static void diagnoseBadCast(Sema &S, unsigned msg, CastType castType, + SourceRange opRange, Expr *src, QualType destType) { + if (msg == diag::err_bad_cxx_cast_generic && + tryDiagnoseOverloadedCast(S, castType, opRange, src, destType)) + return; + + S.Diag(opRange.getBegin(), msg) << castType + << src->getType() << destType << opRange << src->getSourceRange(); +} + /// UnwrapDissimilarPointerTypes - Like Sema::UnwrapSimilarPointerTypes, /// this removes one level of indirection from both types, provided that they're /// the same kind of pointer (plain or to-member). Unlike the Sema function, @@ -492,20 +578,17 @@ CheckReinterpretCast(Sema &Self, Expr *&SrcExpr, QualType DestType, msg, Kind) != TC_Success && msg != 0) { - if (SrcExpr->getType() == Self.Context.OverloadTy) - { + if (SrcExpr->getType() == Self.Context.OverloadTy) { //FIXME: &f; is overloaded and resolvable Self.Diag(OpRange.getBegin(), diag::err_bad_reinterpret_cast_overload) << OverloadExpr::find(SrcExpr).Expression->getName() << DestType << OpRange; NoteAllOverloadCandidates(SrcExpr, Self); + } else { + diagnoseBadCast(Self, msg, CT_Reinterpret, OpRange, SrcExpr, DestType); } - else - Self.Diag(OpRange.getBegin(), msg) << CT_Reinterpret - << SrcExpr->getType() << DestType << OpRange; - } - + } } @@ -533,16 +616,14 @@ CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType, if (TryStaticCast(Self, SrcExpr, DestType, /*CStyle*/false, OpRange, msg, Kind, BasePath) != TC_Success && msg != 0) { - if (SrcExpr->getType() == Self.Context.OverloadTy) - { + if (SrcExpr->getType() == Self.Context.OverloadTy) { OverloadExpr* oe = OverloadExpr::find(SrcExpr).Expression; Self.Diag(OpRange.getBegin(), diag::err_bad_static_cast_overload) << oe->getName() << DestType << OpRange << oe->getQualifierRange(); NoteAllOverloadCandidates(SrcExpr, Self); + } else { + diagnoseBadCast(Self, msg, CT_Static, OpRange, SrcExpr, DestType); } - else - Self.Diag(OpRange.getBegin(), msg) << CT_Static - << SrcExpr->getType() << DestType << OpRange; } else if (Kind == CK_BitCast) Self.CheckCastAlign(SrcExpr, DestType, OpRange); @@ -1374,7 +1455,7 @@ static TryCastResult TryReinterpretCast(Sema &Self, Expr *SrcExpr, // So we finish by allowing everything that remains - it's got to be two // object pointers. return TC_Success; -} +} bool Sema::CXXCheckCStyleCast(SourceRange R, QualType CastTy, ExprValueKind &VK, @@ -1445,9 +1526,10 @@ Sema::CXXCheckCStyleCast(SourceRange R, QualType CastTy, ExprValueKind &VK, Found); assert(!Fn && "cast failed but able to resolve overload expression!!"); (void)Fn; + } else { - Diag(R.getBegin(), msg) << (FunctionalStyle ? CT_Functional : CT_CStyle) - << CastExpr->getType() << CastTy << R; + diagnoseBadCast(*this, msg, (FunctionalStyle ? CT_Functional : CT_CStyle), + R, CastExpr, CastTy); } } else if (Kind == CK_BitCast) diff --git a/test/CXX/special/class.copy/p9.cpp b/test/CXX/special/class.copy/p9.cpp index 494b12fd79..77ab19e210 100644 --- a/test/CXX/special/class.copy/p9.cpp +++ b/test/CXX/special/class.copy/p9.cpp @@ -15,30 +15,30 @@ struct VirtualInheritsNonConstCopy : virtual NonConstCopy { VirtualInheritsNonConstCopy(const VirtualInheritsNonConstCopy&); }; -struct ImplicitNonConstCopy1 : NonConstCopy { - ImplicitNonConstCopy1(); +struct ImplicitNonConstCopy1 : NonConstCopy { // expected-note {{candidate constructor}} + ImplicitNonConstCopy1(); // expected-note {{candidate constructor}} }; -struct ImplicitNonConstCopy2 { - ImplicitNonConstCopy2(); +struct ImplicitNonConstCopy2 { // expected-note {{candidate constructor}} + ImplicitNonConstCopy2(); // expected-note {{candidate constructor}} NonConstCopy ncc; }; -struct ImplicitNonConstCopy3 { - ImplicitNonConstCopy3(); +struct ImplicitNonConstCopy3 { // expected-note {{candidate constructor}} + ImplicitNonConstCopy3(); // expected-note {{candidate constructor}} NonConstCopy ncc_array[2][3]; }; -struct ImplicitNonConstCopy4 : VirtualInheritsNonConstCopy { - ImplicitNonConstCopy4(); +struct ImplicitNonConstCopy4 : VirtualInheritsNonConstCopy { // expected-note {{candidate constructor}} + ImplicitNonConstCopy4(); // expected-note {{candidate constructor}} }; void test_non_const_copy(const ImplicitNonConstCopy1 &cincc1, const ImplicitNonConstCopy2 &cincc2, const ImplicitNonConstCopy3 &cincc3, const ImplicitNonConstCopy4 &cincc4) { - (void)sizeof(ImplicitNonConstCopy1(cincc1)); // expected-error{{functional-style cast from 'const ImplicitNonConstCopy1' to 'ImplicitNonConstCopy1' is not allowed}} - (void)sizeof(ImplicitNonConstCopy2(cincc2)); // expected-error{{functional-style cast from 'const ImplicitNonConstCopy2' to 'ImplicitNonConstCopy2' is not allowed}} - (void)sizeof(ImplicitNonConstCopy3(cincc3)); // expected-error{{functional-style cast from 'const ImplicitNonConstCopy3' to 'ImplicitNonConstCopy3' is not allowed}} - (void)sizeof(ImplicitNonConstCopy4(cincc4)); // expected-error{{functional-style cast from 'const ImplicitNonConstCopy4' to 'ImplicitNonConstCopy4' is not allowed}} + (void)sizeof(ImplicitNonConstCopy1(cincc1)); // expected-error{{no matching conversion for functional-style cast from 'const ImplicitNonConstCopy1' to 'ImplicitNonConstCopy1'}} + (void)sizeof(ImplicitNonConstCopy2(cincc2)); // expected-error{{no matching conversion for functional-style cast from 'const ImplicitNonConstCopy2' to 'ImplicitNonConstCopy2'}} + (void)sizeof(ImplicitNonConstCopy3(cincc3)); // expected-error{{no matching conversion for functional-style cast from 'const ImplicitNonConstCopy3' to 'ImplicitNonConstCopy3'}} + (void)sizeof(ImplicitNonConstCopy4(cincc4)); // expected-error{{no matching conversion for functional-style cast from 'const ImplicitNonConstCopy4' to 'ImplicitNonConstCopy4'}} } diff --git a/test/SemaCXX/cast-conversion.cpp b/test/SemaCXX/cast-conversion.cpp index d68e789c37..80707d13d2 100644 --- a/test/SemaCXX/cast-conversion.cpp +++ b/test/SemaCXX/cast-conversion.cpp @@ -8,14 +8,14 @@ struct A { A(R); }; -struct B { - B(A); +struct B { // expected-note 3 {{candidate constructor (the implicit copy constructor) not viable}} + B(A); // expected-note 3 {{candidate constructor not viable}} }; int main () { - B(10); // expected-error {{functional-style cast from 'int' to 'B' is not allowed}} - (B)10; // expected-error {{C-style cast from 'int' to 'B' is not allowed}} - static_cast(10); // expected-error {{static_cast from 'int' to 'B' is not allowed}} \\ + B(10); // expected-error {{no matching conversion for functional-style cast from 'int' to 'B'}} + (B)10; // expected-error {{no matching conversion for C-style cast from 'int' to 'B'}} + static_cast(10); // expected-error {{no matching conversion for static_cast from 'int' to 'B'}} \\ // expected-warning {{expression result unused}} } diff --git a/test/SemaCXX/vector-casts.cpp b/test/SemaCXX/vector-casts.cpp index 4bb5808439..681a07ea47 100644 --- a/test/SemaCXX/vector-casts.cpp +++ b/test/SemaCXX/vector-casts.cpp @@ -3,7 +3,7 @@ typedef int __v2si __attribute__((__vector_size__(8))); typedef short __v4hi __attribute__((__vector_size__(8))); typedef short __v8hi __attribute__((__vector_size__(16))); -struct S { }; +struct S { }; // expected-note 2 {{candidate constructor}} void f() { __v2si v2si; @@ -23,9 +23,9 @@ void f() { (void)(__v2si)(ll); (void)reinterpret_cast(v2si); // expected-error {{reinterpret_cast from '__v2si' to 'S' is not allowed}} - (void)(S)v2si; // expected-error {{C-style cast from '__v2si' to 'S' is not allowed}} + (void)(S)v2si; // expected-error {{no matching conversion for C-style cast from '__v2si' to 'S'}} (void)reinterpret_cast<__v2si>(s); // expected-error {{reinterpret_cast from 'S' to '__v2si' is not allowed}} - (void)(__v2si)s; // expected-error {{C-style cast from 'S' to '__v2si' is not allowed}} + (void)(__v2si)s; // expected-error {{cannot convert 'S' to '__v2si' without a conversion operator}} (void)reinterpret_cast(v2si); // expected-error {{reinterpret_cast from vector '__v2si' to scalar 'unsigned char' of different size}} (void)(unsigned char)v2si; // expected-error {{C-style cast from vector '__v2si' to scalar 'unsigned char' of different size}} diff --git a/test/SemaObjCXX/cstyle-cast.mm b/test/SemaObjCXX/cstyle-cast.mm index c4b176c975..29a8404660 100644 --- a/test/SemaObjCXX/cstyle-cast.mm +++ b/test/SemaObjCXX/cstyle-cast.mm @@ -18,7 +18,7 @@ void test1(X x) { I

*ip = (I

*)cft; - (id)x; // expected-error {{C-style cast from 'X' to 'id' is not allowed}} + (id)x; // expected-error {{cannot convert 'X' to 'id' without a conversion operator}} id *pid = (id*)ccct; diff --git a/test/SemaTemplate/instantiate-cast.cpp b/test/SemaTemplate/instantiate-cast.cpp index 9669b2035a..abf1b3c31d 100644 --- a/test/SemaTemplate/instantiate-cast.cpp +++ b/test/SemaTemplate/instantiate-cast.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -struct A { int x; }; +struct A { int x; }; // expected-note 2 {{candidate constructor}} class Base { public: @@ -23,7 +23,7 @@ struct Constructible { template struct CStyleCast0 { void f(T t) { - (void)((U)t); // expected-error{{C-style cast from 'A' to 'int' is not allowed}} + (void)((U)t); // expected-error{{cannot convert 'A' to 'int' without a conversion operator}} } }; @@ -36,7 +36,7 @@ template struct CStyleCast0; // expected-note{{instantiation}} template struct StaticCast0 { void f(T t) { - (void)static_cast(t); // expected-error{{static_cast from 'int' to 'A' is not allowed}} + (void)static_cast(t); // expected-error{{no matching conversion for static_cast from 'int' to 'A'}} } }; @@ -89,7 +89,7 @@ template struct ConstCast0; // expected-note{{instantiatio template struct FunctionalCast1 { void f(T t) { - (void)U(t); // expected-error{{functional-style cast from 'A' to 'int' is not allowed}} + (void)U(t); // expected-error{{cannot convert 'A' to 'int' without a conversion operator}} } };