From ffa7dc379f2e5df7a19e4ee01dea11c9d329d2bb Mon Sep 17 00:00:00 2001 From: Nathan Sidwell Date: Wed, 28 Jan 2015 21:31:26 +0000 Subject: [PATCH] PR 17456 More helpful diagnostic on casts between unrelated class hierarchies. llvm-svn: 227371 --- .../clang/Basic/DiagnosticSemaKinds.td | 5 +++ clang/lib/Sema/SemaCast.cpp | 35 +++++++++++++++++++ clang/test/CXX/temp/temp.param/p15-cxx0x.cpp | 2 +- clang/test/SemaCXX/static-cast.cpp | 10 ++++-- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index cbc254e003d4..8090f7a61b85 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5267,6 +5267,11 @@ def err_bad_cstyle_cast_overload : Error< def err_bad_cxx_cast_generic : Error< "%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|" "functional-style cast}0 from %1 to %2 is not allowed">; +def err_bad_cxx_cast_unrelated_class : Error< + "%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|" + "functional-style cast}0 from %1 to %2, which are not related by " + "inheritance, is not allowed">; +def note_type_incomplete : Note<"%0 is incomplete">; def err_bad_cxx_cast_rvalue : Error< "%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|" "functional-style cast}0 from rvalue to reference type %2">; diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 3e06a6477d64..d28a24453e2c 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -389,6 +389,33 @@ static void diagnoseBadCast(Sema &S, unsigned msg, CastType castType, S.Diag(opRange.getBegin(), msg) << castType << src->getType() << destType << opRange << src->getSourceRange(); + + // Detect if both types are (ptr to) class, and note any incompleteness. + int DifferentPtrness = 0; + QualType From = destType; + if (auto Ptr = From->getAs()) { + From = Ptr->getPointeeType(); + DifferentPtrness++; + } + QualType To = src->getType(); + if (auto Ptr = To->getAs()) { + To = Ptr->getPointeeType(); + DifferentPtrness--; + } + if (!DifferentPtrness) { + auto RecFrom = From->getAs(); + auto RecTo = To->getAs(); + if (RecFrom && RecTo) { + auto DeclFrom = RecFrom->getAsCXXRecordDecl(); + if (!DeclFrom->isCompleteDefinition()) + S.Diag(DeclFrom->getLocation(), diag::note_type_incomplete) + << DeclFrom->getDeclName(); + auto DeclTo = RecTo->getAsCXXRecordDecl(); + if (!DeclTo->isCompleteDefinition()) + S.Diag(DeclTo->getLocation(), diag::note_type_incomplete) + << DeclTo->getDeclName(); + } + } } /// UnwrapDissimilarPointerTypes - Like Sema::UnwrapSimilarPointerTypes, @@ -1079,6 +1106,14 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, if (!CStyle && Self.CheckTollFreeBridgeStaticCast(DestType, SrcExpr.get(), Kind)) return TC_Success; + + // See if it looks like the user is trying to convert between + // related record types, and select a better diagnostic if so. + if (auto SrcPointer = SrcType->getAs()) + if (auto DestPointer = DestType->getAs()) + if (SrcPointer->getPointeeType()->getAs() && + DestPointer->getPointeeType()->getAs()) + msg = diag::err_bad_cxx_cast_unrelated_class; // We tried everything. Everything! Nothing works! :-( return TC_NotApplicable; diff --git a/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp b/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp index 59618d263683..ade192b3efa9 100644 --- a/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp +++ b/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s -template struct X; +template struct X; // expected-note {{'X' is incomplete}} template struct Y; X> *x1; diff --git a/clang/test/SemaCXX/static-cast.cpp b/clang/test/SemaCXX/static-cast.cpp index 06fd8636e5d6..b3fe49a88c6b 100644 --- a/clang/test/SemaCXX/static-cast.cpp +++ b/clang/test/SemaCXX/static-cast.cpp @@ -9,8 +9,8 @@ struct F : public C1 {}; // Single path to B with virtual. struct G1 : public B {}; struct G2 : public B {}; struct H : public G1, public G2 {}; // Ambiguous path to B. -struct I; // Incomplete. -struct J; // Incomplete. +struct I; // Incomplete. expected-note {{'I' is incomplete}} +struct J; // Incomplete. expected-note {{'J' is incomplete}} enum Enum { En1, En2 }; enum Onom { On1, On2 }; @@ -92,9 +92,13 @@ void t_529_5_8() (void)static_cast(*((A*)0)); // expected-error {{cannot cast private base class 'A' to 'E'}} (void)static_cast((A*)0); // expected-error {{ambiguous cast from base 'A' to derived 'H':\n struct A -> struct B -> struct G1 -> struct H\n struct A -> struct B -> struct G2 -> struct H}} (void)static_cast(*((A*)0)); // expected-error {{ambiguous cast from base 'A' to derived 'H':\n struct A -> struct B -> struct G1 -> struct H\n struct A -> struct B -> struct G2 -> struct H}} - (void)static_cast((B*)0); // expected-error {{static_cast from 'B *' to 'E *' is not allowed}} + (void)static_cast((B*)0); // expected-error {{static_cast from 'B *' to 'E *', which are not related by inheritance, is not allowed}} (void)static_cast(*((B*)0)); // expected-error {{non-const lvalue reference to type 'E' cannot bind to a value of unrelated type 'B'}} + + (void)static_cast((J*)0); // expected-error {{static_cast from 'J *' to 'E *', which are not related by inheritance, is not allowed}} + (void)static_cast((B*)0); // expected-error {{static_cast from 'B *' to 'I *', which are not related by inheritance, is not allowed}} + // TODO: Test inaccessible base in context where it's accessible, i.e. // member function and friend.