diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index c0fb275a9a8b..10e9a289cce0 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1658,9 +1658,12 @@ private: void CheckBasePath() const { #ifndef NDEBUG switch (getCastKind()) { + case CK_DerivedToBase: + assert(!BasePath.empty() && "Cast kind should have a base path!"); + break; + // FIXME: We should add inheritance paths for these. case CK_BaseToDerived: - case CK_DerivedToBase: case CK_UncheckedDerivedToBase: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: @@ -1687,7 +1690,7 @@ private: case CK_MemberPointerToBoolean: case CK_AnyPointerToObjCPointerCast: case CK_AnyPointerToBlockPointerCast: - assert(BasePath.empty() && "Cast kind shoudl not have a base path!"); + assert(BasePath.empty() && "Cast kind should not have a base path!"); break; } #endif diff --git a/clang/lib/AST/StmtDumper.cpp b/clang/lib/AST/StmtDumper.cpp index 8481055b9d07..f8b817351936 100644 --- a/clang/lib/AST/StmtDumper.cpp +++ b/clang/lib/AST/StmtDumper.cpp @@ -300,9 +300,35 @@ void StmtDumper::VisitExpr(Expr *Node) { DumpExpr(Node); } +static void DumpBasePath(llvm::raw_ostream &OS, CastExpr *Node) { + if (Node->getBasePath().empty()) + return; + + OS << " ("; + bool First = true; + for (CXXBaseSpecifierArray::iterator I = Node->getBasePath().begin(), + E = Node->getBasePath().end(); I != E; ++I) { + const CXXBaseSpecifier *Base = *I; + if (!First) + OS << " -> "; + + const CXXRecordDecl *RD = + cast(Base->getType()->getAs()->getDecl()); + + if (Base->isVirtual()) + OS << "virtual "; + OS << RD->getName(); + First = false; + } + + OS << ')'; +} + void StmtDumper::VisitCastExpr(CastExpr *Node) { DumpExpr(Node); - OS << " <" << Node->getCastKindName() << ">"; + OS << " <" << Node->getCastKindName(); + DumpBasePath(OS, Node); + OS << ">"; } void StmtDumper::VisitImplicitCastExpr(ImplicitCastExpr *Node) { @@ -452,7 +478,9 @@ void StmtDumper::VisitCXXNamedCastExpr(CXXNamedCastExpr *Node) { DumpExpr(Node); OS << " " << Node->getCastName() << "<" << Node->getTypeAsWritten().getAsString() << ">" - << " <" << Node->getCastKindName() << ">"; + << " <" << Node->getCastKindName(); + DumpBasePath(OS, Node); + OS << ">"; } void StmtDumper::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *Node) { diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 16c7e6c1e37c..48c33fa0ced6 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -1087,6 +1087,7 @@ public: QualType& ConvertedType, bool &IncompatibleObjC); bool CheckPointerConversion(Expr *From, QualType ToType, CastExpr::CastKind &Kind, + CXXBaseSpecifierArray& BasePath, bool IgnoreBaseAccess); bool IsMemberPointerConversion(Expr *From, QualType FromType, QualType ToType, bool InOverloadResolution, @@ -2593,7 +2594,11 @@ public: bool IsDerivedFrom(QualType Derived, QualType Base); bool IsDerivedFrom(QualType Derived, QualType Base, CXXBasePaths &Paths); - + + // FIXME: I don't like this name. + void BuildBasePathArray(const CXXBasePaths &Paths, + CXXBaseSpecifierArray &BasePath); + bool CheckDerivedToBaseConversion(QualType Derived, QualType Base, SourceLocation Loc, SourceRange Range, CXXBaseSpecifierArray *BasePath = 0, @@ -4227,7 +4232,7 @@ public: /// CheckCastTypes - Check type constraints for casting between types under /// C semantics, or forward to CXXCheckCStyleCast in C++. bool CheckCastTypes(SourceRange TyRange, QualType CastTy, Expr *&CastExpr, - CastExpr::CastKind &Kind, + CastExpr::CastKind &Kind, CXXBaseSpecifierArray &BasePath, bool FunctionalStyle = false); // CheckVectorCast - check type constraints for vectors. @@ -4248,7 +4253,9 @@ public: /// CXXCheckCStyleCast - Check constraints of a C-style or function-style /// cast under C++ semantics. bool CXXCheckCStyleCast(SourceRange R, QualType CastTy, Expr *&CastExpr, - CastExpr::CastKind &Kind, bool FunctionalStyle); + CastExpr::CastKind &Kind, + CXXBaseSpecifierArray &BasePath, + bool FunctionalStyle); /// CheckMessageArgumentTypes - Check types in an Obj-C message send. /// \param Method - May be null. diff --git a/clang/lib/Sema/SemaCXXCast.cpp b/clang/lib/Sema/SemaCXXCast.cpp index 393c84a5d8a5..a62effa4a713 100644 --- a/clang/lib/Sema/SemaCXXCast.cpp +++ b/clang/lib/Sema/SemaCXXCast.cpp @@ -46,11 +46,13 @@ static void CheckReinterpretCast(Sema &Self, Expr *&SrcExpr, QualType DestType, CastExpr::CastKind &Kind); static void CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType, const SourceRange &OpRange, - CastExpr::CastKind &Kind); + CastExpr::CastKind &Kind, + CXXBaseSpecifierArray &BasePath); static void CheckDynamicCast(Sema &Self, Expr *&SrcExpr, QualType DestType, const SourceRange &OpRange, const SourceRange &DestRange, - CastExpr::CastKind &Kind); + CastExpr::CastKind &Kind, + CXXBaseSpecifierArray &BasePath); static bool CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType); @@ -98,7 +100,8 @@ static TryCastResult TryStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType, bool CStyle, const SourceRange &OpRange, unsigned &msg, - CastExpr::CastKind &Kind); + CastExpr::CastKind &Kind, + CXXBaseSpecifierArray &BasePath); static TryCastResult TryConstCast(Sema &Self, Expr *SrcExpr, QualType DestType, bool CStyle, unsigned &msg); static TryCastResult TryReinterpretCast(Sema &Self, Expr *SrcExpr, @@ -150,30 +153,27 @@ Sema::BuildCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind, case tok::kw_dynamic_cast: { CastExpr::CastKind Kind = CastExpr::CK_Unknown; - // FIXME: Initialize base path! CXXBaseSpecifierArray BasePath; if (!TypeDependent) - CheckDynamicCast(*this, Ex, DestType, OpRange, DestRange, Kind); + CheckDynamicCast(*this, Ex, DestType, OpRange, DestRange, Kind, BasePath); return Owned(new (Context)CXXDynamicCastExpr(DestType.getNonReferenceType(), Kind, Ex, BasePath, DestTInfo, OpLoc)); } case tok::kw_reinterpret_cast: { CastExpr::CastKind Kind = CastExpr::CK_Unknown; - // FIXME: Initialize base path! - CXXBaseSpecifierArray BasePath; if (!TypeDependent) CheckReinterpretCast(*this, Ex, DestType, OpRange, DestRange, Kind); return Owned(new (Context) CXXReinterpretCastExpr( DestType.getNonReferenceType(), - Kind, Ex, BasePath, DestTInfo, OpLoc)); + Kind, Ex, CXXBaseSpecifierArray(), + DestTInfo, OpLoc)); } case tok::kw_static_cast: { CastExpr::CastKind Kind = CastExpr::CK_Unknown; - // FIXME: Initialize base path! CXXBaseSpecifierArray BasePath; if (!TypeDependent) - CheckStaticCast(*this, Ex, DestType, OpRange, Kind); + CheckStaticCast(*this, Ex, DestType, OpRange, Kind, BasePath); return Owned(new (Context) CXXStaticCastExpr(DestType.getNonReferenceType(), Kind, Ex, BasePath, @@ -282,7 +282,8 @@ CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType) { static void CheckDynamicCast(Sema &Self, Expr *&SrcExpr, QualType DestType, const SourceRange &OpRange, - const SourceRange &DestRange, CastExpr::CastKind &Kind) { + const SourceRange &DestRange, CastExpr::CastKind &Kind, + CXXBaseSpecifierArray &BasePath) { QualType OrigDestType = DestType, OrigSrcType = SrcExpr->getType(); DestType = Self.Context.getCanonicalType(DestType); @@ -376,10 +377,12 @@ CheckDynamicCast(Sema &Self, Expr *&SrcExpr, QualType DestType, // C++ 5.2.7p5 // Upcasts are resolved statically. if (DestRecord && Self.IsDerivedFrom(SrcPointee, DestPointee)) { - Self.CheckDerivedToBaseConversion(SrcPointee, DestPointee, - OpRange.getBegin(), OpRange); + if (Self.CheckDerivedToBaseConversion(SrcPointee, DestPointee, + OpRange.getBegin(), OpRange, + &BasePath)) + return; + Kind = CastExpr::CK_DerivedToBase; - // Diagnostic already emitted on error. return; } @@ -439,7 +442,8 @@ CheckReinterpretCast(Sema &Self, Expr *&SrcExpr, QualType DestType, /// implicit conversions explicit and getting rid of data loss warnings. void CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType, - const SourceRange &OpRange, CastExpr::CastKind &Kind) { + const SourceRange &OpRange, CastExpr::CastKind &Kind, + CXXBaseSpecifierArray &BasePath) { // This test is outside everything else because it's the only case where // a non-lvalue-reference target type does not lead to decay. // C++ 5.2.9p4: Any expression can be explicitly converted to type "cv void". @@ -453,8 +457,7 @@ CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType, unsigned msg = diag::err_bad_cxx_cast_generic; if (TryStaticCast(Self, SrcExpr, DestType, /*CStyle*/false, OpRange, msg, - Kind) - != TC_Success && msg != 0) + Kind, BasePath) != TC_Success && msg != 0) Self.Diag(OpRange.getBegin(), msg) << CT_Static << SrcExpr->getType() << DestType << OpRange; } @@ -465,7 +468,8 @@ CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType, static TryCastResult TryStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType, bool CStyle, const SourceRange &OpRange, unsigned &msg, - CastExpr::CastKind &Kind) { + CastExpr::CastKind &Kind, + CXXBaseSpecifierArray &BasePath) { // The order the tests is not entirely arbitrary. There is one conversion // that can be handled in two different ways. Given: // struct A {}; @@ -1189,8 +1193,11 @@ static TryCastResult TryReinterpretCast(Sema &Self, Expr *SrcExpr, return TC_Success; } -bool Sema::CXXCheckCStyleCast(SourceRange R, QualType CastTy, Expr *&CastExpr, - CastExpr::CastKind &Kind, bool FunctionalStyle) { +bool +Sema::CXXCheckCStyleCast(SourceRange R, QualType CastTy, Expr *&CastExpr, + CastExpr::CastKind &Kind, + CXXBaseSpecifierArray &BasePath, + bool FunctionalStyle) { // This test is outside everything else because it's the only case where // a non-lvalue-reference target type does not lead to decay. // C++ 5.2.9p4: Any expression can be explicitly converted to type "cv void". @@ -1225,7 +1232,8 @@ bool Sema::CXXCheckCStyleCast(SourceRange R, QualType CastTy, Expr *&CastExpr, if (tcr == TC_NotApplicable) { // ... or if that is not possible, a static_cast, ignoring const, ... - tcr = TryStaticCast(*this, CastExpr, CastTy, /*CStyle*/true, R, msg, Kind); + tcr = TryStaticCast(*this, CastExpr, CastTy, /*CStyle*/true, R, msg, Kind, + BasePath); if (tcr == TC_NotApplicable) { // ... and finally a reinterpret_cast, ignoring const. tcr = TryReinterpretCast(*this, CastExpr, CastTy, /*CStyle*/true, R, msg, diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 3179a566bd6e..0373ca800691 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -423,7 +423,8 @@ bool Sema::SemaBuiltinAtomicOverloaded(CallExpr *TheCall) { // GCC does an implicit conversion to the pointer or integer ValType. This // can fail in some cases (1i -> int**), check for this error case now. CastExpr::CastKind Kind = CastExpr::CK_Unknown; - if (CheckCastTypes(Arg->getSourceRange(), ValType, Arg, Kind)) + CXXBaseSpecifierArray BasePath; + if (CheckCastTypes(Arg->getSourceRange(), ValType, Arg, Kind, BasePath)) return true; // Okay, we have something that *can* be converted to the right type. Check diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 243d854a941b..6d1d1070fdbc 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -710,6 +710,29 @@ bool Sema::IsDerivedFrom(QualType Derived, QualType Base, CXXBasePaths &Paths) { return DerivedRD->isDerivedFrom(BaseRD, Paths); } +void Sema::BuildBasePathArray(const CXXBasePaths &Paths, + CXXBaseSpecifierArray &BasePathArray) { + assert(BasePathArray.empty() && "Base path array must be empty!"); + assert(Paths.isRecordingPaths() && "Must record paths!"); + + const CXXBasePath &Path = Paths.front(); + + // We first go backward and check if we have a virtual base. + // FIXME: It would be better if CXXBasePath had the base specifier for + // the nearest virtual base. + unsigned Start = 0; + for (unsigned I = Path.size(); I != 0; --I) { + if (Path[I - 1].Base->isVirtual()) { + Start = I - 1; + break; + } + } + + // Now add all bases. + for (unsigned I = Start, E = Path.size(); I != E; ++I) + BasePathArray.push_back(Path[I].Base); +} + /// CheckDerivedToBaseConversion - Check whether the Derived-to-Base /// conversion (where Derived and Base are class types) is /// well-formed, meaning that the conversion is unambiguous (and @@ -737,23 +760,23 @@ Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base, (void)DerivationOkay; if (!Paths.isAmbiguous(Context.getCanonicalType(Base).getUnqualifiedType())) { - if (!InaccessibleBaseID) - return false; - - // Check that the base class can be accessed. - switch (CheckBaseClassAccess(Loc, Base, Derived, Paths.front(), - InaccessibleBaseID)) { - case AR_inaccessible: - return true; - case AR_accessible: - case AR_dependent: - case AR_delayed: - // Build a base path if necessary. - if (BasePath) { - // FIXME: Do this! + if (InaccessibleBaseID) { + // Check that the base class can be accessed. + switch (CheckBaseClassAccess(Loc, Base, Derived, Paths.front(), + InaccessibleBaseID)) { + case AR_inaccessible: + return true; + case AR_accessible: + case AR_dependent: + case AR_delayed: + break; } - return false; } + + // Build a base path if necessary. + if (BasePath) + BuildBasePathArray(Paths, *BasePath); + return false; } // We know that the derived-to-base conversion is ambiguous, and diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index f0b25f90ffa3..c576bcd3aaa2 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3794,9 +3794,11 @@ static CastExpr::CastKind getScalarCastKind(ASTContext &Context, /// CheckCastTypes - Check type constraints for casting between types. bool Sema::CheckCastTypes(SourceRange TyR, QualType castType, Expr *&castExpr, CastExpr::CastKind& Kind, + CXXBaseSpecifierArray &BasePath, bool FunctionalStyle) { if (getLangOptions().CPlusPlus) - return CXXCheckCStyleCast(TyR, castType, castExpr, Kind, FunctionalStyle); + return CXXCheckCStyleCast(TyR, castType, castExpr, Kind, BasePath, + FunctionalStyle); DefaultFunctionArrayLvalueConversion(castExpr); @@ -3957,10 +3959,9 @@ Sema::BuildCStyleCastExpr(SourceLocation LParenLoc, TypeSourceInfo *Ty, Expr *castExpr = static_cast(Op.get()); CastExpr::CastKind Kind = CastExpr::CK_Unknown; - // FIXME: Initialize base path! CXXBaseSpecifierArray BasePath; if (CheckCastTypes(SourceRange(LParenLoc, RParenLoc), Ty->getType(), castExpr, - Kind)) + Kind, BasePath)) return ExprError(); Op.release(); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index b382236e80db..568fc6dd7bf9 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -501,9 +501,9 @@ Sema::ActOnCXXTypeConstructExpr(SourceRange TypeRange, TypeTy *TypeRep, // if (NumExprs == 1) { CastExpr::CastKind Kind = CastExpr::CK_Unknown; - // FIXME: Initialize base path! CXXBaseSpecifierArray BasePath; - if (CheckCastTypes(TypeRange, Ty, Exprs[0], Kind, /*FunctionalStyle=*/true)) + if (CheckCastTypes(TypeRange, Ty, Exprs[0], Kind, BasePath, + /*FunctionalStyle=*/true)) return ExprError(); exprs.release(); @@ -1745,9 +1745,10 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, CastExpr::CastKind Kind = CastExpr::CK_Unknown; - if (CheckPointerConversion(From, ToType, Kind, IgnoreBaseAccess)) + CXXBaseSpecifierArray BasePath; + if (CheckPointerConversion(From, ToType, Kind, BasePath, IgnoreBaseAccess)) return true; - ImpCastExprToType(From, ToType, Kind); + ImpCastExprToType(From, ToType, Kind, /*isLvalue=*/false, BasePath); break; } @@ -1875,7 +1876,7 @@ QualType Sema::CheckPointerToMemberOperands( << OpSpelling << (int)isIndirect)) { return QualType(); } - CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false, + CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, /*DetectVirtual=*/false); // FIXME: Would it be useful to print full ambiguity paths, or is that // overkill? @@ -1888,7 +1889,11 @@ QualType Sema::CheckPointerToMemberOperands( // Cast LHS to type of use. QualType UseType = isIndirect ? Context.getPointerType(Class) : Class; bool isLValue = !isIndirect && lex->isLvalue(Context) == Expr::LV_Valid; - ImpCastExprToType(lex, UseType, CastExpr::CK_DerivedToBase, isLValue); + + CXXBaseSpecifierArray BasePath; + BuildBasePathArray(Paths, BasePath); + ImpCastExprToType(lex, UseType, CastExpr::CK_DerivedToBase, isLValue, + BasePath); } if (isa(rex->IgnoreParens())) { diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 5887c5e7e46b..5c55b56c7d39 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -3457,18 +3457,20 @@ InitializationSequence::Perform(Sema &S, // We have a derived-to-base cast that produces either an rvalue or an // lvalue. Perform that cast. + CXXBaseSpecifierArray BasePath; + // Casts to inaccessible base classes are allowed with C-style casts. bool IgnoreBaseAccess = Kind.isCStyleOrFunctionalCast(); if (S.CheckDerivedToBaseConversion(SourceType, Step->Type, CurInitExpr->getLocStart(), - CurInitExpr->getSourceRange(), 0, - IgnoreBaseAccess)) + CurInitExpr->getSourceRange(), + &BasePath, IgnoreBaseAccess)) return S.ExprError(); CurInit = S.Owned(new (S.Context) ImplicitCastExpr(Step->Type, CastExpr::CK_DerivedToBase, (Expr*)CurInit.release(), - CXXBaseSpecifierArray(), + BasePath, Step->Kind == SK_CastDerivedToBaseLValue)); break; } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 5e5f23352ce6..5ce0284d1649 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1343,6 +1343,7 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType, /// error, or returns false otherwise. bool Sema::CheckPointerConversion(Expr *From, QualType ToType, CastExpr::CastKind &Kind, + CXXBaseSpecifierArray& BasePath, bool IgnoreBaseAccess) { QualType FromType = From->getType(); @@ -1357,7 +1358,7 @@ bool Sema::CheckPointerConversion(Expr *From, QualType ToType, // ambiguous or inaccessible conversion. if (CheckDerivedToBaseConversion(FromPointeeType, ToPointeeType, From->getExprLoc(), - From->getSourceRange(), 0, + From->getSourceRange(), &BasePath, IgnoreBaseAccess)) return true; diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-examples.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-examples.cpp index d757adf6986c..9b3925969c45 100644 --- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-examples.cpp +++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-examples.cpp @@ -17,11 +17,11 @@ struct B : A { } b; // CHECK: example1 void example1() { // CHECK: A &ra = - // CHECK: ImplicitCastExpr{{.*}}'struct A' lvalue + // CHECK: ImplicitCastExpr{{.*}}'struct A' lvalue A &ra = b; // CHECK: A const &rca = // CHECK: ImplicitCastExpr{{.*}}'struct A const' - // CHECK: ImplicitCastExpr{{.*}}'struct A' + // CHECK: ImplicitCastExpr{{.*}}'struct A' const A& rca = b; } @@ -35,12 +35,12 @@ struct X { void example2() { // CHECK: A const &rca = // CHECK: ImplicitCastExpr{{.*}}'struct A const' - // CHECK: ImplicitCastExpr{{.*}}'struct A' + // CHECK: ImplicitCastExpr{{.*}}'struct A' // CHECK: CallExpr{{.*}}B const A &rca = f(); // CHECK: A const &r = // CHECK: ImplicitCastExpr{{.*}}'struct A const' - // CHECK: ImplicitCastExpr{{.*}}'struct A' + // CHECK: ImplicitCastExpr{{.*}}'struct A' // CHECK: CXXMemberCallExpr{{.*}}'struct B' const A& r = x; }