Index: include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -122,7 +122,7 @@ /// Each region is a subregion of itself. virtual bool isSubRegionOf(const MemRegion *R) const; - const MemRegion *StripCasts(bool StripBaseCasts = true) const; + const MemRegion *StripCasts(bool StripBaseAndDerivedCasts = true) const; /// If this is a symbolic region, returns the region. Otherwise, /// goes up the base chain looking for the first symbolic base region. @@ -1176,6 +1176,47 @@ } }; +// CXXDerivedObjectRegion represents a derived-class object that surrounds +// a C++ object. It is identified by the derived class declaration and the +// region of its parent object. It is a bit counter-intuitive (but not otherwise +// unseen) that this region represents a larger segment of memory that its +// super-region. +class CXXDerivedObjectRegion : public TypedValueRegion { + friend class MemRegionManager; + + const CXXRecordDecl *DerivedD; + + CXXDerivedObjectRegion(const CXXRecordDecl *DerivedD, const SubRegion *SReg) + : TypedValueRegion(SReg, CXXDerivedObjectRegionKind), DerivedD(DerivedD) { + assert(DerivedD); + // In case of a concrete region, it should always be possible to model + // the base-to-derived cast by undoing a previous derived-to-base cast, + // otherwise the cast is most likely ill-formed. + assert(SReg->getSymbolicBase() && + "Should have unwrapped a base region instead!"); + } + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, const CXXRecordDecl *RD, + const MemRegion *SReg); + +public: + const CXXRecordDecl *getDecl() const { return DerivedD; } + + QualType getValueType() const override; + + void dumpToStream(raw_ostream &os) const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + bool canPrintPrettyAsExpr() const override; + + void printPrettyAsExpr(raw_ostream &os) const override; + + static bool classof(const MemRegion *region) { + return region->getKind() == CXXDerivedObjectRegionKind; + } +}; + template const RegionTy* MemRegion::getAs() const { if (const auto *RT = dyn_cast(this)) @@ -1326,6 +1367,14 @@ baseReg->isVirtual()); } + /// Create a CXXDerivedObjectRegion with the given derived class for region + /// \p Super. This should not be used for casting an existing + /// CXXBaseObjectRegion back to the derived type; instead, CXXBaseObjectRegion + /// should be removed. + const CXXDerivedObjectRegion * + getCXXDerivedObjectRegion(const CXXRecordDecl *BaseClass, + const SubRegion *Super); + const FunctionCodeRegion *getFunctionCodeRegion(const NamedDecl *FD); const BlockCodeRegion *getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy, Index: include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def +++ include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def @@ -68,6 +68,7 @@ ABSTRACT_REGION(TypedValueRegion, TypedRegion) REGION(CompoundLiteralRegion, TypedValueRegion) REGION(CXXBaseObjectRegion, TypedValueRegion) + REGION(CXXDerivedObjectRegion, TypedValueRegion) REGION(CXXTempObjectRegion, TypedValueRegion) REGION(CXXThisRegion, TypedValueRegion) ABSTRACT_REGION(DeclRegion, TypedValueRegion) Index: lib/StaticAnalyzer/Core/MemRegion.cpp =================================================================== --- lib/StaticAnalyzer/Core/MemRegion.cpp +++ lib/StaticAnalyzer/Core/MemRegion.cpp @@ -225,6 +225,10 @@ return QualType(getDecl()->getTypeForDecl(), 0); } +QualType CXXDerivedObjectRegion::getValueType() const { + return QualType(getDecl()->getTypeForDecl(), 0); +} + //===----------------------------------------------------------------------===// // FoldingSet profiling. //===----------------------------------------------------------------------===// @@ -404,6 +408,17 @@ ProfileRegion(ID, getDecl(), isVirtual(), superRegion); } +void CXXDerivedObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const CXXRecordDecl *RD, + const MemRegion *SReg) { + ID.AddPointer(RD); + ID.AddPointer(SReg); +} + +void CXXDerivedObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ProfileRegion(ID, getDecl(), superRegion); +} + //===----------------------------------------------------------------------===// // Region anchors. //===----------------------------------------------------------------------===// @@ -475,7 +490,11 @@ } void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { - os << "base{" << superRegion << ',' << getDecl()->getName() << '}'; + os << "Base{" << superRegion << ',' << getDecl()->getName() << '}'; +} + +void CXXDerivedObjectRegion::dumpToStream(raw_ostream &os) const { + os << "Derived{" << superRegion << ',' << getDecl()->getName() << '}'; } void CXXThisRegion::dumpToStream(raw_ostream &os) const { @@ -483,7 +502,7 @@ } void ElementRegion::dumpToStream(raw_ostream &os) const { - os << "element{" << superRegion << ',' + os << "Element{" << superRegion << ',' << Index << ',' << getElementType().getAsString() << '}'; } @@ -492,7 +511,7 @@ } void ObjCIvarRegion::dumpToStream(raw_ostream &os) const { - os << "ivar{" << superRegion << ',' << *getDecl() << '}'; + os << "Ivar{" << superRegion << ',' << *getDecl() << '}'; } void StringRegion::dumpToStream(raw_ostream &os) const { @@ -630,6 +649,14 @@ superRegion->printPrettyAsExpr(os); } +bool CXXDerivedObjectRegion::canPrintPrettyAsExpr() const { + return superRegion->canPrintPrettyAsExpr(); +} + +void CXXDerivedObjectRegion::printPrettyAsExpr(raw_ostream &os) const { + superRegion->printPrettyAsExpr(os); +} + std::string MemRegion::getDescriptiveName(bool UseQuotes) const { std::string VariableName; std::string ArrayIndices; @@ -1061,6 +1088,12 @@ return getSubRegion(RD, IsVirtual, Super); } +const CXXDerivedObjectRegion * +MemRegionManager::getCXXDerivedObjectRegion(const CXXRecordDecl *RD, + const SubRegion *Super) { + return getSubRegion(RD, Super); +} + const CXXThisRegion* MemRegionManager::getCXXThisRegion(QualType thisPointerTy, const LocationContext *LC) { @@ -1131,6 +1164,7 @@ case MemRegion::FieldRegionKind: case MemRegion::ObjCIvarRegionKind: case MemRegion::CXXBaseObjectRegionKind: + case MemRegion::CXXDerivedObjectRegionKind: R = cast(R)->getSuperRegion(); continue; default: @@ -1149,7 +1183,7 @@ // View handling. //===----------------------------------------------------------------------===// -const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const { +const MemRegion *MemRegion::StripCasts(bool StripBaseAndDerivedCasts) const { const MemRegion *R = this; while (true) { switch (R->getKind()) { @@ -1161,9 +1195,10 @@ break; } case CXXBaseObjectRegionKind: - if (!StripBaseCasts) + case CXXDerivedObjectRegionKind: + if (!StripBaseAndDerivedCasts) return R; - R = cast(R)->getSuperRegion(); + R = cast(R)->getSuperRegion(); break; default: return R; @@ -1344,6 +1379,12 @@ Offset += BaseOffset.getQuantity() * R->getContext().getCharWidth(); break; } + + case MemRegion::CXXDerivedObjectRegionKind: { + // TODO: Store the base type in the CXXDerivedObjectRegion and use it. + goto Finish; + } + case MemRegion::ElementRegionKind: { const auto *ER = cast(R); R = ER->getSuperRegion(); Index: lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- lib/StaticAnalyzer/Core/RegionStore.cpp +++ lib/StaticAnalyzer/Core/RegionStore.cpp @@ -62,7 +62,9 @@ : P(r, k), Data(offset) { assert(r && "Must have known regions."); assert(getOffset() == offset && "Failed to store offset"); - assert((r == r->getBaseRegion() || isa(r)) && "Not a base"); + assert((r == r->getBaseRegion() || isa(r) || + isa (r)) && + "Not a base"); } public: Index: lib/StaticAnalyzer/Core/Store.cpp =================================================================== --- lib/StaticAnalyzer/Core/Store.cpp +++ lib/StaticAnalyzer/Core/Store.cpp @@ -138,6 +138,7 @@ case MemRegion::VarRegionKind: case MemRegion::CXXTempObjectRegionKind: case MemRegion::CXXBaseObjectRegionKind: + case MemRegion::CXXDerivedObjectRegionKind: return MakeElementRegion(cast(R), PointeeTy); case MemRegion::ElementRegionKind: { @@ -272,9 +273,8 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType, bool IsVirtual) { - Optional DerivedRegVal = - Derived.getAs(); - if (!DerivedRegVal) + const MemRegion *DerivedReg = Derived.getAsRegion(); + if (!DerivedReg) return Derived; const CXXRecordDecl *BaseDecl = BaseType->getPointeeCXXRecordDecl(); @@ -282,8 +282,18 @@ BaseDecl = BaseType->getAsCXXRecordDecl(); assert(BaseDecl && "not a C++ object?"); + if (const auto *AlreadyDerivedReg = + dyn_cast(DerivedReg)) { + if (const auto *SR = + dyn_cast(AlreadyDerivedReg->getSuperRegion())) + if (SR->getSymbol()->getType()->getPointeeCXXRecordDecl() == BaseDecl) + return loc::MemRegionVal(SR); + + DerivedReg = AlreadyDerivedReg->getSuperRegion(); + } + const MemRegion *BaseReg = MRMgr.getCXXBaseObjectRegion( - BaseDecl, cast(DerivedRegVal->getRegion()), IsVirtual); + BaseDecl, cast(DerivedReg), IsVirtual); return loc::MemRegionVal(BaseReg); } @@ -365,6 +375,10 @@ MR = Uncasted; } + if (const auto *SR = dyn_cast(MR)) { + return loc::MemRegionVal(MRMgr.getCXXDerivedObjectRegion(TargetClass, SR)); + } + // We failed if the region we ended up with has perfect type info. Failed = isa(MR); return UnknownVal(); Index: test/Analysis/casts.cpp =================================================================== --- test/Analysis/casts.cpp +++ test/Analysis/casts.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-store=region -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-store=region -verify %s + +void clang_analyzer_eval(bool); bool PR14634(int x) { double y = (double)x; @@ -41,3 +43,32 @@ *reinterpret_cast(&q) = p; return q; } + +namespace base_to_derived { +struct A {}; +struct B : public A{}; + +void foo(A* a) { + B* b = (B* ) a; + A* a2 = (A *) b; + clang_analyzer_eval(a2 == a); // expected-warning{{TRUE}} +} +} + +namespace base_to_derived_double_inheritance { +struct A { + int x; +}; +struct B { + int y; +}; +struct C : A, B {}; + +void foo(B *b) { + C *c = (C *)b; + b->y = 1; + clang_analyzer_eval(c->x); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(c->y); // expected-warning{{TRUE}} +} +} +