From ecb7e5c8afe929ee38155db94de6b084ec32a645 Mon Sep 17 00:00:00 2001 From: Roman Divacky Date: Tue, 16 Feb 2010 09:31:36 +0000 Subject: Update clang to r96341. --- lib/AST/ASTContext.cpp | 247 +- lib/AST/ASTDiagnostic.cpp | 266 ++ lib/AST/ASTImporter.cpp | 2394 ++++++++++++++ lib/AST/AttrImpl.cpp | 65 +- lib/AST/CMakeLists.txt | 2 + lib/AST/CXXInheritance.cpp | 14 +- lib/AST/Decl.cpp | 734 +++-- lib/AST/DeclBase.cpp | 35 +- lib/AST/DeclCXX.cpp | 123 +- lib/AST/DeclObjC.cpp | 14 +- lib/AST/DeclPrinter.cpp | 62 +- lib/AST/DeclTemplate.cpp | 3 +- lib/AST/DeclarationName.cpp | 47 +- lib/AST/Expr.cpp | 134 +- lib/AST/ExprCXX.cpp | 100 +- lib/AST/ExprConstant.cpp | 39 +- lib/AST/Makefile | 1 - lib/AST/RecordLayoutBuilder.cpp | 9 +- lib/AST/Stmt.cpp | 148 +- lib/AST/StmtDumper.cpp | 2 + lib/AST/StmtPrinter.cpp | 4 + lib/AST/StmtProfile.cpp | 4 + lib/AST/Type.cpp | 154 +- lib/AST/TypePrinter.cpp | 40 +- lib/Analysis/AnalysisContext.cpp | 48 +- lib/Analysis/ArrayBoundChecker.cpp | 91 - lib/Analysis/AttrNonNullChecker.cpp | 112 - lib/Analysis/BasicConstraintManager.cpp | 317 -- lib/Analysis/BasicObjCFoundationChecks.cpp | 598 ---- lib/Analysis/BasicObjCFoundationChecks.h | 41 - lib/Analysis/BasicStore.cpp | 625 ---- lib/Analysis/BasicValueFactory.cpp | 290 -- lib/Analysis/BugReporter.cpp | 1879 ----------- lib/Analysis/BugReporterVisitors.cpp | 349 --- lib/Analysis/BuiltinFunctionChecker.cpp | 76 - lib/Analysis/CFRefCount.cpp | 3790 ----------------------- lib/Analysis/CMakeLists.txt | 59 +- lib/Analysis/CallAndMessageChecker.cpp | 252 -- lib/Analysis/CallInliner.cpp | 113 - lib/Analysis/CastToStructChecker.cpp | 77 - lib/Analysis/CheckDeadStores.cpp | 279 -- lib/Analysis/CheckObjCDealloc.cpp | 258 -- lib/Analysis/CheckObjCInstMethSignature.cpp | 119 - lib/Analysis/CheckObjCUnusedIVars.cpp | 162 - lib/Analysis/CheckSecuritySyntaxOnly.cpp | 502 --- lib/Analysis/CheckSizeofPointer.cpp | 71 - lib/Analysis/Checker.cpp | 35 - lib/Analysis/DereferenceChecker.cpp | 135 - lib/Analysis/DivZeroChecker.cpp | 84 - lib/Analysis/Environment.cpp | 164 - lib/Analysis/ExplodedGraph.cpp | 281 -- lib/Analysis/FixedAddressChecker.cpp | 70 - lib/Analysis/GRBlockCounter.cpp | 54 - lib/Analysis/GRCoreEngine.cpp | 599 ---- lib/Analysis/GRExprEngine.cpp | 3325 -------------------- lib/Analysis/GRExprEngineExperimentalChecks.cpp | 40 - lib/Analysis/GRExprEngineExperimentalChecks.h | 26 - lib/Analysis/GRExprEngineInternalChecks.h | 44 - lib/Analysis/GRState.cpp | 358 --- lib/Analysis/LiveVariables.cpp | 2 +- lib/Analysis/Makefile | 1 - lib/Analysis/MallocChecker.cpp | 336 -- lib/Analysis/ManagerRegistry.cpp | 20 - lib/Analysis/MemRegion.cpp | 800 ----- lib/Analysis/NSAutoreleasePoolChecker.cpp | 86 - lib/Analysis/NSErrorChecker.cpp | 237 -- lib/Analysis/NoReturnFunctionChecker.cpp | 79 - lib/Analysis/OSAtomicChecker.cpp | 198 -- lib/Analysis/PathDiagnostic.cpp | 281 -- lib/Analysis/PointerArithChecker.cpp | 71 - lib/Analysis/PointerSubChecker.cpp | 77 - lib/Analysis/PrintfFormatString.cpp | 436 +++ lib/Analysis/PthreadLockChecker.cpp | 141 - lib/Analysis/RangeConstraintManager.cpp | 359 --- lib/Analysis/RegionStore.cpp | 2015 ------------ lib/Analysis/ReturnPointerRangeChecker.cpp | 97 - lib/Analysis/ReturnStackAddressChecker.cpp | 114 - lib/Analysis/ReturnUndefChecker.cpp | 68 - lib/Analysis/SVals.cpp | 327 -- lib/Analysis/SValuator.cpp | 164 - lib/Analysis/SimpleConstraintManager.cpp | 249 -- lib/Analysis/SimpleConstraintManager.h | 83 - lib/Analysis/SimpleSValuator.cpp | 428 --- lib/Analysis/Store.cpp | 250 -- lib/Analysis/SymbolManager.cpp | 228 -- lib/Analysis/UndefBranchChecker.cpp | 117 - lib/Analysis/UndefResultChecker.cpp | 86 - lib/Analysis/UndefinedArraySubscriptChecker.cpp | 56 - lib/Analysis/UndefinedAssignmentChecker.cpp | 79 - lib/Analysis/UninitializedValues.cpp | 1 - lib/Analysis/VLASizeChecker.cpp | 96 - lib/Analysis/ValueManager.cpp | 153 - lib/Basic/Diagnostic.cpp | 219 +- lib/Basic/IdentifierTable.cpp | 4 +- lib/Basic/Makefile | 1 - lib/Basic/SourceManager.cpp | 10 +- lib/Basic/TargetInfo.cpp | 39 +- lib/Basic/Targets.cpp | 157 +- lib/Basic/Version.cpp | 78 +- lib/CMakeLists.txt | 1 + lib/Checker/AdjustedReturnValueChecker.cpp | 98 + lib/Checker/ArrayBoundChecker.cpp | 91 + lib/Checker/AttrNonNullChecker.cpp | 112 + lib/Checker/BasicConstraintManager.cpp | 317 ++ lib/Checker/BasicObjCFoundationChecks.cpp | 559 ++++ lib/Checker/BasicObjCFoundationChecks.h | 41 + lib/Checker/BasicStore.cpp | 494 +++ lib/Checker/BasicValueFactory.cpp | 289 ++ lib/Checker/BugReporter.cpp | 1879 +++++++++++ lib/Checker/BugReporterVisitors.cpp | 367 +++ lib/Checker/BuiltinFunctionChecker.cpp | 76 + lib/Checker/CFRefCount.cpp | 3521 +++++++++++++++++++++ lib/Checker/CMakeLists.txt | 67 + lib/Checker/CallAndMessageChecker.cpp | 252 ++ lib/Checker/CallInliner.cpp | 113 + lib/Checker/CastToStructChecker.cpp | 77 + lib/Checker/CheckDeadStores.cpp | 279 ++ lib/Checker/CheckObjCDealloc.cpp | 258 ++ lib/Checker/CheckObjCInstMethSignature.cpp | 119 + lib/Checker/CheckObjCUnusedIVars.cpp | 162 + lib/Checker/CheckSecuritySyntaxOnly.cpp | 502 +++ lib/Checker/CheckSizeofPointer.cpp | 71 + lib/Checker/Checker.cpp | 35 + lib/Checker/CocoaConventions.cpp | 195 ++ lib/Checker/DereferenceChecker.cpp | 135 + lib/Checker/DivZeroChecker.cpp | 84 + lib/Checker/Environment.cpp | 164 + lib/Checker/ExplodedGraph.cpp | 281 ++ lib/Checker/FixedAddressChecker.cpp | 70 + lib/Checker/FlatStore.cpp | 166 + lib/Checker/GRBlockCounter.cpp | 54 + lib/Checker/GRCoreEngine.cpp | 599 ++++ lib/Checker/GRExprEngine.cpp | 3332 ++++++++++++++++++++ lib/Checker/GRExprEngineExperimentalChecks.cpp | 40 + lib/Checker/GRExprEngineExperimentalChecks.h | 26 + lib/Checker/GRExprEngineInternalChecks.h | 45 + lib/Checker/GRState.cpp | 360 +++ lib/Checker/LLVMConventionsChecker.cpp | 335 ++ lib/Checker/Makefile | 21 + lib/Checker/MallocChecker.cpp | 341 ++ lib/Checker/ManagerRegistry.cpp | 20 + lib/Checker/MemRegion.cpp | 799 +++++ lib/Checker/NSAutoreleasePoolChecker.cpp | 86 + lib/Checker/NSErrorChecker.cpp | 237 ++ lib/Checker/NoReturnFunctionChecker.cpp | 79 + lib/Checker/OSAtomicChecker.cpp | 197 ++ lib/Checker/PathDiagnostic.cpp | 281 ++ lib/Checker/PointerArithChecker.cpp | 71 + lib/Checker/PointerSubChecker.cpp | 77 + lib/Checker/PthreadLockChecker.cpp | 141 + lib/Checker/RangeConstraintManager.cpp | 359 +++ lib/Checker/RegionStore.cpp | 1905 ++++++++++++ lib/Checker/ReturnPointerRangeChecker.cpp | 97 + lib/Checker/ReturnStackAddressChecker.cpp | 125 + lib/Checker/ReturnUndefChecker.cpp | 68 + lib/Checker/SVals.cpp | 327 ++ lib/Checker/SValuator.cpp | 157 + lib/Checker/SimpleConstraintManager.cpp | 249 ++ lib/Checker/SimpleConstraintManager.h | 83 + lib/Checker/SimpleSValuator.cpp | 428 +++ lib/Checker/Store.cpp | 327 ++ lib/Checker/SymbolManager.cpp | 228 ++ lib/Checker/UndefBranchChecker.cpp | 117 + lib/Checker/UndefCapturedBlockVarChecker.cpp | 101 + lib/Checker/UndefResultChecker.cpp | 86 + lib/Checker/UndefinedArraySubscriptChecker.cpp | 56 + lib/Checker/UndefinedAssignmentChecker.cpp | 79 + lib/Checker/VLASizeChecker.cpp | 96 + lib/Checker/ValueManager.cpp | 153 + lib/CodeGen/CGBlocks.cpp | 74 +- lib/CodeGen/CGBlocks.h | 5 +- lib/CodeGen/CGBuiltin.cpp | 3 +- lib/CodeGen/CGCXX.cpp | 8 +- lib/CodeGen/CGCall.cpp | 116 +- lib/CodeGen/CGCall.h | 9 + lib/CodeGen/CGClass.cpp | 445 ++- lib/CodeGen/CGDebugInfo.cpp | 918 +++--- lib/CodeGen/CGDebugInfo.h | 45 +- lib/CodeGen/CGDecl.cpp | 91 +- lib/CodeGen/CGDeclCXX.cpp | 118 +- lib/CodeGen/CGException.cpp | 26 +- lib/CodeGen/CGExpr.cpp | 149 +- lib/CodeGen/CGExprAgg.cpp | 86 +- lib/CodeGen/CGExprCXX.cpp | 125 +- lib/CodeGen/CGExprComplex.cpp | 12 +- lib/CodeGen/CGExprConstant.cpp | 175 +- lib/CodeGen/CGExprScalar.cpp | 57 +- lib/CodeGen/CGObjC.cpp | 26 +- lib/CodeGen/CGObjCGNU.cpp | 135 +- lib/CodeGen/CGObjCMac.cpp | 29 +- lib/CodeGen/CGRTTI.cpp | 7 +- lib/CodeGen/CGRecordLayoutBuilder.cpp | 41 +- lib/CodeGen/CGRecordLayoutBuilder.h | 15 +- lib/CodeGen/CGStmt.cpp | 22 +- lib/CodeGen/CGVtable.cpp | 1683 +++++++++- lib/CodeGen/CodeGenFunction.cpp | 24 +- lib/CodeGen/CodeGenFunction.h | 61 +- lib/CodeGen/CodeGenModule.cpp | 149 +- lib/CodeGen/CodeGenModule.h | 18 +- lib/CodeGen/CodeGenTypes.cpp | 32 +- lib/CodeGen/CodeGenTypes.h | 36 +- lib/CodeGen/GlobalDecl.h | 4 + lib/CodeGen/Makefile | 1 - lib/CodeGen/Mangle.cpp | 320 +- lib/CodeGen/TargetInfo.cpp | 126 +- lib/Driver/Driver.cpp | 81 +- lib/Driver/HostInfo.cpp | 11 +- lib/Driver/Makefile | 1 - lib/Driver/ToolChains.cpp | 158 +- lib/Driver/ToolChains.h | 115 +- lib/Driver/Tools.cpp | 228 +- lib/Driver/Tools.h | 19 +- lib/Frontend/ASTConsumers.cpp | 2 +- lib/Frontend/ASTMerge.cpp | 103 + lib/Frontend/ASTUnit.cpp | 30 +- lib/Frontend/AnalysisConsumer.cpp | 161 +- lib/Frontend/Backend.cpp | 38 +- lib/Frontend/CMakeLists.txt | 1 + lib/Frontend/CompilerInstance.cpp | 54 +- lib/Frontend/CompilerInvocation.cpp | 41 +- lib/Frontend/FrontendActions.cpp | 5 + lib/Frontend/HTMLDiagnostics.cpp | 2 +- lib/Frontend/InitHeaderSearch.cpp | 10 +- lib/Frontend/InitPreprocessor.cpp | 2 + lib/Frontend/Makefile | 1 - lib/Frontend/PCHReader.cpp | 44 +- lib/Frontend/PCHReaderDecl.cpp | 13 +- lib/Frontend/PCHReaderStmt.cpp | 33 +- lib/Frontend/PCHWriter.cpp | 35 +- lib/Frontend/PCHWriterDecl.cpp | 1 + lib/Frontend/PCHWriterStmt.cpp | 21 +- lib/Frontend/PlistDiagnostics.cpp | 2 +- lib/Frontend/PrintParserCallbacks.cpp | 5 +- lib/Frontend/RewriteObjC.cpp | 448 +-- lib/Frontend/TextDiagnosticPrinter.cpp | 26 +- lib/Headers/xmmintrin.h | 16 +- lib/Index/Makefile | 1 - lib/Lex/Lexer.cpp | 16 +- lib/Lex/Makefile | 1 - lib/Lex/PPCaching.cpp | 2 +- lib/Lex/PPDirectives.cpp | 17 +- lib/Lex/PPMacroExpansion.cpp | 38 +- lib/Lex/Preprocessor.cpp | 2 +- lib/Makefile | 2 +- lib/Parse/DeclSpec.cpp | 37 + lib/Parse/Makefile | 1 - lib/Parse/ParseDecl.cpp | 94 +- lib/Parse/ParseDeclCXX.cpp | 122 +- lib/Parse/ParseExpr.cpp | 41 +- lib/Parse/ParseExprCXX.cpp | 7 +- lib/Parse/ParseObjc.cpp | 24 +- lib/Parse/ParseStmt.cpp | 90 +- lib/Parse/ParseTemplate.cpp | 2 +- lib/Parse/ParseTentative.cpp | 9 + lib/Parse/Parser.cpp | 23 +- lib/Rewrite/Makefile | 1 - lib/Sema/IdentifierResolver.cpp | 38 +- lib/Sema/Lookup.h | 44 +- lib/Sema/Makefile | 1 - lib/Sema/Sema.cpp | 280 +- lib/Sema/Sema.h | 327 +- lib/Sema/SemaAccess.cpp | 592 +++- lib/Sema/SemaCXXCast.cpp | 59 +- lib/Sema/SemaCXXScopeSpec.cpp | 4 + lib/Sema/SemaChecking.cpp | 554 ++-- lib/Sema/SemaCodeComplete.cpp | 16 +- lib/Sema/SemaDecl.cpp | 682 ++-- lib/Sema/SemaDeclAttr.cpp | 211 +- lib/Sema/SemaDeclCXX.cpp | 730 +++-- lib/Sema/SemaDeclObjC.cpp | 219 +- lib/Sema/SemaExceptionSpec.cpp | 83 +- lib/Sema/SemaExpr.cpp | 148 +- lib/Sema/SemaExprCXX.cpp | 127 +- lib/Sema/SemaExprObjC.cpp | 31 +- lib/Sema/SemaInit.cpp | 692 ++++- lib/Sema/SemaInit.h | 35 +- lib/Sema/SemaLookup.cpp | 318 +- lib/Sema/SemaOverload.cpp | 646 ++-- lib/Sema/SemaOverload.h | 62 +- lib/Sema/SemaStmt.cpp | 178 +- lib/Sema/SemaTemplate.cpp | 199 +- lib/Sema/SemaTemplateDeduction.cpp | 361 ++- lib/Sema/SemaTemplateInstantiate.cpp | 242 +- lib/Sema/SemaTemplateInstantiateDecl.cpp | 182 +- lib/Sema/SemaType.cpp | 188 +- lib/Sema/TargetAttributesSema.cpp | 44 + lib/Sema/TreeTransform.h | 160 +- 287 files changed, 37816 insertions(+), 28329 deletions(-) create mode 100644 lib/AST/ASTDiagnostic.cpp create mode 100644 lib/AST/ASTImporter.cpp delete mode 100644 lib/Analysis/ArrayBoundChecker.cpp delete mode 100644 lib/Analysis/AttrNonNullChecker.cpp delete mode 100644 lib/Analysis/BasicConstraintManager.cpp delete mode 100644 lib/Analysis/BasicObjCFoundationChecks.cpp delete mode 100644 lib/Analysis/BasicObjCFoundationChecks.h delete mode 100644 lib/Analysis/BasicStore.cpp delete mode 100644 lib/Analysis/BasicValueFactory.cpp delete mode 100644 lib/Analysis/BugReporter.cpp delete mode 100644 lib/Analysis/BugReporterVisitors.cpp delete mode 100644 lib/Analysis/BuiltinFunctionChecker.cpp delete mode 100644 lib/Analysis/CFRefCount.cpp delete mode 100644 lib/Analysis/CallAndMessageChecker.cpp delete mode 100644 lib/Analysis/CallInliner.cpp delete mode 100644 lib/Analysis/CastToStructChecker.cpp delete mode 100644 lib/Analysis/CheckDeadStores.cpp delete mode 100644 lib/Analysis/CheckObjCDealloc.cpp delete mode 100644 lib/Analysis/CheckObjCInstMethSignature.cpp delete mode 100644 lib/Analysis/CheckObjCUnusedIVars.cpp delete mode 100644 lib/Analysis/CheckSecuritySyntaxOnly.cpp delete mode 100644 lib/Analysis/CheckSizeofPointer.cpp delete mode 100644 lib/Analysis/Checker.cpp delete mode 100644 lib/Analysis/DereferenceChecker.cpp delete mode 100644 lib/Analysis/DivZeroChecker.cpp delete mode 100644 lib/Analysis/Environment.cpp delete mode 100644 lib/Analysis/ExplodedGraph.cpp delete mode 100644 lib/Analysis/FixedAddressChecker.cpp delete mode 100644 lib/Analysis/GRBlockCounter.cpp delete mode 100644 lib/Analysis/GRCoreEngine.cpp delete mode 100644 lib/Analysis/GRExprEngine.cpp delete mode 100644 lib/Analysis/GRExprEngineExperimentalChecks.cpp delete mode 100644 lib/Analysis/GRExprEngineExperimentalChecks.h delete mode 100644 lib/Analysis/GRExprEngineInternalChecks.h delete mode 100644 lib/Analysis/GRState.cpp delete mode 100644 lib/Analysis/MallocChecker.cpp delete mode 100644 lib/Analysis/ManagerRegistry.cpp delete mode 100644 lib/Analysis/MemRegion.cpp delete mode 100644 lib/Analysis/NSAutoreleasePoolChecker.cpp delete mode 100644 lib/Analysis/NSErrorChecker.cpp delete mode 100644 lib/Analysis/NoReturnFunctionChecker.cpp delete mode 100644 lib/Analysis/OSAtomicChecker.cpp delete mode 100644 lib/Analysis/PathDiagnostic.cpp delete mode 100644 lib/Analysis/PointerArithChecker.cpp delete mode 100644 lib/Analysis/PointerSubChecker.cpp create mode 100644 lib/Analysis/PrintfFormatString.cpp delete mode 100644 lib/Analysis/PthreadLockChecker.cpp delete mode 100644 lib/Analysis/RangeConstraintManager.cpp delete mode 100644 lib/Analysis/RegionStore.cpp delete mode 100644 lib/Analysis/ReturnPointerRangeChecker.cpp delete mode 100644 lib/Analysis/ReturnStackAddressChecker.cpp delete mode 100644 lib/Analysis/ReturnUndefChecker.cpp delete mode 100644 lib/Analysis/SVals.cpp delete mode 100644 lib/Analysis/SValuator.cpp delete mode 100644 lib/Analysis/SimpleConstraintManager.cpp delete mode 100644 lib/Analysis/SimpleConstraintManager.h delete mode 100644 lib/Analysis/SimpleSValuator.cpp delete mode 100644 lib/Analysis/Store.cpp delete mode 100644 lib/Analysis/SymbolManager.cpp delete mode 100644 lib/Analysis/UndefBranchChecker.cpp delete mode 100644 lib/Analysis/UndefResultChecker.cpp delete mode 100644 lib/Analysis/UndefinedArraySubscriptChecker.cpp delete mode 100644 lib/Analysis/UndefinedAssignmentChecker.cpp delete mode 100644 lib/Analysis/VLASizeChecker.cpp delete mode 100644 lib/Analysis/ValueManager.cpp create mode 100644 lib/Checker/AdjustedReturnValueChecker.cpp create mode 100644 lib/Checker/ArrayBoundChecker.cpp create mode 100644 lib/Checker/AttrNonNullChecker.cpp create mode 100644 lib/Checker/BasicConstraintManager.cpp create mode 100644 lib/Checker/BasicObjCFoundationChecks.cpp create mode 100644 lib/Checker/BasicObjCFoundationChecks.h create mode 100644 lib/Checker/BasicStore.cpp create mode 100644 lib/Checker/BasicValueFactory.cpp create mode 100644 lib/Checker/BugReporter.cpp create mode 100644 lib/Checker/BugReporterVisitors.cpp create mode 100644 lib/Checker/BuiltinFunctionChecker.cpp create mode 100644 lib/Checker/CFRefCount.cpp create mode 100644 lib/Checker/CMakeLists.txt create mode 100644 lib/Checker/CallAndMessageChecker.cpp create mode 100644 lib/Checker/CallInliner.cpp create mode 100644 lib/Checker/CastToStructChecker.cpp create mode 100644 lib/Checker/CheckDeadStores.cpp create mode 100644 lib/Checker/CheckObjCDealloc.cpp create mode 100644 lib/Checker/CheckObjCInstMethSignature.cpp create mode 100644 lib/Checker/CheckObjCUnusedIVars.cpp create mode 100644 lib/Checker/CheckSecuritySyntaxOnly.cpp create mode 100644 lib/Checker/CheckSizeofPointer.cpp create mode 100644 lib/Checker/Checker.cpp create mode 100644 lib/Checker/CocoaConventions.cpp create mode 100644 lib/Checker/DereferenceChecker.cpp create mode 100644 lib/Checker/DivZeroChecker.cpp create mode 100644 lib/Checker/Environment.cpp create mode 100644 lib/Checker/ExplodedGraph.cpp create mode 100644 lib/Checker/FixedAddressChecker.cpp create mode 100644 lib/Checker/FlatStore.cpp create mode 100644 lib/Checker/GRBlockCounter.cpp create mode 100644 lib/Checker/GRCoreEngine.cpp create mode 100644 lib/Checker/GRExprEngine.cpp create mode 100644 lib/Checker/GRExprEngineExperimentalChecks.cpp create mode 100644 lib/Checker/GRExprEngineExperimentalChecks.h create mode 100644 lib/Checker/GRExprEngineInternalChecks.h create mode 100644 lib/Checker/GRState.cpp create mode 100644 lib/Checker/LLVMConventionsChecker.cpp create mode 100644 lib/Checker/Makefile create mode 100644 lib/Checker/MallocChecker.cpp create mode 100644 lib/Checker/ManagerRegistry.cpp create mode 100644 lib/Checker/MemRegion.cpp create mode 100644 lib/Checker/NSAutoreleasePoolChecker.cpp create mode 100644 lib/Checker/NSErrorChecker.cpp create mode 100644 lib/Checker/NoReturnFunctionChecker.cpp create mode 100644 lib/Checker/OSAtomicChecker.cpp create mode 100644 lib/Checker/PathDiagnostic.cpp create mode 100644 lib/Checker/PointerArithChecker.cpp create mode 100644 lib/Checker/PointerSubChecker.cpp create mode 100644 lib/Checker/PthreadLockChecker.cpp create mode 100644 lib/Checker/RangeConstraintManager.cpp create mode 100644 lib/Checker/RegionStore.cpp create mode 100644 lib/Checker/ReturnPointerRangeChecker.cpp create mode 100644 lib/Checker/ReturnStackAddressChecker.cpp create mode 100644 lib/Checker/ReturnUndefChecker.cpp create mode 100644 lib/Checker/SVals.cpp create mode 100644 lib/Checker/SValuator.cpp create mode 100644 lib/Checker/SimpleConstraintManager.cpp create mode 100644 lib/Checker/SimpleConstraintManager.h create mode 100644 lib/Checker/SimpleSValuator.cpp create mode 100644 lib/Checker/Store.cpp create mode 100644 lib/Checker/SymbolManager.cpp create mode 100644 lib/Checker/UndefBranchChecker.cpp create mode 100644 lib/Checker/UndefCapturedBlockVarChecker.cpp create mode 100644 lib/Checker/UndefResultChecker.cpp create mode 100644 lib/Checker/UndefinedArraySubscriptChecker.cpp create mode 100644 lib/Checker/UndefinedAssignmentChecker.cpp create mode 100644 lib/Checker/VLASizeChecker.cpp create mode 100644 lib/Checker/ValueManager.cpp create mode 100644 lib/Frontend/ASTMerge.cpp (limited to 'lib') diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index c1bc70989d4e..c23babb9a4a5 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -56,6 +56,10 @@ ASTContext::ASTContext(const LangOptions& LOpts, SourceManager &SM, } ASTContext::~ASTContext() { + // Release the DenseMaps associated with DeclContext objects. + // FIXME: Is this the ideal solution? + ReleaseDeclContextMaps(); + if (FreeMemory) { // Deallocate all the types. while (!Types.empty()) { @@ -533,12 +537,12 @@ const llvm::fltSemantics &ASTContext::getFloatTypeSemantics(QualType T) const { } } -/// getDeclAlignInBytes - Return a conservative estimate of the alignment of the +/// getDeclAlign - Return a conservative estimate of the alignment of the /// specified decl. Note that bitfields do not have a valid alignment, so /// this method will assert on them. /// If @p RefAsPointee, references are treated like their underlying type /// (for alignof), else they're treated like pointers (for CodeGen). -unsigned ASTContext::getDeclAlignInBytes(const Decl *D, bool RefAsPointee) { +CharUnits ASTContext::getDeclAlign(const Decl *D, bool RefAsPointee) { unsigned Align = Target.getCharWidth(); if (const AlignedAttr* AA = D->getAttr()) @@ -561,7 +565,7 @@ unsigned ASTContext::getDeclAlignInBytes(const Decl *D, bool RefAsPointee) { } } - return Align / Target.getCharWidth(); + return CharUnits::fromQuantity(Align / Target.getCharWidth()); } /// getTypeSize - Return the size of the specified type, in bits. This method @@ -820,6 +824,15 @@ CharUnits ASTContext::getTypeSizeInChars(const Type *T) { return CharUnits::fromQuantity(getTypeSize(T) / getCharWidth()); } +/// getTypeAlignInChars - Return the ABI-specified alignment of a type, in +/// characters. This method does not work on incomplete types. +CharUnits ASTContext::getTypeAlignInChars(QualType T) { + return CharUnits::fromQuantity(getTypeAlign(T) / getCharWidth()); +} +CharUnits ASTContext::getTypeAlignInChars(const Type *T) { + return CharUnits::fromQuantity(getTypeAlign(T) / getCharWidth()); +} + /// getPreferredTypeAlign - Return the "preferred" alignment of the specified /// type for the current target in bits. This can be different than the ABI /// alignment in cases where it is beneficial for performance to overalign @@ -904,12 +917,12 @@ void ASTContext::CollectSynthesizedIvars(const ObjCInterfaceDecl *OI, /// CollectInheritedProtocols - Collect all protocols in current class and /// those inherited by it. void ASTContext::CollectInheritedProtocols(const Decl *CDecl, - llvm::SmallVectorImpl &Protocols) { + llvm::SmallPtrSet &Protocols) { if (const ObjCInterfaceDecl *OI = dyn_cast(CDecl)) { for (ObjCInterfaceDecl::protocol_iterator P = OI->protocol_begin(), PE = OI->protocol_end(); P != PE; ++P) { ObjCProtocolDecl *Proto = (*P); - Protocols.push_back(Proto); + Protocols.insert(Proto); for (ObjCProtocolDecl::protocol_iterator P = Proto->protocol_begin(), PE = Proto->protocol_end(); P != PE; ++P) CollectInheritedProtocols(*P, Protocols); @@ -930,7 +943,7 @@ void ASTContext::CollectInheritedProtocols(const Decl *CDecl, for (ObjCInterfaceDecl::protocol_iterator P = OC->protocol_begin(), PE = OC->protocol_end(); P != PE; ++P) { ObjCProtocolDecl *Proto = (*P); - Protocols.push_back(Proto); + Protocols.insert(Proto); for (ObjCProtocolDecl::protocol_iterator P = Proto->protocol_begin(), PE = Proto->protocol_end(); P != PE; ++P) CollectInheritedProtocols(*P, Protocols); @@ -941,7 +954,7 @@ void ASTContext::CollectInheritedProtocols(const Decl *CDecl, for (ObjCProtocolDecl::protocol_iterator P = OP->protocol_begin(), PE = OP->protocol_end(); P != PE; ++P) { ObjCProtocolDecl *Proto = (*P); - Protocols.push_back(Proto); + Protocols.insert(Proto); for (ObjCProtocolDecl::protocol_iterator P = Proto->protocol_begin(), PE = Proto->protocol_end(); P != PE; ++P) CollectInheritedProtocols(*P, Protocols); @@ -1088,7 +1101,7 @@ ASTContext::getASTObjCImplementationLayout(const ObjCImplementationDecl *D) { /// specified record (struct/union/class), which indicates its size and field /// position information. const ASTRecordLayout &ASTContext::getASTRecordLayout(const RecordDecl *D) { - D = D->getDefinition(*this); + D = D->getDefinition(); assert(D && "Cannot get layout of forward declarations!"); // Look up this layout, if already laid out, return what we have. @@ -1105,7 +1118,7 @@ const ASTRecordLayout &ASTContext::getASTRecordLayout(const RecordDecl *D) { } const CXXMethodDecl *ASTContext::getKeyFunction(const CXXRecordDecl *RD) { - RD = cast(RD->getDefinition(*this)); + RD = cast(RD->getDefinition()); assert(RD && "Cannot get key function for forward declarations!"); const CXXMethodDecl *&Entry = KeyFunctions[RD]; @@ -1519,12 +1532,12 @@ QualType ASTContext::getDependentSizedArrayType(QualType EltTy, void *InsertPos = 0; DependentSizedArrayType *Canon = 0; + llvm::FoldingSetNodeID ID; if (NumElts) { // Dependently-sized array types that do not have a specified // number of elements will have their sizes deduced from an // initializer. - llvm::FoldingSetNodeID ID; DependentSizedArrayType::Profile(ID, *this, getCanonicalType(EltTy), ASM, EltTypeQuals, NumElts); @@ -1545,8 +1558,13 @@ QualType ASTContext::getDependentSizedArrayType(QualType EltTy, DependentSizedArrayType(*this, EltTy, QualType(), NumElts, ASM, EltTypeQuals, Brackets); - if (NumElts) + if (NumElts) { + DependentSizedArrayType *CanonCheck + = DependentSizedArrayTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!CanonCheck && "Dependent-sized canonical array type broken"); + (void)CanonCheck; DependentSizedArrayTypes.InsertNode(New, InsertPos); + } } else { QualType Canon = getDependentSizedArrayType(CanonEltTy, NumElts, ASM, EltTypeQuals, @@ -1596,7 +1614,8 @@ QualType ASTContext::getIncompleteArrayType(QualType EltTy, /// getVectorType - Return the unique reference to a vector type of /// the specified element type and size. VectorType must be a built-in type. -QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts) { +QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts, + bool IsAltiVec, bool IsPixel) { BuiltinType *baseType; baseType = dyn_cast(getCanonicalType(vecType).getTypePtr()); @@ -1604,7 +1623,8 @@ QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts) { // Check if we've already instantiated a vector of this type. llvm::FoldingSetNodeID ID; - VectorType::Profile(ID, vecType, NumElts, Type::Vector); + VectorType::Profile(ID, vecType, NumElts, Type::Vector, + IsAltiVec, IsPixel); void *InsertPos = 0; if (VectorType *VTP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(VTP, 0); @@ -1612,15 +1632,16 @@ QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts) { // If the element type isn't canonical, this won't be a canonical type either, // so fill in the canonical type field. QualType Canonical; - if (!vecType.isCanonical()) { - Canonical = getVectorType(getCanonicalType(vecType), NumElts); + if (!vecType.isCanonical() || IsAltiVec || IsPixel) { + Canonical = getVectorType(getCanonicalType(vecType), + NumElts, false, false); // Get the new insert position for the node we care about. VectorType *NewIP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos); assert(NewIP == 0 && "Shouldn't be in the map!"); NewIP = NewIP; } VectorType *New = new (*this, TypeAlignment) - VectorType(vecType, NumElts, Canonical); + VectorType(vecType, NumElts, Canonical, IsAltiVec, IsPixel); VectorTypes.InsertNode(New, InsertPos); Types.push_back(New); return QualType(New, 0); @@ -1636,7 +1657,7 @@ QualType ASTContext::getExtVectorType(QualType vecType, unsigned NumElts) { // Check if we've already instantiated a vector of this type. llvm::FoldingSetNodeID ID; - VectorType::Profile(ID, vecType, NumElts, Type::ExtVector); + VectorType::Profile(ID, vecType, NumElts, Type::ExtVector, false, false); void *InsertPos = 0; if (VectorType *VTP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(VTP, 0); @@ -1681,6 +1702,11 @@ QualType ASTContext::getDependentSizedExtVectorType(QualType vecType, New = new (*this, TypeAlignment) DependentSizedExtVectorType(*this, vecType, QualType(), SizeExpr, AttrLoc); + + DependentSizedExtVectorType *CanonCheck + = DependentSizedExtVectorTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!CanonCheck && "Dependent-sized ext_vector canonical type broken"); + (void)CanonCheck; DependentSizedExtVectorTypes.InsertNode(New, InsertPos); } else { QualType Canon = getDependentSizedExtVectorType(CanonVecTy, SizeExpr, @@ -1694,12 +1720,6 @@ QualType ASTContext::getDependentSizedExtVectorType(QualType vecType, return QualType(New, 0); } -static CallingConv getCanonicalCallingConv(CallingConv CC) { - if (CC == CC_C) - return CC_Default; - return CC; -} - /// getFunctionNoProtoType - Return a K&R style C function type like 'int()'. /// QualType ASTContext::getFunctionNoProtoType(QualType ResultTy, bool NoReturn, @@ -1707,7 +1727,7 @@ QualType ASTContext::getFunctionNoProtoType(QualType ResultTy, bool NoReturn, // Unique functions, to guarantee there is only one function of a particular // structure. llvm::FoldingSetNodeID ID; - FunctionNoProtoType::Profile(ID, ResultTy, NoReturn); + FunctionNoProtoType::Profile(ID, ResultTy, NoReturn, CallConv); void *InsertPos = 0; if (FunctionNoProtoType *FT = @@ -1716,9 +1736,9 @@ QualType ASTContext::getFunctionNoProtoType(QualType ResultTy, bool NoReturn, QualType Canonical; if (!ResultTy.isCanonical() || - getCanonicalCallingConv(CallConv) != CallConv) { + getCanonicalCallConv(CallConv) != CallConv) { Canonical = getFunctionNoProtoType(getCanonicalType(ResultTy), NoReturn, - getCanonicalCallingConv(CallConv)); + getCanonicalCallConv(CallConv)); // Get the new insert position for the node we care about. FunctionNoProtoType *NewIP = @@ -1727,7 +1747,7 @@ QualType ASTContext::getFunctionNoProtoType(QualType ResultTy, bool NoReturn, } FunctionNoProtoType *New = new (*this, TypeAlignment) - FunctionNoProtoType(ResultTy, Canonical, NoReturn); + FunctionNoProtoType(ResultTy, Canonical, NoReturn, CallConv); Types.push_back(New); FunctionNoProtoTypes.InsertNode(New, InsertPos); return QualType(New, 0); @@ -1746,7 +1766,7 @@ QualType ASTContext::getFunctionType(QualType ResultTy,const QualType *ArgArray, llvm::FoldingSetNodeID ID; FunctionProtoType::Profile(ID, ResultTy, ArgArray, NumArgs, isVariadic, TypeQuals, hasExceptionSpec, hasAnyExceptionSpec, - NumExs, ExArray, NoReturn); + NumExs, ExArray, NoReturn, CallConv); void *InsertPos = 0; if (FunctionProtoType *FTP = @@ -1762,7 +1782,7 @@ QualType ASTContext::getFunctionType(QualType ResultTy,const QualType *ArgArray, // If this type isn't canonical, get the canonical version of it. // The exception spec is not part of the canonical type. QualType Canonical; - if (!isCanonical || getCanonicalCallingConv(CallConv) != CallConv) { + if (!isCanonical || getCanonicalCallConv(CallConv) != CallConv) { llvm::SmallVector CanonicalArgs; CanonicalArgs.reserve(NumArgs); for (unsigned i = 0; i != NumArgs; ++i) @@ -1772,7 +1792,7 @@ QualType ASTContext::getFunctionType(QualType ResultTy,const QualType *ArgArray, CanonicalArgs.data(), NumArgs, isVariadic, TypeQuals, false, false, 0, 0, NoReturn, - getCanonicalCallingConv(CallConv)); + getCanonicalCallConv(CallConv)); // Get the new insert position for the node we care about. FunctionProtoType *NewIP = @@ -1797,29 +1817,30 @@ QualType ASTContext::getFunctionType(QualType ResultTy,const QualType *ArgArray, /// getTypeDeclType - Return the unique reference to the type for the /// specified type declaration. -QualType ASTContext::getTypeDeclType(TypeDecl *Decl, TypeDecl* PrevDecl) { +QualType ASTContext::getTypeDeclType(const TypeDecl *Decl, + const TypeDecl* PrevDecl) { assert(Decl && "Passed null for Decl param"); if (Decl->TypeForDecl) return QualType(Decl->TypeForDecl, 0); - if (TypedefDecl *Typedef = dyn_cast(Decl)) + if (const TypedefDecl *Typedef = dyn_cast(Decl)) return getTypedefType(Typedef); else if (isa(Decl)) { assert(false && "Template type parameter types are always available."); - } else if (ObjCInterfaceDecl *ObjCInterface + } else if (const ObjCInterfaceDecl *ObjCInterface = dyn_cast(Decl)) return getObjCInterfaceType(ObjCInterface); - if (RecordDecl *Record = dyn_cast(Decl)) { + if (const RecordDecl *Record = dyn_cast(Decl)) { if (PrevDecl) Decl->TypeForDecl = PrevDecl->TypeForDecl; else Decl->TypeForDecl = new (*this, TypeAlignment) RecordType(Record); - } else if (EnumDecl *Enum = dyn_cast(Decl)) { + } else if (const EnumDecl *Enum = dyn_cast(Decl)) { if (PrevDecl) Decl->TypeForDecl = PrevDecl->TypeForDecl; else Decl->TypeForDecl = new (*this, TypeAlignment) EnumType(Enum); - } else if (UnresolvedUsingTypenameDecl *Using = + } else if (const UnresolvedUsingTypenameDecl *Using = dyn_cast(Decl)) { Decl->TypeForDecl = new (*this, TypeAlignment) UnresolvedUsingType(Using); } else @@ -1831,7 +1852,7 @@ QualType ASTContext::getTypeDeclType(TypeDecl *Decl, TypeDecl* PrevDecl) { /// getTypedefType - Return the unique reference to the type for the /// specified typename decl. -QualType ASTContext::getTypedefType(TypedefDecl *Decl) { +QualType ASTContext::getTypedefType(const TypedefDecl *Decl) { if (Decl->TypeForDecl) return QualType(Decl->TypeForDecl, 0); QualType Canonical = getCanonicalType(Decl->getUnderlyingType()); @@ -1883,6 +1904,11 @@ QualType ASTContext::getTemplateTypeParmType(unsigned Depth, unsigned Index, QualType Canon = getTemplateTypeParmType(Depth, Index, ParameterPack); TypeParm = new (*this, TypeAlignment) TemplateTypeParmType(Depth, Index, ParameterPack, Name, Canon); + + TemplateTypeParmType *TypeCheck + = TemplateTypeParmTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!TypeCheck && "Template type parameter canonical type broken"); + (void)TypeCheck; } else TypeParm = new (*this, TypeAlignment) TemplateTypeParmType(Depth, Index, ParameterPack); @@ -1976,8 +2002,16 @@ ASTContext::getQualifiedNameType(NestedNameSpecifier *NNS, if (T) return QualType(T, 0); - T = new (*this) QualifiedNameType(NNS, NamedType, - getCanonicalType(NamedType)); + QualType Canon = NamedType; + if (!Canon.isCanonical()) { + Canon = getCanonicalType(NamedType); + QualifiedNameType *CheckT + = QualifiedNameTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!CheckT && "Qualified name canonical type broken"); + (void)CheckT; + } + + T = new (*this) QualifiedNameType(NNS, NamedType, Canon); Types.push_back(T); QualifiedNameTypes.InsertNode(T, InsertPos); return QualType(T, 0); @@ -2015,6 +2049,15 @@ ASTContext::getTypenameType(NestedNameSpecifier *NNS, QualType Canon) { assert(NNS->isDependent() && "nested-name-specifier must be dependent"); + llvm::FoldingSetNodeID ID; + TypenameType::Profile(ID, NNS, TemplateId); + + void *InsertPos = 0; + TypenameType *T + = TypenameTypes.FindNodeOrInsertPos(ID, InsertPos); + if (T) + return QualType(T, 0); + if (Canon.isNull()) { NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS); QualType CanonType = getCanonicalType(QualType(TemplateId, 0)); @@ -2025,16 +2068,11 @@ ASTContext::getTypenameType(NestedNameSpecifier *NNS, "Canonical type must also be a template specialization type"); Canon = getTypenameType(CanonNNS, CanonTemplateId); } - } - llvm::FoldingSetNodeID ID; - TypenameType::Profile(ID, NNS, TemplateId); - - void *InsertPos = 0; - TypenameType *T - = TypenameTypes.FindNodeOrInsertPos(ID, InsertPos); - if (T) - return QualType(T, 0); + TypenameType *CheckT + = TypenameTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!CheckT && "Typename canonical type is broken"); (void)CheckT; + } T = new (*this) TypenameType(NNS, TemplateId, Canon); Types.push_back(T); @@ -2053,7 +2091,12 @@ ASTContext::getElaboratedType(QualType UnderlyingType, if (T) return QualType(T, 0); - QualType Canon = getCanonicalType(UnderlyingType); + QualType Canon = UnderlyingType; + if (!Canon.isCanonical()) { + Canon = getCanonicalType(Canon); + ElaboratedType *CheckT = ElaboratedTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!CheckT && "Elaborated canonical type is broken"); (void)CheckT; + } T = new (*this) ElaboratedType(UnderlyingType, Tag, Canon); Types.push_back(T); @@ -2125,10 +2168,14 @@ QualType ASTContext::getObjCObjectPointerType(QualType InterfaceT, ObjCObjectPointerTypes.FindNodeOrInsertPos(ID, InsertPos); } - // No Match; - ObjCObjectPointerType *QType = new (*this, TypeAlignment) - ObjCObjectPointerType(*this, Canonical, InterfaceT, Protocols, - NumProtocols); + // No match. + unsigned Size = sizeof(ObjCObjectPointerType) + + NumProtocols * sizeof(ObjCProtocolDecl *); + void *Mem = Allocate(Size, TypeAlignment); + ObjCObjectPointerType *QType = new (Mem) ObjCObjectPointerType(Canonical, + InterfaceT, + Protocols, + NumProtocols); Types.push_back(QType); ObjCObjectPointerTypes.InsertNode(QType, InsertPos); @@ -2161,9 +2208,13 @@ QualType ASTContext::getObjCInterfaceType(const ObjCInterfaceDecl *Decl, ObjCInterfaceTypes.FindNodeOrInsertPos(ID, InsertPos); } - ObjCInterfaceType *QType = new (*this, TypeAlignment) - ObjCInterfaceType(*this, Canonical, const_cast(Decl), - Protocols, NumProtocols); + unsigned Size = sizeof(ObjCInterfaceType) + + NumProtocols * sizeof(ObjCProtocolDecl *); + void *Mem = Allocate(Size, TypeAlignment); + ObjCInterfaceType *QType = new (Mem) ObjCInterfaceType(Canonical, + const_cast(Decl), + Protocols, + NumProtocols); Types.push_back(QType); ObjCInterfaceTypes.InsertNode(QType, InsertPos); @@ -2858,6 +2909,7 @@ QualType ASTContext::getCFConstantStringType() { CFConstantStringTypeDecl = CreateRecordDecl(*this, TagDecl::TK_struct, TUDecl, SourceLocation(), &Idents.get("NSConstantString")); + CFConstantStringTypeDecl->startDefinition(); QualType FieldTypes[4]; @@ -2880,7 +2932,7 @@ QualType ASTContext::getCFConstantStringType() { CFConstantStringTypeDecl->addDecl(Field); } - CFConstantStringTypeDecl->completeDefinition(*this); + CFConstantStringTypeDecl->completeDefinition(); } return getTagDeclType(CFConstantStringTypeDecl); @@ -2897,6 +2949,7 @@ QualType ASTContext::getObjCFastEnumerationStateType() { ObjCFastEnumerationStateTypeDecl = CreateRecordDecl(*this, TagDecl::TK_struct, TUDecl, SourceLocation(), &Idents.get("__objcFastEnumerationState")); + ObjCFastEnumerationStateTypeDecl->startDefinition(); QualType FieldTypes[] = { UnsignedLongTy, @@ -2916,7 +2969,7 @@ QualType ASTContext::getObjCFastEnumerationStateType() { ObjCFastEnumerationStateTypeDecl->addDecl(Field); } - ObjCFastEnumerationStateTypeDecl->completeDefinition(*this); + ObjCFastEnumerationStateTypeDecl->completeDefinition(); } return getTagDeclType(ObjCFastEnumerationStateTypeDecl); @@ -2930,6 +2983,7 @@ QualType ASTContext::getBlockDescriptorType() { // FIXME: Needs the FlagAppleBlock bit. T = CreateRecordDecl(*this, TagDecl::TK_struct, TUDecl, SourceLocation(), &Idents.get("__block_descriptor")); + T->startDefinition(); QualType FieldTypes[] = { UnsignedLongTy, @@ -2952,7 +3006,7 @@ QualType ASTContext::getBlockDescriptorType() { T->addDecl(Field); } - T->completeDefinition(*this); + T->completeDefinition(); BlockDescriptorType = T; @@ -2973,6 +3027,7 @@ QualType ASTContext::getBlockDescriptorExtendedType() { // FIXME: Needs the FlagAppleBlock bit. T = CreateRecordDecl(*this, TagDecl::TK_struct, TUDecl, SourceLocation(), &Idents.get("__block_descriptor_withcopydispose")); + T->startDefinition(); QualType FieldTypes[] = { UnsignedLongTy, @@ -2999,7 +3054,7 @@ QualType ASTContext::getBlockDescriptorExtendedType() { T->addDecl(Field); } - T->completeDefinition(*this); + T->completeDefinition(); BlockDescriptorExtendedType = T; @@ -3076,7 +3131,7 @@ QualType ASTContext::BuildByRefType(const char *DeclName, QualType Ty) { T->addDecl(Field); } - T->completeDefinition(*this); + T->completeDefinition(); return getPointerType(getTagDeclType(T)); } @@ -3093,6 +3148,7 @@ QualType ASTContext::getBlockParmType( RecordDecl *T; T = CreateRecordDecl(*this, TagDecl::TK_struct, TUDecl, SourceLocation(), &Idents.get(Name.str())); + T->startDefinition(); QualType FieldTypes[] = { getPointerType(VoidPtrTy), IntTy, @@ -3139,7 +3195,7 @@ QualType ASTContext::getBlockParmType( T->addDecl(Field); } - T->completeDefinition(*this); + T->completeDefinition(); return getPointerType(getTagDeclType(T)); } @@ -3786,6 +3842,7 @@ TemplateName ASTContext::getOverloadedTemplateName(UnresolvedSetIterator Begin, TemplateName ASTContext::getQualifiedTemplateName(NestedNameSpecifier *NNS, bool TemplateKeyword, TemplateDecl *Template) { + // FIXME: Canonicalization? llvm::FoldingSetNodeID ID; QualifiedTemplateName::Profile(ID, NNS, TemplateKeyword, Template); @@ -3823,6 +3880,10 @@ TemplateName ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, } else { TemplateName Canon = getDependentTemplateName(CanonNNS, Name); QTN = new (*this,4) DependentTemplateName(NNS, Name, Canon); + DependentTemplateName *CheckQTN = + DependentTemplateNames.FindNodeOrInsertPos(ID, InsertPos); + assert(!CheckQTN && "Dependent type name canonicalization broken"); + (void)CheckQTN; } DependentTemplateNames.InsertNode(QTN, InsertPos); @@ -3841,8 +3902,8 @@ ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, DependentTemplateName::Profile(ID, NNS, Operator); void *InsertPos = 0; - DependentTemplateName *QTN = - DependentTemplateNames.FindNodeOrInsertPos(ID, InsertPos); + DependentTemplateName *QTN + = DependentTemplateNames.FindNodeOrInsertPos(ID, InsertPos); if (QTN) return TemplateName(QTN); @@ -3853,6 +3914,11 @@ ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, } else { TemplateName Canon = getDependentTemplateName(CanonNNS, Operator); QTN = new (*this,4) DependentTemplateName(NNS, Operator, Canon); + + DependentTemplateName *CheckQTN + = DependentTemplateNames.FindNodeOrInsertPos(ID, InsertPos); + assert(!CheckQTN && "Dependent template name canonicalization broken"); + (void)CheckQTN; } DependentTemplateNames.InsertNode(QTN, InsertPos); @@ -4123,8 +4189,8 @@ void getIntersectionOfProtocols(ASTContext &Context, if (LHSNumProtocols > 0) InheritedProtocolSet.insert(LHS->qual_begin(), LHS->qual_end()); else { - llvm::SmallVector LHSInheritedProtocols; - Context.CollectInheritedProtocols(LHS->getDecl(), LHSInheritedProtocols); + llvm::SmallPtrSet LHSInheritedProtocols; + Context.CollectInheritedProtocols(LHS->getDecl(), LHSInheritedProtocols); InheritedProtocolSet.insert(LHSInheritedProtocols.begin(), LHSInheritedProtocols.end()); } @@ -4137,13 +4203,13 @@ void getIntersectionOfProtocols(ASTContext &Context, IntersectionOfProtocols.push_back(RHSProtocols[i]); } else { - llvm::SmallVector RHSInheritedProtocols; + llvm::SmallPtrSet RHSInheritedProtocols; Context.CollectInheritedProtocols(RHS->getDecl(), RHSInheritedProtocols); - // FIXME. This may cause duplication of protocols in the list, but should - // be harmless. - for (unsigned i = 0, len = RHSInheritedProtocols.size(); i < len; ++i) - if (InheritedProtocolSet.count(RHSInheritedProtocols[i])) - IntersectionOfProtocols.push_back(RHSInheritedProtocols[i]); + for (llvm::SmallPtrSet::iterator I = + RHSInheritedProtocols.begin(), + E = RHSInheritedProtocols.end(); I != E; ++I) + if (InheritedProtocolSet.count((*I))) + IntersectionOfProtocols.push_back((*I)); } } @@ -4235,13 +4301,12 @@ bool ASTContext::areComparableObjCPointerTypes(QualType LHS, QualType RHS) { /// C99 6.2.7p1: Two types have compatible types if their types are the /// same. See 6.7.[2,3,5] for additional rules. bool ASTContext::typesAreCompatible(QualType LHS, QualType RHS) { + if (getLangOptions().CPlusPlus) + return hasSameType(LHS, RHS); + return !mergeTypes(LHS, RHS).isNull(); } -static bool isSameCallingConvention(CallingConv lcc, CallingConv rcc) { - return (getCanonicalCallingConv(lcc) == getCanonicalCallingConv(rcc)); -} - QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs) { const FunctionType *lbase = lhs->getAs(); const FunctionType *rbase = rhs->getAs(); @@ -4266,7 +4331,7 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs) { CallingConv lcc = lbase->getCallConv(); CallingConv rcc = rbase->getCallConv(); // Compatible functions must have compatible calling conventions - if (!isSameCallingConvention(lcc, rcc)) + if (!isSameCallConv(lcc, rcc)) return QualType(); if (lproto && rproto) { // two C99 style function prototypes @@ -4303,7 +4368,7 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs) { if (allRTypes) return rhs; return getFunctionType(retType, types.begin(), types.size(), lproto->isVariadic(), lproto->getTypeQuals(), - NoReturn, lcc); + false, false, 0, 0, NoReturn, lcc); } if (lproto) allRTypes = false; @@ -4321,6 +4386,12 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs) { unsigned proto_nargs = proto->getNumArgs(); for (unsigned i = 0; i < proto_nargs; ++i) { QualType argTy = proto->getArgType(i); + + // Look at the promotion type of enum types, since that is the type used + // to pass enum values. + if (const EnumType *Enum = argTy->getAs()) + argTy = Enum->getDecl()->getPromotionType(); + if (argTy->isPromotableIntegerType() || getCanonicalType(argTy).getUnqualifiedType() == FloatTy) return QualType(); @@ -4344,15 +4415,9 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS) { // designates the object or function denoted by the reference, and the // expression is an lvalue unless the reference is an rvalue reference and // the expression is a function call (possibly inside parentheses). - // FIXME: C++ shouldn't be going through here! The rules are different - // enough that they should be handled separately. - // FIXME: Merging of lvalue and rvalue references is incorrect. C++ *really* - // shouldn't be going through here! - if (const ReferenceType *RT = LHS->getAs()) - LHS = RT->getPointeeType(); - if (const ReferenceType *RT = RHS->getAs()) - RHS = RT->getPointeeType(); - + assert(!LHS->getAs() && "LHS is a reference type?"); + assert(!RHS->getAs() && "RHS is a reference type?"); + QualType LHSCan = getCanonicalType(LHS), RHSCan = getCanonicalType(RHS); @@ -4582,7 +4647,7 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) { // Turn <4 x signed int> -> <4 x unsigned int> if (const VectorType *VTy = T->getAs()) return getVectorType(getCorrespondingUnsignedType(VTy->getElementType()), - VTy->getNumElements()); + VTy->getNumElements(), VTy->isAltiVec(), VTy->isPixel()); // For enums, we return the unsigned version of the base type. if (const EnumType *ETy = T->getAs()) @@ -4739,7 +4804,8 @@ static QualType DecodeTypeFromStr(const char *&Str, ASTContext &Context, Str = End; QualType ElementType = DecodeTypeFromStr(Str, Context, Error, false); - Type = Context.getVectorType(ElementType, NumElements); + // FIXME: Don't know what to do about AltiVec. + Type = Context.getVectorType(ElementType, NumElements, false, false); break; } case 'X': { @@ -4784,6 +4850,9 @@ static QualType DecodeTypeFromStr(const char *&Str, ASTContext &Context, case 'C': Type = Type.withConst(); break; + case 'D': + Type = Context.getVolatileType(Type); + break; } } diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp new file mode 100644 index 000000000000..7402b7dda4ec --- /dev/null +++ b/lib/AST/ASTDiagnostic.cpp @@ -0,0 +1,266 @@ +//===--- ASTDiagnostic.cpp - Diagnostic Printing Hooks for AST Nodes ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a diagnostic formatting hook for AST elements. +// +//===----------------------------------------------------------------------===// +#include "clang/AST/ASTDiagnostic.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Type.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +/// Determines whether we should have an a.k.a. clause when +/// pretty-printing a type. There are three main criteria: +/// +/// 1) Some types provide very minimal sugar that doesn't impede the +/// user's understanding --- for example, elaborated type +/// specifiers. If this is all the sugar we see, we don't want an +/// a.k.a. clause. +/// 2) Some types are technically sugared but are much more familiar +/// when seen in their sugared form --- for example, va_list, +/// vector types, and the magic Objective C types. We don't +/// want to desugar these, even if we do produce an a.k.a. clause. +/// 3) Some types may have already been desugared previously in this diagnostic. +/// if this is the case, doing another "aka" would just be clutter. +/// +static bool ShouldAKA(ASTContext &Context, QualType QT, + const Diagnostic::ArgumentValue *PrevArgs, + unsigned NumPrevArgs, + QualType &DesugaredQT) { + QualType InputTy = QT; + + bool AKA = false; + QualifierCollector Qc; + + while (true) { + const Type *Ty = Qc.strip(QT); + + // Don't aka just because we saw an elaborated type... + if (isa(Ty)) { + QT = cast(Ty)->desugar(); + continue; + } + + // ...or a qualified name type... + if (isa(Ty)) { + QT = cast(Ty)->desugar(); + continue; + } + + // ...or a substituted template type parameter. + if (isa(Ty)) { + QT = cast(Ty)->desugar(); + continue; + } + + // Don't desugar template specializations. + if (isa(Ty)) + break; + + // Don't desugar magic Objective-C types. + if (QualType(Ty,0) == Context.getObjCIdType() || + QualType(Ty,0) == Context.getObjCClassType() || + QualType(Ty,0) == Context.getObjCSelType() || + QualType(Ty,0) == Context.getObjCProtoType()) + break; + + // Don't desugar va_list. + if (QualType(Ty,0) == Context.getBuiltinVaListType()) + break; + + // Otherwise, do a single-step desugar. + QualType Underlying; + bool IsSugar = false; + switch (Ty->getTypeClass()) { +#define ABSTRACT_TYPE(Class, Base) +#define TYPE(Class, Base) \ +case Type::Class: { \ +const Class##Type *CTy = cast(Ty); \ +if (CTy->isSugared()) { \ +IsSugar = true; \ +Underlying = CTy->desugar(); \ +} \ +break; \ +} +#include "clang/AST/TypeNodes.def" + } + + // If it wasn't sugared, we're done. + if (!IsSugar) + break; + + // If the desugared type is a vector type, we don't want to expand + // it, it will turn into an attribute mess. People want their "vec4". + if (isa(Underlying)) + break; + + // Don't desugar through the primary typedef of an anonymous type. + if (isa(Underlying) && isa(QT)) + if (cast(Underlying)->getDecl()->getTypedefForAnonDecl() == + cast(QT)->getDecl()) + break; + + // Otherwise, we're tearing through something opaque; note that + // we'll eventually need an a.k.a. clause and keep going. + AKA = true; + QT = Underlying; + continue; + } + + // If we never tore through opaque sugar, don't print aka. + if (!AKA) return false; + + // If we did, check to see if we already desugared this type in this + // diagnostic. If so, don't do it again. + for (unsigned i = 0; i != NumPrevArgs; ++i) { + // TODO: Handle ak_declcontext case. + if (PrevArgs[i].first == Diagnostic::ak_qualtype) { + void *Ptr = (void*)PrevArgs[i].second; + QualType PrevTy(QualType::getFromOpaquePtr(Ptr)); + if (PrevTy == InputTy) + return false; + } + } + + DesugaredQT = Qc.apply(QT); + return true; +} + +/// \brief Convert the given type to a string suitable for printing as part of +/// a diagnostic. +/// +/// \param Context the context in which the type was allocated +/// \param Ty the type to print +static std::string +ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, + const Diagnostic::ArgumentValue *PrevArgs, + unsigned NumPrevArgs) { + // FIXME: Playing with std::string is really slow. + std::string S = Ty.getAsString(Context.PrintingPolicy); + + // Consider producing an a.k.a. clause if removing all the direct + // sugar gives us something "significantly different". + + QualType DesugaredTy; + if (ShouldAKA(Context, Ty, PrevArgs, NumPrevArgs, DesugaredTy)) { + S = "'"+S+"' (aka '"; + S += DesugaredTy.getAsString(Context.PrintingPolicy); + S += "')"; + return S; + } + + S = "'" + S + "'"; + return S; +} + +void clang::FormatASTNodeDiagnosticArgument(Diagnostic::ArgumentKind Kind, + intptr_t Val, + const char *Modifier, + unsigned ModLen, + const char *Argument, + unsigned ArgLen, + const Diagnostic::ArgumentValue *PrevArgs, + unsigned NumPrevArgs, + llvm::SmallVectorImpl &Output, + void *Cookie) { + ASTContext &Context = *static_cast(Cookie); + + std::string S; + bool NeedQuotes = true; + + switch (Kind) { + default: assert(0 && "unknown ArgumentKind"); + case Diagnostic::ak_qualtype: { + assert(ModLen == 0 && ArgLen == 0 && + "Invalid modifier for QualType argument"); + + QualType Ty(QualType::getFromOpaquePtr(reinterpret_cast(Val))); + S = ConvertTypeToDiagnosticString(Context, Ty, PrevArgs, NumPrevArgs); + NeedQuotes = false; + break; + } + case Diagnostic::ak_declarationname: { + DeclarationName N = DeclarationName::getFromOpaqueInteger(Val); + S = N.getAsString(); + + if (ModLen == 9 && !memcmp(Modifier, "objcclass", 9) && ArgLen == 0) + S = '+' + S; + else if (ModLen == 12 && !memcmp(Modifier, "objcinstance", 12) + && ArgLen==0) + S = '-' + S; + else + assert(ModLen == 0 && ArgLen == 0 && + "Invalid modifier for DeclarationName argument"); + break; + } + case Diagnostic::ak_nameddecl: { + bool Qualified; + if (ModLen == 1 && Modifier[0] == 'q' && ArgLen == 0) + Qualified = true; + else { + assert(ModLen == 0 && ArgLen == 0 && + "Invalid modifier for NamedDecl* argument"); + Qualified = false; + } + reinterpret_cast(Val)-> + getNameForDiagnostic(S, Context.PrintingPolicy, Qualified); + break; + } + case Diagnostic::ak_nestednamespec: { + llvm::raw_string_ostream OS(S); + reinterpret_cast(Val)->print(OS, + Context.PrintingPolicy); + NeedQuotes = false; + break; + } + case Diagnostic::ak_declcontext: { + DeclContext *DC = reinterpret_cast (Val); + assert(DC && "Should never have a null declaration context"); + + if (DC->isTranslationUnit()) { + // FIXME: Get these strings from some localized place + if (Context.getLangOptions().CPlusPlus) + S = "the global namespace"; + else + S = "the global scope"; + } else if (TypeDecl *Type = dyn_cast(DC)) { + S = ConvertTypeToDiagnosticString(Context, + Context.getTypeDeclType(Type), + PrevArgs, NumPrevArgs); + } else { + // FIXME: Get these strings from some localized place + NamedDecl *ND = cast(DC); + if (isa(ND)) + S += "namespace "; + else if (isa(ND)) + S += "method "; + else if (isa(ND)) + S += "function "; + + S += "'"; + ND->getNameForDiagnostic(S, Context.PrintingPolicy, true); + S += "'"; + } + NeedQuotes = false; + break; + } + } + + if (NeedQuotes) + Output.push_back('\''); + + Output.append(S.begin(), S.end()); + + if (NeedQuotes) + Output.push_back('\''); +} diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp new file mode 100644 index 000000000000..dee0d2b342fc --- /dev/null +++ b/lib/AST/ASTImporter.cpp @@ -0,0 +1,2394 @@ +//===--- ASTImporter.cpp - Importing ASTs from other Contexts ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ASTImporter class which imports AST nodes from one +// context into another context. +// +//===----------------------------------------------------------------------===// +#include "clang/AST/ASTImporter.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeLoc.h" +#include "clang/AST/TypeVisitor.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/MemoryBuffer.h" +#include + +using namespace clang; + +namespace { + class ASTNodeImporter : public TypeVisitor, + public DeclVisitor, + public StmtVisitor { + ASTImporter &Importer; + + public: + explicit ASTNodeImporter(ASTImporter &Importer) : Importer(Importer) { } + + using TypeVisitor::Visit; + using DeclVisitor::Visit; + using StmtVisitor::Visit; + + // Importing types + QualType VisitType(Type *T); + QualType VisitBuiltinType(BuiltinType *T); + QualType VisitComplexType(ComplexType *T); + QualType VisitPointerType(PointerType *T); + QualType VisitBlockPointerType(BlockPointerType *T); + QualType VisitLValueReferenceType(LValueReferenceType *T); + QualType VisitRValueReferenceType(RValueReferenceType *T); + QualType VisitMemberPointerType(MemberPointerType *T); + QualType VisitConstantArrayType(ConstantArrayType *T); + QualType VisitIncompleteArrayType(IncompleteArrayType *T); + QualType VisitVariableArrayType(VariableArrayType *T); + // FIXME: DependentSizedArrayType + // FIXME: DependentSizedExtVectorType + QualType VisitVectorType(VectorType *T); + QualType VisitExtVectorType(ExtVectorType *T); + QualType VisitFunctionNoProtoType(FunctionNoProtoType *T); + QualType VisitFunctionProtoType(FunctionProtoType *T); + // FIXME: UnresolvedUsingType + QualType VisitTypedefType(TypedefType *T); + QualType VisitTypeOfExprType(TypeOfExprType *T); + // FIXME: DependentTypeOfExprType + QualType VisitTypeOfType(TypeOfType *T); + QualType VisitDecltypeType(DecltypeType *T); + // FIXME: DependentDecltypeType + QualType VisitRecordType(RecordType *T); + QualType VisitEnumType(EnumType *T); + QualType VisitElaboratedType(ElaboratedType *T); + // FIXME: TemplateTypeParmType + // FIXME: SubstTemplateTypeParmType + // FIXME: TemplateSpecializationType + QualType VisitQualifiedNameType(QualifiedNameType *T); + // FIXME: TypenameType + QualType VisitObjCInterfaceType(ObjCInterfaceType *T); + QualType VisitObjCObjectPointerType(ObjCObjectPointerType *T); + + // Importing declarations + bool ImportDeclParts(NamedDecl *D, DeclContext *&DC, + DeclContext *&LexicalDC, DeclarationName &Name, + SourceLocation &Loc); + bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord); + bool IsStructuralMatch(EnumDecl *FromEnum, EnumDecl *ToRecord); + Decl *VisitDecl(Decl *D); + Decl *VisitTypedefDecl(TypedefDecl *D); + Decl *VisitEnumDecl(EnumDecl *D); + Decl *VisitRecordDecl(RecordDecl *D); + Decl *VisitEnumConstantDecl(EnumConstantDecl *D); + Decl *VisitFunctionDecl(FunctionDecl *D); + Decl *VisitFieldDecl(FieldDecl *D); + Decl *VisitVarDecl(VarDecl *D); + Decl *VisitParmVarDecl(ParmVarDecl *D); + Decl *VisitObjCInterfaceDecl(ObjCInterfaceDecl *D); + + // Importing statements + Stmt *VisitStmt(Stmt *S); + + // Importing expressions + Expr *VisitExpr(Expr *E); + Expr *VisitIntegerLiteral(IntegerLiteral *E); + Expr *VisitImplicitCastExpr(ImplicitCastExpr *E); + }; +} + +//---------------------------------------------------------------------------- +// Structural Equivalence +//---------------------------------------------------------------------------- + +namespace { + struct StructuralEquivalenceContext { + /// \brief AST contexts for which we are checking structural equivalence. + ASTContext &C1, &C2; + + /// \brief Diagnostic object used to emit diagnostics. + Diagnostic &Diags; + + /// \brief The set of "tentative" equivalences between two canonical + /// declarations, mapping from a declaration in the first context to the + /// declaration in the second context that we believe to be equivalent. + llvm::DenseMap TentativeEquivalences; + + /// \brief Queue of declarations in the first context whose equivalence + /// with a declaration in the second context still needs to be verified. + std::deque DeclsToCheck; + + /// \brief Declaration (from, to) pairs that are known not to be equivalent + /// (which we have already complained about). + llvm::DenseSet > &NonEquivalentDecls; + + /// \brief Whether we're being strict about the spelling of types when + /// unifying two types. + bool StrictTypeSpelling; + + StructuralEquivalenceContext(ASTContext &C1, ASTContext &C2, + Diagnostic &Diags, + llvm::DenseSet > &NonEquivalentDecls, + bool StrictTypeSpelling = false) + : C1(C1), C2(C2), Diags(Diags), NonEquivalentDecls(NonEquivalentDecls), + StrictTypeSpelling(StrictTypeSpelling) { } + + /// \brief Determine whether the two declarations are structurally + /// equivalent. + bool IsStructurallyEquivalent(Decl *D1, Decl *D2); + + /// \brief Determine whether the two types are structurally equivalent. + bool IsStructurallyEquivalent(QualType T1, QualType T2); + + private: + /// \brief Finish checking all of the structural equivalences. + /// + /// \returns true if an error occurred, false otherwise. + bool Finish(); + + public: + DiagnosticBuilder Diag1(SourceLocation Loc, unsigned DiagID) { + return Diags.Report(FullSourceLoc(Loc, C1.getSourceManager()), DiagID); + } + + DiagnosticBuilder Diag2(SourceLocation Loc, unsigned DiagID) { + return Diags.Report(FullSourceLoc(Loc, C2.getSourceManager()), DiagID); + } + }; +} + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + QualType T1, QualType T2); +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + Decl *D1, Decl *D2); + +/// \brief Determine if two APInts have the same value, after zero-extending +/// one of them (if needed!) to ensure that the bit-widths match. +static bool IsSameValue(const llvm::APInt &I1, const llvm::APInt &I2) { + if (I1.getBitWidth() == I2.getBitWidth()) + return I1 == I2; + + if (I1.getBitWidth() > I2.getBitWidth()) + return I1 == llvm::APInt(I2).zext(I1.getBitWidth()); + + return llvm::APInt(I1).zext(I2.getBitWidth()) == I2; +} + +/// \brief Determine if two APSInts have the same value, zero- or sign-extending +/// as needed. +static bool IsSameValue(const llvm::APSInt &I1, const llvm::APSInt &I2) { + if (I1.getBitWidth() == I2.getBitWidth() && I1.isSigned() == I2.isSigned()) + return I1 == I2; + + // Check for a bit-width mismatch. + if (I1.getBitWidth() > I2.getBitWidth()) + return IsSameValue(I1, llvm::APSInt(I2).extend(I1.getBitWidth())); + else if (I2.getBitWidth() > I1.getBitWidth()) + return IsSameValue(llvm::APSInt(I1).extend(I2.getBitWidth()), I2); + + // We have a signedness mismatch. Turn the signed value into an unsigned + // value. + if (I1.isSigned()) { + if (I1.isNegative()) + return false; + + return llvm::APSInt(I1, true) == I2; + } + + if (I2.isNegative()) + return false; + + return I1 == llvm::APSInt(I2, true); +} + +/// \brief Determine structural equivalence of two expressions. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + Expr *E1, Expr *E2) { + if (!E1 || !E2) + return E1 == E2; + + // FIXME: Actually perform a structural comparison! + return true; +} + +/// \brief Determine whether two identifiers are equivalent. +static bool IsStructurallyEquivalent(const IdentifierInfo *Name1, + const IdentifierInfo *Name2) { + if (!Name1 || !Name2) + return Name1 == Name2; + + return Name1->getName() == Name2->getName(); +} + +/// \brief Determine whether two nested-name-specifiers are equivalent. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + NestedNameSpecifier *NNS1, + NestedNameSpecifier *NNS2) { + // FIXME: Implement! + return true; +} + +/// \brief Determine whether two template arguments are equivalent. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const TemplateArgument &Arg1, + const TemplateArgument &Arg2) { + // FIXME: Implement! + return true; +} + +/// \brief Determine structural equivalence for the common part of array +/// types. +static bool IsArrayStructurallyEquivalent(StructuralEquivalenceContext &Context, + const ArrayType *Array1, + const ArrayType *Array2) { + if (!IsStructurallyEquivalent(Context, + Array1->getElementType(), + Array2->getElementType())) + return false; + if (Array1->getSizeModifier() != Array2->getSizeModifier()) + return false; + if (Array1->getIndexTypeQualifiers() != Array2->getIndexTypeQualifiers()) + return false; + + return true; +} + +/// \brief Determine structural equivalence of two types. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + QualType T1, QualType T2) { + if (T1.isNull() || T2.isNull()) + return T1.isNull() && T2.isNull(); + + if (!Context.StrictTypeSpelling) { + // We aren't being strict about token-to-token equivalence of types, + // so map down to the canonical type. + T1 = Context.C1.getCanonicalType(T1); + T2 = Context.C2.getCanonicalType(T2); + } + + if (T1.getQualifiers() != T2.getQualifiers()) + return false; + + Type::TypeClass TC = T1->getTypeClass(); + + if (T1->getTypeClass() != T2->getTypeClass()) { + // Compare function types with prototypes vs. without prototypes as if + // both did not have prototypes. + if (T1->getTypeClass() == Type::FunctionProto && + T2->getTypeClass() == Type::FunctionNoProto) + TC = Type::FunctionNoProto; + else if (T1->getTypeClass() == Type::FunctionNoProto && + T2->getTypeClass() == Type::FunctionProto) + TC = Type::FunctionNoProto; + else + return false; + } + + switch (TC) { + case Type::Builtin: + // FIXME: Deal with Char_S/Char_U. + if (cast(T1)->getKind() != cast(T2)->getKind()) + return false; + break; + + case Type::Complex: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getElementType(), + cast(T2)->getElementType())) + return false; + break; + + case Type::Pointer: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getPointeeType(), + cast(T2)->getPointeeType())) + return false; + break; + + case Type::BlockPointer: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getPointeeType(), + cast(T2)->getPointeeType())) + return false; + break; + + case Type::LValueReference: + case Type::RValueReference: { + const ReferenceType *Ref1 = cast(T1); + const ReferenceType *Ref2 = cast(T2); + if (Ref1->isSpelledAsLValue() != Ref2->isSpelledAsLValue()) + return false; + if (Ref1->isInnerRef() != Ref2->isInnerRef()) + return false; + if (!IsStructurallyEquivalent(Context, + Ref1->getPointeeTypeAsWritten(), + Ref2->getPointeeTypeAsWritten())) + return false; + break; + } + + case Type::MemberPointer: { + const MemberPointerType *MemPtr1 = cast(T1); + const MemberPointerType *MemPtr2 = cast(T2); + if (!IsStructurallyEquivalent(Context, + MemPtr1->getPointeeType(), + MemPtr2->getPointeeType())) + return false; + if (!IsStructurallyEquivalent(Context, + QualType(MemPtr1->getClass(), 0), + QualType(MemPtr2->getClass(), 0))) + return false; + break; + } + + case Type::ConstantArray: { + const ConstantArrayType *Array1 = cast(T1); + const ConstantArrayType *Array2 = cast(T2); + if (!IsSameValue(Array1->getSize(), Array2->getSize())) + return false; + + if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) + return false; + break; + } + + case Type::IncompleteArray: + if (!IsArrayStructurallyEquivalent(Context, + cast(T1), + cast(T2))) + return false; + break; + + case Type::VariableArray: { + const VariableArrayType *Array1 = cast(T1); + const VariableArrayType *Array2 = cast(T2); + if (!IsStructurallyEquivalent(Context, + Array1->getSizeExpr(), Array2->getSizeExpr())) + return false; + + if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) + return false; + + break; + } + + case Type::DependentSizedArray: { + const DependentSizedArrayType *Array1 = cast(T1); + const DependentSizedArrayType *Array2 = cast(T2); + if (!IsStructurallyEquivalent(Context, + Array1->getSizeExpr(), Array2->getSizeExpr())) + return false; + + if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) + return false; + + break; + } + + case Type::DependentSizedExtVector: { + const DependentSizedExtVectorType *Vec1 + = cast(T1); + const DependentSizedExtVectorType *Vec2 + = cast(T2); + if (!IsStructurallyEquivalent(Context, + Vec1->getSizeExpr(), Vec2->getSizeExpr())) + return false; + if (!IsStructurallyEquivalent(Context, + Vec1->getElementType(), + Vec2->getElementType())) + return false; + break; + } + + case Type::Vector: + case Type::ExtVector: { + const VectorType *Vec1 = cast(T1); + const VectorType *Vec2 = cast(T2); + if (!IsStructurallyEquivalent(Context, + Vec1->getElementType(), + Vec2->getElementType())) + return false; + if (Vec1->getNumElements() != Vec2->getNumElements()) + return false; + if (Vec1->isAltiVec() != Vec2->isAltiVec()) + return false; + if (Vec1->isPixel() != Vec2->isPixel()) + return false; + } + + case Type::FunctionProto: { + const FunctionProtoType *Proto1 = cast(T1); + const FunctionProtoType *Proto2 = cast(T2); + if (Proto1->getNumArgs() != Proto2->getNumArgs()) + return false; + for (unsigned I = 0, N = Proto1->getNumArgs(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, + Proto1->getArgType(I), + Proto2->getArgType(I))) + return false; + } + if (Proto1->isVariadic() != Proto2->isVariadic()) + return false; + if (Proto1->hasExceptionSpec() != Proto2->hasExceptionSpec()) + return false; + if (Proto1->hasAnyExceptionSpec() != Proto2->hasAnyExceptionSpec()) + return false; + if (Proto1->getNumExceptions() != Proto2->getNumExceptions()) + return false; + for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, + Proto1->getExceptionType(I), + Proto2->getExceptionType(I))) + return false; + } + if (Proto1->getTypeQuals() != Proto2->getTypeQuals()) + return false; + + // Fall through to check the bits common with FunctionNoProtoType. + } + + case Type::FunctionNoProto: { + const FunctionType *Function1 = cast(T1); + const FunctionType *Function2 = cast(T2); + if (!IsStructurallyEquivalent(Context, + Function1->getResultType(), + Function2->getResultType())) + return false; + if (Function1->getNoReturnAttr() != Function2->getNoReturnAttr()) + return false; + if (Function1->getCallConv() != Function2->getCallConv()) + return false; + break; + } + + case Type::UnresolvedUsing: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getDecl(), + cast(T2)->getDecl())) + return false; + + break; + + case Type::Typedef: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getDecl(), + cast(T2)->getDecl())) + return false; + break; + + case Type::TypeOfExpr: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getUnderlyingExpr(), + cast(T2)->getUnderlyingExpr())) + return false; + break; + + case Type::TypeOf: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getUnderlyingType(), + cast(T2)->getUnderlyingType())) + return false; + break; + + case Type::Decltype: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getUnderlyingExpr(), + cast(T2)->getUnderlyingExpr())) + return false; + break; + + case Type::Record: + case Type::Enum: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getDecl(), + cast(T2)->getDecl())) + return false; + break; + + case Type::Elaborated: { + const ElaboratedType *Elab1 = cast(T1); + const ElaboratedType *Elab2 = cast(T2); + if (Elab1->getTagKind() != Elab2->getTagKind()) + return false; + if (!IsStructurallyEquivalent(Context, + Elab1->getUnderlyingType(), + Elab2->getUnderlyingType())) + return false; + break; + } + + case Type::TemplateTypeParm: { + const TemplateTypeParmType *Parm1 = cast(T1); + const TemplateTypeParmType *Parm2 = cast(T2); + if (Parm1->getDepth() != Parm2->getDepth()) + return false; + if (Parm1->getIndex() != Parm2->getIndex()) + return false; + if (Parm1->isParameterPack() != Parm2->isParameterPack()) + return false; + + // Names of template type parameters are never significant. + break; + } + + case Type::SubstTemplateTypeParm: { + const SubstTemplateTypeParmType *Subst1 + = cast(T1); + const SubstTemplateTypeParmType *Subst2 + = cast(T2); + if (!IsStructurallyEquivalent(Context, + QualType(Subst1->getReplacedParameter(), 0), + QualType(Subst2->getReplacedParameter(), 0))) + return false; + if (!IsStructurallyEquivalent(Context, + Subst1->getReplacementType(), + Subst2->getReplacementType())) + return false; + break; + } + + case Type::TemplateSpecialization: { + const TemplateSpecializationType *Spec1 + = cast(T1); + const TemplateSpecializationType *Spec2 + = cast(T2); + if (!IsStructurallyEquivalent(Context, + Spec1->getTemplateName(), + Spec2->getTemplateName())) + return false; + if (Spec1->getNumArgs() != Spec2->getNumArgs()) + return false; + for (unsigned I = 0, N = Spec1->getNumArgs(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, + Spec1->getArg(I), Spec2->getArg(I))) + return false; + } + break; + } + + case Type::QualifiedName: { + const QualifiedNameType *Qual1 = cast(T1); + const QualifiedNameType *Qual2 = cast(T2); + if (!IsStructurallyEquivalent(Context, + Qual1->getQualifier(), + Qual2->getQualifier())) + return false; + if (!IsStructurallyEquivalent(Context, + Qual1->getNamedType(), + Qual2->getNamedType())) + return false; + break; + } + + case Type::Typename: { + const TypenameType *Typename1 = cast(T1); + const TypenameType *Typename2 = cast(T2); + if (!IsStructurallyEquivalent(Context, + Typename1->getQualifier(), + Typename2->getQualifier())) + return false; + if (!IsStructurallyEquivalent(Typename1->getIdentifier(), + Typename2->getIdentifier())) + return false; + if (!IsStructurallyEquivalent(Context, + QualType(Typename1->getTemplateId(), 0), + QualType(Typename2->getTemplateId(), 0))) + return false; + + break; + } + + case Type::ObjCInterface: { + const ObjCInterfaceType *Iface1 = cast(T1); + const ObjCInterfaceType *Iface2 = cast(T2); + if (!IsStructurallyEquivalent(Context, + Iface1->getDecl(), Iface2->getDecl())) + return false; + if (Iface1->getNumProtocols() != Iface2->getNumProtocols()) + return false; + for (unsigned I = 0, N = Iface1->getNumProtocols(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, + Iface1->getProtocol(I), + Iface2->getProtocol(I))) + return false; + } + break; + } + + case Type::ObjCObjectPointer: { + const ObjCObjectPointerType *Ptr1 = cast(T1); + const ObjCObjectPointerType *Ptr2 = cast(T2); + if (!IsStructurallyEquivalent(Context, + Ptr1->getPointeeType(), + Ptr2->getPointeeType())) + return false; + if (Ptr1->getNumProtocols() != Ptr2->getNumProtocols()) + return false; + for (unsigned I = 0, N = Ptr1->getNumProtocols(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, + Ptr1->getProtocol(I), + Ptr2->getProtocol(I))) + return false; + } + break; + } + + } // end switch + + return true; +} + +/// \brief Determine structural equivalence of two records. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + RecordDecl *D1, RecordDecl *D2) { + if (D1->isUnion() != D2->isUnion()) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag1(D1->getLocation(), diag::note_odr_tag_kind_here) + << D1->getDeclName() << (unsigned)D1->getTagKind(); + return false; + } + + // Compare the definitions of these two records. If either or both are + // incomplete, we assume that they are equivalent. + D1 = D1->getDefinition(); + D2 = D2->getDefinition(); + if (!D1 || !D2) + return true; + + if (CXXRecordDecl *D1CXX = dyn_cast(D1)) { + if (CXXRecordDecl *D2CXX = dyn_cast(D2)) { + if (D1CXX->getNumBases() != D2CXX->getNumBases()) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag2(D2->getLocation(), diag::note_odr_number_of_bases) + << D2CXX->getNumBases(); + Context.Diag1(D1->getLocation(), diag::note_odr_number_of_bases) + << D1CXX->getNumBases(); + return false; + } + + // Check the base classes. + for (CXXRecordDecl::base_class_iterator Base1 = D1CXX->bases_begin(), + BaseEnd1 = D1CXX->bases_end(), + Base2 = D2CXX->bases_begin(); + Base1 != BaseEnd1; + ++Base1, ++Base2) { + if (!IsStructurallyEquivalent(Context, + Base1->getType(), Base2->getType())) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag2(Base2->getSourceRange().getBegin(), diag::note_odr_base) + << Base2->getType() + << Base2->getSourceRange(); + Context.Diag1(Base1->getSourceRange().getBegin(), diag::note_odr_base) + << Base1->getType() + << Base1->getSourceRange(); + return false; + } + + // Check virtual vs. non-virtual inheritance mismatch. + if (Base1->isVirtual() != Base2->isVirtual()) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag2(Base2->getSourceRange().getBegin(), + diag::note_odr_virtual_base) + << Base2->isVirtual() << Base2->getSourceRange(); + Context.Diag1(Base1->getSourceRange().getBegin(), diag::note_odr_base) + << Base1->isVirtual() + << Base1->getSourceRange(); + return false; + } + } + } else if (D1CXX->getNumBases() > 0) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + const CXXBaseSpecifier *Base1 = D1CXX->bases_begin(); + Context.Diag1(Base1->getSourceRange().getBegin(), diag::note_odr_base) + << Base1->getType() + << Base1->getSourceRange(); + Context.Diag2(D2->getLocation(), diag::note_odr_missing_base); + return false; + } + } + + // Check the fields for consistency. + CXXRecordDecl::field_iterator Field2 = D2->field_begin(), + Field2End = D2->field_end(); + for (CXXRecordDecl::field_iterator Field1 = D1->field_begin(), + Field1End = D1->field_end(); + Field1 != Field1End; + ++Field1, ++Field2) { + if (Field2 == Field2End) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag1(Field1->getLocation(), diag::note_odr_field) + << Field1->getDeclName() << Field1->getType(); + Context.Diag2(D2->getLocation(), diag::note_odr_missing_field); + return false; + } + + if (!IsStructurallyEquivalent(Context, + Field1->getType(), Field2->getType())) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag2(Field2->getLocation(), diag::note_odr_field) + << Field2->getDeclName() << Field2->getType(); + Context.Diag1(Field1->getLocation(), diag::note_odr_field) + << Field1->getDeclName() << Field1->getType(); + return false; + } + + if (Field1->isBitField() != Field2->isBitField()) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + if (Field1->isBitField()) { + llvm::APSInt Bits; + Field1->getBitWidth()->isIntegerConstantExpr(Bits, Context.C1); + Context.Diag1(Field1->getLocation(), diag::note_odr_bit_field) + << Field1->getDeclName() << Field1->getType() + << Bits.toString(10, false); + Context.Diag2(Field2->getLocation(), diag::note_odr_not_bit_field) + << Field2->getDeclName(); + } else { + llvm::APSInt Bits; + Field2->getBitWidth()->isIntegerConstantExpr(Bits, Context.C2); + Context.Diag2(Field2->getLocation(), diag::note_odr_bit_field) + << Field2->getDeclName() << Field2->getType() + << Bits.toString(10, false); + Context.Diag1(Field1->getLocation(), + diag::note_odr_not_bit_field) + << Field1->getDeclName(); + } + return false; + } + + if (Field1->isBitField()) { + // Make sure that the bit-fields are the same length. + llvm::APSInt Bits1, Bits2; + if (!Field1->getBitWidth()->isIntegerConstantExpr(Bits1, Context.C1)) + return false; + if (!Field2->getBitWidth()->isIntegerConstantExpr(Bits2, Context.C2)) + return false; + + if (!IsSameValue(Bits1, Bits2)) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag2(Field2->getLocation(), diag::note_odr_bit_field) + << Field2->getDeclName() << Field2->getType() + << Bits2.toString(10, false); + Context.Diag1(Field1->getLocation(), diag::note_odr_bit_field) + << Field1->getDeclName() << Field1->getType() + << Bits1.toString(10, false); + return false; + } + } + } + + if (Field2 != Field2End) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag2(Field2->getLocation(), diag::note_odr_field) + << Field2->getDeclName() << Field2->getType(); + Context.Diag1(D1->getLocation(), diag::note_odr_missing_field); + return false; + } + + return true; +} + +/// \brief Determine structural equivalence of two enums. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + EnumDecl *D1, EnumDecl *D2) { + EnumDecl::enumerator_iterator EC2 = D2->enumerator_begin(), + EC2End = D2->enumerator_end(); + for (EnumDecl::enumerator_iterator EC1 = D1->enumerator_begin(), + EC1End = D1->enumerator_end(); + EC1 != EC1End; ++EC1, ++EC2) { + if (EC2 == EC2End) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag1(EC1->getLocation(), diag::note_odr_enumerator) + << EC1->getDeclName() + << EC1->getInitVal().toString(10); + Context.Diag2(D2->getLocation(), diag::note_odr_missing_enumerator); + return false; + } + + llvm::APSInt Val1 = EC1->getInitVal(); + llvm::APSInt Val2 = EC2->getInitVal(); + if (!IsSameValue(Val1, Val2) || + !IsStructurallyEquivalent(EC1->getIdentifier(), EC2->getIdentifier())) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator) + << EC2->getDeclName() + << EC2->getInitVal().toString(10); + Context.Diag1(EC1->getLocation(), diag::note_odr_enumerator) + << EC1->getDeclName() + << EC1->getInitVal().toString(10); + return false; + } + } + + if (EC2 != EC2End) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.C2.getTypeDeclType(D2); + Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator) + << EC2->getDeclName() + << EC2->getInitVal().toString(10); + Context.Diag1(D1->getLocation(), diag::note_odr_missing_enumerator); + return false; + } + + return true; +} + +/// \brief Determine structural equivalence of two declarations. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + Decl *D1, Decl *D2) { + // FIXME: Check for known structural equivalences via a callback of some sort. + + // Check whether we already know that these two declarations are not + // structurally equivalent. + if (Context.NonEquivalentDecls.count(std::make_pair(D1->getCanonicalDecl(), + D2->getCanonicalDecl()))) + return false; + + // Determine whether we've already produced a tentative equivalence for D1. + Decl *&EquivToD1 = Context.TentativeEquivalences[D1->getCanonicalDecl()]; + if (EquivToD1) + return EquivToD1 == D2->getCanonicalDecl(); + + // Produce a tentative equivalence D1 <-> D2, which will be checked later. + EquivToD1 = D2->getCanonicalDecl(); + Context.DeclsToCheck.push_back(D1->getCanonicalDecl()); + return true; +} + +bool StructuralEquivalenceContext::IsStructurallyEquivalent(Decl *D1, + Decl *D2) { + if (!::IsStructurallyEquivalent(*this, D1, D2)) + return false; + + return !Finish(); +} + +bool StructuralEquivalenceContext::IsStructurallyEquivalent(QualType T1, + QualType T2) { + if (!::IsStructurallyEquivalent(*this, T1, T2)) + return false; + + return !Finish(); +} + +bool StructuralEquivalenceContext::Finish() { + while (!DeclsToCheck.empty()) { + // Check the next declaration. + Decl *D1 = DeclsToCheck.front(); + DeclsToCheck.pop_front(); + + Decl *D2 = TentativeEquivalences[D1]; + assert(D2 && "Unrecorded tentative equivalence?"); + + bool Equivalent = true; + + // FIXME: Switch on all declaration kinds. For now, we're just going to + // check the obvious ones. + if (RecordDecl *Record1 = dyn_cast(D1)) { + if (RecordDecl *Record2 = dyn_cast(D2)) { + // Check for equivalent structure names. + IdentifierInfo *Name1 = Record1->getIdentifier(); + if (!Name1 && Record1->getTypedefForAnonDecl()) + Name1 = Record1->getTypedefForAnonDecl()->getIdentifier(); + IdentifierInfo *Name2 = Record2->getIdentifier(); + if (!Name2 && Record2->getTypedefForAnonDecl()) + Name2 = Record2->getTypedefForAnonDecl()->getIdentifier(); + if (!::IsStructurallyEquivalent(Name1, Name2) || + !::IsStructurallyEquivalent(*this, Record1, Record2)) + Equivalent = false; + } else { + // Record/non-record mismatch. + Equivalent = false; + } + } else if (EnumDecl *Enum1 = dyn_cast(D1)) { + if (EnumDecl *Enum2 = dyn_cast(D2)) { + // Check for equivalent enum names. + IdentifierInfo *Name1 = Enum1->getIdentifier(); + if (!Name1 && Enum1->getTypedefForAnonDecl()) + Name1 = Enum1->getTypedefForAnonDecl()->getIdentifier(); + IdentifierInfo *Name2 = Enum2->getIdentifier(); + if (!Name2 && Enum2->getTypedefForAnonDecl()) + Name2 = Enum2->getTypedefForAnonDecl()->getIdentifier(); + if (!::IsStructurallyEquivalent(Name1, Name2) || + !::IsStructurallyEquivalent(*this, Enum1, Enum2)) + Equivalent = false; + } else { + // Enum/non-enum mismatch + Equivalent = false; + } + } else if (TypedefDecl *Typedef1 = dyn_cast(D1)) { + if (TypedefDecl *Typedef2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(Typedef1->getIdentifier(), + Typedef2->getIdentifier()) || + !::IsStructurallyEquivalent(*this, + Typedef1->getUnderlyingType(), + Typedef2->getUnderlyingType())) + Equivalent = false; + } else { + // Typedef/non-typedef mismatch. + Equivalent = false; + } + } + + if (!Equivalent) { + // Note that these two declarations are not equivalent (and we already + // know about it). + NonEquivalentDecls.insert(std::make_pair(D1->getCanonicalDecl(), + D2->getCanonicalDecl())); + return true; + } + // FIXME: Check other declaration kinds! + } + + return false; +} + +//---------------------------------------------------------------------------- +// Import Types +//---------------------------------------------------------------------------- + +QualType ASTNodeImporter::VisitType(Type *T) { + Importer.FromDiag(SourceLocation(), diag::err_unsupported_ast_node) + << T->getTypeClassName(); + return QualType(); +} + +QualType ASTNodeImporter::VisitBuiltinType(BuiltinType *T) { + switch (T->getKind()) { + case BuiltinType::Void: return Importer.getToContext().VoidTy; + case BuiltinType::Bool: return Importer.getToContext().BoolTy; + + case BuiltinType::Char_U: + // The context we're importing from has an unsigned 'char'. If we're + // importing into a context with a signed 'char', translate to + // 'unsigned char' instead. + if (Importer.getToContext().getLangOptions().CharIsSigned) + return Importer.getToContext().UnsignedCharTy; + + return Importer.getToContext().CharTy; + + case BuiltinType::UChar: return Importer.getToContext().UnsignedCharTy; + + case BuiltinType::Char16: + // FIXME: Make sure that the "to" context supports C++! + return Importer.getToContext().Char16Ty; + + case BuiltinType::Char32: + // FIXME: Make sure that the "to" context supports C++! + return Importer.getToContext().Char32Ty; + + case BuiltinType::UShort: return Importer.getToContext().UnsignedShortTy; + case BuiltinType::UInt: return Importer.getToContext().UnsignedIntTy; + case BuiltinType::ULong: return Importer.getToContext().UnsignedLongTy; + case BuiltinType::ULongLong: + return Importer.getToContext().UnsignedLongLongTy; + case BuiltinType::UInt128: return Importer.getToContext().UnsignedInt128Ty; + + case BuiltinType::Char_S: + // The context we're importing from has an unsigned 'char'. If we're + // importing into a context with a signed 'char', translate to + // 'unsigned char' instead. + if (!Importer.getToContext().getLangOptions().CharIsSigned) + return Importer.getToContext().SignedCharTy; + + return Importer.getToContext().CharTy; + + case BuiltinType::SChar: return Importer.getToContext().SignedCharTy; + case BuiltinType::WChar: + // FIXME: If not in C++, shall we translate to the C equivalent of + // wchar_t? + return Importer.getToContext().WCharTy; + + case BuiltinType::Short : return Importer.getToContext().ShortTy; + case BuiltinType::Int : return Importer.getToContext().IntTy; + case BuiltinType::Long : return Importer.getToContext().LongTy; + case BuiltinType::LongLong : return Importer.getToContext().LongLongTy; + case BuiltinType::Int128 : return Importer.getToContext().Int128Ty; + case BuiltinType::Float: return Importer.getToContext().FloatTy; + case BuiltinType::Double: return Importer.getToContext().DoubleTy; + case BuiltinType::LongDouble: return Importer.getToContext().LongDoubleTy; + + case BuiltinType::NullPtr: + // FIXME: Make sure that the "to" context supports C++0x! + return Importer.getToContext().NullPtrTy; + + case BuiltinType::Overload: return Importer.getToContext().OverloadTy; + case BuiltinType::Dependent: return Importer.getToContext().DependentTy; + case BuiltinType::UndeducedAuto: + // FIXME: Make sure that the "to" context supports C++0x! + return Importer.getToContext().UndeducedAutoTy; + + case BuiltinType::ObjCId: + // FIXME: Make sure that the "to" context supports Objective-C! + return Importer.getToContext().ObjCBuiltinIdTy; + + case BuiltinType::ObjCClass: + return Importer.getToContext().ObjCBuiltinClassTy; + + case BuiltinType::ObjCSel: + return Importer.getToContext().ObjCBuiltinSelTy; + } + + return QualType(); +} + +QualType ASTNodeImporter::VisitComplexType(ComplexType *T) { + QualType ToElementType = Importer.Import(T->getElementType()); + if (ToElementType.isNull()) + return QualType(); + + return Importer.getToContext().getComplexType(ToElementType); +} + +QualType ASTNodeImporter::VisitPointerType(PointerType *T) { + QualType ToPointeeType = Importer.Import(T->getPointeeType()); + if (ToPointeeType.isNull()) + return QualType(); + + return Importer.getToContext().getPointerType(ToPointeeType); +} + +QualType ASTNodeImporter::VisitBlockPointerType(BlockPointerType *T) { + // FIXME: Check for blocks support in "to" context. + QualType ToPointeeType = Importer.Import(T->getPointeeType()); + if (ToPointeeType.isNull()) + return QualType(); + + return Importer.getToContext().getBlockPointerType(ToPointeeType); +} + +QualType ASTNodeImporter::VisitLValueReferenceType(LValueReferenceType *T) { + // FIXME: Check for C++ support in "to" context. + QualType ToPointeeType = Importer.Import(T->getPointeeTypeAsWritten()); + if (ToPointeeType.isNull()) + return QualType(); + + return Importer.getToContext().getLValueReferenceType(ToPointeeType); +} + +QualType ASTNodeImporter::VisitRValueReferenceType(RValueReferenceType *T) { + // FIXME: Check for C++0x support in "to" context. + QualType ToPointeeType = Importer.Import(T->getPointeeTypeAsWritten()); + if (ToPointeeType.isNull()) + return QualType(); + + return Importer.getToContext().getRValueReferenceType(ToPointeeType); +} + +QualType ASTNodeImporter::VisitMemberPointerType(MemberPointerType *T) { + // FIXME: Check for C++ support in "to" context. + QualType ToPointeeType = Importer.Import(T->getPointeeType()); + if (ToPointeeType.isNull()) + return QualType(); + + QualType ClassType = Importer.Import(QualType(T->getClass(), 0)); + return Importer.getToContext().getMemberPointerType(ToPointeeType, + ClassType.getTypePtr()); +} + +QualType ASTNodeImporter::VisitConstantArrayType(ConstantArrayType *T) { + QualType ToElementType = Importer.Import(T->getElementType()); + if (ToElementType.isNull()) + return QualType(); + + return Importer.getToContext().getConstantArrayType(ToElementType, + T->getSize(), + T->getSizeModifier(), + T->getIndexTypeCVRQualifiers()); +} + +QualType ASTNodeImporter::VisitIncompleteArrayType(IncompleteArrayType *T) { + QualType ToElementType = Importer.Import(T->getElementType()); + if (ToElementType.isNull()) + return QualType(); + + return Importer.getToContext().getIncompleteArrayType(ToElementType, + T->getSizeModifier(), + T->getIndexTypeCVRQualifiers()); +} + +QualType ASTNodeImporter::VisitVariableArrayType(VariableArrayType *T) { + QualType ToElementType = Importer.Import(T->getElementType()); + if (ToElementType.isNull()) + return QualType(); + + Expr *Size = Importer.Import(T->getSizeExpr()); + if (!Size) + return QualType(); + + SourceRange Brackets = Importer.Import(T->getBracketsRange()); + return Importer.getToContext().getVariableArrayType(ToElementType, Size, + T->getSizeModifier(), + T->getIndexTypeCVRQualifiers(), + Brackets); +} + +QualType ASTNodeImporter::VisitVectorType(VectorType *T) { + QualType ToElementType = Importer.Import(T->getElementType()); + if (ToElementType.isNull()) + return QualType(); + + return Importer.getToContext().getVectorType(ToElementType, + T->getNumElements(), + T->isAltiVec(), + T->isPixel()); +} + +QualType ASTNodeImporter::VisitExtVectorType(ExtVectorType *T) { + QualType ToElementType = Importer.Import(T->getElementType()); + if (ToElementType.isNull()) + return QualType(); + + return Importer.getToContext().getExtVectorType(ToElementType, + T->getNumElements()); +} + +QualType ASTNodeImporter::VisitFunctionNoProtoType(FunctionNoProtoType *T) { + // FIXME: What happens if we're importing a function without a prototype + // into C++? Should we make it variadic? + QualType ToResultType = Importer.Import(T->getResultType()); + if (ToResultType.isNull()) + return QualType(); + + return Importer.getToContext().getFunctionNoProtoType(ToResultType, + T->getNoReturnAttr(), + T->getCallConv()); +} + +QualType ASTNodeImporter::VisitFunctionProtoType(FunctionProtoType *T) { + QualType ToResultType = Importer.Import(T->getResultType()); + if (ToResultType.isNull()) + return QualType(); + + // Import argument types + llvm::SmallVector ArgTypes; + for (FunctionProtoType::arg_type_iterator A = T->arg_type_begin(), + AEnd = T->arg_type_end(); + A != AEnd; ++A) { + QualType ArgType = Importer.Import(*A); + if (ArgType.isNull()) + return QualType(); + ArgTypes.push_back(ArgType); + } + + // Import exception types + llvm::SmallVector ExceptionTypes; + for (FunctionProtoType::exception_iterator E = T->exception_begin(), + EEnd = T->exception_end(); + E != EEnd; ++E) { + QualType ExceptionType = Importer.Import(*E); + if (ExceptionType.isNull()) + return QualType(); + ExceptionTypes.push_back(ExceptionType); + } + + return Importer.getToContext().getFunctionType(ToResultType, ArgTypes.data(), + ArgTypes.size(), + T->isVariadic(), + T->getTypeQuals(), + T->hasExceptionSpec(), + T->hasAnyExceptionSpec(), + ExceptionTypes.size(), + ExceptionTypes.data(), + T->getNoReturnAttr(), + T->getCallConv()); +} + +QualType ASTNodeImporter::VisitTypedefType(TypedefType *T) { + TypedefDecl *ToDecl + = dyn_cast_or_null(Importer.Import(T->getDecl())); + if (!ToDecl) + return QualType(); + + return Importer.getToContext().getTypeDeclType(ToDecl); +} + +QualType ASTNodeImporter::VisitTypeOfExprType(TypeOfExprType *T) { + Expr *ToExpr = Importer.Import(T->getUnderlyingExpr()); + if (!ToExpr) + return QualType(); + + return Importer.getToContext().getTypeOfExprType(ToExpr); +} + +QualType ASTNodeImporter::VisitTypeOfType(TypeOfType *T) { + QualType ToUnderlyingType = Importer.Import(T->getUnderlyingType()); + if (ToUnderlyingType.isNull()) + return QualType(); + + return Importer.getToContext().getTypeOfType(ToUnderlyingType); +} + +QualType ASTNodeImporter::VisitDecltypeType(DecltypeType *T) { + Expr *ToExpr = Importer.Import(T->getUnderlyingExpr()); + if (!ToExpr) + return QualType(); + + return Importer.getToContext().getDecltypeType(ToExpr); +} + +QualType ASTNodeImporter::VisitRecordType(RecordType *T) { + RecordDecl *ToDecl + = dyn_cast_or_null(Importer.Import(T->getDecl())); + if (!ToDecl) + return QualType(); + + return Importer.getToContext().getTagDeclType(ToDecl); +} + +QualType ASTNodeImporter::VisitEnumType(EnumType *T) { + EnumDecl *ToDecl + = dyn_cast_or_null(Importer.Import(T->getDecl())); + if (!ToDecl) + return QualType(); + + return Importer.getToContext().getTagDeclType(ToDecl); +} + +QualType ASTNodeImporter::VisitElaboratedType(ElaboratedType *T) { + QualType ToUnderlyingType = Importer.Import(T->getUnderlyingType()); + if (ToUnderlyingType.isNull()) + return QualType(); + + return Importer.getToContext().getElaboratedType(ToUnderlyingType, + T->getTagKind()); +} + +QualType ASTNodeImporter::VisitQualifiedNameType(QualifiedNameType *T) { + NestedNameSpecifier *ToQualifier = Importer.Import(T->getQualifier()); + if (!ToQualifier) + return QualType(); + + QualType ToNamedType = Importer.Import(T->getNamedType()); + if (ToNamedType.isNull()) + return QualType(); + + return Importer.getToContext().getQualifiedNameType(ToQualifier, ToNamedType); +} + +QualType ASTNodeImporter::VisitObjCInterfaceType(ObjCInterfaceType *T) { + ObjCInterfaceDecl *Class + = dyn_cast_or_null(Importer.Import(T->getDecl())); + if (!Class) + return QualType(); + + llvm::SmallVector Protocols; + for (ObjCInterfaceType::qual_iterator P = T->qual_begin(), + PEnd = T->qual_end(); + P != PEnd; ++P) { + ObjCProtocolDecl *Protocol + = dyn_cast_or_null(Importer.Import(*P)); + if (!Protocol) + return QualType(); + Protocols.push_back(Protocol); + } + + return Importer.getToContext().getObjCInterfaceType(Class, + Protocols.data(), + Protocols.size()); +} + +QualType ASTNodeImporter::VisitObjCObjectPointerType(ObjCObjectPointerType *T) { + QualType ToPointeeType = Importer.Import(T->getPointeeType()); + if (ToPointeeType.isNull()) + return QualType(); + + llvm::SmallVector Protocols; + for (ObjCObjectPointerType::qual_iterator P = T->qual_begin(), + PEnd = T->qual_end(); + P != PEnd; ++P) { + ObjCProtocolDecl *Protocol + = dyn_cast_or_null(Importer.Import(*P)); + if (!Protocol) + return QualType(); + Protocols.push_back(Protocol); + } + + return Importer.getToContext().getObjCObjectPointerType(ToPointeeType, + Protocols.data(), + Protocols.size()); +} + +//---------------------------------------------------------------------------- +// Import Declarations +//---------------------------------------------------------------------------- +bool ASTNodeImporter::ImportDeclParts(NamedDecl *D, DeclContext *&DC, + DeclContext *&LexicalDC, + DeclarationName &Name, + SourceLocation &Loc) { + // Import the context of this declaration. + DC = Importer.ImportContext(D->getDeclContext()); + if (!DC) + return true; + + LexicalDC = DC; + if (D->getDeclContext() != D->getLexicalDeclContext()) { + LexicalDC = Importer.ImportContext(D->getLexicalDeclContext()); + if (!LexicalDC) + return true; + } + + // Import the name of this declaration. + Name = Importer.Import(D->getDeclName()); + if (D->getDeclName() && !Name) + return true; + + // Import the location of this declaration. + Loc = Importer.Import(D->getLocation()); + return false; +} + +bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, + RecordDecl *ToRecord) { + StructuralEquivalenceContext SEC(Importer.getFromContext(), + Importer.getToContext(), + Importer.getDiags(), + Importer.getNonEquivalentDecls()); + return SEC.IsStructurallyEquivalent(FromRecord, ToRecord); +} + +bool ASTNodeImporter::IsStructuralMatch(EnumDecl *FromEnum, EnumDecl *ToEnum) { + StructuralEquivalenceContext SEC(Importer.getFromContext(), + Importer.getToContext(), + Importer.getDiags(), + Importer.getNonEquivalentDecls()); + return SEC.IsStructurallyEquivalent(FromEnum, ToEnum); +} + +Decl *ASTNodeImporter::VisitDecl(Decl *D) { + Importer.FromDiag(D->getLocation(), diag::err_unsupported_ast_node) + << D->getDeclKindName(); + return 0; +} + +Decl *ASTNodeImporter::VisitTypedefDecl(TypedefDecl *D) { + // Import the major distinguishing characteristics of this typedef. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + // If this typedef is not in block scope, determine whether we've + // seen a typedef with the same name (that we can merge with) or any + // other entity by that name (which name lookup could conflict with). + if (!DC->isFunctionOrMethod()) { + llvm::SmallVector ConflictingDecls; + unsigned IDNS = Decl::IDNS_Ordinary; + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (!(*Lookup.first)->isInIdentifierNamespace(IDNS)) + continue; + if (TypedefDecl *FoundTypedef = dyn_cast(*Lookup.first)) { + if (Importer.IsStructurallyEquivalent(D->getUnderlyingType(), + FoundTypedef->getUnderlyingType())) + return Importer.Imported(D, FoundTypedef); + } + + ConflictingDecls.push_back(*Lookup.first); + } + + if (!ConflictingDecls.empty()) { + Name = Importer.HandleNameConflict(Name, DC, IDNS, + ConflictingDecls.data(), + ConflictingDecls.size()); + if (!Name) + return 0; + } + } + + // Import the underlying type of this typedef; + QualType T = Importer.Import(D->getUnderlyingType()); + if (T.isNull()) + return 0; + + // Create the new typedef node. + TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo()); + TypedefDecl *ToTypedef = TypedefDecl::Create(Importer.getToContext(), DC, + Loc, Name.getAsIdentifierInfo(), + TInfo); + ToTypedef->setLexicalDeclContext(LexicalDC); + Importer.Imported(D, ToTypedef); + LexicalDC->addDecl(ToTypedef); + + return ToTypedef; +} + +Decl *ASTNodeImporter::VisitEnumDecl(EnumDecl *D) { + // Import the major distinguishing characteristics of this enum. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + // Figure out what enum name we're looking for. + unsigned IDNS = Decl::IDNS_Tag; + DeclarationName SearchName = Name; + if (!SearchName && D->getTypedefForAnonDecl()) { + SearchName = Importer.Import(D->getTypedefForAnonDecl()->getDeclName()); + IDNS = Decl::IDNS_Ordinary; + } else if (Importer.getToContext().getLangOptions().CPlusPlus) + IDNS |= Decl::IDNS_Ordinary; + + // We may already have an enum of the same name; try to find and match it. + if (!DC->isFunctionOrMethod() && SearchName) { + llvm::SmallVector ConflictingDecls; + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (!(*Lookup.first)->isInIdentifierNamespace(IDNS)) + continue; + + Decl *Found = *Lookup.first; + if (TypedefDecl *Typedef = dyn_cast(Found)) { + if (const TagType *Tag = Typedef->getUnderlyingType()->getAs()) + Found = Tag->getDecl(); + } + + if (EnumDecl *FoundEnum = dyn_cast(Found)) { + if (IsStructuralMatch(D, FoundEnum)) + return Importer.Imported(D, FoundEnum); + } + + ConflictingDecls.push_back(*Lookup.first); + } + + if (!ConflictingDecls.empty()) { + Name = Importer.HandleNameConflict(Name, DC, IDNS, + ConflictingDecls.data(), + ConflictingDecls.size()); + } + } + + // Create the enum declaration. + EnumDecl *D2 = EnumDecl::Create(Importer.getToContext(), DC, Loc, + Name.getAsIdentifierInfo(), + Importer.Import(D->getTagKeywordLoc()), + 0); + D2->setLexicalDeclContext(LexicalDC); + Importer.Imported(D, D2); + LexicalDC->addDecl(D2); + + // Import the integer type. + QualType ToIntegerType = Importer.Import(D->getIntegerType()); + if (ToIntegerType.isNull()) + return 0; + D2->setIntegerType(ToIntegerType); + + // Import the definition + if (D->isDefinition()) { + QualType T = Importer.Import(Importer.getFromContext().getTypeDeclType(D)); + if (T.isNull()) + return 0; + + QualType ToPromotionType = Importer.Import(D->getPromotionType()); + if (ToPromotionType.isNull()) + return 0; + + D2->startDefinition(); + for (DeclContext::decl_iterator FromMem = D->decls_begin(), + FromMemEnd = D->decls_end(); + FromMem != FromMemEnd; + ++FromMem) + Importer.Import(*FromMem); + + D2->completeDefinition(T, ToPromotionType); + } + + return D2; +} + +Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { + // If this record has a definition in the translation unit we're coming from, + // but this particular declaration is not that definition, import the + // definition and map to that. + TagDecl *Definition = D->getDefinition(); + if (Definition && Definition != D) { + Decl *ImportedDef = Importer.Import(Definition); + if (!ImportedDef) + return 0; + + return Importer.Imported(D, ImportedDef); + } + + // Import the major distinguishing characteristics of this record. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + // Figure out what structure name we're looking for. + unsigned IDNS = Decl::IDNS_Tag; + DeclarationName SearchName = Name; + if (!SearchName && D->getTypedefForAnonDecl()) { + SearchName = Importer.Import(D->getTypedefForAnonDecl()->getDeclName()); + IDNS = Decl::IDNS_Ordinary; + } else if (Importer.getToContext().getLangOptions().CPlusPlus) + IDNS |= Decl::IDNS_Ordinary; + + // We may already have a record of the same name; try to find and match it. + RecordDecl *AdoptDecl = 0; + if (!DC->isFunctionOrMethod() && SearchName) { + llvm::SmallVector ConflictingDecls; + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (!(*Lookup.first)->isInIdentifierNamespace(IDNS)) + continue; + + Decl *Found = *Lookup.first; + if (TypedefDecl *Typedef = dyn_cast(Found)) { + if (const TagType *Tag = Typedef->getUnderlyingType()->getAs()) + Found = Tag->getDecl(); + } + + if (RecordDecl *FoundRecord = dyn_cast(Found)) { + if (RecordDecl *FoundDef = FoundRecord->getDefinition()) { + if (!D->isDefinition() || IsStructuralMatch(D, FoundDef)) { + // The record types structurally match, or the "from" translation + // unit only had a forward declaration anyway; call it the same + // function. + // FIXME: For C++, we should also merge methods here. + return Importer.Imported(D, FoundDef); + } + } else { + // We have a forward declaration of this type, so adopt that forward + // declaration rather than building a new one. + AdoptDecl = FoundRecord; + continue; + } + } + + ConflictingDecls.push_back(*Lookup.first); + } + + if (!ConflictingDecls.empty()) { + Name = Importer.HandleNameConflict(Name, DC, IDNS, + ConflictingDecls.data(), + ConflictingDecls.size()); + } + } + + // Create the record declaration. + RecordDecl *D2 = AdoptDecl; + if (!D2) { + if (CXXRecordDecl *D1CXX = dyn_cast(D)) { + CXXRecordDecl *D2CXX = CXXRecordDecl::Create(Importer.getToContext(), + D->getTagKind(), + DC, Loc, + Name.getAsIdentifierInfo(), + Importer.Import(D->getTagKeywordLoc())); + D2 = D2CXX; + + if (D->isDefinition()) { + // Add base classes. + llvm::SmallVector Bases; + for (CXXRecordDecl::base_class_iterator + Base1 = D1CXX->bases_begin(), + FromBaseEnd = D1CXX->bases_end(); + Base1 != FromBaseEnd; + ++Base1) { + QualType T = Importer.Import(Base1->getType()); + if (T.isNull()) + return 0; + + Bases.push_back( + new (Importer.getToContext()) + CXXBaseSpecifier(Importer.Import(Base1->getSourceRange()), + Base1->isVirtual(), + Base1->isBaseOfClass(), + Base1->getAccessSpecifierAsWritten(), + T)); + } + if (!Bases.empty()) + D2CXX->setBases(Bases.data(), Bases.size()); + } + } else { + D2 = RecordDecl::Create(Importer.getToContext(), D->getTagKind(), + DC, Loc, + Name.getAsIdentifierInfo(), + Importer.Import(D->getTagKeywordLoc())); + } + D2->setLexicalDeclContext(LexicalDC); + LexicalDC->addDecl(D2); + } + + Importer.Imported(D, D2); + + if (D->isDefinition()) { + D2->startDefinition(); + for (DeclContext::decl_iterator FromMem = D->decls_begin(), + FromMemEnd = D->decls_end(); + FromMem != FromMemEnd; + ++FromMem) + Importer.Import(*FromMem); + + D2->completeDefinition(); + } + + return D2; +} + +Decl *ASTNodeImporter::VisitEnumConstantDecl(EnumConstantDecl *D) { + // Import the major distinguishing characteristics of this enumerator. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + QualType T = Importer.Import(D->getType()); + if (T.isNull()) + return 0; + + // Determine whether there are any other declarations with the same name and + // in the same context. + if (!LexicalDC->isFunctionOrMethod()) { + llvm::SmallVector ConflictingDecls; + unsigned IDNS = Decl::IDNS_Ordinary; + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (!(*Lookup.first)->isInIdentifierNamespace(IDNS)) + continue; + + ConflictingDecls.push_back(*Lookup.first); + } + + if (!ConflictingDecls.empty()) { + Name = Importer.HandleNameConflict(Name, DC, IDNS, + ConflictingDecls.data(), + ConflictingDecls.size()); + if (!Name) + return 0; + } + } + + Expr *Init = Importer.Import(D->getInitExpr()); + if (D->getInitExpr() && !Init) + return 0; + + EnumConstantDecl *ToEnumerator + = EnumConstantDecl::Create(Importer.getToContext(), cast(DC), Loc, + Name.getAsIdentifierInfo(), T, + Init, D->getInitVal()); + ToEnumerator->setLexicalDeclContext(LexicalDC); + Importer.Imported(D, ToEnumerator); + LexicalDC->addDecl(ToEnumerator); + return ToEnumerator; +} + +Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { + // Import the major distinguishing characteristics of this function. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + // Try to find a function in our own ("to") context with the same name, same + // type, and in the same context as the function we're importing. + if (!LexicalDC->isFunctionOrMethod()) { + llvm::SmallVector ConflictingDecls; + unsigned IDNS = Decl::IDNS_Ordinary; + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (!(*Lookup.first)->isInIdentifierNamespace(IDNS)) + continue; + + if (FunctionDecl *FoundFunction = dyn_cast(*Lookup.first)) { + if (isExternalLinkage(FoundFunction->getLinkage()) && + isExternalLinkage(D->getLinkage())) { + if (Importer.IsStructurallyEquivalent(D->getType(), + FoundFunction->getType())) { + // FIXME: Actually try to merge the body and other attributes. + return Importer.Imported(D, FoundFunction); + } + + // FIXME: Check for overloading more carefully, e.g., by boosting + // Sema::IsOverload out to the AST library. + + // Function overloading is okay in C++. + if (Importer.getToContext().getLangOptions().CPlusPlus) + continue; + + // Complain about inconsistent function types. + Importer.ToDiag(Loc, diag::err_odr_function_type_inconsistent) + << Name << D->getType() << FoundFunction->getType(); + Importer.ToDiag(FoundFunction->getLocation(), + diag::note_odr_value_here) + << FoundFunction->getType(); + } + } + + ConflictingDecls.push_back(*Lookup.first); + } + + if (!ConflictingDecls.empty()) { + Name = Importer.HandleNameConflict(Name, DC, IDNS, + ConflictingDecls.data(), + ConflictingDecls.size()); + if (!Name) + return 0; + } + } + + // Import the type. + QualType T = Importer.Import(D->getType()); + if (T.isNull()) + return 0; + + // Import the function parameters. + llvm::SmallVector Parameters; + for (FunctionDecl::param_iterator P = D->param_begin(), PEnd = D->param_end(); + P != PEnd; ++P) { + ParmVarDecl *ToP = cast_or_null(Importer.Import(*P)); + if (!ToP) + return 0; + + Parameters.push_back(ToP); + } + + // Create the imported function. + TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo()); + FunctionDecl *ToEnumerator + = FunctionDecl::Create(Importer.getToContext(), DC, Loc, + Name, T, TInfo, D->getStorageClass(), + D->isInlineSpecified(), + D->hasWrittenPrototype()); + ToEnumerator->setLexicalDeclContext(LexicalDC); + Importer.Imported(D, ToEnumerator); + LexicalDC->addDecl(ToEnumerator); + + // Set the parameters. + for (unsigned I = 0, N = Parameters.size(); I != N; ++I) { + Parameters[I]->setOwningFunction(ToEnumerator); + ToEnumerator->addDecl(Parameters[I]); + } + ToEnumerator->setParams(Parameters.data(), Parameters.size()); + + // FIXME: Other bits to merge? + + return ToEnumerator; +} + +Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) { + // Import the major distinguishing characteristics of a variable. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + // Import the type. + QualType T = Importer.Import(D->getType()); + if (T.isNull()) + return 0; + + TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo()); + Expr *BitWidth = Importer.Import(D->getBitWidth()); + if (!BitWidth && D->getBitWidth()) + return 0; + + FieldDecl *ToField = FieldDecl::Create(Importer.getToContext(), DC, + Loc, Name.getAsIdentifierInfo(), + T, TInfo, BitWidth, D->isMutable()); + ToField->setLexicalDeclContext(LexicalDC); + Importer.Imported(D, ToField); + LexicalDC->addDecl(ToField); + return ToField; +} + +Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) { + // Import the major distinguishing characteristics of a variable. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + // Try to find a variable in our own ("to") context with the same name and + // in the same context as the variable we're importing. + if (D->isFileVarDecl()) { + VarDecl *MergeWithVar = 0; + llvm::SmallVector ConflictingDecls; + unsigned IDNS = Decl::IDNS_Ordinary; + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (!(*Lookup.first)->isInIdentifierNamespace(IDNS)) + continue; + + if (VarDecl *FoundVar = dyn_cast(*Lookup.first)) { + // We have found a variable that we may need to merge with. Check it. + if (isExternalLinkage(FoundVar->getLinkage()) && + isExternalLinkage(D->getLinkage())) { + if (Importer.IsStructurallyEquivalent(D->getType(), + FoundVar->getType())) { + MergeWithVar = FoundVar; + break; + } + + const ArrayType *FoundArray + = Importer.getToContext().getAsArrayType(FoundVar->getType()); + const ArrayType *TArray + = Importer.getToContext().getAsArrayType(D->getType()); + if (FoundArray && TArray) { + if (isa(FoundArray) && + isa(TArray)) { + // Import the type. + QualType T = Importer.Import(D->getType()); + if (T.isNull()) + return 0; + + FoundVar->setType(T); + MergeWithVar = FoundVar; + break; + } else if (isa(TArray) && + isa(FoundArray)) { + MergeWithVar = FoundVar; + break; + } + } + + Importer.ToDiag(Loc, diag::err_odr_variable_type_inconsistent) + << Name << D->getType() << FoundVar->getType(); + Importer.ToDiag(FoundVar->getLocation(), diag::note_odr_value_here) + << FoundVar->getType(); + } + } + + ConflictingDecls.push_back(*Lookup.first); + } + + if (MergeWithVar) { + // An equivalent variable with external linkage has been found. Link + // the two declarations, then merge them. + Importer.Imported(D, MergeWithVar); + + if (VarDecl *DDef = D->getDefinition()) { + if (VarDecl *ExistingDef = MergeWithVar->getDefinition()) { + Importer.ToDiag(ExistingDef->getLocation(), + diag::err_odr_variable_multiple_def) + << Name; + Importer.FromDiag(DDef->getLocation(), diag::note_odr_defined_here); + } else { + Expr *Init = Importer.Import(DDef->getInit()); + MergeWithVar->setInit(Init); + } + } + + return MergeWithVar; + } + + if (!ConflictingDecls.empty()) { + Name = Importer.HandleNameConflict(Name, DC, IDNS, + ConflictingDecls.data(), + ConflictingDecls.size()); + if (!Name) + return 0; + } + } + + // Import the type. + QualType T = Importer.Import(D->getType()); + if (T.isNull()) + return 0; + + // Create the imported variable. + TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo()); + VarDecl *ToVar = VarDecl::Create(Importer.getToContext(), DC, Loc, + Name.getAsIdentifierInfo(), T, TInfo, + D->getStorageClass()); + ToVar->setLexicalDeclContext(LexicalDC); + Importer.Imported(D, ToVar); + LexicalDC->addDecl(ToVar); + + // Merge the initializer. + // FIXME: Can we really import any initializer? Alternatively, we could force + // ourselves to import every declaration of a variable and then only use + // getInit() here. + ToVar->setInit(Importer.Import(const_cast(D->getAnyInitializer()))); + + // FIXME: Other bits to merge? + + return ToVar; +} + +Decl *ASTNodeImporter::VisitParmVarDecl(ParmVarDecl *D) { + // Parameters are created in the translation unit's context, then moved + // into the function declaration's context afterward. + DeclContext *DC = Importer.getToContext().getTranslationUnitDecl(); + + // Import the name of this declaration. + DeclarationName Name = Importer.Import(D->getDeclName()); + if (D->getDeclName() && !Name) + return 0; + + // Import the location of this declaration. + SourceLocation Loc = Importer.Import(D->getLocation()); + + // Import the parameter's type. + QualType T = Importer.Import(D->getType()); + if (T.isNull()) + return 0; + + // Create the imported parameter. + TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo()); + ParmVarDecl *ToParm = ParmVarDecl::Create(Importer.getToContext(), DC, + Loc, Name.getAsIdentifierInfo(), + T, TInfo, D->getStorageClass(), + /*FIXME: Default argument*/ 0); + return Importer.Imported(D, ToParm); +} + +Decl *ASTNodeImporter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { + // Import the major distinguishing characteristics of an @interface. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + ObjCInterfaceDecl *MergeWithIface = 0; + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (!(*Lookup.first)->isInIdentifierNamespace(Decl::IDNS_Ordinary)) + continue; + + if ((MergeWithIface = dyn_cast(*Lookup.first))) + break; + } + + ObjCInterfaceDecl *ToIface = MergeWithIface; + if (!ToIface || ToIface->isForwardDecl()) { + if (!ToIface) { + ToIface = ObjCInterfaceDecl::Create(Importer.getToContext(), + DC, Loc, + Name.getAsIdentifierInfo(), + Importer.Import(D->getClassLoc()), + D->isForwardDecl(), + D->isImplicitInterfaceDecl()); + ToIface->setLexicalDeclContext(LexicalDC); + LexicalDC->addDecl(ToIface); + } + Importer.Imported(D, ToIface); + + // Import superclass + // FIXME: If we're merging, make sure that both decls have the same + // superclass. + if (D->getSuperClass()) { + ObjCInterfaceDecl *Super + = cast_or_null(Importer.Import(D->getSuperClass())); + if (!Super) + return 0; + + ToIface->setSuperClass(Super); + ToIface->setSuperClassLoc(Importer.Import(D->getSuperClassLoc())); + } + + // Import protocols + llvm::SmallVector Protocols; + llvm::SmallVector ProtocolLocs; + ObjCInterfaceDecl::protocol_loc_iterator + FromProtoLoc = D->protocol_loc_begin(); + for (ObjCInterfaceDecl::protocol_iterator FromProto = D->protocol_begin(), + FromProtoEnd = D->protocol_end(); + FromProto != FromProtoEnd; + ++FromProto, ++FromProtoLoc) { + ObjCProtocolDecl *ToProto + = cast_or_null(Importer.Import(*FromProto)); + if (!ToProto) + return 0; + Protocols.push_back(ToProto); + ProtocolLocs.push_back(Importer.Import(*FromProtoLoc)); + } + + // FIXME: If we're merging, make sure that the protocol list is the same. + ToIface->setProtocolList(Protocols.data(), Protocols.size(), + ProtocolLocs.data(), Importer.getToContext()); + + // FIXME: Import categories + + // Import @end range + ToIface->setAtEndRange(Importer.Import(D->getAtEndRange())); + } else { + Importer.Imported(D, ToIface); + } + + // Import all of the members of this class. + for (DeclContext::decl_iterator FromMem = D->decls_begin(), + FromMemEnd = D->decls_end(); + FromMem != FromMemEnd; + ++FromMem) + Importer.Import(*FromMem); + + // If we have an @implementation, import it as well. + if (D->getImplementation()) { + ObjCImplementationDecl *Impl + = cast(Importer.Import(D->getImplementation())); + if (!Impl) + return 0; + + ToIface->setImplementation(Impl); + } + + return 0; +} + +//---------------------------------------------------------------------------- +// Import Statements +//---------------------------------------------------------------------------- + +Stmt *ASTNodeImporter::VisitStmt(Stmt *S) { + Importer.FromDiag(S->getLocStart(), diag::err_unsupported_ast_node) + << S->getStmtClassName(); + return 0; +} + +//---------------------------------------------------------------------------- +// Import Expressions +//---------------------------------------------------------------------------- +Expr *ASTNodeImporter::VisitExpr(Expr *E) { + Importer.FromDiag(E->getLocStart(), diag::err_unsupported_ast_node) + << E->getStmtClassName(); + return 0; +} + +Expr *ASTNodeImporter::VisitIntegerLiteral(IntegerLiteral *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return 0; + + return new (Importer.getToContext()) + IntegerLiteral(E->getValue(), T, Importer.Import(E->getLocation())); +} + +Expr *ASTNodeImporter::VisitImplicitCastExpr(ImplicitCastExpr *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return 0; + + Expr *SubExpr = Importer.Import(E->getSubExpr()); + if (!SubExpr) + return 0; + + return new (Importer.getToContext()) ImplicitCastExpr(T, E->getCastKind(), + SubExpr, + E->isLvalueCast()); +} + +ASTImporter::ASTImporter(Diagnostic &Diags, + ASTContext &ToContext, FileManager &ToFileManager, + ASTContext &FromContext, FileManager &FromFileManager) + : ToContext(ToContext), FromContext(FromContext), + ToFileManager(ToFileManager), FromFileManager(FromFileManager), + Diags(Diags) { + ImportedDecls[FromContext.getTranslationUnitDecl()] + = ToContext.getTranslationUnitDecl(); +} + +ASTImporter::~ASTImporter() { } + +QualType ASTImporter::Import(QualType FromT) { + if (FromT.isNull()) + return QualType(); + + // Check whether we've already imported this type. + llvm::DenseMap::iterator Pos + = ImportedTypes.find(FromT.getTypePtr()); + if (Pos != ImportedTypes.end()) + return ToContext.getQualifiedType(Pos->second, FromT.getQualifiers()); + + // Import the type + ASTNodeImporter Importer(*this); + QualType ToT = Importer.Visit(FromT.getTypePtr()); + if (ToT.isNull()) + return ToT; + + // Record the imported type. + ImportedTypes[FromT.getTypePtr()] = ToT.getTypePtr(); + + return ToContext.getQualifiedType(ToT, FromT.getQualifiers()); +} + +TypeSourceInfo *ASTImporter::Import(TypeSourceInfo *FromTSI) { + if (!FromTSI) + return FromTSI; + + // FIXME: For now we just create a "trivial" type source info based + // on the type and a seingle location. Implement a real version of + // this. + QualType T = Import(FromTSI->getType()); + if (T.isNull()) + return 0; + + return ToContext.getTrivialTypeSourceInfo(T, + FromTSI->getTypeLoc().getFullSourceRange().getBegin()); +} + +Decl *ASTImporter::Import(Decl *FromD) { + if (!FromD) + return 0; + + // Check whether we've already imported this declaration. + llvm::DenseMap::iterator Pos = ImportedDecls.find(FromD); + if (Pos != ImportedDecls.end()) + return Pos->second; + + // Import the type + ASTNodeImporter Importer(*this); + Decl *ToD = Importer.Visit(FromD); + if (!ToD) + return 0; + + // Record the imported declaration. + ImportedDecls[FromD] = ToD; + + if (TagDecl *FromTag = dyn_cast(FromD)) { + // Keep track of anonymous tags that have an associated typedef. + if (FromTag->getTypedefForAnonDecl()) + AnonTagsWithPendingTypedefs.push_back(FromTag); + } else if (TypedefDecl *FromTypedef = dyn_cast(FromD)) { + // When we've finished transforming a typedef, see whether it was the + // typedef for an anonymous tag. + for (llvm::SmallVector::iterator + FromTag = AnonTagsWithPendingTypedefs.begin(), + FromTagEnd = AnonTagsWithPendingTypedefs.end(); + FromTag != FromTagEnd; ++FromTag) { + if ((*FromTag)->getTypedefForAnonDecl() == FromTypedef) { + if (TagDecl *ToTag = cast_or_null(Import(*FromTag))) { + // We found the typedef for an anonymous tag; link them. + ToTag->setTypedefForAnonDecl(cast(ToD)); + AnonTagsWithPendingTypedefs.erase(FromTag); + break; + } + } + } + } + + return ToD; +} + +DeclContext *ASTImporter::ImportContext(DeclContext *FromDC) { + if (!FromDC) + return FromDC; + + return cast_or_null(Import(cast(FromDC))); +} + +Expr *ASTImporter::Import(Expr *FromE) { + if (!FromE) + return 0; + + return cast_or_null(Import(cast(FromE))); +} + +Stmt *ASTImporter::Import(Stmt *FromS) { + if (!FromS) + return 0; + + // Check whether we've already imported this declaration. + llvm::DenseMap::iterator Pos = ImportedStmts.find(FromS); + if (Pos != ImportedStmts.end()) + return Pos->second; + + // Import the type + ASTNodeImporter Importer(*this); + Stmt *ToS = Importer.Visit(FromS); + if (!ToS) + return 0; + + // Record the imported declaration. + ImportedStmts[FromS] = ToS; + return ToS; +} + +NestedNameSpecifier *ASTImporter::Import(NestedNameSpecifier *FromNNS) { + if (!FromNNS) + return 0; + + // FIXME: Implement! + return 0; +} + +SourceLocation ASTImporter::Import(SourceLocation FromLoc) { + if (FromLoc.isInvalid()) + return SourceLocation(); + + SourceManager &FromSM = FromContext.getSourceManager(); + + // For now, map everything down to its spelling location, so that we + // don't have to import macro instantiations. + // FIXME: Import macro instantiations! + FromLoc = FromSM.getSpellingLoc(FromLoc); + std::pair Decomposed = FromSM.getDecomposedLoc(FromLoc); + SourceManager &ToSM = ToContext.getSourceManager(); + return ToSM.getLocForStartOfFile(Import(Decomposed.first)) + .getFileLocWithOffset(Decomposed.second); +} + +SourceRange ASTImporter::Import(SourceRange FromRange) { + return SourceRange(Import(FromRange.getBegin()), Import(FromRange.getEnd())); +} + +FileID ASTImporter::Import(FileID FromID) { + llvm::DenseMap::iterator Pos + = ImportedFileIDs.find(FromID.getHashValue()); + if (Pos != ImportedFileIDs.end()) + return Pos->second; + + SourceManager &FromSM = FromContext.getSourceManager(); + SourceManager &ToSM = ToContext.getSourceManager(); + const SrcMgr::SLocEntry &FromSLoc = FromSM.getSLocEntry(FromID); + assert(FromSLoc.isFile() && "Cannot handle macro instantiations yet"); + + // Include location of this file. + SourceLocation ToIncludeLoc = Import(FromSLoc.getFile().getIncludeLoc()); + + // Map the FileID for to the "to" source manager. + FileID ToID; + const SrcMgr::ContentCache *Cache = FromSLoc.getFile().getContentCache(); + if (Cache->Entry) { + // FIXME: We probably want to use getVirtualFile(), so we don't hit the + // disk again + // FIXME: We definitely want to re-use the existing MemoryBuffer, rather + // than mmap the files several times. + const FileEntry *Entry = ToFileManager.getFile(Cache->Entry->getName()); + ToID = ToSM.createFileID(Entry, ToIncludeLoc, + FromSLoc.getFile().getFileCharacteristic()); + } else { + // FIXME: We want to re-use the existing MemoryBuffer! + const llvm::MemoryBuffer *FromBuf = Cache->getBuffer(); + llvm::MemoryBuffer *ToBuf + = llvm::MemoryBuffer::getMemBufferCopy(FromBuf->getBufferStart(), + FromBuf->getBufferEnd(), + FromBuf->getBufferIdentifier()); + ToID = ToSM.createFileIDForMemBuffer(ToBuf); + } + + + ImportedFileIDs[FromID.getHashValue()] = ToID; + return ToID; +} + +DeclarationName ASTImporter::Import(DeclarationName FromName) { + if (!FromName) + return DeclarationName(); + + switch (FromName.getNameKind()) { + case DeclarationName::Identifier: + return Import(FromName.getAsIdentifierInfo()); + + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + return Import(FromName.getObjCSelector()); + + case DeclarationName::CXXConstructorName: { + QualType T = Import(FromName.getCXXNameType()); + if (T.isNull()) + return DeclarationName(); + + return ToContext.DeclarationNames.getCXXConstructorName( + ToContext.getCanonicalType(T)); + } + + case DeclarationName::CXXDestructorName: { + QualType T = Import(FromName.getCXXNameType()); + if (T.isNull()) + return DeclarationName(); + + return ToContext.DeclarationNames.getCXXDestructorName( + ToContext.getCanonicalType(T)); + } + + case DeclarationName::CXXConversionFunctionName: { + QualType T = Import(FromName.getCXXNameType()); + if (T.isNull()) + return DeclarationName(); + + return ToContext.DeclarationNames.getCXXConversionFunctionName( + ToContext.getCanonicalType(T)); + } + + case DeclarationName::CXXOperatorName: + return ToContext.DeclarationNames.getCXXOperatorName( + FromName.getCXXOverloadedOperator()); + + case DeclarationName::CXXLiteralOperatorName: + return ToContext.DeclarationNames.getCXXLiteralOperatorName( + Import(FromName.getCXXLiteralIdentifier())); + + case DeclarationName::CXXUsingDirective: + // FIXME: STATICS! + return DeclarationName::getUsingDirectiveName(); + } + + // Silence bogus GCC warning + return DeclarationName(); +} + +IdentifierInfo *ASTImporter::Import(IdentifierInfo *FromId) { + if (!FromId) + return 0; + + return &ToContext.Idents.get(FromId->getName()); +} + +DeclarationName ASTImporter::HandleNameConflict(DeclarationName Name, + DeclContext *DC, + unsigned IDNS, + NamedDecl **Decls, + unsigned NumDecls) { + return Name; +} + +DiagnosticBuilder ASTImporter::ToDiag(SourceLocation Loc, unsigned DiagID) { + return Diags.Report(FullSourceLoc(Loc, ToContext.getSourceManager()), + DiagID); +} + +DiagnosticBuilder ASTImporter::FromDiag(SourceLocation Loc, unsigned DiagID) { + return Diags.Report(FullSourceLoc(Loc, FromContext.getSourceManager()), + DiagID); +} + +Decl *ASTImporter::Imported(Decl *From, Decl *To) { + ImportedDecls[From] = To; + return To; +} + +bool ASTImporter::IsStructurallyEquivalent(QualType From, QualType To) { + llvm::DenseMap::iterator Pos + = ImportedTypes.find(From.getTypePtr()); + if (Pos != ImportedTypes.end() && ToContext.hasSameType(Import(From), To)) + return true; + + StructuralEquivalenceContext SEC(FromContext, ToContext, Diags, + NonEquivalentDecls); + return SEC.IsStructurallyEquivalent(From, To); +} diff --git a/lib/AST/AttrImpl.cpp b/lib/AST/AttrImpl.cpp index 02c70b651119..d81979734b3a 100644 --- a/lib/AST/AttrImpl.cpp +++ b/lib/AST/AttrImpl.cpp @@ -11,11 +11,61 @@ // //===----------------------------------------------------------------------===// - #include "clang/AST/Attr.h" #include "clang/AST/ASTContext.h" using namespace clang; +void Attr::Destroy(ASTContext &C) { + if (Next) { + Next->Destroy(C); + Next = 0; + } + this->~Attr(); + C.Deallocate((void*)this); +} + +AttrWithString::AttrWithString(Attr::Kind AK, ASTContext &C, llvm::StringRef s) + : Attr(AK) { + assert(!s.empty()); + StrLen = s.size(); + Str = new (C) char[StrLen]; + memcpy(const_cast(Str), s.data(), StrLen); +} + +void AttrWithString::Destroy(ASTContext &C) { + C.Deallocate(const_cast(Str)); + Attr::Destroy(C); +} + +void AttrWithString::ReplaceString(ASTContext &C, llvm::StringRef newS) { + if (newS.size() > StrLen) { + C.Deallocate(const_cast(Str)); + Str = new (C) char[newS.size()]; + } + StrLen = newS.size(); + memcpy(const_cast(Str), newS.data(), StrLen); +} + +void FormatAttr::setType(ASTContext &C, llvm::StringRef type) { + ReplaceString(C, type); +} + +NonNullAttr::NonNullAttr(ASTContext &C, unsigned* arg_nums, unsigned size) + : Attr(NonNull), ArgNums(0), Size(0) { + if (size == 0) + return; + assert(arg_nums); + ArgNums = new (C) unsigned[size]; + Size = size; + memcpy(ArgNums, arg_nums, sizeof(*ArgNums)*size); +} + +void NonNullAttr::Destroy(ASTContext &C) { + if (ArgNums) + C.Deallocate(ArgNums); + Attr::Destroy(C); +} + #define DEF_SIMPLE_ATTR_CLONE(ATTR) \ Attr *ATTR##Attr::clone(ASTContext &C) const { \ return ::new (C) ATTR##Attr; \ @@ -55,6 +105,7 @@ DEF_SIMPLE_ATTR_CLONE(Hiding) DEF_SIMPLE_ATTR_CLONE(Override) DEF_SIMPLE_ATTR_CLONE(DLLImport) DEF_SIMPLE_ATTR_CLONE(DLLExport) +DEF_SIMPLE_ATTR_CLONE(X86ForceAlignArgPointer) Attr* PragmaPackAttr::clone(ASTContext &C) const { return ::new (C) PragmaPackAttr(Alignment); @@ -65,15 +116,15 @@ Attr* AlignedAttr::clone(ASTContext &C) const { } Attr* AnnotateAttr::clone(ASTContext &C) const { - return ::new (C) AnnotateAttr(Annotation); + return ::new (C) AnnotateAttr(C, getAnnotation()); } Attr *AsmLabelAttr::clone(ASTContext &C) const { - return ::new (C) AsmLabelAttr(Label); + return ::new (C) AsmLabelAttr(C, getLabel()); } Attr *AliasAttr::clone(ASTContext &C) const { - return ::new (C) AliasAttr(Aliasee); + return ::new (C) AliasAttr(C, getAliasee()); } Attr *ConstructorAttr::clone(ASTContext &C) const { @@ -93,15 +144,15 @@ Attr *GNUInlineAttr::clone(ASTContext &C) const { } Attr *SectionAttr::clone(ASTContext &C) const { - return ::new (C) SectionAttr(Name); + return ::new (C) SectionAttr(C, getName()); } Attr *NonNullAttr::clone(ASTContext &C) const { - return ::new (C) NonNullAttr(ArgNums, Size); + return ::new (C) NonNullAttr(C, ArgNums, Size); } Attr *FormatAttr::clone(ASTContext &C) const { - return ::new (C) FormatAttr(Type, formatIdx, firstArg); + return ::new (C) FormatAttr(C, getType(), formatIdx, firstArg); } Attr *FormatArgAttr::clone(ASTContext &C) const { diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index dea96e7866ec..e5bd9b7722c5 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -4,6 +4,8 @@ add_clang_library(clangAST APValue.cpp ASTConsumer.cpp ASTContext.cpp + ASTImporter.cpp + ASTDiagnostic.cpp AttrImpl.cpp CXXInheritance.cpp Decl.cpp diff --git a/lib/AST/CXXInheritance.cpp b/lib/AST/CXXInheritance.cpp index 720832869266..99f908caeab6 100644 --- a/lib/AST/CXXInheritance.cpp +++ b/lib/AST/CXXInheritance.cpp @@ -102,7 +102,6 @@ bool CXXRecordDecl::isProvablyNotDerivedFrom(const CXXRecordDecl *Base) const { bool CXXRecordDecl::forallBases(ForallBasesCallback *BaseMatches, void *OpaqueData, bool AllowShortCircuit) const { - ASTContext &Context = getASTContext(); llvm::SmallVector Queue; const CXXRecordDecl *Record = this; @@ -118,7 +117,7 @@ bool CXXRecordDecl::forallBases(ForallBasesCallback *BaseMatches, } CXXRecordDecl *Base = - cast_or_null(Ty->getDecl()->getDefinition(Context)); + cast_or_null(Ty->getDecl()->getDefinition()); if (!Base) { if (AllowShortCircuit) return false; AllMatches = false; @@ -215,10 +214,13 @@ bool CXXRecordDecl::lookupInBases(BaseMatchesCallback *BaseMatches, Paths.ScratchPath.Access = MergeAccess(AccessToHere, BaseSpec->getAccessSpecifier()); } - + + // Track whether there's a path involving this specific base. + bool FoundPathThroughBase = false; + if (BaseMatches(BaseSpec, Paths.ScratchPath, UserData)) { // We've found a path that terminates at this base. - FoundPath = true; + FoundPath = FoundPathThroughBase = true; if (Paths.isRecordingPaths()) { // We have a path. Make a copy of it before moving on. Paths.Paths.push_back(Paths.ScratchPath); @@ -240,7 +242,7 @@ bool CXXRecordDecl::lookupInBases(BaseMatchesCallback *BaseMatches, // There is a path to a base class that meets the criteria. If we're // not collecting paths or finding ambiguities, we're done. - FoundPath = true; + FoundPath = FoundPathThroughBase = true; if (!Paths.isFindingAmbiguities()) return FoundPath; } @@ -253,7 +255,7 @@ bool CXXRecordDecl::lookupInBases(BaseMatchesCallback *BaseMatches, } // If we set a virtual earlier, and this isn't a path, forget it again. - if (SetVirtual && !FoundPath) { + if (SetVirtual && !FoundPathThroughBase) { Paths.DetectedVirtual = 0; } } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 794b14a1f4a1..5acb82f31a29 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -29,224 +29,81 @@ using namespace clang; -void Attr::Destroy(ASTContext &C) { - if (Next) { - Next->Destroy(C); - Next = 0; - } - this->~Attr(); - C.Deallocate((void*)this); -} - /// \brief Return the TypeLoc wrapper for the type source info. TypeLoc TypeSourceInfo::getTypeLoc() const { return TypeLoc(Ty, (void*)(this + 1)); } //===----------------------------------------------------------------------===// -// Decl Allocation/Deallocation Method Implementations +// NamedDecl Implementation //===----------------------------------------------------------------------===// +/// \brief Get the most restrictive linkage for the types in the given +/// template parameter list. +static Linkage +getLinkageForTemplateParameterList(const TemplateParameterList *Params) { + Linkage L = ExternalLinkage; + for (TemplateParameterList::const_iterator P = Params->begin(), + PEnd = Params->end(); + P != PEnd; ++P) { + if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*P)) + if (!NTTP->getType()->isDependentType()) { + L = minLinkage(L, NTTP->getType()->getLinkage()); + continue; + } -TranslationUnitDecl *TranslationUnitDecl::Create(ASTContext &C) { - return new (C) TranslationUnitDecl(C); -} - -NamespaceDecl *NamespaceDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation L, IdentifierInfo *Id) { - return new (C) NamespaceDecl(DC, L, Id); -} - -void NamespaceDecl::Destroy(ASTContext& C) { - // NamespaceDecl uses "NextDeclarator" to chain namespace declarations - // together. They are all top-level Decls. - - this->~NamespaceDecl(); - C.Deallocate((void *)this); -} - - -ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation L, IdentifierInfo *Id, QualType T) { - return new (C) ImplicitParamDecl(ImplicitParam, DC, L, Id, T); -} - -const char *VarDecl::getStorageClassSpecifierString(StorageClass SC) { - switch (SC) { - case VarDecl::None: break; - case VarDecl::Auto: return "auto"; break; - case VarDecl::Extern: return "extern"; break; - case VarDecl::PrivateExtern: return "__private_extern__"; break; - case VarDecl::Register: return "register"; break; - case VarDecl::Static: return "static"; break; + if (TemplateTemplateParmDecl *TTP + = dyn_cast(*P)) { + L = minLinkage(L, + getLinkageForTemplateParameterList(TTP->getTemplateParameters())); + } } - assert(0 && "Invalid storage class"); - return 0; -} - -ParmVarDecl *ParmVarDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation L, IdentifierInfo *Id, - QualType T, TypeSourceInfo *TInfo, - StorageClass S, Expr *DefArg) { - return new (C) ParmVarDecl(ParmVar, DC, L, Id, T, TInfo, S, DefArg); -} - -Expr *ParmVarDecl::getDefaultArg() { - assert(!hasUnparsedDefaultArg() && "Default argument is not yet parsed!"); - assert(!hasUninstantiatedDefaultArg() && - "Default argument is not yet instantiated!"); - - Expr *Arg = getInit(); - if (CXXExprWithTemporaries *E = dyn_cast_or_null(Arg)) - return E->getSubExpr(); - - return Arg; -} - -unsigned ParmVarDecl::getNumDefaultArgTemporaries() const { - if (const CXXExprWithTemporaries *E = - dyn_cast(getInit())) - return E->getNumTemporaries(); - - return 0; + return L; } -CXXTemporary *ParmVarDecl::getDefaultArgTemporary(unsigned i) { - assert(getNumDefaultArgTemporaries() && - "Default arguments does not have any temporaries!"); - - CXXExprWithTemporaries *E = cast(getInit()); - return E->getTemporary(i); -} +/// \brief Get the most restrictive linkage for the types and +/// declarations in the given template argument list. +static Linkage getLinkageForTemplateArgumentList(const TemplateArgument *Args, + unsigned NumArgs) { + Linkage L = ExternalLinkage; -SourceRange ParmVarDecl::getDefaultArgRange() const { - if (const Expr *E = getInit()) - return E->getSourceRange(); - - if (hasUninstantiatedDefaultArg()) - return getUninstantiatedDefaultArg()->getSourceRange(); - - return SourceRange(); -} - -void VarDecl::setInit(ASTContext &C, Expr *I) { - if (EvaluatedStmt *Eval = Init.dyn_cast()) { - Eval->~EvaluatedStmt(); - C.Deallocate(Eval); - } - - Init = I; -} + for (unsigned I = 0; I != NumArgs; ++I) { + switch (Args[I].getKind()) { + case TemplateArgument::Null: + case TemplateArgument::Integral: + case TemplateArgument::Expression: + break; + + case TemplateArgument::Type: + L = minLinkage(L, Args[I].getAsType()->getLinkage()); + break; -bool VarDecl::isExternC() const { - ASTContext &Context = getASTContext(); - if (!Context.getLangOptions().CPlusPlus) - return (getDeclContext()->isTranslationUnit() && - getStorageClass() != Static) || - (getDeclContext()->isFunctionOrMethod() && hasExternalStorage()); + case TemplateArgument::Declaration: + if (NamedDecl *ND = dyn_cast(Args[I].getAsDecl())) + L = minLinkage(L, ND->getLinkage()); + if (ValueDecl *VD = dyn_cast(Args[I].getAsDecl())) + L = minLinkage(L, VD->getType()->getLinkage()); + break; - for (const DeclContext *DC = getDeclContext(); !DC->isTranslationUnit(); - DC = DC->getParent()) { - if (const LinkageSpecDecl *Linkage = dyn_cast(DC)) { - if (Linkage->getLanguage() == LinkageSpecDecl::lang_c) - return getStorageClass() != Static; + case TemplateArgument::Template: + if (TemplateDecl *Template + = Args[I].getAsTemplate().getAsTemplateDecl()) + L = minLinkage(L, Template->getLinkage()); + break; + case TemplateArgument::Pack: + L = minLinkage(L, + getLinkageForTemplateArgumentList(Args[I].pack_begin(), + Args[I].pack_size())); break; } - - if (DC->isFunctionOrMethod()) - return false; } - return false; + return L; } -FunctionDecl *FunctionDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation L, - DeclarationName N, QualType T, - TypeSourceInfo *TInfo, - StorageClass S, bool isInline, - bool hasWrittenPrototype) { - FunctionDecl *New - = new (C) FunctionDecl(Function, DC, L, N, T, TInfo, S, isInline); - New->HasWrittenPrototype = hasWrittenPrototype; - return New; -} - -BlockDecl *BlockDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) { - return new (C) BlockDecl(DC, L); -} - -FieldDecl *FieldDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, - IdentifierInfo *Id, QualType T, - TypeSourceInfo *TInfo, Expr *BW, bool Mutable) { - return new (C) FieldDecl(Decl::Field, DC, L, Id, T, TInfo, BW, Mutable); -} - -bool FieldDecl::isAnonymousStructOrUnion() const { - if (!isImplicit() || getDeclName()) - return false; - - if (const RecordType *Record = getType()->getAs()) - return Record->getDecl()->isAnonymousStructOrUnion(); - - return false; -} - -EnumConstantDecl *EnumConstantDecl::Create(ASTContext &C, EnumDecl *CD, - SourceLocation L, - IdentifierInfo *Id, QualType T, - Expr *E, const llvm::APSInt &V) { - return new (C) EnumConstantDecl(CD, L, Id, T, E, V); -} - -void EnumConstantDecl::Destroy(ASTContext& C) { - if (Init) Init->Destroy(C); - Decl::Destroy(C); -} - -TypedefDecl *TypedefDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation L, IdentifierInfo *Id, - TypeSourceInfo *TInfo) { - return new (C) TypedefDecl(DC, L, Id, TInfo); -} - -// Anchor TypedefDecl's vtable here. -TypedefDecl::~TypedefDecl() {} - -EnumDecl *EnumDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, - IdentifierInfo *Id, SourceLocation TKL, - EnumDecl *PrevDecl) { - EnumDecl *Enum = new (C) EnumDecl(DC, L, Id, PrevDecl, TKL); - C.getTypeDeclType(Enum, PrevDecl); - return Enum; -} - -void EnumDecl::Destroy(ASTContext& C) { - Decl::Destroy(C); -} - -void EnumDecl::completeDefinition(ASTContext &C, - QualType NewType, - QualType NewPromotionType) { - assert(!isDefinition() && "Cannot redefine enums!"); - IntegerType = NewType; - PromotionType = NewPromotionType; - TagDecl::completeDefinition(); -} - -FileScopeAsmDecl *FileScopeAsmDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation L, - StringLiteral *Str) { - return new (C) FileScopeAsmDecl(DC, L, Str); -} - -//===----------------------------------------------------------------------===// -// NamedDecl Implementation -//===----------------------------------------------------------------------===// - -static NamedDecl::Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) { +static Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) { assert(D->getDeclContext()->getLookupContext()->isFileContext() && "Not a name having namespace scope"); ASTContext &Context = D->getASTContext(); @@ -260,7 +117,7 @@ static NamedDecl::Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) { if (const VarDecl *Var = dyn_cast(D)) { // Explicitly declared static. if (Var->getStorageClass() == VarDecl::Static) - return NamedDecl::InternalLinkage; + return InternalLinkage; // - an object or reference that is explicitly declared const // and neither explicitly declared extern nor previously @@ -274,13 +131,16 @@ static NamedDecl::Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) { for (const VarDecl *PrevVar = Var->getPreviousDeclaration(); PrevVar && !FoundExtern; PrevVar = PrevVar->getPreviousDeclaration()) - if (PrevVar->getLinkage() == NamedDecl::ExternalLinkage) + if (isExternalLinkage(PrevVar->getLinkage())) FoundExtern = true; if (!FoundExtern) - return NamedDecl::InternalLinkage; + return InternalLinkage; } } else if (isa(D) || isa(D)) { + // C++ [temp]p4: + // A non-member function template can have internal linkage; any + // other template name shall have external linkage. const FunctionDecl *Function = 0; if (const FunctionTemplateDecl *FunTmpl = dyn_cast(D)) @@ -290,11 +150,11 @@ static NamedDecl::Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) { // Explicitly declared static. if (Function->getStorageClass() == FunctionDecl::Static) - return NamedDecl::InternalLinkage; + return InternalLinkage; } else if (const FieldDecl *Field = dyn_cast(D)) { // - a data member of an anonymous union. if (cast(Field->getDeclContext())->isAnonymousStructOrUnion()) - return NamedDecl::InternalLinkage; + return InternalLinkage; } // C++ [basic.link]p4: @@ -317,7 +177,7 @@ static NamedDecl::Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) { // is visible, or if the prior declaration specifies no // linkage, then the identifier has external linkage. if (const VarDecl *PrevVar = Var->getPreviousDeclaration()) { - if (NamedDecl::Linkage L = PrevVar->getLinkage()) + if (Linkage L = PrevVar->getLinkage()) return L; } } @@ -326,7 +186,10 @@ static NamedDecl::Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) { // If the declaration of an identifier for an object has file // scope and no storage-class specifier, its linkage is // external. - return NamedDecl::ExternalLinkage; + if (Var->isInAnonymousNamespace()) + return UniqueExternalLinkage; + + return ExternalLinkage; } // - a function, unless it has internal linkage; or @@ -350,12 +213,26 @@ static NamedDecl::Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) { // is visible, or if the prior declaration specifies no // linkage, then the identifier has external linkage. if (const FunctionDecl *PrevFunc = Function->getPreviousDeclaration()) { - if (NamedDecl::Linkage L = PrevFunc->getLinkage()) + if (Linkage L = PrevFunc->getLinkage()) return L; } } - return NamedDecl::ExternalLinkage; + if (Function->isInAnonymousNamespace()) + return UniqueExternalLinkage; + + if (FunctionTemplateSpecializationInfo *SpecInfo + = Function->getTemplateSpecializationInfo()) { + Linkage L = SpecInfo->getTemplate()->getLinkage(); + const TemplateArgumentList &TemplateArgs = *SpecInfo->TemplateArguments; + L = minLinkage(L, + getLinkageForTemplateArgumentList( + TemplateArgs.getFlatArgumentList(), + TemplateArgs.flat_size())); + return L; + } + + return ExternalLinkage; } // - a named class (Clause 9), or an unnamed class defined in a @@ -365,29 +242,50 @@ static NamedDecl::Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) { // defined in a typedef declaration in which the enumeration // has the typedef name for linkage purposes (7.1.3); or if (const TagDecl *Tag = dyn_cast(D)) - if (Tag->getDeclName() || Tag->getTypedefForAnonDecl()) - return NamedDecl::ExternalLinkage; + if (Tag->getDeclName() || Tag->getTypedefForAnonDecl()) { + if (Tag->isInAnonymousNamespace()) + return UniqueExternalLinkage; + + // If this is a class template specialization, consider the + // linkage of the template and template arguments. + if (const ClassTemplateSpecializationDecl *Spec + = dyn_cast(Tag)) { + const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs(); + Linkage L = getLinkageForTemplateArgumentList( + TemplateArgs.getFlatArgumentList(), + TemplateArgs.flat_size()); + return minLinkage(L, Spec->getSpecializedTemplate()->getLinkage()); + } + + return ExternalLinkage; + } // - an enumerator belonging to an enumeration with external linkage; - if (isa(D)) - if (cast(D->getDeclContext())->getLinkage() - == NamedDecl::ExternalLinkage) - return NamedDecl::ExternalLinkage; + if (isa(D)) { + Linkage L = cast(D->getDeclContext())->getLinkage(); + if (isExternalLinkage(L)) + return L; + } // - a template, unless it is a function template that has // internal linkage (Clause 14); - if (isa(D)) - return NamedDecl::ExternalLinkage; + if (const TemplateDecl *Template = dyn_cast(D)) { + if (D->isInAnonymousNamespace()) + return UniqueExternalLinkage; + + return getLinkageForTemplateParameterList( + Template->getTemplateParameters()); + } // - a namespace (7.3), unless it is declared within an unnamed // namespace. if (isa(D) && !D->isInAnonymousNamespace()) - return NamedDecl::ExternalLinkage; + return ExternalLinkage; - return NamedDecl::NoLinkage; + return NoLinkage; } -NamedDecl::Linkage NamedDecl::getLinkage() const { +Linkage NamedDecl::getLinkage() const { // Handle linkage for namespace-scope names. if (getDeclContext()->getLookupContext()->isFileContext()) if (Linkage L = getLinkageForNamespaceScopeDecl(this)) @@ -403,9 +301,11 @@ NamedDecl::Linkage NamedDecl::getLinkage() const { if (getDeclContext()->isRecord() && (isa(this) || isa(this) || (isa(this) && - (getDeclName() || cast(this)->getTypedefForAnonDecl()))) && - cast(getDeclContext())->getLinkage() == ExternalLinkage) - return ExternalLinkage; + (getDeclName() || cast(this)->getTypedefForAnonDecl())))) { + Linkage L = cast(getDeclContext())->getLinkage(); + if (isExternalLinkage(L)) + return L; + } // C++ [basic.link]p6: // The name of a function declared in block scope and the name of @@ -424,6 +324,9 @@ NamedDecl::Linkage NamedDecl::getLinkage() const { if (Linkage L = Function->getPreviousDeclaration()->getLinkage()) return L; + if (Function->isInAnonymousNamespace()) + return UniqueExternalLinkage; + return ExternalLinkage; } @@ -434,6 +337,9 @@ NamedDecl::Linkage NamedDecl::getLinkage() const { if (Linkage L = Var->getPreviousDeclaration()->getLinkage()) return L; + if (Var->isInAnonymousNamespace()) + return UniqueExternalLinkage; + return ExternalLinkage; } } @@ -441,7 +347,7 @@ NamedDecl::Linkage NamedDecl::getLinkage() const { // C++ [basic.link]p6: // Names not covered by these rules have no linkage. return NoLinkage; -} + } std::string NamedDecl::getQualifiedNameAsString() const { return getQualifiedNameAsString(getASTContext().getLangOptions()); @@ -606,6 +512,20 @@ SourceLocation DeclaratorDecl::getTypeSpecStartLoc() const { // VarDecl Implementation //===----------------------------------------------------------------------===// +const char *VarDecl::getStorageClassSpecifierString(StorageClass SC) { + switch (SC) { + case VarDecl::None: break; + case VarDecl::Auto: return "auto"; break; + case VarDecl::Extern: return "extern"; break; + case VarDecl::PrivateExtern: return "__private_extern__"; break; + case VarDecl::Register: return "register"; break; + case VarDecl::Static: return "static"; break; + } + + assert(0 && "Invalid storage class"); + return 0; +} + VarDecl *VarDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, StorageClass S) { @@ -638,6 +558,127 @@ SourceRange VarDecl::getSourceRange() const { return SourceRange(Start, getLocation()); } +bool VarDecl::isExternC() const { + ASTContext &Context = getASTContext(); + if (!Context.getLangOptions().CPlusPlus) + return (getDeclContext()->isTranslationUnit() && + getStorageClass() != Static) || + (getDeclContext()->isFunctionOrMethod() && hasExternalStorage()); + + for (const DeclContext *DC = getDeclContext(); !DC->isTranslationUnit(); + DC = DC->getParent()) { + if (const LinkageSpecDecl *Linkage = dyn_cast(DC)) { + if (Linkage->getLanguage() == LinkageSpecDecl::lang_c) + return getStorageClass() != Static; + + break; + } + + if (DC->isFunctionOrMethod()) + return false; + } + + return false; +} + +VarDecl *VarDecl::getCanonicalDecl() { + return getFirstDeclaration(); +} + +VarDecl::DefinitionKind VarDecl::isThisDeclarationADefinition() const { + // C++ [basic.def]p2: + // A declaration is a definition unless [...] it contains the 'extern' + // specifier or a linkage-specification and neither an initializer [...], + // it declares a static data member in a class declaration [...]. + // C++ [temp.expl.spec]p15: + // An explicit specialization of a static data member of a template is a + // definition if the declaration includes an initializer; otherwise, it is + // a declaration. + if (isStaticDataMember()) { + if (isOutOfLine() && (hasInit() || + getTemplateSpecializationKind() != TSK_ExplicitSpecialization)) + return Definition; + else + return DeclarationOnly; + } + // C99 6.7p5: + // A definition of an identifier is a declaration for that identifier that + // [...] causes storage to be reserved for that object. + // Note: that applies for all non-file-scope objects. + // C99 6.9.2p1: + // If the declaration of an identifier for an object has file scope and an + // initializer, the declaration is an external definition for the identifier + if (hasInit()) + return Definition; + // AST for 'extern "C" int foo;' is annotated with 'extern'. + if (hasExternalStorage()) + return DeclarationOnly; + + // C99 6.9.2p2: + // A declaration of an object that has file scope without an initializer, + // and without a storage class specifier or the scs 'static', constitutes + // a tentative definition. + // No such thing in C++. + if (!getASTContext().getLangOptions().CPlusPlus && isFileVarDecl()) + return TentativeDefinition; + + // What's left is (in C, block-scope) declarations without initializers or + // external storage. These are definitions. + return Definition; +} + +VarDecl *VarDecl::getActingDefinition() { + DefinitionKind Kind = isThisDeclarationADefinition(); + if (Kind != TentativeDefinition) + return 0; + + VarDecl *LastTentative = false; + VarDecl *First = getFirstDeclaration(); + for (redecl_iterator I = First->redecls_begin(), E = First->redecls_end(); + I != E; ++I) { + Kind = (*I)->isThisDeclarationADefinition(); + if (Kind == Definition) + return 0; + else if (Kind == TentativeDefinition) + LastTentative = *I; + } + return LastTentative; +} + +bool VarDecl::isTentativeDefinitionNow() const { + DefinitionKind Kind = isThisDeclarationADefinition(); + if (Kind != TentativeDefinition) + return false; + + for (redecl_iterator I = redecls_begin(), E = redecls_end(); I != E; ++I) { + if ((*I)->isThisDeclarationADefinition() == Definition) + return false; + } + return true; +} + +VarDecl *VarDecl::getDefinition() { + VarDecl *First = getFirstDeclaration(); + for (redecl_iterator I = First->redecls_begin(), E = First->redecls_end(); + I != E; ++I) { + if ((*I)->isThisDeclarationADefinition() == Definition) + return *I; + } + return 0; +} + +const Expr *VarDecl::getAnyInitializer(const VarDecl *&D) const { + redecl_iterator I = redecls_begin(), E = redecls_end(); + while (I != E && !I->getInit()) + ++I; + + if (I != E) { + D = *I; + return I->getInit(); + } + return 0; +} + bool VarDecl::isOutOfLine() const { if (!isStaticDataMember()) return false; @@ -667,6 +708,15 @@ VarDecl *VarDecl::getOutOfLineDefinition() { return 0; } +void VarDecl::setInit(Expr *I) { + if (EvaluatedStmt *Eval = Init.dyn_cast()) { + Eval->~EvaluatedStmt(); + getASTContext().Deallocate(Eval); + } + + Init = I; +} + VarDecl *VarDecl::getInstantiatedFromStaticDataMember() const { if (MemberSpecializationInfo *MSI = getMemberSpecializationInfo()) return cast(MSI->getInstantiatedFrom()); @@ -675,8 +725,7 @@ VarDecl *VarDecl::getInstantiatedFromStaticDataMember() const { } TemplateSpecializationKind VarDecl::getTemplateSpecializationKind() const { - if (MemberSpecializationInfo *MSI - = getASTContext().getInstantiatedFromStaticDataMember(this)) + if (MemberSpecializationInfo *MSI = getMemberSpecializationInfo()) return MSI->getTemplateSpecializationKind(); return TSK_Undeclared; @@ -697,29 +746,53 @@ void VarDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK, MSI->setPointOfInstantiation(PointOfInstantiation); } -bool VarDecl::isTentativeDefinition(ASTContext &Context) const { - if (!isFileVarDecl() || Context.getLangOptions().CPlusPlus) - return false; +//===----------------------------------------------------------------------===// +// ParmVarDecl Implementation +//===----------------------------------------------------------------------===// - const VarDecl *Def = 0; - return (!getDefinition(Def) && - (getStorageClass() == None || getStorageClass() == Static)); +ParmVarDecl *ParmVarDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation L, IdentifierInfo *Id, + QualType T, TypeSourceInfo *TInfo, + StorageClass S, Expr *DefArg) { + return new (C) ParmVarDecl(ParmVar, DC, L, Id, T, TInfo, S, DefArg); } -const Expr *VarDecl::getDefinition(const VarDecl *&Def) const { - redecl_iterator I = redecls_begin(), E = redecls_end(); - while (I != E && !I->getInit()) - ++I; +Expr *ParmVarDecl::getDefaultArg() { + assert(!hasUnparsedDefaultArg() && "Default argument is not yet parsed!"); + assert(!hasUninstantiatedDefaultArg() && + "Default argument is not yet instantiated!"); + + Expr *Arg = getInit(); + if (CXXExprWithTemporaries *E = dyn_cast_or_null(Arg)) + return E->getSubExpr(); + + return Arg; +} + +unsigned ParmVarDecl::getNumDefaultArgTemporaries() const { + if (const CXXExprWithTemporaries *E = + dyn_cast(getInit())) + return E->getNumTemporaries(); - if (I != E) { - Def = *I; - return I->getInit(); - } return 0; } -VarDecl *VarDecl::getCanonicalDecl() { - return getFirstDeclaration(); +CXXTemporary *ParmVarDecl::getDefaultArgTemporary(unsigned i) { + assert(getNumDefaultArgTemporaries() && + "Default arguments does not have any temporaries!"); + + CXXExprWithTemporaries *E = cast(getInit()); + return E->getTemporary(i); +} + +SourceRange ParmVarDecl::getDefaultArgRange() const { + if (const Expr *E = getInit()) + return E->getSourceRange(); + + if (hasUninstantiatedDefaultArg()) + return getUninstantiatedDefaultArg()->getSourceRange(); + + return SourceRange(); } //===----------------------------------------------------------------------===// @@ -826,6 +899,26 @@ bool FunctionDecl::isGlobal() const { return true; } +void +FunctionDecl::setPreviousDeclaration(FunctionDecl *PrevDecl) { + redeclarable_base::setPreviousDeclaration(PrevDecl); + + if (FunctionTemplateDecl *FunTmpl = getDescribedFunctionTemplate()) { + FunctionTemplateDecl *PrevFunTmpl + = PrevDecl? PrevDecl->getDescribedFunctionTemplate() : 0; + assert((!PrevDecl || PrevFunTmpl) && "Function/function template mismatch"); + FunTmpl->setPreviousDeclaration(PrevFunTmpl); + } +} + +const FunctionDecl *FunctionDecl::getCanonicalDecl() const { + return getFirstDeclaration(); +} + +FunctionDecl *FunctionDecl::getCanonicalDecl() { + return getFirstDeclaration(); +} + /// \brief Returns a value indicating whether this function /// corresponds to a builtin function. /// @@ -882,14 +975,13 @@ unsigned FunctionDecl::getNumParams() const { } -void FunctionDecl::setParams(ASTContext& C, ParmVarDecl **NewParamInfo, - unsigned NumParams) { +void FunctionDecl::setParams(ParmVarDecl **NewParamInfo, unsigned NumParams) { assert(ParamInfo == 0 && "Already has param info!"); assert(NumParams == getNumParams() && "Parameter count mismatch!"); // Zero params -> null pointer. if (NumParams) { - void *Mem = C.Allocate(sizeof(ParmVarDecl*)*NumParams); + void *Mem = getASTContext().Allocate(sizeof(ParmVarDecl*)*NumParams); ParamInfo = new (Mem) ParmVarDecl*[NumParams]; memcpy(ParamInfo, NewParamInfo, sizeof(ParmVarDecl*)*NumParams); @@ -1014,26 +1106,6 @@ bool FunctionDecl::isInlineDefinitionExternallyVisible() const { return false; } -void -FunctionDecl::setPreviousDeclaration(FunctionDecl *PrevDecl) { - redeclarable_base::setPreviousDeclaration(PrevDecl); - - if (FunctionTemplateDecl *FunTmpl = getDescribedFunctionTemplate()) { - FunctionTemplateDecl *PrevFunTmpl - = PrevDecl? PrevDecl->getDescribedFunctionTemplate() : 0; - assert((!PrevDecl || PrevFunTmpl) && "Function/function template mismatch"); - FunTmpl->setPreviousDeclaration(PrevFunTmpl); - } -} - -const FunctionDecl *FunctionDecl::getCanonicalDecl() const { - return getFirstDeclaration(); -} - -FunctionDecl *FunctionDecl::getCanonicalDecl() { - return getFirstDeclaration(); -} - /// getOverloadedOperator - Which C++ overloaded operator this /// function represents, if any. OverloadedOperatorKind FunctionDecl::getOverloadedOperator() const { @@ -1146,8 +1218,7 @@ FunctionDecl::getTemplateSpecializationArgs() const { } void -FunctionDecl::setFunctionTemplateSpecialization(ASTContext &Context, - FunctionTemplateDecl *Template, +FunctionDecl::setFunctionTemplateSpecialization(FunctionTemplateDecl *Template, const TemplateArgumentList *TemplateArgs, void *InsertPos, TemplateSpecializationKind TSK) { @@ -1156,7 +1227,7 @@ FunctionDecl::setFunctionTemplateSpecialization(ASTContext &Context, FunctionTemplateSpecializationInfo *Info = TemplateOrSpecialization.dyn_cast(); if (!Info) - Info = new (Context) FunctionTemplateSpecializationInfo; + Info = new (getASTContext()) FunctionTemplateSpecializationInfo; Info->Function = this; Info->Template.setPointer(Template); @@ -1253,6 +1324,26 @@ bool FunctionDecl::isOutOfLine() const { return false; } +//===----------------------------------------------------------------------===// +// FieldDecl Implementation +//===----------------------------------------------------------------------===// + +FieldDecl *FieldDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, + IdentifierInfo *Id, QualType T, + TypeSourceInfo *TInfo, Expr *BW, bool Mutable) { + return new (C) FieldDecl(Decl::Field, DC, L, Id, T, TInfo, BW, Mutable); +} + +bool FieldDecl::isAnonymousStructOrUnion() const { + if (!isImplicit() || getDeclName()) + return false; + + if (const RecordType *Record = getType()->getAs()) + return Record->getDecl()->isAnonymousStructOrUnion(); + + return false; +} + //===----------------------------------------------------------------------===// // TagDecl Implementation //===----------------------------------------------------------------------===// @@ -1271,9 +1362,23 @@ void TagDecl::startDefinition() { TagT->decl.setPointer(this); TagT->decl.setInt(1); } + + if (isa(this)) { + CXXRecordDecl *D = cast(this); + struct CXXRecordDecl::DefinitionData *Data = + new (getASTContext()) struct CXXRecordDecl::DefinitionData(D); + do { + D->DefinitionData = Data; + D = cast_or_null(D->getPreviousDeclaration()); + } while (D); + } } void TagDecl::completeDefinition() { + assert((!isa(this) || + cast(this)->hasDefinition()) && + "definition completed but not started"); + IsDefinition = true; if (TagType *TagT = const_cast(TypeForDecl->getAs())) { assert(TagT->decl.getPointer() == this && @@ -1282,7 +1387,7 @@ void TagDecl::completeDefinition() { } } -TagDecl* TagDecl::getDefinition(ASTContext& C) const { +TagDecl* TagDecl::getDefinition() const { if (isDefinition()) return const_cast(this); @@ -1304,6 +1409,30 @@ TagDecl::TagKind TagDecl::getTagKindForTypeSpec(unsigned TypeSpec) { } } +//===----------------------------------------------------------------------===// +// EnumDecl Implementation +//===----------------------------------------------------------------------===// + +EnumDecl *EnumDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, + IdentifierInfo *Id, SourceLocation TKL, + EnumDecl *PrevDecl) { + EnumDecl *Enum = new (C) EnumDecl(DC, L, Id, PrevDecl, TKL); + C.getTypeDeclType(Enum, PrevDecl); + return Enum; +} + +void EnumDecl::Destroy(ASTContext& C) { + Decl::Destroy(C); +} + +void EnumDecl::completeDefinition(QualType NewType, + QualType NewPromotionType) { + assert(!isDefinition() && "Cannot redefine enums!"); + IntegerType = NewType; + PromotionType = NewPromotionType; + TagDecl::completeDefinition(); +} + //===----------------------------------------------------------------------===// // RecordDecl Implementation //===----------------------------------------------------------------------===// @@ -1341,7 +1470,7 @@ bool RecordDecl::isInjectedClassName() const { /// completeDefinition - Notes that the definition of this type is now /// complete. -void RecordDecl::completeDefinition(ASTContext& C) { +void RecordDecl::completeDefinition() { assert(!isDefinition() && "Cannot redefine record!"); TagDecl::completeDefinition(); } @@ -1364,14 +1493,14 @@ void BlockDecl::Destroy(ASTContext& C) { Decl::Destroy(C); } -void BlockDecl::setParams(ASTContext& C, ParmVarDecl **NewParamInfo, +void BlockDecl::setParams(ParmVarDecl **NewParamInfo, unsigned NParms) { assert(ParamInfo == 0 && "Already has param info!"); // Zero params -> null pointer. if (NParms) { NumParams = NParms; - void *Mem = C.Allocate(sizeof(ParmVarDecl*)*NumParams); + void *Mem = getASTContext().Allocate(sizeof(ParmVarDecl*)*NumParams); ParamInfo = new (Mem) ParmVarDecl*[NumParams]; memcpy(ParamInfo, NewParamInfo, sizeof(ParmVarDecl*)*NumParams); } @@ -1380,3 +1509,74 @@ void BlockDecl::setParams(ASTContext& C, ParmVarDecl **NewParamInfo, unsigned BlockDecl::getNumParams() const { return NumParams; } + + +//===----------------------------------------------------------------------===// +// Other Decl Allocation/Deallocation Method Implementations +//===----------------------------------------------------------------------===// + +TranslationUnitDecl *TranslationUnitDecl::Create(ASTContext &C) { + return new (C) TranslationUnitDecl(C); +} + +NamespaceDecl *NamespaceDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation L, IdentifierInfo *Id) { + return new (C) NamespaceDecl(DC, L, Id); +} + +void NamespaceDecl::Destroy(ASTContext& C) { + // NamespaceDecl uses "NextDeclarator" to chain namespace declarations + // together. They are all top-level Decls. + + this->~NamespaceDecl(); + C.Deallocate((void *)this); +} + + +ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation L, IdentifierInfo *Id, QualType T) { + return new (C) ImplicitParamDecl(ImplicitParam, DC, L, Id, T); +} + +FunctionDecl *FunctionDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation L, + DeclarationName N, QualType T, + TypeSourceInfo *TInfo, + StorageClass S, bool isInline, + bool hasWrittenPrototype) { + FunctionDecl *New + = new (C) FunctionDecl(Function, DC, L, N, T, TInfo, S, isInline); + New->HasWrittenPrototype = hasWrittenPrototype; + return New; +} + +BlockDecl *BlockDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) { + return new (C) BlockDecl(DC, L); +} + +EnumConstantDecl *EnumConstantDecl::Create(ASTContext &C, EnumDecl *CD, + SourceLocation L, + IdentifierInfo *Id, QualType T, + Expr *E, const llvm::APSInt &V) { + return new (C) EnumConstantDecl(CD, L, Id, T, E, V); +} + +void EnumConstantDecl::Destroy(ASTContext& C) { + if (Init) Init->Destroy(C); + Decl::Destroy(C); +} + +TypedefDecl *TypedefDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation L, IdentifierInfo *Id, + TypeSourceInfo *TInfo) { + return new (C) TypedefDecl(DC, L, Id, TInfo); +} + +// Anchor TypedefDecl's vtable here. +TypedefDecl::~TypedefDecl() {} + +FileScopeAsmDecl *FileScopeAsmDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation L, + StringLiteral *Str) { + return new (C) FileScopeAsmDecl(DC, L, Str); +} diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 84aa81ca76db..863a1cbd03c4 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -448,7 +448,10 @@ bool DeclContext::classof(const Decl *D) { } DeclContext::~DeclContext() { - delete static_cast(LookupPtr); + // FIXME: Currently ~ASTContext will delete the StoredDeclsMaps because + // ~DeclContext() is not guaranteed to be called when ASTContext uses + // a BumpPtrAllocator. + // delete static_cast(LookupPtr); } void DeclContext::DestroyDecls(ASTContext &C) { @@ -622,7 +625,8 @@ DeclContext::LoadVisibleDeclsFromExternalStorage() const { // Load the declaration IDs for all of the names visible in this // context. assert(!LookupPtr && "Have a lookup map before de-serialization?"); - StoredDeclsMap *Map = new StoredDeclsMap; + StoredDeclsMap *Map = + (StoredDeclsMap*) getParentASTContext().CreateStoredDeclsMap(); LookupPtr = Map; for (unsigned I = 0, N = Decls.size(); I != N; ++I) { (*Map)[Decls[I].Name].setFromDeclIDs(Decls[I].Declarations); @@ -830,8 +834,11 @@ void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D) { if (isa(D)) return; - if (!LookupPtr) - LookupPtr = new StoredDeclsMap; + ASTContext *C = 0; + if (!LookupPtr) { + C = &getParentASTContext(); + LookupPtr = (StoredDeclsMap*) C->CreateStoredDeclsMap(); + } // Insert this declaration into the map. StoredDeclsMap &Map = *static_cast(LookupPtr); @@ -844,7 +851,10 @@ void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D) { // If it is possible that this is a redeclaration, check to see if there is // already a decl for which declarationReplaces returns true. If there is // one, just replace it and return. - if (DeclNameEntries.HandleRedeclaration(getParentASTContext(), D)) + if (!C) + C = &getParentASTContext(); + + if (DeclNameEntries.HandleRedeclaration(*C, D)) return; // Put this declaration into the appropriate slot. @@ -896,3 +906,18 @@ void StoredDeclsList::materializeDecls(ASTContext &Context) { } } } + +//===----------------------------------------------------------------------===// +// Creation and Destruction of StoredDeclsMaps. // +//===----------------------------------------------------------------------===// + +void *ASTContext::CreateStoredDeclsMap() { + StoredDeclsMap *M = new StoredDeclsMap(); + SDMs.push_back(M); + return M; +} + +void ASTContext::ReleaseDeclContextMaps() { + for (std::vector::iterator I = SDMs.begin(), E = SDMs.end(); I!=E; ++I) + delete (StoredDeclsMap*) *I; +} diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index fe6064df325a..b0569d68015f 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -25,18 +25,23 @@ using namespace clang; // Decl Allocation/Deallocation Method Implementations //===----------------------------------------------------------------------===// -CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC, - SourceLocation L, IdentifierInfo *Id, - CXXRecordDecl *PrevDecl, - SourceLocation TKL) - : RecordDecl(K, TK, DC, L, Id, PrevDecl, TKL), - UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false), +CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) + : UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false), UserDeclaredCopyAssignment(false), UserDeclaredDestructor(false), Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false), Abstract(false), HasTrivialConstructor(true), HasTrivialCopyConstructor(true), HasTrivialCopyAssignment(true), HasTrivialDestructor(true), ComputedVisibleConversions(false), Bases(0), NumBases(0), VBases(0), NumVBases(0), + Definition(D) { +} + +CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC, + SourceLocation L, IdentifierInfo *Id, + CXXRecordDecl *PrevDecl, + SourceLocation TKL) + : RecordDecl(K, TK, DC, L, Id, PrevDecl, TKL), + DefinitionData(PrevDecl ? PrevDecl->DefinitionData : 0), TemplateOrInstantiation() { } CXXRecordDecl *CXXRecordDecl::Create(ASTContext &C, TagKind TK, DeclContext *DC, @@ -57,31 +62,35 @@ CXXRecordDecl::~CXXRecordDecl() { } void CXXRecordDecl::Destroy(ASTContext &C) { - C.Deallocate(Bases); - C.Deallocate(VBases); + if (data().Definition == this) { + C.Deallocate(data().Bases); + C.Deallocate(data().VBases); + C.Deallocate(&data()); + } this->RecordDecl::Destroy(C); } void -CXXRecordDecl::setBases(ASTContext &C, - CXXBaseSpecifier const * const *Bases, +CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases) { + ASTContext &C = getASTContext(); + // C++ [dcl.init.aggr]p1: // An aggregate is an array or a class (clause 9) with [...] // no base classes [...]. - Aggregate = false; + data().Aggregate = false; - if (this->Bases) - C.Deallocate(this->Bases); + if (data().Bases) + C.Deallocate(data().Bases); int vbaseCount = 0; llvm::SmallVector UniqueVbases; bool hasDirectVirtualBase = false; - this->Bases = new(C) CXXBaseSpecifier [NumBases]; - this->NumBases = NumBases; + data().Bases = new(C) CXXBaseSpecifier [NumBases]; + data().NumBases = NumBases; for (unsigned i = 0; i < NumBases; ++i) { - this->Bases[i] = *Bases[i]; + data().Bases[i] = *Bases[i]; // Keep track of inherited vbases for this base class. const CXXBaseSpecifier *Base = Bases[i]; QualType BaseType = Base->getType(); @@ -130,13 +139,13 @@ CXXRecordDecl::setBases(ASTContext &C, } if (vbaseCount > 0) { // build AST for inhireted, direct or indirect, virtual bases. - this->VBases = new (C) CXXBaseSpecifier [vbaseCount]; - this->NumVBases = vbaseCount; + data().VBases = new (C) CXXBaseSpecifier [vbaseCount]; + data().NumVBases = vbaseCount; for (int i = 0; i < vbaseCount; i++) { QualType QT = UniqueVbases[i]->getType(); CXXRecordDecl *VBaseClassDecl = cast(QT->getAs()->getDecl()); - this->VBases[i] = + data().VBases[i] = CXXBaseSpecifier(VBaseClassDecl->getSourceRange(), true, VBaseClassDecl->getTagKind() == RecordDecl::TK_class, UniqueVbases[i]->getAccessSpecifier(), QT); @@ -239,32 +248,32 @@ CXXRecordDecl::addedConstructor(ASTContext &Context, CXXConstructorDecl *ConDecl) { assert(!ConDecl->isImplicit() && "addedConstructor - not for implicit decl"); // Note that we have a user-declared constructor. - UserDeclaredConstructor = true; + data().UserDeclaredConstructor = true; // C++ [dcl.init.aggr]p1: // An aggregate is an array or a class (clause 9) with no // user-declared constructors (12.1) [...]. - Aggregate = false; + data().Aggregate = false; // C++ [class]p4: // A POD-struct is an aggregate class [...] - PlainOldData = false; + data().PlainOldData = false; // C++ [class.ctor]p5: // A constructor is trivial if it is an implicitly-declared default // constructor. // FIXME: C++0x: don't do this for "= default" default constructors. - HasTrivialConstructor = false; + data().HasTrivialConstructor = false; // Note when we have a user-declared copy constructor, which will // suppress the implicit declaration of a copy constructor. if (ConDecl->isCopyConstructor()) { - UserDeclaredCopyConstructor = true; + data().UserDeclaredCopyConstructor = true; // C++ [class.copy]p6: // A copy constructor is trivial if it is implicitly declared. // FIXME: C++0x: don't do this for "= default" copy constructors. - HasTrivialCopyConstructor = false; + data().HasTrivialCopyConstructor = false; } } @@ -295,17 +304,17 @@ void CXXRecordDecl::addedAssignmentOperator(ASTContext &Context, OpDecl->setCopyAssignment(true); // Suppress the implicit declaration of a copy constructor. - UserDeclaredCopyAssignment = true; + data().UserDeclaredCopyAssignment = true; // C++ [class.copy]p11: // A copy assignment operator is trivial if it is implicitly declared. // FIXME: C++0x: don't do this for "= default" copy operators. - HasTrivialCopyAssignment = false; + data().HasTrivialCopyAssignment = false; // C++ [class]p4: // A POD-struct is an aggregate class that [...] has no user-defined copy // assignment operator [...]. - PlainOldData = false; + data().PlainOldData = false; } void @@ -415,42 +424,42 @@ CXXRecordDecl::getNestedVisibleConversionFunctions(CXXRecordDecl *RD, const UnresolvedSetImpl *CXXRecordDecl::getVisibleConversionFunctions() { // If root class, all conversions are visible. if (bases_begin() == bases_end()) - return &Conversions; + return &data().Conversions; // If visible conversion list is already evaluated, return it. - if (ComputedVisibleConversions) - return &VisibleConversions; + if (data().ComputedVisibleConversions) + return &data().VisibleConversions; llvm::SmallPtrSet TopConversionsTypeSet; collectConversionFunctions(TopConversionsTypeSet); getNestedVisibleConversionFunctions(this, TopConversionsTypeSet, TopConversionsTypeSet); - ComputedVisibleConversions = true; - return &VisibleConversions; + data().ComputedVisibleConversions = true; + return &data().VisibleConversions; } void CXXRecordDecl::addVisibleConversionFunction( CXXConversionDecl *ConvDecl) { assert(!ConvDecl->getDescribedFunctionTemplate() && "Conversion function templates should cast to FunctionTemplateDecl."); - VisibleConversions.addDecl(ConvDecl); + data().VisibleConversions.addDecl(ConvDecl); } void CXXRecordDecl::addVisibleConversionFunction( FunctionTemplateDecl *ConvDecl) { assert(isa(ConvDecl->getTemplatedDecl()) && "Function template is not a conversion function template"); - VisibleConversions.addDecl(ConvDecl); + data().VisibleConversions.addDecl(ConvDecl); } void CXXRecordDecl::addConversionFunction(CXXConversionDecl *ConvDecl) { assert(!ConvDecl->getDescribedFunctionTemplate() && "Conversion function templates should cast to FunctionTemplateDecl."); - Conversions.addDecl(ConvDecl); + data().Conversions.addDecl(ConvDecl); } void CXXRecordDecl::addConversionFunction(FunctionTemplateDecl *ConvDecl) { assert(isa(ConvDecl->getTemplatedDecl()) && "Function template is not a conversion function template"); - Conversions.addDecl(ConvDecl); + data().Conversions.addDecl(ConvDecl); } @@ -579,7 +588,8 @@ bool CXXMethodDecl::isUsualDeallocationFunction() const { // then this function is a usual deallocation function. ASTContext &Context = getASTContext(); if (getNumParams() != 2 || - !Context.hasSameType(getParamDecl(1)->getType(), Context.getSizeType())) + !Context.hasSameUnqualifiedType(getParamDecl(1)->getType(), + Context.getSizeType())) return false; // This function is a usual deallocation function if there are no @@ -604,7 +614,9 @@ static OverriddenMethodsMapTy *OverriddenMethods = 0; void CXXMethodDecl::addOverriddenMethod(const CXXMethodDecl *MD) { assert(MD->isCanonicalDecl() && "Method is not canonical!"); - + assert(!MD->getParent()->isDependentContext() && + "Can't add an overridden method to a class template!"); + // FIXME: The CXXMethodDecl dtor needs to remove and free the entry. if (!OverriddenMethods) @@ -671,42 +683,25 @@ bool CXXMethodDecl::hasInlineBody() const { CXXBaseOrMemberInitializer:: CXXBaseOrMemberInitializer(ASTContext &Context, - TypeSourceInfo *TInfo, CXXConstructorDecl *C, - SourceLocation L, - Expr **Args, unsigned NumArgs, - SourceLocation R) - : BaseOrMember(TInfo), Args(0), NumArgs(0), CtorOrAnonUnion(C), + TypeSourceInfo *TInfo, + SourceLocation L, Expr *Init, SourceLocation R) + : BaseOrMember(TInfo), Init(Init), AnonUnionMember(0), LParenLoc(L), RParenLoc(R) { - if (NumArgs > 0) { - this->NumArgs = NumArgs; - this->Args = new (Context) Stmt*[NumArgs]; - for (unsigned Idx = 0; Idx < NumArgs; ++Idx) - this->Args[Idx] = Args[Idx]; - } } CXXBaseOrMemberInitializer:: CXXBaseOrMemberInitializer(ASTContext &Context, FieldDecl *Member, SourceLocation MemberLoc, - CXXConstructorDecl *C, SourceLocation L, - Expr **Args, unsigned NumArgs, - SourceLocation R) - : BaseOrMember(Member), MemberLocation(MemberLoc), Args(0), NumArgs(0), - CtorOrAnonUnion(C), LParenLoc(L), RParenLoc(R) + SourceLocation L, Expr *Init, SourceLocation R) + : BaseOrMember(Member), MemberLocation(MemberLoc), Init(Init), + AnonUnionMember(0), LParenLoc(L), RParenLoc(R) { - if (NumArgs > 0) { - this->NumArgs = NumArgs; - this->Args = new (Context) Stmt*[NumArgs]; - for (unsigned Idx = 0; Idx < NumArgs; ++Idx) - this->Args[Idx] = Args[Idx]; - } } void CXXBaseOrMemberInitializer::Destroy(ASTContext &Context) { - for (unsigned I = 0; I != NumArgs; ++I) - Args[I]->Destroy(Context); - Context.Deallocate(Args); + if (Init) + Init->Destroy(Context); this->~CXXBaseOrMemberInitializer(); } diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index ffda505aaab1..131e098d0467 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -111,8 +111,9 @@ ObjCContainerDecl::FindPropertyDeclaration(IdentifierInfo *PropertyId) const { // Look through categories. for (ObjCCategoryDecl *Category = OID->getCategoryList(); Category; Category = Category->getNextClassCategory()) { - if (ObjCPropertyDecl *P = Category->FindPropertyDeclaration(PropertyId)) - return P; + if (!Category->IsClassExtension()) + if (ObjCPropertyDecl *P = Category->FindPropertyDeclaration(PropertyId)) + return P; } // Look through protocols. for (ObjCInterfaceDecl::protocol_iterator I = OID->protocol_begin(), @@ -124,10 +125,11 @@ ObjCContainerDecl::FindPropertyDeclaration(IdentifierInfo *PropertyId) const { return OID->getSuperClass()->FindPropertyDeclaration(PropertyId); } else if (const ObjCCategoryDecl *OCD = dyn_cast(this)) { // Look through protocols. - for (ObjCInterfaceDecl::protocol_iterator I = OCD->protocol_begin(), - E = OCD->protocol_end(); I != E; ++I) { - if (ObjCPropertyDecl *P = (*I)->FindPropertyDeclaration(PropertyId)) - return P; + if (!OCD->IsClassExtension()) + for (ObjCInterfaceDecl::protocol_iterator I = OCD->protocol_begin(), + E = OCD->protocol_end(); I != E; ++I) { + if (ObjCPropertyDecl *P = (*I)->FindPropertyDeclaration(PropertyId)) + return P; } } return 0; diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 32ac53d85be8..a625865ecdb3 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyPrinter.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -403,32 +404,51 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { CXXBaseOrMemberInitializer * BMInitializer = (*B); if (B != CDecl->init_begin()) Out << ", "; - bool hasArguments = (BMInitializer->arg_begin() != - BMInitializer->arg_end()); if (BMInitializer->isMemberInitializer()) { FieldDecl *FD = BMInitializer->getMember(); Out << FD->getNameAsString(); + } else { + Out << QualType(BMInitializer->getBaseClass(), 0).getAsString(); } - else // FIXME. skip dependent types for now. - if (const RecordType *RT = - BMInitializer->getBaseClass()->getAs()) { - const CXXRecordDecl *BaseDecl = - cast(RT->getDecl()); - Out << BaseDecl->getNameAsString(); - } - if (hasArguments) { - Out << "("; - for (CXXBaseOrMemberInitializer::const_arg_iterator BE = - BMInitializer->const_arg_begin(), - EE = BMInitializer->const_arg_end(); BE != EE; ++BE) { - if (BE != BMInitializer->const_arg_begin()) - Out<< ", "; - const Expr *Exp = (*BE); - Exp->printPretty(Out, Context, 0, Policy, Indentation); + + Out << "("; + if (!BMInitializer->getInit()) { + // Nothing to print + } else { + Expr *Init = BMInitializer->getInit(); + if (CXXExprWithTemporaries *Tmp + = dyn_cast(Init)) + Init = Tmp->getSubExpr(); + + Init = Init->IgnoreParens(); + + Expr *SimpleInit = 0; + Expr **Args = 0; + unsigned NumArgs = 0; + if (ParenListExpr *ParenList = dyn_cast(Init)) { + Args = ParenList->getExprs(); + NumArgs = ParenList->getNumExprs(); + } else if (CXXConstructExpr *Construct + = dyn_cast(Init)) { + Args = Construct->getArgs(); + NumArgs = Construct->getNumArgs(); + } else + SimpleInit = Init; + + if (SimpleInit) + SimpleInit->printPretty(Out, Context, 0, Policy, Indentation); + else { + for (unsigned I = 0; I != NumArgs; ++I) { + if (isa(Args[I])) + break; + + if (I) + Out << ", "; + Args[I]->printPretty(Out, Context, 0, Policy, Indentation); + } } - Out << ")"; - } else - Out << "()"; + } + Out << ")"; } } } diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index 75b3975322b2..d80db45f4557 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -213,7 +213,8 @@ QualType ClassTemplateDecl::getInjectedClassNameType(ASTContext &Context) { TemplateArgs.push_back(TemplateArgument(ParamType)); } else if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*Param)) { - Expr *E = new (Context) DeclRefExpr(NTTP, NTTP->getType(), + Expr *E = new (Context) DeclRefExpr(NTTP, + NTTP->getType().getNonReferenceType(), NTTP->getLocation()); TemplateArgs.push_back(TemplateArgument(E)); } else { diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp index ff810735bc81..19b58bcbaf03 100644 --- a/lib/AST/DeclarationName.cpp +++ b/lib/AST/DeclarationName.cpp @@ -66,27 +66,33 @@ public: } }; -bool operator<(DeclarationName LHS, DeclarationName RHS) { +static int compareInt(unsigned A, unsigned B) { + return (A < B ? -1 : (A > B ? 1 : 0)); +} + +int DeclarationName::compare(DeclarationName LHS, DeclarationName RHS) { if (LHS.getNameKind() != RHS.getNameKind()) - return LHS.getNameKind() < RHS.getNameKind(); + return (LHS.getNameKind() < RHS.getNameKind() ? -1 : 1); switch (LHS.getNameKind()) { - case DeclarationName::Identifier: - return LHS.getAsIdentifierInfo()->getName() < - RHS.getAsIdentifierInfo()->getName(); + case DeclarationName::Identifier: { + IdentifierInfo *LII = LHS.getAsIdentifierInfo(); + IdentifierInfo *RII = RHS.getAsIdentifierInfo(); + if (!LII) return RII ? -1 : 0; + if (!RII) return 1; + + return LII->getName().compare(RII->getName()); + } case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: { Selector LHSSelector = LHS.getObjCSelector(); Selector RHSSelector = RHS.getObjCSelector(); - for (unsigned I = 0, - N = std::min(LHSSelector.getNumArgs(), RHSSelector.getNumArgs()); - I != N; ++I) { + unsigned LN = LHSSelector.getNumArgs(), RN = RHSSelector.getNumArgs(); + for (unsigned I = 0, N = std::min(LN, RN); I != N; ++I) { IdentifierInfo *LHSId = LHSSelector.getIdentifierInfoForSlot(I); IdentifierInfo *RHSId = RHSSelector.getIdentifierInfoForSlot(I); - if (!LHSId || !RHSId) - return LHSId && !RHSId; switch (LHSId->getName().compare(RHSId->getName())) { case -1: return true; @@ -94,27 +100,32 @@ bool operator<(DeclarationName LHS, DeclarationName RHS) { default: break; } } - - return LHSSelector.getNumArgs() < RHSSelector.getNumArgs(); + + return compareInt(LN, RN); } case DeclarationName::CXXConstructorName: case DeclarationName::CXXDestructorName: case DeclarationName::CXXConversionFunctionName: - return QualTypeOrdering()(LHS.getCXXNameType(), RHS.getCXXNameType()); + if (QualTypeOrdering()(LHS.getCXXNameType(), RHS.getCXXNameType())) + return -1; + if (QualTypeOrdering()(RHS.getCXXNameType(), LHS.getCXXNameType())) + return 1; + return 0; case DeclarationName::CXXOperatorName: - return LHS.getCXXOverloadedOperator() < RHS.getCXXOverloadedOperator(); + return compareInt(LHS.getCXXOverloadedOperator(), + RHS.getCXXOverloadedOperator()); case DeclarationName::CXXLiteralOperatorName: - return LHS.getCXXLiteralIdentifier()->getName() < - RHS.getCXXLiteralIdentifier()->getName(); + return LHS.getCXXLiteralIdentifier()->getName().compare( + RHS.getCXXLiteralIdentifier()->getName()); case DeclarationName::CXXUsingDirective: - return false; + return 0; } - return false; + return 0; } } // end namespace clang diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index fa44b510e99a..4cb0aa4560de 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -99,8 +99,7 @@ void DeclRefExpr::computeDependence() { else if (VarDecl *Var = dyn_cast(D)) { if (Var->getType()->isIntegralType() && Var->getType().getCVRQualifiers() == Qualifiers::Const) { - const VarDecl *Def = 0; - if (const Expr *Init = Var->getDefinition(Def)) + if (const Expr *Init = Var->getAnyInitializer()) if (Init->isValueDependent()) ValueDependent = true; } @@ -164,17 +163,18 @@ SourceRange DeclRefExpr::getSourceRange() const { // FIXME: Maybe this should use DeclPrinter with a special "print predefined // expr" policy instead. -std::string PredefinedExpr::ComputeName(ASTContext &Context, IdentType IT, - const Decl *CurrentDecl) { +std::string PredefinedExpr::ComputeName(IdentType IT, const Decl *CurrentDecl) { + ASTContext &Context = CurrentDecl->getASTContext(); + if (const FunctionDecl *FD = dyn_cast(CurrentDecl)) { - if (IT != PrettyFunction) + if (IT != PrettyFunction && IT != PrettyFunctionNoVirtual) return FD->getNameAsString(); llvm::SmallString<256> Name; llvm::raw_svector_ostream Out(Name); if (const CXXMethodDecl *MD = dyn_cast(FD)) { - if (MD->isVirtual()) + if (MD->isVirtual() && IT != PrettyFunctionNoVirtual) Out << "virtual "; if (MD->isStatic()) Out << "static "; @@ -827,9 +827,17 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, case BinaryOperatorClass: { const BinaryOperator *BO = cast(this); // Consider comma to have side effects if the LHS or RHS does. - if (BO->getOpcode() == BinaryOperator::Comma) + if (BO->getOpcode() == BinaryOperator::Comma) { + // ((foo = ), 0) is an idiom for hiding the result (and + // lvalue-ness) of an assignment written in a macro. + if (IntegerLiteral *IE = + dyn_cast(BO->getRHS()->IgnoreParens())) + if (IE->getValue() == 0) + return false; + return (BO->getRHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx) || BO->getLHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx)); + } if (BO->isAssignmentOp()) return false; @@ -1068,9 +1076,7 @@ Expr::isLvalueResult Expr::isLvalueInternal(ASTContext &Ctx) const { if (isa(Member)) { if (m->isArrow()) return LV_Valid; - Expr *BaseExp = m->getBase(); - return (BaseExp->getStmtClass() == ObjCPropertyRefExprClass) ? - LV_SubObjCPropertySetting : BaseExp->isLvalue(Ctx); + return m->getBase()->isLvalue(Ctx); } // -- If it refers to a static member function [...], then @@ -1093,8 +1099,11 @@ Expr::isLvalueResult Expr::isLvalueInternal(ASTContext &Ctx) const { if (m->isArrow()) return LV_Valid; Expr *BaseExp = m->getBase(); - return (BaseExp->getStmtClass() == ObjCPropertyRefExprClass) ? - LV_SubObjCPropertySetting : BaseExp->isLvalue(Ctx); + if (BaseExp->getStmtClass() == ObjCPropertyRefExprClass) + return LV_SubObjCPropertySetting; + return + (BaseExp->getStmtClass() == ObjCImplicitSetterGetterRefExprClass) ? + LV_SubObjCPropertyGetterSetting : BaseExp->isLvalue(Ctx); } case UnaryOperatorClass: if (cast(this)->getOpcode() == UnaryOperator::Deref) @@ -1205,6 +1214,9 @@ Expr::isLvalueResult Expr::isLvalueInternal(ASTContext &Ctx) const { case CXXBindTemporaryExprClass: return cast(this)->getSubExpr()-> isLvalueInternal(Ctx); + case CXXBindReferenceExprClass: + // Something that's bound to a reference is always an lvalue. + return LV_Valid; case ConditionalOperatorClass: { // Complicated handling is only for C++. if (!Ctx.getLangOptions().CPlusPlus) @@ -1281,7 +1293,9 @@ Expr::isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc) const { } return MLV_InvalidExpression; case LV_MemberFunction: return MLV_MemberFunction; - case LV_SubObjCPropertySetting: return MLV_SubObjCPropertySetting; + case LV_SubObjCPropertySetting: return MLV_SubObjCPropertySetting; + case LV_SubObjCPropertyGetterSetting: + return MLV_SubObjCPropertyGetterSetting; } // The following is illegal: @@ -1594,6 +1608,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::DependentScopeDeclRefExprClass: case Expr::CXXConstructExprClass: case Expr::CXXBindTemporaryExprClass: + case Expr::CXXBindReferenceExprClass: case Expr::CXXExprWithTemporariesClass: case Expr::CXXTemporaryObjectExprClass: case Expr::CXXUnresolvedConstructExprClass: @@ -1613,7 +1628,6 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::BlockExprClass: case Expr::BlockDeclRefExprClass: case Expr::NoStmtClass: - case Expr::ExprClass: return ICEDiag(2, E->getLocStart()); case Expr::GNUNullExprClass: @@ -1650,30 +1664,22 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { if (Quals.hasVolatile() || !Quals.hasConst()) return ICEDiag(2, cast(E)->getLocation()); - // Look for the definition of this variable, which will actually have - // an initializer. - const VarDecl *Def = 0; - const Expr *Init = Dcl->getDefinition(Def); + // Look for a declaration of this variable that has an initializer. + const VarDecl *ID = 0; + const Expr *Init = Dcl->getAnyInitializer(ID); if (Init) { - if (Def->isInitKnownICE()) { + if (ID->isInitKnownICE()) { // We have already checked whether this subexpression is an // integral constant expression. - if (Def->isInitICE()) + if (ID->isInitICE()) return NoDiag(); else return ICEDiag(2, cast(E)->getLocation()); } - // C++ [class.static.data]p4: - // If a static data member is of const integral or const - // enumeration type, its declaration in the class definition can - // specify a constant-initializer which shall be an integral - // constant expression (5.19). In that case, the member can appear - // in integral constant expressions. - if (Def->isOutOfLine()) { - Dcl->setInitKnownICE(false); - return ICEDiag(2, cast(E)->getLocation()); - } + // It's an ICE whether or not the definition we found is + // out-of-line. See DR 721 and the discussion in Clang PR + // 6206 for details. if (Dcl->isCheckingICE()) { return ICEDiag(2, cast(E)->getLocation()); @@ -1810,9 +1816,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { } } } - case Expr::CastExprClass: case Expr::ImplicitCastExprClass: - case Expr::ExplicitCastExprClass: case Expr::CStyleCastExprClass: case Expr::CXXFunctionalCastExprClass: case Expr::CXXNamedCastExprClass: @@ -1954,6 +1958,13 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx, FieldDecl *Expr::getBitField() { Expr *E = this->IgnoreParens(); + while (ImplicitCastExpr *ICE = dyn_cast(E)) { + if (ICE->isLvalueCast() && ICE->getCastKind() == CastExpr::CK_NoOp) + E = ICE->getSubExpr()->IgnoreParens(); + else + break; + } + if (MemberExpr *MemRef = dyn_cast(E)) if (FieldDecl *Field = dyn_cast(MemRef->getMemberDecl())) if (Field->isBitField()) @@ -1966,6 +1977,25 @@ FieldDecl *Expr::getBitField() { return 0; } +bool Expr::refersToVectorElement() const { + const Expr *E = this->IgnoreParens(); + + while (const ImplicitCastExpr *ICE = dyn_cast(E)) { + if (ICE->isLvalueCast() && ICE->getCastKind() == CastExpr::CK_NoOp) + E = ICE->getSubExpr()->IgnoreParens(); + else + break; + } + + if (const ArraySubscriptExpr *ASE = dyn_cast(E)) + return ASE->getBase()->getType()->isVectorType(); + + if (isa(E)) + return true; + + return false; +} + /// isArrow - Return true if the base expression is a pointer to vector, /// return false if the base expression is a vector. bool ExtVectorElementExpr::isArrow() const { @@ -2030,14 +2060,15 @@ void ExtVectorElementExpr::getEncodedElementAccess( } // constructor for instance messages. -ObjCMessageExpr::ObjCMessageExpr(Expr *receiver, Selector selInfo, - QualType retType, ObjCMethodDecl *mproto, - SourceLocation LBrac, SourceLocation RBrac, - Expr **ArgExprs, unsigned nargs) +ObjCMessageExpr::ObjCMessageExpr(ASTContext &C, Expr *receiver, + Selector selInfo, + QualType retType, ObjCMethodDecl *mproto, + SourceLocation LBrac, SourceLocation RBrac, + Expr **ArgExprs, unsigned nargs) : Expr(ObjCMessageExprClass, retType, false, false), SelName(selInfo), MethodProto(mproto) { NumArgs = nargs; - SubExprs = new Stmt*[NumArgs+1]; + SubExprs = new (C) Stmt*[NumArgs+1]; SubExprs[RECEIVER] = receiver; if (NumArgs) { for (unsigned i = 0; i != NumArgs; ++i) @@ -2049,14 +2080,15 @@ ObjCMessageExpr::ObjCMessageExpr(Expr *receiver, Selector selInfo, // constructor for class messages. // FIXME: clsName should be typed to ObjCInterfaceType -ObjCMessageExpr::ObjCMessageExpr(IdentifierInfo *clsName, Selector selInfo, - QualType retType, ObjCMethodDecl *mproto, - SourceLocation LBrac, SourceLocation RBrac, - Expr **ArgExprs, unsigned nargs) +ObjCMessageExpr::ObjCMessageExpr(ASTContext &C, IdentifierInfo *clsName, + Selector selInfo, QualType retType, + ObjCMethodDecl *mproto, + SourceLocation LBrac, SourceLocation RBrac, + Expr **ArgExprs, unsigned nargs) : Expr(ObjCMessageExprClass, retType, false, false), SelName(selInfo), MethodProto(mproto) { NumArgs = nargs; - SubExprs = new Stmt*[NumArgs+1]; + SubExprs = new (C) Stmt*[NumArgs+1]; SubExprs[RECEIVER] = (Expr*) ((uintptr_t) clsName | IsClsMethDeclUnknown); if (NumArgs) { for (unsigned i = 0; i != NumArgs; ++i) @@ -2067,14 +2099,15 @@ ObjCMessageExpr::ObjCMessageExpr(IdentifierInfo *clsName, Selector selInfo, } // constructor for class messages. -ObjCMessageExpr::ObjCMessageExpr(ObjCInterfaceDecl *cls, Selector selInfo, - QualType retType, ObjCMethodDecl *mproto, - SourceLocation LBrac, SourceLocation RBrac, - Expr **ArgExprs, unsigned nargs) +ObjCMessageExpr::ObjCMessageExpr(ASTContext &C, ObjCInterfaceDecl *cls, + Selector selInfo, QualType retType, + ObjCMethodDecl *mproto, SourceLocation LBrac, + SourceLocation RBrac, Expr **ArgExprs, + unsigned nargs) : Expr(ObjCMessageExprClass, retType, false, false), SelName(selInfo), MethodProto(mproto) { NumArgs = nargs; - SubExprs = new Stmt*[NumArgs+1]; + SubExprs = new (C) Stmt*[NumArgs+1]; SubExprs[RECEIVER] = (Expr*) ((uintptr_t) cls | IsClsMethDeclKnown); if (NumArgs) { for (unsigned i = 0; i != NumArgs; ++i) @@ -2109,6 +2142,13 @@ void ObjCMessageExpr::setClassInfo(const ObjCMessageExpr::ClassInfo &CI) { SubExprs[RECEIVER] = (Expr*)((uintptr_t)CI.first | IsClsMethDeclKnown); } +void ObjCMessageExpr::DoDestroy(ASTContext &C) { + DestroyChildren(C); + if (SubExprs) + C.Deallocate(SubExprs); + this->~ObjCMessageExpr(); + C.Deallocate((void*) this); +} bool ChooseExpr::isConditionTrue(ASTContext &C) const { return getCond()->EvaluateAsInt(C) != 0; diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index a6574efda8ee..f4b8333dd3ae 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -73,7 +73,7 @@ Stmt::child_iterator CXXZeroInitValueExpr::child_end() { } // CXXNewExpr -CXXNewExpr::CXXNewExpr(bool globalNew, FunctionDecl *operatorNew, +CXXNewExpr::CXXNewExpr(ASTContext &C, bool globalNew, FunctionDecl *operatorNew, Expr **placementArgs, unsigned numPlaceArgs, bool parenTypeId, Expr *arraySize, CXXConstructorDecl *constructor, bool initializer, @@ -87,7 +87,7 @@ CXXNewExpr::CXXNewExpr(bool globalNew, FunctionDecl *operatorNew, OperatorDelete(operatorDelete), Constructor(constructor), StartLoc(startLoc), EndLoc(endLoc) { unsigned TotalSize = Array + NumPlacementArgs + NumConstructorArgs; - SubExprs = new Stmt*[TotalSize]; + SubExprs = new (C) Stmt*[TotalSize]; unsigned i = 0; if (Array) SubExprs[i++] = arraySize; @@ -98,6 +98,14 @@ CXXNewExpr::CXXNewExpr(bool globalNew, FunctionDecl *operatorNew, assert(i == TotalSize); } +void CXXNewExpr::DoDestroy(ASTContext &C) { + DestroyChildren(C); + if (SubExprs) + C.Deallocate(SubExprs); + this->~CXXNewExpr(); + C.Deallocate((void*)this); +} + Stmt::child_iterator CXXNewExpr::child_begin() { return &SubExprs[0]; } Stmt::child_iterator CXXNewExpr::child_end() { return &SubExprs[0] + Array + getNumPlacementArgs() + getNumConstructorArgs(); @@ -116,6 +124,7 @@ Stmt::child_iterator CXXPseudoDestructorExpr::child_end() { // UnresolvedLookupExpr UnresolvedLookupExpr * UnresolvedLookupExpr::Create(ASTContext &C, bool Dependent, + CXXRecordDecl *NamingClass, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, DeclarationName Name, SourceLocation NameLoc, bool ADL, @@ -125,7 +134,8 @@ UnresolvedLookupExpr::Create(ASTContext &C, bool Dependent, ExplicitTemplateArgumentList::sizeFor(Args)); UnresolvedLookupExpr *ULE = new (Mem) UnresolvedLookupExpr(Dependent ? C.DependentTy : C.OverloadTy, - Dependent, Qualifier, QualifierRange, + Dependent, NamingClass, + Qualifier, QualifierRange, Name, NameLoc, ADL, /*Overload*/ true, /*ExplicitTemplateArgs*/ true); @@ -135,10 +145,9 @@ UnresolvedLookupExpr::Create(ASTContext &C, bool Dependent, return ULE; } -bool UnresolvedLookupExpr:: - ComputeDependence(UnresolvedSetImpl::const_iterator Begin, - UnresolvedSetImpl::const_iterator End, - const TemplateArgumentListInfo *Args) { +bool OverloadExpr::ComputeDependence(UnresolvedSetIterator Begin, + UnresolvedSetIterator End, + const TemplateArgumentListInfo *Args) { for (UnresolvedSetImpl::const_iterator I = Begin; I != End; ++I) if ((*I)->getDeclContext()->isDependentContext()) return true; @@ -393,6 +402,19 @@ void CXXBindTemporaryExpr::DoDestroy(ASTContext &C) { C.Deallocate(this); } +CXXBindReferenceExpr *CXXBindReferenceExpr::Create(ASTContext &C, Expr *SubExpr, + bool ExtendsLifetime, + bool RequiresTemporaryCopy) { + return new (C) CXXBindReferenceExpr(SubExpr, + ExtendsLifetime, + RequiresTemporaryCopy); +} + +void CXXBindReferenceExpr::DoDestroy(ASTContext &C) { + this->~CXXBindReferenceExpr(); + C.Deallocate(this); +} + CXXTemporaryObjectExpr::CXXTemporaryObjectExpr(ASTContext &C, CXXConstructorDecl *Cons, QualType writtenTy, @@ -409,22 +431,26 @@ CXXConstructExpr *CXXConstructExpr::Create(ASTContext &C, QualType T, SourceLocation Loc, CXXConstructorDecl *D, bool Elidable, Expr **Args, unsigned NumArgs, - bool ZeroInitialization) { + bool ZeroInitialization, + bool BaseInitialization) { return new (C) CXXConstructExpr(C, CXXConstructExprClass, T, Loc, D, - Elidable, Args, NumArgs, ZeroInitialization); + Elidable, Args, NumArgs, ZeroInitialization, + BaseInitialization); } CXXConstructExpr::CXXConstructExpr(ASTContext &C, StmtClass SC, QualType T, SourceLocation Loc, CXXConstructorDecl *D, bool elidable, Expr **args, unsigned numargs, - bool ZeroInitialization) + bool ZeroInitialization, + bool BaseInitialization) : Expr(SC, T, T->isDependentType(), (T->isDependentType() || CallExpr::hasAnyValueDependentArguments(args, numargs))), Constructor(D), Loc(Loc), Elidable(elidable), - ZeroInitialization(ZeroInitialization), Args(0), NumArgs(numargs) + ZeroInitialization(ZeroInitialization), + BaseInitialization(BaseInitialization), Args(0), NumArgs(numargs) { if (NumArgs) { Args = new (C) Stmt*[NumArgs]; @@ -491,6 +517,15 @@ Stmt::child_iterator CXXBindTemporaryExpr::child_end() { return &SubExpr + 1; } +// CXXBindReferenceExpr +Stmt::child_iterator CXXBindReferenceExpr::child_begin() { + return &SubExpr; +} + +Stmt::child_iterator CXXBindReferenceExpr::child_end() { + return &SubExpr + 1; +} + // CXXConstructExpr Stmt::child_iterator CXXConstructExpr::child_begin() { return &Args[0]; @@ -618,15 +653,13 @@ UnresolvedMemberExpr::UnresolvedMemberExpr(QualType T, bool Dependent, DeclarationName MemberName, SourceLocation MemberLoc, const TemplateArgumentListInfo *TemplateArgs) - : Expr(UnresolvedMemberExprClass, T, Dependent, Dependent), - Base(Base), BaseType(BaseType), IsArrow(IsArrow), - HasUnresolvedUsing(HasUnresolvedUsing), - HasExplicitTemplateArgs(TemplateArgs != 0), - OperatorLoc(OperatorLoc), - Qualifier(Qualifier), QualifierRange(QualifierRange), - MemberName(MemberName), MemberLoc(MemberLoc) { + : OverloadExpr(UnresolvedMemberExprClass, T, Dependent, + Qualifier, QualifierRange, MemberName, MemberLoc, + TemplateArgs != 0), + IsArrow(IsArrow), HasUnresolvedUsing(HasUnresolvedUsing), + Base(Base), BaseType(BaseType), OperatorLoc(OperatorLoc) { if (TemplateArgs) - getExplicitTemplateArgs()->initializeFrom(*TemplateArgs); + getExplicitTemplateArgs().initializeFrom(*TemplateArgs); } UnresolvedMemberExpr * @@ -651,6 +684,35 @@ UnresolvedMemberExpr::Create(ASTContext &C, bool Dependent, Member, MemberLoc, TemplateArgs); } +CXXRecordDecl *UnresolvedMemberExpr::getNamingClass() const { + // Unlike for UnresolvedLookupExpr, it is very easy to re-derive this. + + // If there was a nested name specifier, it names the naming class. + // It can't be dependent: after all, we were actually able to do the + // lookup. + const RecordType *RT; + if (getQualifier()) { + Type *T = getQualifier()->getAsType(); + assert(T && "qualifier in member expression does not name type"); + RT = T->getAs(); + assert(RT && "qualifier in member expression does not name record"); + + // Otherwise the naming class must have been the base class. + } else { + QualType BaseType = getBaseType().getNonReferenceType(); + if (isArrow()) { + const PointerType *PT = BaseType->getAs(); + assert(PT && "base of arrow member access is not pointer"); + BaseType = PT->getPointeeType(); + } + + RT = BaseType->getAs(); + assert(RT && "base of member expression does not name record"); + } + + return cast(RT->getDecl()); +} + Stmt::child_iterator UnresolvedMemberExpr::child_begin() { return child_iterator(&Base); } diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 086249c811f9..1a44cd02d9c1 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -263,8 +263,7 @@ APValue LValueExprEvaluator::VisitDeclRefExpr(DeclRefExpr *E) { if (!VD->getType()->isReferenceType()) return APValue(E); // FIXME: Check whether VD might be overridden! - const VarDecl *Def = 0; - if (const Expr *Init = VD->getDefinition(Def)) + if (const Expr *Init = VD->getAnyInitializer()) return Visit(const_cast(Init)); } @@ -813,7 +812,7 @@ public: return false; } - bool VisitCallExpr(const CallExpr *E); + bool VisitCallExpr(CallExpr *E); bool VisitBinaryOperator(const BinaryOperator *E); bool VisitUnaryOperator(const UnaryOperator *E); bool VisitConditionalOperator(const ConditionalOperator *E); @@ -849,8 +848,8 @@ public: bool VisitUnaryImag(const UnaryOperator *E); private: - unsigned GetAlignOfExpr(const Expr *E); - unsigned GetAlignOfType(QualType T); + CharUnits GetAlignOfExpr(const Expr *E); + CharUnits GetAlignOfType(QualType T); // FIXME: Missing: array subscript of vector, member of vector }; } // end anonymous namespace @@ -879,9 +878,12 @@ bool IntExprEvaluator::CheckReferencedDecl(const Expr* E, const Decl* D) { // In C, they can also be folded, although they are not ICEs. if (Info.Ctx.getCanonicalType(E->getType()).getCVRQualifiers() == Qualifiers::Const) { + + if (isa(D)) + return Error(E->getLocStart(), diag::note_invalid_subexpr_in_ice, E); + if (const VarDecl *VD = dyn_cast(D)) { - const VarDecl *Def = 0; - if (const Expr *Init = VD->getDefinition(Def)) { + if (const Expr *Init = VD->getAnyInitializer()) { if (APValue *V = VD->getEvaluatedValue()) { if (V->isInt()) return Success(V->getInt(), E); @@ -965,7 +967,7 @@ static int EvaluateBuiltinClassifyType(const CallExpr *E) { return -1; } -bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { +bool IntExprEvaluator::VisitCallExpr(CallExpr *E) { switch (E->isBuiltinCall(Info.Ctx)) { default: return Error(E->getLocStart(), diag::note_invalid_subexpr_in_ice, E); @@ -1020,6 +1022,9 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { Operand = Info.Ctx.Target.getEHDataRegisterNumber(Operand); return Success(Operand, E); } + + case Builtin::BI__builtin_expect: + return Visit(E->getArg(0)); } } @@ -1288,7 +1293,7 @@ bool IntExprEvaluator::VisitConditionalOperator(const ConditionalOperator *E) { return Visit(Cond ? E->getTrueExpr() : E->getFalseExpr()); } -unsigned IntExprEvaluator::GetAlignOfType(QualType T) { +CharUnits IntExprEvaluator::GetAlignOfType(QualType T) { // C++ [expr.sizeof]p2: "When applied to a reference or a reference type, // the result is the size of the referenced type." // C++ [expr.alignof]p3: "When alignof is applied to a reference type, the @@ -1300,20 +1305,22 @@ unsigned IntExprEvaluator::GetAlignOfType(QualType T) { unsigned CharSize = Info.Ctx.Target.getCharWidth(); // __alignof is defined to return the preferred alignment. - return Info.Ctx.getPreferredTypeAlign(T.getTypePtr()) / CharSize; + return CharUnits::fromQuantity( + Info.Ctx.getPreferredTypeAlign(T.getTypePtr()) / CharSize); } -unsigned IntExprEvaluator::GetAlignOfExpr(const Expr *E) { +CharUnits IntExprEvaluator::GetAlignOfExpr(const Expr *E) { E = E->IgnoreParens(); // alignof decl is always accepted, even if it doesn't make sense: we default // to 1 in those cases. if (const DeclRefExpr *DRE = dyn_cast(E)) - return Info.Ctx.getDeclAlignInBytes(DRE->getDecl(), /*RefAsPointee*/true); + return Info.Ctx.getDeclAlign(DRE->getDecl(), + /*RefAsPointee*/true); if (const MemberExpr *ME = dyn_cast(E)) - return Info.Ctx.getDeclAlignInBytes(ME->getMemberDecl(), - /*RefAsPointee*/true); + return Info.Ctx.getDeclAlign(ME->getMemberDecl(), + /*RefAsPointee*/true); return GetAlignOfType(E->getType()); } @@ -1325,9 +1332,9 @@ bool IntExprEvaluator::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr *E) { // Handle alignof separately. if (!E->isSizeOf()) { if (E->isArgumentType()) - return Success(GetAlignOfType(E->getArgumentType()), E); + return Success(GetAlignOfType(E->getArgumentType()).getQuantity(), E); else - return Success(GetAlignOfExpr(E->getArgumentExpr()), E); + return Success(GetAlignOfExpr(E->getArgumentExpr()).getQuantity(), E); } QualType SrcTy = E->getTypeOfArgument(); diff --git a/lib/AST/Makefile b/lib/AST/Makefile index f7d4e9f62d5f..8afc629d6b3b 100644 --- a/lib/AST/Makefile +++ b/lib/AST/Makefile @@ -14,7 +14,6 @@ LEVEL = ../../../.. LIBRARYNAME := clangAST BUILD_ARCHIVE = 1 -CXXFLAGS = -fno-rtti CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index 0aa01801959c..50acd15fde05 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -119,11 +119,10 @@ ASTRecordLayoutBuilder::SelectPrimaryVBase(const CXXRecordDecl *RD, return; } } - if (i->isVirtual()) { - SelectPrimaryVBase(Base, FirstPrimary); - if (PrimaryBase.getBase()) - return; - } + assert(i->isVirtual()); + SelectPrimaryVBase(Base, FirstPrimary); + if (PrimaryBase.getBase()) + return; } } diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 104e3361892f..8347249a466b 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -35,6 +35,7 @@ static StmtClassNameTable &getStmtInfoTableEntry(Stmt::StmtClass E) { // Intialize the table on the first use. Initialized = true; +#define ABSTRACT_EXPR(CLASS, PARENT) #define STMT(CLASS, PARENT) \ StmtClassInfo[(unsigned)Stmt::CLASS##Class].Name = #CLASS; \ StmtClassInfo[(unsigned)Stmt::CLASS##Class].Size = sizeof(CLASS); @@ -132,9 +133,8 @@ Expr *AsmStmt::getOutputExpr(unsigned i) { /// getOutputConstraint - Return the constraint string for the specified /// output operand. All output constraints are known to be non-empty (either /// '=' or '+'). -std::string AsmStmt::getOutputConstraint(unsigned i) const { - return std::string(Constraints[i]->getStrData(), - Constraints[i]->getByteLength()); +llvm::StringRef AsmStmt::getOutputConstraint(unsigned i) const { + return getOutputConstraintLiteral(i)->getString(); } /// getNumPlusOperands - Return the number of output operands that have a "+" @@ -147,40 +147,52 @@ unsigned AsmStmt::getNumPlusOperands() const { return Res; } - - Expr *AsmStmt::getInputExpr(unsigned i) { return cast(Exprs[i + NumOutputs]); } /// getInputConstraint - Return the specified input constraint. Unlike output /// constraints, these can be empty. -std::string AsmStmt::getInputConstraint(unsigned i) const { - return std::string(Constraints[i + NumOutputs]->getStrData(), - Constraints[i + NumOutputs]->getByteLength()); +llvm::StringRef AsmStmt::getInputConstraint(unsigned i) const { + return getInputConstraintLiteral(i)->getString(); } -void AsmStmt::setOutputsAndInputs(unsigned NumOutputs, - unsigned NumInputs, - const std::string *Names, - StringLiteral **Constraints, - Stmt **Exprs) { +void AsmStmt::setOutputsAndInputsAndClobbers(ASTContext &C, + IdentifierInfo **Names, + StringLiteral **Constraints, + Stmt **Exprs, + unsigned NumOutputs, + unsigned NumInputs, + StringLiteral **Clobbers, + unsigned NumClobbers) { this->NumOutputs = NumOutputs; this->NumInputs = NumInputs; - this->Names.clear(); - this->Names.insert(this->Names.end(), Names, Names + NumOutputs + NumInputs); - this->Constraints.clear(); - this->Constraints.insert(this->Constraints.end(), - Constraints, Constraints + NumOutputs + NumInputs); - this->Exprs.clear(); - this->Exprs.insert(this->Exprs.end(), Exprs, Exprs + NumOutputs + NumInputs); + this->NumClobbers = NumClobbers; + + unsigned NumExprs = NumOutputs + NumInputs; + + C.Deallocate(this->Names); + this->Names = new (C) IdentifierInfo*[NumExprs]; + std::copy(Names, Names + NumExprs, this->Names); + + C.Deallocate(this->Exprs); + this->Exprs = new (C) Stmt*[NumExprs]; + std::copy(Exprs, Exprs + NumExprs, this->Exprs); + + C.Deallocate(this->Constraints); + this->Constraints = new (C) StringLiteral*[NumExprs]; + std::copy(Constraints, Constraints + NumExprs, this->Constraints); + + C.Deallocate(this->Clobbers); + this->Clobbers = new (C) StringLiteral*[NumClobbers]; + std::copy(Clobbers, Clobbers + NumClobbers, this->Clobbers); } /// getNamedOperand - Given a symbolic operand reference like %[foo], /// translate this into a numeric value needed to reference the same operand. /// This returns -1 if the operand name is invalid. -int AsmStmt::getNamedOperand(const std::string &SymbolicName) const { +int AsmStmt::getNamedOperand(llvm::StringRef SymbolicName) const { unsigned NumPlusOperands = 0; // Check if this is an output operand. @@ -197,11 +209,6 @@ int AsmStmt::getNamedOperand(const std::string &SymbolicName) const { return -1; } -void AsmStmt::setClobbers(StringLiteral **Clobbers, unsigned NumClobbers) { - this->Clobbers.clear(); - this->Clobbers.insert(this->Clobbers.end(), Clobbers, Clobbers + NumClobbers); -} - /// AnalyzeAsmString - Analyze the asm string of the current asm, decomposing /// it into pieces. If the asm string is erroneous, emit errors and return /// true, otherwise return false. @@ -313,7 +320,7 @@ unsigned AsmStmt::AnalyzeAsmString(llvm::SmallVectorImpl&Pieces, if (NameEnd == CurPtr) return diag::err_asm_empty_symbolic_operand_name; - std::string SymbolicName(CurPtr, NameEnd); + llvm::StringRef SymbolicName(CurPtr, NameEnd - CurPtr); int N = getNamedOperand(SymbolicName); if (N == -1) { @@ -332,26 +339,39 @@ unsigned AsmStmt::AnalyzeAsmString(llvm::SmallVectorImpl&Pieces, } } +QualType CXXCatchStmt::getCaughtType() const { + if (ExceptionDecl) + return ExceptionDecl->getType(); + return QualType(); +} + //===----------------------------------------------------------------------===// // Constructors //===----------------------------------------------------------------------===// -AsmStmt::AsmStmt(SourceLocation asmloc, bool issimple, bool isvolatile, - bool msasm, unsigned numoutputs, unsigned numinputs, - std::string *names, StringLiteral **constraints, +AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, + bool isvolatile, bool msasm, + unsigned numoutputs, unsigned numinputs, + IdentifierInfo **names, StringLiteral **constraints, Expr **exprs, StringLiteral *asmstr, unsigned numclobbers, StringLiteral **clobbers, SourceLocation rparenloc) : Stmt(AsmStmtClass), AsmLoc(asmloc), RParenLoc(rparenloc), AsmStr(asmstr) , IsSimple(issimple), IsVolatile(isvolatile), MSAsm(msasm) - , NumOutputs(numoutputs), NumInputs(numinputs) { - for (unsigned i = 0, e = numinputs + numoutputs; i != e; i++) { - Names.push_back(names[i]); - Exprs.push_back(exprs[i]); - Constraints.push_back(constraints[i]); - } + , NumOutputs(numoutputs), NumInputs(numinputs), NumClobbers(numclobbers) { - for (unsigned i = 0; i != numclobbers; i++) - Clobbers.push_back(clobbers[i]); + unsigned NumExprs = NumOutputs +NumInputs; + + Names = new (C) IdentifierInfo*[NumExprs]; + std::copy(names, names + NumExprs, Names); + + Exprs = new (C) Stmt*[NumExprs]; + std::copy(exprs, exprs + NumExprs, Exprs); + + Constraints = new (C) StringLiteral*[NumExprs]; + std::copy(constraints, constraints + NumExprs, Constraints); + + Clobbers = new (C) StringLiteral*[NumClobbers]; + std::copy(clobbers, clobbers + NumClobbers, Clobbers); } ObjCForCollectionStmt::ObjCForCollectionStmt(Stmt *Elem, Expr *Collect, @@ -387,6 +407,24 @@ ObjCAtCatchStmt::ObjCAtCatchStmt(SourceLocation atCatchLoc, RParenLoc = rparenloc; } +CXXTryStmt *CXXTryStmt::Create(ASTContext &C, SourceLocation tryLoc, + Stmt *tryBlock, Stmt **handlers, + unsigned numHandlers) { + std::size_t Size = sizeof(CXXTryStmt); + Size += ((numHandlers + 1) * sizeof(Stmt)); + + void *Mem = C.Allocate(Size, llvm::alignof()); + return new (Mem) CXXTryStmt(tryLoc, tryBlock, handlers, numHandlers); +} + +CXXTryStmt::CXXTryStmt(SourceLocation tryLoc, Stmt *tryBlock, + Stmt **handlers, unsigned numHandlers) + : Stmt(CXXTryStmtClass), TryLoc(tryLoc), NumHandlers(numHandlers) { + Stmt **Stmts = reinterpret_cast(this + 1); + Stmts[0] = tryBlock; + std::copy(handlers, handlers + NumHandlers, Stmts + 1); +} + //===----------------------------------------------------------------------===// // AST Destruction. //===----------------------------------------------------------------------===// @@ -453,6 +491,18 @@ void WhileStmt::DoDestroy(ASTContext &C) { BranchDestroy(C, this, SubExprs, END_EXPR); } +void AsmStmt::DoDestroy(ASTContext &C) { + DestroyChildren(C); + + C.Deallocate(Names); + C.Deallocate(Constraints); + C.Deallocate(Exprs); + C.Deallocate(Clobbers); + + this->~AsmStmt(); + C.Deallocate((void *)this); +} + //===----------------------------------------------------------------------===// // Child Iterators for iterating over subexpressions/substatements //===----------------------------------------------------------------------===// @@ -566,10 +616,10 @@ Stmt::child_iterator ReturnStmt::child_end() { // AsmStmt Stmt::child_iterator AsmStmt::child_begin() { - return Exprs.empty() ? 0 : &Exprs[0]; + return NumOutputs + NumInputs == 0 ? 0 : &Exprs[0]; } Stmt::child_iterator AsmStmt::child_end() { - return Exprs.empty() ? 0 : &Exprs[0] + Exprs.size(); + return NumOutputs + NumInputs == 0 ? 0 : &Exprs[0] + NumOutputs + NumInputs; } // ObjCAtCatchStmt @@ -615,19 +665,11 @@ Stmt::child_iterator CXXCatchStmt::child_end() { return &HandlerBlock + 1; } -QualType CXXCatchStmt::getCaughtType() const { - if (ExceptionDecl) - return ExceptionDecl->getType(); - return QualType(); -} - // CXXTryStmt -Stmt::child_iterator CXXTryStmt::child_begin() { return &Stmts[0]; } -Stmt::child_iterator CXXTryStmt::child_end() { return &Stmts[0]+Stmts.size(); } +Stmt::child_iterator CXXTryStmt::child_begin() { + return reinterpret_cast(this + 1); +} -CXXTryStmt::CXXTryStmt(SourceLocation tryLoc, Stmt *tryBlock, - Stmt **handlers, unsigned numHandlers) - : Stmt(CXXTryStmtClass), TryLoc(tryLoc) { - Stmts.push_back(tryBlock); - Stmts.insert(Stmts.end(), handlers, handlers + numHandlers); +Stmt::child_iterator CXXTryStmt::child_end() { + return reinterpret_cast(this + 1) + NumHandlers + 1; } diff --git a/lib/AST/StmtDumper.cpp b/lib/AST/StmtDumper.cpp index ae76526b7939..ba6218be1426 100644 --- a/lib/AST/StmtDumper.cpp +++ b/lib/AST/StmtDumper.cpp @@ -472,6 +472,8 @@ void StmtDumper::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *Node) { void StmtDumper::VisitCXXConstructExpr(CXXConstructExpr *Node) { DumpExpr(Node); + CXXConstructorDecl *Ctor = Node->getConstructor(); + DumpType(Ctor->getType()); if (Node->isElidable()) OS << " elidable"; } diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index bbb904de79b3..3ae306d3c7ac 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1038,6 +1038,10 @@ void StmtPrinter::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *Node) { PrintExpr(Node->getSubExpr()); } +void StmtPrinter::VisitCXXBindReferenceExpr(CXXBindReferenceExpr *Node) { + PrintExpr(Node->getSubExpr()); +} + void StmtPrinter::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *Node) { OS << Node->getType().getAsString(); OS << "("; diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index b74e1ef0bab4..3a19ec212c14 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -465,6 +465,10 @@ void StmtProfiler::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *S) { const_cast(S->getTemporary()->getDestructor())); } +void StmtProfiler::VisitCXXBindReferenceExpr(CXXBindReferenceExpr *S) { + VisitExpr(S); +} + void StmtProfiler::VisitCXXConstructExpr(CXXConstructExpr *S) { VisitExpr(S); VisitDecl(S->getConstructor()); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index edfb580cc35f..76cc38292064 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -339,21 +339,18 @@ const RecordType *Type::getAsUnionType() const { return 0; } -ObjCInterfaceType::ObjCInterfaceType(ASTContext &Ctx, QualType Canonical, +ObjCInterfaceType::ObjCInterfaceType(QualType Canonical, ObjCInterfaceDecl *D, ObjCProtocolDecl **Protos, unsigned NumP) : Type(ObjCInterface, Canonical, /*Dependent=*/false), - Decl(D), Protocols(0), NumProtocols(NumP) + Decl(D), NumProtocols(NumP) { - if (NumProtocols) { - Protocols = new (Ctx) ObjCProtocolDecl*[NumProtocols]; - memcpy(Protocols, Protos, NumProtocols * sizeof(*Protocols)); - } + if (NumProtocols) + memcpy(reinterpret_cast(this + 1), Protos, + NumProtocols * sizeof(*Protos)); } void ObjCInterfaceType::Destroy(ASTContext& C) { - if (Protocols) - C.Deallocate(Protocols); this->~ObjCInterfaceType(); C.Deallocate(this); } @@ -372,22 +369,18 @@ bool Type::isObjCQualifiedInterfaceType() const { return getAsObjCQualifiedInterfaceType() != 0; } -ObjCObjectPointerType::ObjCObjectPointerType(ASTContext &Ctx, - QualType Canonical, QualType T, +ObjCObjectPointerType::ObjCObjectPointerType(QualType Canonical, QualType T, ObjCProtocolDecl **Protos, unsigned NumP) : Type(ObjCObjectPointer, Canonical, /*Dependent=*/false), - PointeeType(T), Protocols(NULL), NumProtocols(NumP) + PointeeType(T), NumProtocols(NumP) { - if (NumProtocols) { - Protocols = new (Ctx) ObjCProtocolDecl*[NumProtocols]; - memcpy(Protocols, Protos, NumProtocols * sizeof(*Protocols)); - } + if (NumProtocols) + memcpy(reinterpret_cast(this + 1), Protos, + NumProtocols * sizeof(*Protos)); } void ObjCObjectPointerType::Destroy(ASTContext& C) { - if (Protocols) - C.Deallocate(Protocols); this->~ObjCObjectPointerType(); C.Deallocate(this); } @@ -720,6 +713,19 @@ bool Type::isPromotableIntegerType() const { default: return false; } + + // Enumerated types are promotable to their compatible integer types + // (C99 6.3.1.1) a.k.a. its underlying type (C++ [conv.prom]p2). + if (const EnumType *ET = getAs()){ + if (this->isDependentType() || ET->getDecl()->getPromotionType().isNull()) + return false; + + const BuiltinType *BT + = ET->getDecl()->getPromotionType()->getAs(); + return BT->getKind() == BuiltinType::Int + || BT->getKind() == BuiltinType::UInt; + } + return false; } @@ -797,12 +803,24 @@ const char *BuiltinType::getName(const LangOptions &LO) const { } } +llvm::StringRef FunctionType::getNameForCallConv(CallingConv CC) { + switch (CC) { + case CC_Default: llvm_unreachable("no name for default cc"); + default: return ""; + + case CC_C: return "cdecl"; + case CC_X86StdCall: return "stdcall"; + case CC_X86FastCall: return "fastcall"; + } +} + void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, arg_type_iterator ArgTys, unsigned NumArgs, bool isVariadic, unsigned TypeQuals, bool hasExceptionSpec, bool anyExceptionSpec, unsigned NumExceptions, - exception_iterator Exs, bool NoReturn) { + exception_iterator Exs, bool NoReturn, + CallingConv CallConv) { ID.AddPointer(Result.getAsOpaquePtr()); for (unsigned i = 0; i != NumArgs; ++i) ID.AddPointer(ArgTys[i].getAsOpaquePtr()); @@ -815,16 +833,19 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, ID.AddPointer(Exs[i].getAsOpaquePtr()); } ID.AddInteger(NoReturn); + ID.AddInteger(CallConv); } void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getResultType(), arg_type_begin(), NumArgs, isVariadic(), getTypeQuals(), hasExceptionSpec(), hasAnyExceptionSpec(), - getNumExceptions(), exception_begin(), getNoReturnAttr()); + getNumExceptions(), exception_begin(), getNoReturnAttr(), + getCallConv()); } void ObjCObjectPointerType::Profile(llvm::FoldingSetNodeID &ID, - QualType OIT, ObjCProtocolDecl **protocols, + QualType OIT, + ObjCProtocolDecl * const *protocols, unsigned NumProtocols) { ID.AddPointer(OIT.getAsOpaquePtr()); for (unsigned i = 0; i != NumProtocols; i++) @@ -832,10 +853,7 @@ void ObjCObjectPointerType::Profile(llvm::FoldingSetNodeID &ID, } void ObjCObjectPointerType::Profile(llvm::FoldingSetNodeID &ID) { - if (getNumProtocols()) - Profile(ID, getPointeeType(), &Protocols[0], getNumProtocols()); - else - Profile(ID, getPointeeType(), 0, 0); + Profile(ID, getPointeeType(), qual_begin(), getNumProtocols()); } /// LookThroughTypedefs - Return the ultimate type this typedef corresponds to @@ -894,8 +912,9 @@ void DependentDecltypeType::Profile(llvm::FoldingSetNodeID &ID, E->Profile(ID, Context, true); } -TagType::TagType(TypeClass TC, TagDecl *D, QualType can) - : Type(TC, can, D->isDependentType()), decl(D, 0) {} +TagType::TagType(TypeClass TC, const TagDecl *D, QualType can) + : Type(TC, can, D->isDependentType()), + decl(const_cast(D), 0) {} bool RecordType::classof(const TagType *TT) { return isa(TT->getDecl()); @@ -1023,7 +1042,7 @@ QualType QualifierCollector::apply(const Type *T) const { void ObjCInterfaceType::Profile(llvm::FoldingSetNodeID &ID, const ObjCInterfaceDecl *Decl, - ObjCProtocolDecl **protocols, + ObjCProtocolDecl * const *protocols, unsigned NumProtocols) { ID.AddPointer(Decl); for (unsigned i = 0; i != NumProtocols; i++) @@ -1031,8 +1050,81 @@ void ObjCInterfaceType::Profile(llvm::FoldingSetNodeID &ID, } void ObjCInterfaceType::Profile(llvm::FoldingSetNodeID &ID) { - if (getNumProtocols()) - Profile(ID, getDecl(), &Protocols[0], getNumProtocols()); - else - Profile(ID, getDecl(), 0, 0); + Profile(ID, getDecl(), qual_begin(), getNumProtocols()); +} + +Linkage Type::getLinkage() const { + // C++ [basic.link]p8: + // Names not covered by these rules have no linkage. + if (this != CanonicalType.getTypePtr()) + return CanonicalType->getLinkage(); + + return NoLinkage; +} + +Linkage BuiltinType::getLinkage() const { + // C++ [basic.link]p8: + // A type is said to have linkage if and only if: + // - it is a fundamental type (3.9.1); or + return ExternalLinkage; +} + +Linkage TagType::getLinkage() const { + // C++ [basic.link]p8: + // - it is a class or enumeration type that is named (or has a name for + // linkage purposes (7.1.3)) and the name has linkage; or + // - it is a specialization of a class template (14); or + return getDecl()->getLinkage(); +} + +// C++ [basic.link]p8: +// - it is a compound type (3.9.2) other than a class or enumeration, +// compounded exclusively from types that have linkage; or +Linkage ComplexType::getLinkage() const { + return ElementType->getLinkage(); +} + +Linkage PointerType::getLinkage() const { + return PointeeType->getLinkage(); +} + +Linkage BlockPointerType::getLinkage() const { + return PointeeType->getLinkage(); +} + +Linkage ReferenceType::getLinkage() const { + return PointeeType->getLinkage(); +} + +Linkage MemberPointerType::getLinkage() const { + return minLinkage(Class->getLinkage(), PointeeType->getLinkage()); +} + +Linkage ArrayType::getLinkage() const { + return ElementType->getLinkage(); +} + +Linkage VectorType::getLinkage() const { + return ElementType->getLinkage(); +} + +Linkage FunctionNoProtoType::getLinkage() const { + return getResultType()->getLinkage(); +} + +Linkage FunctionProtoType::getLinkage() const { + Linkage L = getResultType()->getLinkage(); + for (arg_type_iterator A = arg_type_begin(), AEnd = arg_type_end(); + A != AEnd; ++A) + L = minLinkage(L, (*A)->getLinkage()); + + return L; +} + +Linkage ObjCInterfaceType::getLinkage() const { + return ExternalLinkage; +} + +Linkage ObjCObjectPointerType::getLinkage() const { + return ExternalLinkage; } diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 00b74bc21a14..5b621cf7280d 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -225,15 +225,24 @@ void TypePrinter::PrintDependentSizedExtVector( } void TypePrinter::PrintVector(const VectorType *T, std::string &S) { - // FIXME: We prefer to print the size directly here, but have no way - // to get the size of the type. - Print(T->getElementType(), S); - std::string V = "__attribute__((__vector_size__("; - V += llvm::utostr_32(T->getNumElements()); // convert back to bytes. - std::string ET; - Print(T->getElementType(), ET); - V += " * sizeof(" + ET + ")))) "; - S = V + S; + if (T->isAltiVec()) { + if (T->isPixel()) + S = "__vector __pixel " + S; + else { + Print(T->getElementType(), S); + S = "__vector " + S; + } + } else { + // FIXME: We prefer to print the size directly here, but have no way + // to get the size of the type. + Print(T->getElementType(), S); + std::string V = "__attribute__((__vector_size__("; + V += llvm::utostr_32(T->getNumElements()); // convert back to bytes. + std::string ET; + Print(T->getElementType(), ET); + V += " * sizeof(" + ET + ")))) "; + S = V + S; + } } void TypePrinter::PrintExtVector(const ExtVectorType *T, std::string &S) { @@ -271,6 +280,19 @@ void TypePrinter::PrintFunctionProto(const FunctionProtoType *T, S += ")"; + switch(T->getCallConv()) { + case CC_Default: + default: break; + case CC_C: + S += " __attribute__((cdecl))"; + break; + case CC_X86StdCall: + S += " __attribute__((stdcall))"; + break; + case CC_X86FastCall: + S += " __attribute__((fastcall))"; + break; + } if (T->getNoReturnAttr()) S += " __attribute__((noreturn))"; diff --git a/lib/Analysis/AnalysisContext.cpp b/lib/Analysis/AnalysisContext.cpp index ad9f6dd19413..ccd5088f2ec7 100644 --- a/lib/Analysis/AnalysisContext.cpp +++ b/lib/Analysis/AnalysisContext.cpp @@ -12,10 +12,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/AnalysisContext.h" -#include "clang/Analysis/PathSensitive/MemRegion.h" -#include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/CFG.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" @@ -87,12 +86,6 @@ AnalysisContext *AnalysisContextManager::getContext(const Decl *D) { return AC; } -const BlockDecl *BlockInvocationContext::getBlockDecl() const { - return Data.is() ? - Data.get()->getDecl() - : Data.get(); -} - //===----------------------------------------------------------------------===// // FoldingSet profiling. //===----------------------------------------------------------------------===// @@ -117,11 +110,7 @@ void ScopeContext::Profile(llvm::FoldingSetNodeID &ID) { } void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) { - if (const BlockDataRegion *BR = getBlockRegion()) - Profile(ID, getAnalysisContext(), getParent(), BR); - else - Profile(ID, getAnalysisContext(), getParent(), - Data.get()); + Profile(ID, getAnalysisContext(), getParent(), BD); } //===----------------------------------------------------------------------===// @@ -170,15 +159,6 @@ LocationContextManager::getScope(AnalysisContext *ctx, return getLocationContext(ctx, parent, s); } -const BlockInvocationContext * -LocationContextManager::getBlockInvocation(AnalysisContext *ctx, - const LocationContext *parent, - const BlockDataRegion *BR) { - return getLocationContext(ctx, - parent, - BR); -} - //===----------------------------------------------------------------------===// // LocationContext methods. //===----------------------------------------------------------------------===// @@ -214,6 +194,7 @@ namespace { class FindBlockDeclRefExprsVals : public StmtVisitor{ BumpVector &BEVals; BumpVectorContext &BC; + llvm::DenseMap Visited; public: FindBlockDeclRefExprsVals(BumpVector &bevals, BumpVectorContext &bc) @@ -224,10 +205,27 @@ public: if (Stmt *child = *I) Visit(child); } + + void VisitDeclRefExpr(const DeclRefExpr *DR) { + // Non-local variables are also directly modified. + if (const VarDecl *VD = dyn_cast(DR->getDecl())) + if (!VD->hasLocalStorage()) { + unsigned &flag = Visited[VD]; + if (!flag) { + flag = 1; + BEVals.push_back(VD, BC); + } + } + } void VisitBlockDeclRefExpr(BlockDeclRefExpr *DR) { - if (const VarDecl *VD = dyn_cast(DR->getDecl())) - BEVals.push_back(VD, BC); + if (const VarDecl *VD = dyn_cast(DR->getDecl())) { + unsigned &flag = Visited[VD]; + if (!flag) { + flag = 1; + BEVals.push_back(VD, BC); + } + } } }; } // end anonymous namespace diff --git a/lib/Analysis/ArrayBoundChecker.cpp b/lib/Analysis/ArrayBoundChecker.cpp deleted file mode 100644 index 49c86068265b..000000000000 --- a/lib/Analysis/ArrayBoundChecker.cpp +++ /dev/null @@ -1,91 +0,0 @@ -//== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ArrayBoundChecker, which is a path-sensitive check -// which looks for an out-of-bound array element access. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" - -using namespace clang; - -namespace { -class ArrayBoundChecker : - public CheckerVisitor { - BuiltinBug *BT; -public: - ArrayBoundChecker() : BT(0) {} - static void *getTag(); - void VisitLocation(CheckerContext &C, const Stmt *S, SVal l); -}; -} - -void clang::RegisterArrayBoundChecker(GRExprEngine &Eng) { - Eng.registerCheck(new ArrayBoundChecker()); -} - -void *ArrayBoundChecker::getTag() { - static int x = 0; return &x; -} - -void ArrayBoundChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l){ - // Check for out of bound array element access. - const MemRegion *R = l.getAsRegion(); - if (!R) - return; - - R = R->StripCasts(); - - const ElementRegion *ER = dyn_cast(R); - if (!ER) - return; - - // Get the index of the accessed element. - DefinedOrUnknownSVal &Idx = cast(ER->getIndex()); - - const GRState *state = C.getState(); - - // Get the size of the array. - DefinedOrUnknownSVal NumElements - = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), - ER->getValueType(C.getASTContext())); - - const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); - const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); - if (StOutBound && !StInBound) { - ExplodedNode *N = C.GenerateSink(StOutBound); - if (!N) - return; - - if (!BT) - BT = new BuiltinBug("Out-of-bound array access", - "Access out-of-bound array element (buffer overflow)"); - - // FIXME: It would be nice to eventually make this diagnostic more clear, - // e.g., by referencing the original declaration or by saying *why* this - // reference is outside the range. - - // Generate a report for this bug. - RangedBugReport *report = - new RangedBugReport(*BT, BT->getDescription(), N); - - report->addRange(S->getSourceRange()); - C.EmitReport(report); - return; - } - - // Array bound check succeeded. From this point forward the array bound - // should always succeed. - assert(StInBound); - C.addTransition(StInBound); -} diff --git a/lib/Analysis/AttrNonNullChecker.cpp b/lib/Analysis/AttrNonNullChecker.cpp deleted file mode 100644 index aa21700c2483..000000000000 --- a/lib/Analysis/AttrNonNullChecker.cpp +++ /dev/null @@ -1,112 +0,0 @@ -//===--- AttrNonNullChecker.h - Undefined arguments checker ----*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines AttrNonNullChecker, a builtin check in GRExprEngine that -// performs checks for arguments declared to have nonnull attribute. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class AttrNonNullChecker - : public CheckerVisitor { - BugType *BT; -public: - AttrNonNullChecker() : BT(0) {} - static void *getTag() { - static int x = 0; - return &x; - } - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); -}; -} // end anonymous namespace - -void clang::RegisterAttrNonNullChecker(GRExprEngine &Eng) { - Eng.registerCheck(new AttrNonNullChecker()); -} - -void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, - const CallExpr *CE) { - const GRState *state = C.getState(); - - // Check if the callee has a 'nonnull' attribute. - SVal X = state->getSVal(CE->getCallee()); - - const FunctionDecl* FD = X.getAsFunctionDecl(); - if (!FD) - return; - - const NonNullAttr* Att = FD->getAttr(); - if (!Att) - return; - - // Iterate through the arguments of CE and check them for null. - unsigned idx = 0; - - for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; - ++I, ++idx) { - - if (!Att->isNonNull(idx)) - continue; - - const SVal &V = state->getSVal(*I); - const DefinedSVal *DV = dyn_cast(&V); - - if (!DV) - continue; - - ConstraintManager &CM = C.getConstraintManager(); - const GRState *stateNotNull, *stateNull; - llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, *DV); - - if (stateNull && !stateNotNull) { - // Generate an error node. Check for a null node in case - // we cache out. - if (ExplodedNode *errorNode = C.GenerateSink(stateNull)) { - - // Lazily allocate the BugType object if it hasn't already been - // created. Ownership is transferred to the BugReporter object once - // the BugReport is passed to 'EmitWarning'. - if (!BT) - BT = new BugType("Argument with 'nonnull' attribute passed null", - "API"); - - EnhancedBugReport *R = - new EnhancedBugReport(*BT, - "Null pointer passed as an argument to a " - "'nonnull' parameter", errorNode); - - // Highlight the range of the argument that was null. - const Expr *arg = *I; - R->addRange(arg->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, arg); - - // Emit the bug report. - C.EmitReport(R); - } - - // Always return. Either we cached out or we just emitted an error. - return; - } - - // If a pointer value passed the check we should assume that it is - // indeed not null from this point forward. - assert(stateNotNull); - state = stateNotNull; - } - - // If we reach here all of the arguments passed the nonnull check. - // If 'state' has been updated generated a new node. - C.addTransition(state); -} diff --git a/lib/Analysis/BasicConstraintManager.cpp b/lib/Analysis/BasicConstraintManager.cpp deleted file mode 100644 index 6dfc470530a4..000000000000 --- a/lib/Analysis/BasicConstraintManager.cpp +++ /dev/null @@ -1,317 +0,0 @@ -//== BasicConstraintManager.cpp - Manage basic constraints.------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines BasicConstraintManager, a class that tracks simple -// equality and inequality constraints on symbolic values of GRState. -// -//===----------------------------------------------------------------------===// - -#include "SimpleConstraintManager.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - - -namespace { class ConstNotEq {}; } -namespace { class ConstEq {}; } - -typedef llvm::ImmutableMap ConstNotEqTy; -typedef llvm::ImmutableMap ConstEqTy; - -static int ConstEqIndex = 0; -static int ConstNotEqIndex = 0; - -namespace clang { -template<> -struct GRStateTrait : public GRStatePartialTrait { - static inline void* GDMIndex() { return &ConstNotEqIndex; } -}; - -template<> -struct GRStateTrait : public GRStatePartialTrait { - static inline void* GDMIndex() { return &ConstEqIndex; } -}; -} - -namespace { -// BasicConstraintManager only tracks equality and inequality constraints of -// constants and integer variables. -class BasicConstraintManager - : public SimpleConstraintManager { - GRState::IntSetTy::Factory ISetFactory; -public: - BasicConstraintManager(GRStateManager &statemgr, GRSubEngine &subengine) - : SimpleConstraintManager(subengine), - ISetFactory(statemgr.getAllocator()) {} - - const GRState* AssumeSymNE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymEQ(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymLT(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymGT(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymGE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymLE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AddEQ(const GRState* state, SymbolRef sym, const llvm::APSInt& V); - - const GRState* AddNE(const GRState* state, SymbolRef sym, const llvm::APSInt& V); - - const llvm::APSInt* getSymVal(const GRState* state, SymbolRef sym) const; - bool isNotEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) - const; - bool isEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) - const; - - const GRState* RemoveDeadBindings(const GRState* state, SymbolReaper& SymReaper); - - void print(const GRState* state, llvm::raw_ostream& Out, - const char* nl, const char *sep); -}; - -} // end anonymous namespace - -ConstraintManager* clang::CreateBasicConstraintManager(GRStateManager& statemgr, - GRSubEngine &subengine) { - return new BasicConstraintManager(statemgr, subengine); -} - -const GRState* -BasicConstraintManager::AssumeSymNE(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) { - // First, determine if sym == X, where X != V. - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = (*X != V); - return isFeasible ? state : NULL; - } - - // Second, determine if sym != V. - if (isNotEqual(state, sym, V)) - return state; - - // If we reach here, sym is not a constant and we don't know if it is != V. - // Make that assumption. - return AddNE(state, sym, V); -} - -const GRState *BasicConstraintManager::AssumeSymEQ(const GRState *state, - SymbolRef sym, - const llvm::APSInt &V) { - // First, determine if sym == X, where X != V. - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = *X == V; - return isFeasible ? state : NULL; - } - - // Second, determine if sym != V. - if (isNotEqual(state, sym, V)) - return NULL; - - // If we reach here, sym is not a constant and we don't know if it is == V. - // Make that assumption. - return AddEQ(state, sym, V); -} - -// These logic will be handled in another ConstraintManager. -const GRState *BasicConstraintManager::AssumeSymLT(const GRState *state, - SymbolRef sym, - const llvm::APSInt& V) { - // Is 'V' the smallest possible value? - if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { - // sym cannot be any value less than 'V'. This path is infeasible. - return NULL; - } - - // FIXME: For now have assuming x < y be the same as assuming sym != V; - return AssumeSymNE(state, sym, V); -} - -const GRState *BasicConstraintManager::AssumeSymGT(const GRState *state, - SymbolRef sym, - const llvm::APSInt& V) { - - // Is 'V' the largest possible value? - if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { - // sym cannot be any value greater than 'V'. This path is infeasible. - return NULL; - } - - // FIXME: For now have assuming x > y be the same as assuming sym != V; - return AssumeSymNE(state, sym, V); -} - -const GRState *BasicConstraintManager::AssumeSymGE(const GRState *state, - SymbolRef sym, - const llvm::APSInt &V) { - - // Reject a path if the value of sym is a constant X and !(X >= V). - if (const llvm::APSInt *X = getSymVal(state, sym)) { - bool isFeasible = *X >= V; - return isFeasible ? state : NULL; - } - - // Sym is not a constant, but it is worth looking to see if V is the - // maximum integer value. - if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { - // If we know that sym != V, then this condition is infeasible since - // there is no other value greater than V. - bool isFeasible = !isNotEqual(state, sym, V); - - // If the path is still feasible then as a consequence we know that - // 'sym == V' because we cannot have 'sym > V' (no larger values). - // Add this constraint. - return isFeasible ? AddEQ(state, sym, V) : NULL; - } - - return state; -} - -const GRState* -BasicConstraintManager::AssumeSymLE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) { - - // Reject a path if the value of sym is a constant X and !(X <= V). - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = *X <= V; - return isFeasible ? state : NULL; - } - - // Sym is not a constant, but it is worth looking to see if V is the - // minimum integer value. - if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { - // If we know that sym != V, then this condition is infeasible since - // there is no other value less than V. - bool isFeasible = !isNotEqual(state, sym, V); - - // If the path is still feasible then as a consequence we know that - // 'sym == V' because we cannot have 'sym < V' (no smaller values). - // Add this constraint. - return isFeasible ? AddEQ(state, sym, V) : NULL; - } - - return state; -} - -const GRState* BasicConstraintManager::AddEQ(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) { - // Create a new state with the old binding replaced. - return state->set(sym, &V); -} - -const GRState* BasicConstraintManager::AddNE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) { - - // First, retrieve the NE-set associated with the given symbol. - ConstNotEqTy::data_type* T = state->get(sym); - GRState::IntSetTy S = T ? *T : ISetFactory.GetEmptySet(); - - // Now add V to the NE set. - S = ISetFactory.Add(S, &V); - - // Create a new state with the old binding replaced. - return state->set(sym, S); -} - -const llvm::APSInt* BasicConstraintManager::getSymVal(const GRState* state, - SymbolRef sym) const { - const ConstEqTy::data_type* T = state->get(sym); - return T ? *T : NULL; -} - -bool BasicConstraintManager::isNotEqual(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) const { - - // Retrieve the NE-set associated with the given symbol. - const ConstNotEqTy::data_type* T = state->get(sym); - - // See if V is present in the NE-set. - return T ? T->contains(&V) : false; -} - -bool BasicConstraintManager::isEqual(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) const { - // Retrieve the EQ-set associated with the given symbol. - const ConstEqTy::data_type* T = state->get(sym); - // See if V is present in the EQ-set. - return T ? **T == V : false; -} - -/// Scan all symbols referenced by the constraints. If the symbol is not alive -/// as marked in LSymbols, mark it as dead in DSymbols. -const GRState* -BasicConstraintManager::RemoveDeadBindings(const GRState* state, - SymbolReaper& SymReaper) { - - ConstEqTy CE = state->get(); - ConstEqTy::Factory& CEFactory = state->get_context(); - - for (ConstEqTy::iterator I = CE.begin(), E = CE.end(); I!=E; ++I) { - SymbolRef sym = I.getKey(); - if (SymReaper.maybeDead(sym)) CE = CEFactory.Remove(CE, sym); - } - state = state->set(CE); - - ConstNotEqTy CNE = state->get(); - ConstNotEqTy::Factory& CNEFactory = state->get_context(); - - for (ConstNotEqTy::iterator I = CNE.begin(), E = CNE.end(); I != E; ++I) { - SymbolRef sym = I.getKey(); - if (SymReaper.maybeDead(sym)) CNE = CNEFactory.Remove(CNE, sym); - } - - return state->set(CNE); -} - -void BasicConstraintManager::print(const GRState* state, llvm::raw_ostream& Out, - const char* nl, const char *sep) { - // Print equality constraints. - - ConstEqTy CE = state->get(); - - if (!CE.isEmpty()) { - Out << nl << sep << "'==' constraints:"; - for (ConstEqTy::iterator I = CE.begin(), E = CE.end(); I!=E; ++I) - Out << nl << " $" << I.getKey() << " : " << *I.getData(); - } - - // Print != constraints. - - ConstNotEqTy CNE = state->get(); - - if (!CNE.isEmpty()) { - Out << nl << sep << "'!=' constraints:"; - - for (ConstNotEqTy::iterator I = CNE.begin(), EI = CNE.end(); I!=EI; ++I) { - Out << nl << " $" << I.getKey() << " : "; - bool isFirst = true; - - GRState::IntSetTy::iterator J = I.getData().begin(), - EJ = I.getData().end(); - - for ( ; J != EJ; ++J) { - if (isFirst) isFirst = false; - else Out << ", "; - - Out << (*J)->getSExtValue(); // Hack: should print to raw_ostream. - } - } - } -} diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp deleted file mode 100644 index 67483d979291..000000000000 --- a/lib/Analysis/BasicObjCFoundationChecks.cpp +++ /dev/null @@ -1,598 +0,0 @@ -//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines BasicObjCFoundationChecks, a class that encapsulates -// a set of simple checks to run on Objective-C code using Apple's Foundation -// classes. -// -//===----------------------------------------------------------------------===// - -#include "BasicObjCFoundationChecks.h" - -#include "clang/Analysis/PathSensitive/ExplodedGraph.h" -#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/MemRegion.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/LocalCheckers.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/ASTContext.h" - -using namespace clang; - -static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) { - const Expr* Receiver = ME->getReceiver(); - - if (!Receiver) - return NULL; - - if (const ObjCObjectPointerType *PT = - Receiver->getType()->getAs()) - return PT->getInterfaceType(); - - return NULL; -} - -static const char* GetReceiverNameType(const ObjCMessageExpr* ME) { - if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME)) - return ReceiverType->getDecl()->getIdentifier()->getNameStart(); - return NULL; -} - -namespace { - -class APIMisuse : public BugType { -public: - APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} -}; - -class BasicObjCFoundationChecks : public GRSimpleAPICheck { - APIMisuse *BT; - BugReporter& BR; - ASTContext &Ctx; - - bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix); - bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME); - - void Warn(ExplodedNode* N, const Expr* E, const std::string& s); - void WarnNilArg(ExplodedNode* N, const Expr* E); - - bool CheckNilArg(ExplodedNode* N, unsigned Arg); - -public: - BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br) - : BT(0), BR(br), Ctx(ctx) {} - - bool Audit(ExplodedNode* N, GRStateManager&); - -private: - void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Argument to '" << GetReceiverNameType(ME) << "' method '" - << ME->getSelector().getAsString() << "' cannot be nil."; - - // Lazily create the BugType object for NilArg. This will be owned - // by the BugReporter object 'BR' once we call BR.EmitWarning. - if (!BT) BT = new APIMisuse("nil argument"); - - RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); - R->addRange(ME->getArg(Arg)->getSourceRange()); - BR.EmitReport(R); - } -}; - -} // end anonymous namespace - - -GRSimpleAPICheck* -clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) { - return new BasicObjCFoundationChecks(Ctx, BR); -} - - - -bool BasicObjCFoundationChecks::Audit(ExplodedNode* N, - GRStateManager&) { - - const ObjCMessageExpr* ME = - cast(cast(N->getLocation()).getStmt()); - - const ObjCInterfaceType *ReceiverType = GetReceiverType(ME); - - if (!ReceiverType) - return false; - - if (isNSString(ReceiverType, - ReceiverType->getDecl()->getIdentifier()->getName())) - return AuditNSString(N, ME); - - return false; -} - -static inline bool isNil(SVal X) { - return isa(X); -} - -//===----------------------------------------------------------------------===// -// Error reporting. -//===----------------------------------------------------------------------===// - -bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) { - const ObjCMessageExpr* ME = - cast(cast(N->getLocation()).getStmt()); - - const Expr * E = ME->getArg(Arg); - - if (isNil(N->getState()->getSVal(E))) { - WarnNilArg(N, ME, Arg); - return true; - } - - return false; -} - -//===----------------------------------------------------------------------===// -// NSString checking. -//===----------------------------------------------------------------------===// - -bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T, - llvm::StringRef ClassName) { - return ClassName == "NSString" || ClassName == "NSMutableString"; -} - -bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N, - const ObjCMessageExpr* ME) { - - Selector S = ME->getSelector(); - - if (S.isUnarySelector()) - return false; - - // FIXME: This is going to be really slow doing these checks with - // lexical comparisons. - - std::string name = S.getAsString(); - assert (!name.empty()); - const char* cstr = &name[0]; - unsigned len = name.size(); - - switch (len) { - default: - break; - case 8: - if (!strcmp(cstr, "compare:")) - return CheckNilArg(N, 0); - - break; - - case 15: - // FIXME: Checking for initWithFormat: will not work in most cases - // yet because [NSString alloc] returns id, not NSString*. We will - // need support for tracking expected-type information in the analyzer - // to find these errors. - if (!strcmp(cstr, "initWithFormat:")) - return CheckNilArg(N, 0); - - break; - - case 16: - if (!strcmp(cstr, "compare:options:")) - return CheckNilArg(N, 0); - - break; - - case 22: - if (!strcmp(cstr, "compare:options:range:")) - return CheckNilArg(N, 0); - - break; - - case 23: - - if (!strcmp(cstr, "caseInsensitiveCompare:")) - return CheckNilArg(N, 0); - - break; - - case 29: - if (!strcmp(cstr, "compare:options:range:locale:")) - return CheckNilArg(N, 0); - - break; - - case 37: - if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:")) - return CheckNilArg(N, 0); - - break; - } - - return false; -} - -//===----------------------------------------------------------------------===// -// Error reporting. -//===----------------------------------------------------------------------===// - -namespace { - -class AuditCFNumberCreate : public GRSimpleAPICheck { - APIMisuse* BT; - - // FIXME: Either this should be refactored into GRSimpleAPICheck, or - // it should always be passed with a call to Audit. The latter - // approach makes this class more stateless. - ASTContext& Ctx; - IdentifierInfo* II; - BugReporter& BR; - -public: - AuditCFNumberCreate(ASTContext& ctx, BugReporter& br) - : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){} - - ~AuditCFNumberCreate() {} - - bool Audit(ExplodedNode* N, GRStateManager&); - -private: - void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N, - uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); -}; -} // end anonymous namespace - -enum CFNumberType { - kCFNumberSInt8Type = 1, - kCFNumberSInt16Type = 2, - kCFNumberSInt32Type = 3, - kCFNumberSInt64Type = 4, - kCFNumberFloat32Type = 5, - kCFNumberFloat64Type = 6, - kCFNumberCharType = 7, - kCFNumberShortType = 8, - kCFNumberIntType = 9, - kCFNumberLongType = 10, - kCFNumberLongLongType = 11, - kCFNumberFloatType = 12, - kCFNumberDoubleType = 13, - kCFNumberCFIndexType = 14, - kCFNumberNSIntegerType = 15, - kCFNumberCGFloatType = 16 -}; - -namespace { - template - class Optional { - bool IsKnown; - T Val; - public: - Optional() : IsKnown(false), Val(0) {} - Optional(const T& val) : IsKnown(true), Val(val) {} - - bool isKnown() const { return IsKnown; } - - const T& getValue() const { - assert (isKnown()); - return Val; - } - - operator const T&() const { - return getValue(); - } - }; -} - -static Optional GetCFNumberSize(ASTContext& Ctx, uint64_t i) { - static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; - - if (i < kCFNumberCharType) - return FixedSize[i-1]; - - QualType T; - - switch (i) { - case kCFNumberCharType: T = Ctx.CharTy; break; - case kCFNumberShortType: T = Ctx.ShortTy; break; - case kCFNumberIntType: T = Ctx.IntTy; break; - case kCFNumberLongType: T = Ctx.LongTy; break; - case kCFNumberLongLongType: T = Ctx.LongLongTy; break; - case kCFNumberFloatType: T = Ctx.FloatTy; break; - case kCFNumberDoubleType: T = Ctx.DoubleTy; break; - case kCFNumberCFIndexType: - case kCFNumberNSIntegerType: - case kCFNumberCGFloatType: - // FIXME: We need a way to map from names to Type*. - default: - return Optional(); - } - - return Ctx.getTypeSize(T); -} - -#if 0 -static const char* GetCFNumberTypeStr(uint64_t i) { - static const char* Names[] = { - "kCFNumberSInt8Type", - "kCFNumberSInt16Type", - "kCFNumberSInt32Type", - "kCFNumberSInt64Type", - "kCFNumberFloat32Type", - "kCFNumberFloat64Type", - "kCFNumberCharType", - "kCFNumberShortType", - "kCFNumberIntType", - "kCFNumberLongType", - "kCFNumberLongLongType", - "kCFNumberFloatType", - "kCFNumberDoubleType", - "kCFNumberCFIndexType", - "kCFNumberNSIntegerType", - "kCFNumberCGFloatType" - }; - - return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; -} -#endif - -bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){ - const CallExpr* CE = - cast(cast(N->getLocation()).getStmt()); - const Expr* Callee = CE->getCallee(); - SVal CallV = N->getState()->getSVal(Callee); - const FunctionDecl* FD = CallV.getAsFunctionDecl(); - - if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3) - return false; - - // Get the value of the "theType" argument. - SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1)); - - // FIXME: We really should allow ranges of valid theType values, and - // bifurcate the state appropriately. - nonloc::ConcreteInt* V = dyn_cast(&TheTypeVal); - - if (!V) - return false; - - uint64_t NumberKind = V->getValue().getLimitedValue(); - Optional TargetSize = GetCFNumberSize(Ctx, NumberKind); - - // FIXME: In some cases we can emit an error. - if (!TargetSize.isKnown()) - return false; - - // Look at the value of the integer being passed by reference. Essentially - // we want to catch cases where the value passed in is not equal to the - // size of the type being created. - SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2)); - - // FIXME: Eventually we should handle arbitrary locations. We can do this - // by having an enhanced memory model that does low-level typing. - loc::MemRegionVal* LV = dyn_cast(&TheValueExpr); - - if (!LV) - return false; - - const TypedRegion* R = dyn_cast(LV->StripCasts()); - - if (!R) - return false; - - QualType T = Ctx.getCanonicalType(R->getValueType(Ctx)); - - // FIXME: If the pointee isn't an integer type, should we flag a warning? - // People can do weird stuff with pointers. - - if (!T->isIntegerType()) - return false; - - uint64_t SourceSize = Ctx.getTypeSize(T); - - // CHECK: is SourceSize == TargetSize - - if (SourceSize == TargetSize) - return false; - - AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind); - - // FIXME: We can actually create an abstract "CFNumber" object that has - // the bits initialized to the provided values. - return SourceSize < TargetSize; -} - -void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex, - ExplodedNode *N, - uint64_t SourceSize, uint64_t TargetSize, - uint64_t NumberKind) { - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << (SourceSize == 8 ? "An " : "A ") - << SourceSize << " bit integer is used to initialize a CFNumber " - "object that represents " - << (TargetSize == 8 ? "an " : "a ") - << TargetSize << " bit integer. "; - - if (SourceSize < TargetSize) - os << (TargetSize - SourceSize) - << " bits of the CFNumber value will be garbage." ; - else - os << (SourceSize - TargetSize) - << " bits of the input integer will be lost."; - - // Lazily create the BugType object. This will be owned - // by the BugReporter object 'BR' once we call BR.EmitWarning. - if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate"); - RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); - report->addRange(Ex->getSourceRange()); - BR.EmitReport(report); -} - -GRSimpleAPICheck* -clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) { - return new AuditCFNumberCreate(Ctx, BR); -} - -//===----------------------------------------------------------------------===// -// CFRetain/CFRelease auditing for null arguments. -//===----------------------------------------------------------------------===// - -namespace { -class AuditCFRetainRelease : public GRSimpleAPICheck { - APIMisuse *BT; - - // FIXME: Either this should be refactored into GRSimpleAPICheck, or - // it should always be passed with a call to Audit. The latter - // approach makes this class more stateless. - ASTContext& Ctx; - IdentifierInfo *Retain, *Release; - BugReporter& BR; - -public: - AuditCFRetainRelease(ASTContext& ctx, BugReporter& br) - : BT(0), Ctx(ctx), - Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")), - BR(br){} - - ~AuditCFRetainRelease() {} - - bool Audit(ExplodedNode* N, GRStateManager&); -}; -} // end anonymous namespace - - -bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) { - const CallExpr* CE = cast(cast(N->getLocation()).getStmt()); - - // If the CallExpr doesn't have exactly 1 argument just give up checking. - if (CE->getNumArgs() != 1) - return false; - - // Check if we called CFRetain/CFRelease. - const GRState* state = N->getState(); - SVal X = state->getSVal(CE->getCallee()); - const FunctionDecl* FD = X.getAsFunctionDecl(); - - if (!FD) - return false; - - const IdentifierInfo *FuncII = FD->getIdentifier(); - if (!(FuncII == Retain || FuncII == Release)) - return false; - - // Finally, check if the argument is NULL. - // FIXME: We should be able to bifurcate the state here, as a successful - // check will result in the value not being NULL afterwards. - // FIXME: Need a way to register vistors for the BugReporter. Would like - // to benefit from the same diagnostics that regular null dereference - // reporting has. - if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) { - if (!BT) - BT = new APIMisuse("null passed to CFRetain/CFRelease"); - - const char *description = (FuncII == Retain) - ? "Null pointer argument in call to CFRetain" - : "Null pointer argument in call to CFRelease"; - - RangedBugReport *report = new RangedBugReport(*BT, description, N); - report->addRange(CE->getArg(0)->getSourceRange()); - BR.EmitReport(report); - return true; - } - - return false; -} - - -GRSimpleAPICheck* -clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) { - return new AuditCFRetainRelease(Ctx, BR); -} - -//===----------------------------------------------------------------------===// -// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. -//===----------------------------------------------------------------------===// - -namespace { -class ClassReleaseChecker : - public CheckerVisitor { - Selector releaseS; - Selector retainS; - Selector autoreleaseS; - Selector drainS; - BugType *BT; -public: - ClassReleaseChecker(ASTContext &Ctx) - : releaseS(GetNullarySelector("release", Ctx)), - retainS(GetNullarySelector("retain", Ctx)), - autoreleaseS(GetNullarySelector("autorelease", Ctx)), - drainS(GetNullarySelector("drain", Ctx)), - BT(0) {} - - static void *getTag() { static int x = 0; return &x; } - - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); -}; -} - -void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) { - - const IdentifierInfo *ClsName = ME->getClassName(); - if (!ClsName) - return; - - Selector S = ME->getSelector(); - if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) - return; - - if (!BT) - BT = new APIMisuse("message incorrectly sent to class instead of class " - "instance"); - - ExplodedNode *N = C.GenerateNode(); - - if (!N) - return; - - llvm::SmallString<200> buf; - llvm::raw_svector_ostream os(buf); - - os << "The '" << S.getAsString() << "' message should be sent to instances " - "of class '" << ClsName->getName() - << "' and not the class directly"; - - RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); - report->addRange(ME->getSourceRange()); - C.EmitReport(report); -} - -//===----------------------------------------------------------------------===// -// Check registration. -//===----------------------------------------------------------------------===// - -void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) { - ASTContext& Ctx = Eng.getContext(); - BugReporter &BR = Eng.getBugReporter(); - - Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR), - Stmt::ObjCMessageExprClass); - Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass); - Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass); - - RegisterNSErrorChecks(BR, Eng, D); - RegisterNSAutoreleasePoolChecks(Eng); - Eng.registerCheck(new ClassReleaseChecker(Ctx)); -} diff --git a/lib/Analysis/BasicObjCFoundationChecks.h b/lib/Analysis/BasicObjCFoundationChecks.h deleted file mode 100644 index 679c6dc1df2d..000000000000 --- a/lib/Analysis/BasicObjCFoundationChecks.h +++ /dev/null @@ -1,41 +0,0 @@ -//== BasicObjCFoundationChecks.h - Simple Apple-Foundation checks -*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines BasicObjCFoundationChecks, a class that encapsulates -// a set of simple checks to run on Objective-C code using Apple's Foundation -// classes. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_ANALYSIS_BASICOBJCFOUNDATIONCHECKS -#define LLVM_CLANG_ANALYSIS_BASICOBJCFOUNDATIONCHECKS - -namespace clang { - -class ASTContext; -class BugReporter; -class Decl; -class GRExprEngine; -class GRSimpleAPICheck; - -GRSimpleAPICheck *CreateBasicObjCFoundationChecks(ASTContext& Ctx, - BugReporter& BR); - -GRSimpleAPICheck *CreateAuditCFNumberCreate(ASTContext& Ctx, - BugReporter& BR); - -GRSimpleAPICheck *CreateAuditCFRetainRelease(ASTContext& Ctx, - BugReporter& BR); - -void RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng, const Decl &D); -void RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng); - -} // end clang namespace - -#endif diff --git a/lib/Analysis/BasicStore.cpp b/lib/Analysis/BasicStore.cpp deleted file mode 100644 index 224281b17770..000000000000 --- a/lib/Analysis/BasicStore.cpp +++ /dev/null @@ -1,625 +0,0 @@ -//== BasicStore.cpp - Basic map from Locations to Values --------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defined the BasicStore and BasicStoreManager classes. -// -//===----------------------------------------------------------------------===// - -#include "clang/AST/ExprObjC.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/Analysis/PathSensitive/AnalysisContext.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "llvm/ADT/ImmutableMap.h" - -using namespace clang; - -typedef llvm::ImmutableMap BindingsTy; - -namespace { - -class BasicStoreSubRegionMap : public SubRegionMap { -public: - BasicStoreSubRegionMap() {} - - bool iterSubRegions(const MemRegion* R, Visitor& V) const { - return true; // Do nothing. No subregions. - } -}; - -class BasicStoreManager : public StoreManager { - BindingsTy::Factory VBFactory; -public: - BasicStoreManager(GRStateManager& mgr) - : StoreManager(mgr), VBFactory(mgr.getAllocator()) {} - - ~BasicStoreManager() {} - - SubRegionMap *getSubRegionMap(const GRState *state) { - return new BasicStoreSubRegionMap(); - } - - SValuator::CastResult Retrieve(const GRState *state, Loc loc, - QualType T = QualType()); - - const GRState *InvalidateRegion(const GRState *state, const MemRegion *R, - const Expr *E, unsigned Count, - InvalidatedSymbols *IS); - - const GRState *Bind(const GRState *state, Loc L, SVal V) { - return state->makeWithStore(BindInternal(state->getStore(), L, V)); - } - - Store scanForIvars(Stmt *B, const Decl* SelfDecl, - const MemRegion *SelfRegion, Store St); - - Store BindInternal(Store St, Loc loc, SVal V); - Store Remove(Store St, Loc loc); - Store getInitialStore(const LocationContext *InitLoc); - - // FIXME: Investigate what is using this. This method should be removed. - virtual Loc getLoc(const VarDecl* VD, const LocationContext *LC) { - return ValMgr.makeLoc(MRMgr.getVarRegion(VD, LC)); - } - - const GRState *BindCompoundLiteral(const GRState *state, - const CompoundLiteralExpr*, - const LocationContext*, - SVal val) { - return state; - } - - SVal getLValueVar(const VarDecl *VD, const LocationContext *LC); - SVal getLValueString(const StringLiteral *S); - SVal getLValueIvar(const ObjCIvarDecl* D, SVal Base); - SVal getLValueField(const FieldDecl *D, SVal Base); - SVal getLValueElement(QualType elementType, SVal Offset, SVal Base); - - /// ArrayToPointer - Used by GRExprEngine::VistCast to handle implicit - /// conversions between arrays and pointers. - SVal ArrayToPointer(Loc Array) { return Array; } - - /// RemoveDeadBindings - Scans a BasicStore of 'state' for dead values. - /// It updatees the GRState object in place with the values removed. - void RemoveDeadBindings(GRState &state, Stmt* Loc, SymbolReaper& SymReaper, - llvm::SmallVectorImpl& RegionRoots); - - void iterBindings(Store store, BindingsHandler& f); - - const GRState *BindDecl(const GRState *state, const VarRegion *VR, - SVal InitVal) { - return state->makeWithStore(BindDeclInternal(state->getStore(), VR, - &InitVal)); - } - - const GRState *BindDeclWithNoInit(const GRState *state, const VarRegion *VR) { - return state->makeWithStore(BindDeclInternal(state->getStore(), VR, 0)); - } - - Store BindDeclInternal(Store store, const VarRegion *VR, SVal *InitVal); - - static inline BindingsTy GetBindings(Store store) { - return BindingsTy(static_cast(store)); - } - - void print(Store store, llvm::raw_ostream& Out, const char* nl, - const char *sep); - -private: - ASTContext& getContext() { return StateMgr.getContext(); } -}; - -} // end anonymous namespace - - -StoreManager* clang::CreateBasicStoreManager(GRStateManager& StMgr) { - return new BasicStoreManager(StMgr); -} - -SVal BasicStoreManager::getLValueVar(const VarDecl* VD, - const LocationContext *LC) { - return ValMgr.makeLoc(MRMgr.getVarRegion(VD, LC)); -} - -SVal BasicStoreManager::getLValueString(const StringLiteral* S) { - return ValMgr.makeLoc(MRMgr.getStringRegion(S)); -} - -SVal BasicStoreManager::getLValueIvar(const ObjCIvarDecl* D, SVal Base) { - - if (Base.isUnknownOrUndef()) - return Base; - - Loc BaseL = cast(Base); - - if (isa(BaseL)) { - const MemRegion *BaseR = cast(BaseL).getRegion(); - return ValMgr.makeLoc(MRMgr.getObjCIvarRegion(D, BaseR)); - } - - return UnknownVal(); -} - -SVal BasicStoreManager::getLValueField(const FieldDecl* D, SVal Base) { - - if (Base.isUnknownOrUndef()) - return Base; - - Loc BaseL = cast(Base); - const MemRegion* BaseR = 0; - - switch(BaseL.getSubKind()) { - case loc::GotoLabelKind: - return UndefinedVal(); - - case loc::MemRegionKind: - BaseR = cast(BaseL).getRegion(); - break; - - case loc::ConcreteIntKind: - // While these seem funny, this can happen through casts. - // FIXME: What we should return is the field offset. For example, - // add the field offset to the integer value. That way funny things - // like this work properly: &(((struct foo *) 0xa)->f) - return Base; - - default: - assert ("Unhandled Base."); - return Base; - } - - return ValMgr.makeLoc(MRMgr.getFieldRegion(D, BaseR)); -} - -SVal BasicStoreManager::getLValueElement(QualType elementType, - SVal Offset, SVal Base) { - - if (Base.isUnknownOrUndef()) - return Base; - - Loc BaseL = cast(Base); - const MemRegion* BaseR = 0; - - switch(BaseL.getSubKind()) { - case loc::GotoLabelKind: - // Technically we can get here if people do funny things with casts. - return UndefinedVal(); - - case loc::MemRegionKind: { - const MemRegion *R = cast(BaseL).getRegion(); - - if (isa(R)) { - // int x; - // char* y = (char*) &x; - // 'y' => ElementRegion(0, VarRegion('x')) - // y[0] = 'a'; - return Base; - } - - if (isa(R) || isa(R)) { - BaseR = R; - break; - } - - break; - } - - case loc::ConcreteIntKind: - // While these seem funny, this can happen through casts. - // FIXME: What we should return is the field offset. For example, - // add the field offset to the integer value. That way funny things - // like this work properly: &(((struct foo *) 0xa)->f) - return Base; - - default: - assert ("Unhandled Base."); - return Base; - } - - if (BaseR) { - return ValMgr.makeLoc(MRMgr.getElementRegion(elementType, UnknownVal(), - BaseR, getContext())); - } - else - return UnknownVal(); -} - -static bool isHigherOrderRawPtr(QualType T, ASTContext &C) { - bool foundPointer = false; - while (1) { - const PointerType *PT = T->getAs(); - if (!PT) { - if (!foundPointer) - return false; - - // intptr_t* or intptr_t**, etc? - if (T->isIntegerType() && C.getTypeSize(T) == C.getTypeSize(C.VoidPtrTy)) - return true; - - QualType X = C.getCanonicalType(T).getUnqualifiedType(); - return X == C.VoidTy; - } - - foundPointer = true; - T = PT->getPointeeType(); - } -} - -SValuator::CastResult BasicStoreManager::Retrieve(const GRState *state, - Loc loc, QualType T) { - - if (isa(loc)) - return SValuator::CastResult(state, UnknownVal()); - - assert(!isa(loc)); - - switch (loc.getSubKind()) { - - case loc::MemRegionKind: { - const MemRegion* R = cast(loc).getRegion(); - - if (!(isa(R) || isa(R))) - return SValuator::CastResult(state, UnknownVal()); - - BindingsTy B = GetBindings(state->getStore()); - BindingsTy::data_type *Val = B.lookup(R); - - if (!Val) - break; - - return SValuator::CastResult(state, - CastRetrievedVal(*Val, cast(R), T)); - } - - case loc::ConcreteIntKind: - // Some clients may call GetSVal with such an option simply because - // they are doing a quick scan through their Locs (potentially to - // invalidate their bindings). Just return Undefined. - return SValuator::CastResult(state, UndefinedVal()); - - default: - assert (false && "Invalid Loc."); - break; - } - - return SValuator::CastResult(state, UnknownVal()); -} - -Store BasicStoreManager::BindInternal(Store store, Loc loc, SVal V) { - if (isa(loc)) - return store; - - const MemRegion* R = cast(loc).getRegion(); - ASTContext &C = StateMgr.getContext(); - - // Special case: handle store of pointer values (Loc) to pointers via - // a cast to intXX_t*, void*, etc. This is needed to handle - // OSCompareAndSwap32Barrier/OSCompareAndSwap64Barrier. - if (isa(V) || isa(V)) - if (const ElementRegion *ER = dyn_cast(R)) { - // FIXME: Should check for index 0. - QualType T = ER->getLocationType(C); - - if (isHigherOrderRawPtr(T, C)) - R = ER->getSuperRegion(); - } - - if (!(isa(R) || isa(R))) - return store; - - const TypedRegion *TyR = cast(R); - - // Do not bind to arrays. We need to explicitly check for this so that - // we do not encounter any weirdness of trying to load/store from arrays. - if (TyR->isBoundable() && TyR->getValueType(C)->isArrayType()) - return store; - - if (nonloc::LocAsInteger *X = dyn_cast(&V)) { - // Only convert 'V' to a location iff the underlying region type - // is a location as well. - // FIXME: We are allowing a store of an arbitrary location to - // a pointer. We may wish to flag a type error here if the types - // are incompatible. This may also cause lots of breakage - // elsewhere. Food for thought. - if (TyR->isBoundable() && Loc::IsLocType(TyR->getValueType(C))) - V = X->getLoc(); - } - - BindingsTy B = GetBindings(store); - return V.isUnknown() - ? VBFactory.Remove(B, R).getRoot() - : VBFactory.Add(B, R, V).getRoot(); -} - -Store BasicStoreManager::Remove(Store store, Loc loc) { - switch (loc.getSubKind()) { - case loc::MemRegionKind: { - const MemRegion* R = cast(loc).getRegion(); - - if (!(isa(R) || isa(R))) - return store; - - return VBFactory.Remove(GetBindings(store), R).getRoot(); - } - default: - assert ("Remove for given Loc type not yet implemented."); - return store; - } -} - -void -BasicStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl& RegionRoots) -{ - Store store = state.getStore(); - BindingsTy B = GetBindings(store); - typedef SVal::symbol_iterator symbol_iterator; - - // Iterate over the variable bindings. - for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { - if (const VarRegion *VR = dyn_cast(I.getKey())) { - if (SymReaper.isLive(Loc, VR)) - RegionRoots.push_back(VR); - else - continue; - } - else if (isa(I.getKey())) { - RegionRoots.push_back(I.getKey()); - } - else - continue; - - // Mark the bindings in the data as live. - SVal X = I.getData(); - for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) - SymReaper.markLive(*SI); - } - - // Scan for live variables and live symbols. - llvm::SmallPtrSet Marked; - - while (!RegionRoots.empty()) { - const MemRegion* MR = RegionRoots.back(); - RegionRoots.pop_back(); - - while (MR) { - if (const SymbolicRegion* SymR = dyn_cast(MR)) { - SymReaper.markLive(SymR->getSymbol()); - break; - } - else if (isa(MR) || isa(MR)) { - if (Marked.count(MR)) - break; - - Marked.insert(MR); - SVal X = Retrieve(&state, loc::MemRegionVal(MR)).getSVal(); - - // FIXME: We need to handle symbols nested in region definitions. - for (symbol_iterator SI=X.symbol_begin(),SE=X.symbol_end();SI!=SE;++SI) - SymReaper.markLive(*SI); - - if (!isa(X)) - break; - - const loc::MemRegionVal& LVD = cast(X); - RegionRoots.push_back(LVD.getRegion()); - break; - } - else if (const SubRegion* R = dyn_cast(MR)) - MR = R->getSuperRegion(); - else - break; - } - } - - // Remove dead variable bindings. - for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { - const MemRegion* R = I.getKey(); - - if (!Marked.count(R)) { - store = Remove(store, ValMgr.makeLoc(R)); - SVal X = I.getData(); - - for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) - SymReaper.maybeDead(*SI); - } - } - - // Write the store back. - state.setStore(store); -} - -Store BasicStoreManager::scanForIvars(Stmt *B, const Decl* SelfDecl, - const MemRegion *SelfRegion, Store St) { - for (Stmt::child_iterator CI=B->child_begin(), CE=B->child_end(); - CI != CE; ++CI) { - - if (!*CI) - continue; - - // Check if the statement is an ivar reference. We only - // care about self.ivar. - if (ObjCIvarRefExpr *IV = dyn_cast(*CI)) { - const Expr *Base = IV->getBase()->IgnoreParenCasts(); - if (const DeclRefExpr *DR = dyn_cast(Base)) { - if (DR->getDecl() == SelfDecl) { - const MemRegion *IVR = MRMgr.getObjCIvarRegion(IV->getDecl(), - SelfRegion); - SVal X = ValMgr.getRegionValueSymbolVal(IVR); - St = BindInternal(St, ValMgr.makeLoc(IVR), X); - } - } - } - else - St = scanForIvars(*CI, SelfDecl, SelfRegion, St); - } - - return St; -} - -Store BasicStoreManager::getInitialStore(const LocationContext *InitLoc) { - // The LiveVariables information already has a compilation of all VarDecls - // used in the function. Iterate through this set, and "symbolicate" - // any VarDecl whose value originally comes from outside the function. - typedef LiveVariables::AnalysisDataTy LVDataTy; - LVDataTy& D = InitLoc->getLiveVariables()->getAnalysisData(); - Store St = VBFactory.GetEmptyMap().getRoot(); - - for (LVDataTy::decl_iterator I=D.begin_decl(), E=D.end_decl(); I != E; ++I) { - NamedDecl* ND = const_cast(I->first); - - // Handle implicit parameters. - if (ImplicitParamDecl* PD = dyn_cast(ND)) { - const Decl& CD = *InitLoc->getDecl(); - if (const ObjCMethodDecl* MD = dyn_cast(&CD)) { - if (MD->getSelfDecl() == PD) { - // FIXME: Add type constraints (when they become available) to - // SelfRegion? (i.e., it implements MD->getClassInterface()). - const MemRegion *VR = MRMgr.getVarRegion(PD, InitLoc); - const MemRegion *SelfRegion = - ValMgr.getRegionValueSymbolVal(VR).getAsRegion(); - assert(SelfRegion); - St = BindInternal(St, ValMgr.makeLoc(VR), - loc::MemRegionVal(SelfRegion)); - // Scan the method for ivar references. While this requires an - // entire AST scan, the cost should not be high in practice. - St = scanForIvars(MD->getBody(), PD, SelfRegion, St); - } - } - } - else if (VarDecl* VD = dyn_cast(ND)) { - // Only handle simple types that we can symbolicate. - if (!SymbolManager::canSymbolicate(VD->getType())) - continue; - - // Initialize globals and parameters to symbolic values. - // Initialize local variables to undefined. - const MemRegion *R = ValMgr.getRegionManager().getVarRegion(VD, InitLoc); - SVal X = UndefinedVal(); - if (R->hasGlobalsOrParametersStorage()) - X = ValMgr.getRegionValueSymbolVal(R); - - St = BindInternal(St, ValMgr.makeLoc(R), X); - } - } - return St; -} - -Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR, - SVal* InitVal) { - - BasicValueFactory& BasicVals = StateMgr.getBasicVals(); - const VarDecl *VD = VR->getDecl(); - - // BasicStore does not model arrays and structs. - if (VD->getType()->isArrayType() || VD->getType()->isStructureType()) - return store; - - if (VD->hasGlobalStorage()) { - // Handle variables with global storage: extern, static, PrivateExtern. - - // FIXME:: static variables may have an initializer, but the second time a - // function is called those values may not be current. Currently, a function - // will not be called more than once. - - // Static global variables should not be visited here. - assert(!(VD->getStorageClass() == VarDecl::Static && - VD->isFileVarDecl())); - - // Process static variables. - if (VD->getStorageClass() == VarDecl::Static) { - // C99: 6.7.8 Initialization - // If an object that has static storage duration is not initialized - // explicitly, then: - // —if it has pointer type, it is initialized to a null pointer; - // —if it has arithmetic type, it is initialized to (positive or - // unsigned) zero; - if (!InitVal) { - QualType T = VD->getType(); - if (Loc::IsLocType(T)) - store = BindInternal(store, loc::MemRegionVal(VR), - loc::ConcreteInt(BasicVals.getValue(0, T))); - else if (T->isIntegerType()) - store = BindInternal(store, loc::MemRegionVal(VR), - nonloc::ConcreteInt(BasicVals.getValue(0, T))); - else { - // assert(0 && "ignore other types of variables"); - } - } else { - store = BindInternal(store, loc::MemRegionVal(VR), *InitVal); - } - } - } else { - // Process local scalar variables. - QualType T = VD->getType(); - if (ValMgr.getSymbolManager().canSymbolicate(T)) { - SVal V = InitVal ? *InitVal : UndefinedVal(); - store = BindInternal(store, loc::MemRegionVal(VR), V); - } - } - - return store; -} - -void BasicStoreManager::print(Store store, llvm::raw_ostream& Out, - const char* nl, const char *sep) { - - BindingsTy B = GetBindings(store); - Out << "Variables:" << nl; - - bool isFirst = true; - - for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) { - if (isFirst) - isFirst = false; - else - Out << nl; - - Out << ' ' << I.getKey() << " : " << I.getData(); - } -} - - -void BasicStoreManager::iterBindings(Store store, BindingsHandler& f) { - BindingsTy B = GetBindings(store); - - for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) - f.HandleBinding(*this, store, I.getKey(), I.getData()); - -} - -StoreManager::BindingsHandler::~BindingsHandler() {} - -//===----------------------------------------------------------------------===// -// Binding invalidation. -//===----------------------------------------------------------------------===// - -const GRState *BasicStoreManager::InvalidateRegion(const GRState *state, - const MemRegion *R, - const Expr *E, - unsigned Count, - InvalidatedSymbols *IS) { - R = R->StripCasts(); - - if (!(isa(R) || isa(R))) - return state; - - if (IS) { - BindingsTy B = GetBindings(state->getStore()); - if (BindingsTy::data_type *Val = B.lookup(R)) { - if (SymbolRef Sym = Val->getAsSymbol()) - IS->insert(Sym); - } - } - - QualType T = cast(R)->getValueType(R->getContext()); - SVal V = ValMgr.getConjuredSymbolVal(R, E, T, Count); - return Bind(state, loc::MemRegionVal(R), V); -} - diff --git a/lib/Analysis/BasicValueFactory.cpp b/lib/Analysis/BasicValueFactory.cpp deleted file mode 100644 index b33c277f86f9..000000000000 --- a/lib/Analysis/BasicValueFactory.cpp +++ /dev/null @@ -1,290 +0,0 @@ -//=== BasicValueFactory.cpp - Basic values for Path Sens analysis --*- C++ -*-// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines BasicValueFactory, a class that manages the lifetime -// of APSInt objects and symbolic constraints used by GRExprEngine -// and related classes. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/BasicValueFactory.h" - -using namespace clang; - -void CompoundValData::Profile(llvm::FoldingSetNodeID& ID, QualType T, - llvm::ImmutableList L) { - T.Profile(ID); - ID.AddPointer(L.getInternalPointer()); -} - -void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID, - const GRState *state, - const TypedRegion *region) { - ID.AddPointer(state); - ID.AddPointer(region); -} - -typedef std::pair SValData; -typedef std::pair SValPair; - -namespace llvm { -template<> struct FoldingSetTrait { - static inline void Profile(const SValData& X, llvm::FoldingSetNodeID& ID) { - X.first.Profile(ID); - ID.AddPointer( (void*) X.second); - } -}; - -template<> struct FoldingSetTrait { - static inline void Profile(const SValPair& X, llvm::FoldingSetNodeID& ID) { - X.first.Profile(ID); - X.second.Profile(ID); - } -}; -} - -typedef llvm::FoldingSet > - PersistentSValsTy; - -typedef llvm::FoldingSet > - PersistentSValPairsTy; - -BasicValueFactory::~BasicValueFactory() { - // Note that the dstor for the contents of APSIntSet will never be called, - // so we iterate over the set and invoke the dstor for each APSInt. This - // frees an aux. memory allocated to represent very large constants. - for (APSIntSetTy::iterator I=APSIntSet.begin(), E=APSIntSet.end(); I!=E; ++I) - I->getValue().~APSInt(); - - delete (PersistentSValsTy*) PersistentSVals; - delete (PersistentSValPairsTy*) PersistentSValPairs; -} - -const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) { - llvm::FoldingSetNodeID ID; - void* InsertPos; - typedef llvm::FoldingSetNodeWrapper FoldNodeTy; - - X.Profile(ID); - FoldNodeTy* P = APSIntSet.FindNodeOrInsertPos(ID, InsertPos); - - if (!P) { - P = (FoldNodeTy*) BPAlloc.Allocate(); - new (P) FoldNodeTy(X); - APSIntSet.InsertNode(P, InsertPos); - } - - return *P; -} - -const llvm::APSInt& BasicValueFactory::getValue(const llvm::APInt& X, - bool isUnsigned) { - llvm::APSInt V(X, isUnsigned); - return getValue(V); -} - -const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, unsigned BitWidth, - bool isUnsigned) { - llvm::APSInt V(BitWidth, isUnsigned); - V = X; - return getValue(V); -} - -const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, QualType T) { - - unsigned bits = Ctx.getTypeSize(T); - llvm::APSInt V(bits, T->isUnsignedIntegerType() || Loc::IsLocType(T)); - V = X; - return getValue(V); -} - -const CompoundValData* -BasicValueFactory::getCompoundValData(QualType T, - llvm::ImmutableList Vals) { - - llvm::FoldingSetNodeID ID; - CompoundValData::Profile(ID, T, Vals); - void* InsertPos; - - CompoundValData* D = CompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); - - if (!D) { - D = (CompoundValData*) BPAlloc.Allocate(); - new (D) CompoundValData(T, Vals); - CompoundValDataSet.InsertNode(D, InsertPos); - } - - return D; -} - -const LazyCompoundValData* -BasicValueFactory::getLazyCompoundValData(const GRState *state, - const TypedRegion *region) { - llvm::FoldingSetNodeID ID; - LazyCompoundValData::Profile(ID, state, region); - void* InsertPos; - - LazyCompoundValData *D = - LazyCompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); - - if (!D) { - D = (LazyCompoundValData*) BPAlloc.Allocate(); - new (D) LazyCompoundValData(state, region); - LazyCompoundValDataSet.InsertNode(D, InsertPos); - } - - return D; -} - -const llvm::APSInt* -BasicValueFactory::EvaluateAPSInt(BinaryOperator::Opcode Op, - const llvm::APSInt& V1, const llvm::APSInt& V2) { - - switch (Op) { - default: - assert (false && "Invalid Opcode."); - - case BinaryOperator::Mul: - return &getValue( V1 * V2 ); - - case BinaryOperator::Div: - return &getValue( V1 / V2 ); - - case BinaryOperator::Rem: - return &getValue( V1 % V2 ); - - case BinaryOperator::Add: - return &getValue( V1 + V2 ); - - case BinaryOperator::Sub: - return &getValue( V1 - V2 ); - - case BinaryOperator::Shl: { - - // FIXME: This logic should probably go higher up, where we can - // test these conditions symbolically. - - // FIXME: Expand these checks to include all undefined behavior. - - if (V2.isSigned() && V2.isNegative()) - return NULL; - - uint64_t Amt = V2.getZExtValue(); - - if (Amt > V1.getBitWidth()) - return NULL; - - return &getValue( V1.operator<<( (unsigned) Amt )); - } - - case BinaryOperator::Shr: { - - // FIXME: This logic should probably go higher up, where we can - // test these conditions symbolically. - - // FIXME: Expand these checks to include all undefined behavior. - - if (V2.isSigned() && V2.isNegative()) - return NULL; - - uint64_t Amt = V2.getZExtValue(); - - if (Amt > V1.getBitWidth()) - return NULL; - - return &getValue( V1.operator>>( (unsigned) Amt )); - } - - case BinaryOperator::LT: - return &getTruthValue( V1 < V2 ); - - case BinaryOperator::GT: - return &getTruthValue( V1 > V2 ); - - case BinaryOperator::LE: - return &getTruthValue( V1 <= V2 ); - - case BinaryOperator::GE: - return &getTruthValue( V1 >= V2 ); - - case BinaryOperator::EQ: - return &getTruthValue( V1 == V2 ); - - case BinaryOperator::NE: - return &getTruthValue( V1 != V2 ); - - // Note: LAnd, LOr, Comma are handled specially by higher-level logic. - - case BinaryOperator::And: - return &getValue( V1 & V2 ); - - case BinaryOperator::Or: - return &getValue( V1 | V2 ); - - case BinaryOperator::Xor: - return &getValue( V1 ^ V2 ); - } -} - - -const std::pair& -BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) { - - // Lazily create the folding set. - if (!PersistentSVals) PersistentSVals = new PersistentSValsTy(); - - llvm::FoldingSetNodeID ID; - void* InsertPos; - V.Profile(ID); - ID.AddPointer((void*) Data); - - PersistentSValsTy& Map = *((PersistentSValsTy*) PersistentSVals); - - typedef llvm::FoldingSetNodeWrapper FoldNodeTy; - FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); - - if (!P) { - P = (FoldNodeTy*) BPAlloc.Allocate(); - new (P) FoldNodeTy(std::make_pair(V, Data)); - Map.InsertNode(P, InsertPos); - } - - return P->getValue(); -} - -const std::pair& -BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) { - - // Lazily create the folding set. - if (!PersistentSValPairs) PersistentSValPairs = new PersistentSValPairsTy(); - - llvm::FoldingSetNodeID ID; - void* InsertPos; - V1.Profile(ID); - V2.Profile(ID); - - PersistentSValPairsTy& Map = *((PersistentSValPairsTy*) PersistentSValPairs); - - typedef llvm::FoldingSetNodeWrapper FoldNodeTy; - FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); - - if (!P) { - P = (FoldNodeTy*) BPAlloc.Allocate(); - new (P) FoldNodeTy(std::make_pair(V1, V2)); - Map.InsertNode(P, InsertPos); - } - - return P->getValue(); -} - -const SVal* BasicValueFactory::getPersistentSVal(SVal X) { - return &getPersistentSValWithData(X, 0).first; -} - - diff --git a/lib/Analysis/BugReporter.cpp b/lib/Analysis/BugReporter.cpp deleted file mode 100644 index 2a9531df60f1..000000000000 --- a/lib/Analysis/BugReporter.cpp +++ /dev/null @@ -1,1879 +0,0 @@ -// BugReporter.cpp - Generate PathDiagnostics for Bugs ------------*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines BugReporter, a utility class for generating -// PathDiagnostics. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/AST/ASTContext.h" -#include "clang/Analysis/CFG.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ParentMap.h" -#include "clang/AST/StmtObjC.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Analysis/ProgramPoint.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/OwningPtr.h" -#include - -using namespace clang; - -BugReporterVisitor::~BugReporterVisitor() {} -BugReporterContext::~BugReporterContext() { - for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) - if ((*I)->isOwnedByReporterContext()) delete *I; -} - -//===----------------------------------------------------------------------===// -// Helper routines for walking the ExplodedGraph and fetching statements. -//===----------------------------------------------------------------------===// - -static inline const Stmt* GetStmt(ProgramPoint P) { - if (const StmtPoint* SP = dyn_cast(&P)) - return SP->getStmt(); - else if (const BlockEdge* BE = dyn_cast(&P)) - return BE->getSrc()->getTerminator(); - - return 0; -} - -static inline const ExplodedNode* -GetPredecessorNode(const ExplodedNode* N) { - return N->pred_empty() ? NULL : *(N->pred_begin()); -} - -static inline const ExplodedNode* -GetSuccessorNode(const ExplodedNode* N) { - return N->succ_empty() ? NULL : *(N->succ_begin()); -} - -static const Stmt* GetPreviousStmt(const ExplodedNode* N) { - for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N)) - if (const Stmt *S = GetStmt(N->getLocation())) - return S; - - return 0; -} - -static const Stmt* GetNextStmt(const ExplodedNode* N) { - for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N)) - if (const Stmt *S = GetStmt(N->getLocation())) { - // Check if the statement is '?' or '&&'/'||'. These are "merges", - // not actual statement points. - switch (S->getStmtClass()) { - case Stmt::ChooseExprClass: - case Stmt::ConditionalOperatorClass: continue; - case Stmt::BinaryOperatorClass: { - BinaryOperator::Opcode Op = cast(S)->getOpcode(); - if (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr) - continue; - break; - } - default: - break; - } - - // Some expressions don't have locations. - if (S->getLocStart().isInvalid()) - continue; - - return S; - } - - return 0; -} - -static inline const Stmt* -GetCurrentOrPreviousStmt(const ExplodedNode* N) { - if (const Stmt *S = GetStmt(N->getLocation())) - return S; - - return GetPreviousStmt(N); -} - -static inline const Stmt* -GetCurrentOrNextStmt(const ExplodedNode* N) { - if (const Stmt *S = GetStmt(N->getLocation())) - return S; - - return GetNextStmt(N); -} - -//===----------------------------------------------------------------------===// -// PathDiagnosticBuilder and its associated routines and helper objects. -//===----------------------------------------------------------------------===// - -typedef llvm::DenseMap NodeBackMap; - -namespace { -class NodeMapClosure : public BugReport::NodeResolver { - NodeBackMap& M; -public: - NodeMapClosure(NodeBackMap *m) : M(*m) {} - ~NodeMapClosure() {} - - const ExplodedNode* getOriginalNode(const ExplodedNode* N) { - NodeBackMap::iterator I = M.find(N); - return I == M.end() ? 0 : I->second; - } -}; - -class PathDiagnosticBuilder : public BugReporterContext { - BugReport *R; - PathDiagnosticClient *PDC; - llvm::OwningPtr PM; - NodeMapClosure NMC; -public: - PathDiagnosticBuilder(GRBugReporter &br, - BugReport *r, NodeBackMap *Backmap, - PathDiagnosticClient *pdc) - : BugReporterContext(br), - R(r), PDC(pdc), NMC(Backmap) { - addVisitor(R); - } - - PathDiagnosticLocation ExecutionContinues(const ExplodedNode* N); - - PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream& os, - const ExplodedNode* N); - - Decl const &getCodeDecl() { return R->getEndNode()->getCodeDecl(); } - - ParentMap& getParentMap() { return R->getEndNode()->getParentMap(); } - - const Stmt *getParent(const Stmt *S) { - return getParentMap().getParent(S); - } - - virtual NodeMapClosure& getNodeResolver() { return NMC; } - BugReport& getReport() { return *R; } - - PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); - - PathDiagnosticLocation - getEnclosingStmtLocation(const PathDiagnosticLocation &L) { - if (const Stmt *S = L.asStmt()) - return getEnclosingStmtLocation(S); - - return L; - } - - PathDiagnosticClient::PathGenerationScheme getGenerationScheme() const { - return PDC ? PDC->getGenerationScheme() : PathDiagnosticClient::Extensive; - } - - bool supportsLogicalOpControlFlow() const { - return PDC ? PDC->supportsLogicalOpControlFlow() : true; - } -}; -} // end anonymous namespace - -PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode* N) { - if (const Stmt *S = GetNextStmt(N)) - return PathDiagnosticLocation(S, getSourceManager()); - - return FullSourceLoc(N->getLocationContext()->getDecl()->getBodyRBrace(), - getSourceManager()); -} - -PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os, - const ExplodedNode* N) { - - // Slow, but probably doesn't matter. - if (os.str().empty()) - os << ' '; - - const PathDiagnosticLocation &Loc = ExecutionContinues(N); - - if (Loc.asStmt()) - os << "Execution continues on line " - << getSourceManager().getInstantiationLineNumber(Loc.asLocation()) - << '.'; - else { - os << "Execution jumps to the end of the "; - const Decl *D = N->getLocationContext()->getDecl(); - if (isa(D)) - os << "method"; - else if (isa(D)) - os << "function"; - else { - assert(isa(D)); - os << "anonymous block"; - } - os << '.'; - } - - return Loc; -} - -static bool IsNested(const Stmt *S, ParentMap &PM) { - if (isa(S) && PM.isConsumedExpr(cast(S))) - return true; - - const Stmt *Parent = PM.getParentIgnoreParens(S); - - if (Parent) - switch (Parent->getStmtClass()) { - case Stmt::ForStmtClass: - case Stmt::DoStmtClass: - case Stmt::WhileStmtClass: - return true; - default: - break; - } - - return false; -} - -PathDiagnosticLocation -PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { - assert(S && "Null Stmt* passed to getEnclosingStmtLocation"); - ParentMap &P = getParentMap(); - SourceManager &SMgr = getSourceManager(); - - while (IsNested(S, P)) { - const Stmt *Parent = P.getParentIgnoreParens(S); - - if (!Parent) - break; - - switch (Parent->getStmtClass()) { - case Stmt::BinaryOperatorClass: { - const BinaryOperator *B = cast(Parent); - if (B->isLogicalOp()) - return PathDiagnosticLocation(S, SMgr); - break; - } - case Stmt::CompoundStmtClass: - case Stmt::StmtExprClass: - return PathDiagnosticLocation(S, SMgr); - case Stmt::ChooseExprClass: - // Similar to '?' if we are referring to condition, just have the edge - // point to the entire choose expression. - if (cast(Parent)->getCond() == S) - return PathDiagnosticLocation(Parent, SMgr); - else - return PathDiagnosticLocation(S, SMgr); - case Stmt::ConditionalOperatorClass: - // For '?', if we are referring to condition, just have the edge point - // to the entire '?' expression. - if (cast(Parent)->getCond() == S) - return PathDiagnosticLocation(Parent, SMgr); - else - return PathDiagnosticLocation(S, SMgr); - case Stmt::DoStmtClass: - return PathDiagnosticLocation(S, SMgr); - case Stmt::ForStmtClass: - if (cast(Parent)->getBody() == S) - return PathDiagnosticLocation(S, SMgr); - break; - case Stmt::IfStmtClass: - if (cast(Parent)->getCond() != S) - return PathDiagnosticLocation(S, SMgr); - break; - case Stmt::ObjCForCollectionStmtClass: - if (cast(Parent)->getBody() == S) - return PathDiagnosticLocation(S, SMgr); - break; - case Stmt::WhileStmtClass: - if (cast(Parent)->getCond() != S) - return PathDiagnosticLocation(S, SMgr); - break; - default: - break; - } - - S = Parent; - } - - assert(S && "Cannot have null Stmt for PathDiagnosticLocation"); - - // Special case: DeclStmts can appear in for statement declarations, in which - // case the ForStmt is the context. - if (isa(S)) { - if (const Stmt *Parent = P.getParent(S)) { - switch (Parent->getStmtClass()) { - case Stmt::ForStmtClass: - case Stmt::ObjCForCollectionStmtClass: - return PathDiagnosticLocation(Parent, SMgr); - default: - break; - } - } - } - else if (isa(S)) { - // Special case: the binary operator represents the initialization - // code in a for statement (this can happen when the variable being - // initialized is an old variable. - if (const ForStmt *FS = - dyn_cast_or_null(P.getParentIgnoreParens(S))) { - if (FS->getInit() == S) - return PathDiagnosticLocation(FS, SMgr); - } - } - - return PathDiagnosticLocation(S, SMgr); -} - -//===----------------------------------------------------------------------===// -// ScanNotableSymbols: closure-like callback for scanning Store bindings. -//===----------------------------------------------------------------------===// - -static const VarDecl* -GetMostRecentVarDeclBinding(const ExplodedNode* N, - GRStateManager& VMgr, SVal X) { - - for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) { - - ProgramPoint P = N->getLocation(); - - if (!isa(P)) - continue; - - const DeclRefExpr* DR = dyn_cast(cast(P).getStmt()); - - if (!DR) - continue; - - SVal Y = N->getState()->getSVal(DR); - - if (X != Y) - continue; - - const VarDecl* VD = dyn_cast(DR->getDecl()); - - if (!VD) - continue; - - return VD; - } - - return 0; -} - -namespace { -class NotableSymbolHandler -: public StoreManager::BindingsHandler { - - SymbolRef Sym; - const GRState* PrevSt; - const Stmt* S; - GRStateManager& VMgr; - const ExplodedNode* Pred; - PathDiagnostic& PD; - BugReporter& BR; - -public: - - NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s, - GRStateManager& vmgr, const ExplodedNode* pred, - PathDiagnostic& pd, BugReporter& br) - : Sym(sym), PrevSt(prevst), S(s), VMgr(vmgr), Pred(pred), PD(pd), BR(br) {} - - bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, - SVal V) { - - SymbolRef ScanSym = V.getAsSymbol(); - - if (ScanSym != Sym) - return true; - - // Check if the previous state has this binding. - SVal X = PrevSt->getSVal(loc::MemRegionVal(R)); - - if (X == V) // Same binding? - return true; - - // Different binding. Only handle assignments for now. We don't pull - // this check out of the loop because we will eventually handle other - // cases. - - VarDecl *VD = 0; - - if (const BinaryOperator* B = dyn_cast(S)) { - if (!B->isAssignmentOp()) - return true; - - // What variable did we assign to? - DeclRefExpr* DR = dyn_cast(B->getLHS()->IgnoreParenCasts()); - - if (!DR) - return true; - - VD = dyn_cast(DR->getDecl()); - } - else if (const DeclStmt* DS = dyn_cast(S)) { - // FIXME: Eventually CFGs won't have DeclStmts. Right now we - // assume that each DeclStmt has a single Decl. This invariant - // holds by contruction in the CFG. - VD = dyn_cast(*DS->decl_begin()); - } - - if (!VD) - return true; - - // What is the most recently referenced variable with this binding? - const VarDecl* MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V); - - if (!MostRecent) - return true; - - // Create the diagnostic. - FullSourceLoc L(S->getLocStart(), BR.getSourceManager()); - - if (Loc::IsLocType(VD->getType())) { - std::string msg = "'" + std::string(VD->getNameAsString()) + - "' now aliases '" + MostRecent->getNameAsString() + "'"; - - PD.push_front(new PathDiagnosticEventPiece(L, msg)); - } - - return true; - } -}; -} - -static void HandleNotableSymbol(const ExplodedNode* N, - const Stmt* S, - SymbolRef Sym, BugReporter& BR, - PathDiagnostic& PD) { - - const ExplodedNode* Pred = N->pred_empty() ? 0 : *N->pred_begin(); - const GRState* PrevSt = Pred ? Pred->getState() : 0; - - if (!PrevSt) - return; - - // Look at the region bindings of the current state that map to the - // specified symbol. Are any of them not in the previous state? - GRStateManager& VMgr = cast(BR).getStateManager(); - NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR); - cast(BR).getStateManager().iterBindings(N->getState(), H); -} - -namespace { -class ScanNotableSymbols -: public StoreManager::BindingsHandler { - - llvm::SmallSet AlreadyProcessed; - const ExplodedNode* N; - const Stmt* S; - GRBugReporter& BR; - PathDiagnostic& PD; - -public: - ScanNotableSymbols(const ExplodedNode* n, const Stmt* s, - GRBugReporter& br, PathDiagnostic& pd) - : N(n), S(s), BR(br), PD(pd) {} - - bool HandleBinding(StoreManager& SMgr, Store store, - const MemRegion* R, SVal V) { - - SymbolRef ScanSym = V.getAsSymbol(); - - if (!ScanSym) - return true; - - if (!BR.isNotable(ScanSym)) - return true; - - if (AlreadyProcessed.count(ScanSym)) - return true; - - AlreadyProcessed.insert(ScanSym); - - HandleNotableSymbol(N, S, ScanSym, BR, PD); - return true; - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// "Minimal" path diagnostic generation algorithm. -//===----------------------------------------------------------------------===// - -static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM); - -static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode *N) { - - SourceManager& SMgr = PDB.getSourceManager(); - const ExplodedNode* NextNode = N->pred_empty() - ? NULL : *(N->pred_begin()); - while (NextNode) { - N = NextNode; - NextNode = GetPredecessorNode(N); - - ProgramPoint P = N->getLocation(); - - if (const BlockEdge* BE = dyn_cast(&P)) { - CFGBlock* Src = BE->getSrc(); - CFGBlock* Dst = BE->getDst(); - Stmt* T = Src->getTerminator(); - - if (!T) - continue; - - FullSourceLoc Start(T->getLocStart(), SMgr); - - switch (T->getStmtClass()) { - default: - break; - - case Stmt::GotoStmtClass: - case Stmt::IndirectGotoStmtClass: { - const Stmt* S = GetNextStmt(N); - - if (!S) - continue; - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); - - os << "Control jumps to line " - << End.asLocation().getInstantiationLineNumber(); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - break; - } - - case Stmt::SwitchStmtClass: { - // Figure out what case arm we took. - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - if (Stmt* S = Dst->getLabel()) { - PathDiagnosticLocation End(S, SMgr); - - switch (S->getStmtClass()) { - default: - os << "No cases match in the switch statement. " - "Control jumps to line " - << End.asLocation().getInstantiationLineNumber(); - break; - case Stmt::DefaultStmtClass: - os << "Control jumps to the 'default' case at line " - << End.asLocation().getInstantiationLineNumber(); - break; - - case Stmt::CaseStmtClass: { - os << "Control jumps to 'case "; - CaseStmt* Case = cast(S); - Expr* LHS = Case->getLHS()->IgnoreParenCasts(); - - // Determine if it is an enum. - bool GetRawInt = true; - - if (DeclRefExpr* DR = dyn_cast(LHS)) { - // FIXME: Maybe this should be an assertion. Are there cases - // were it is not an EnumConstantDecl? - EnumConstantDecl* D = - dyn_cast(DR->getDecl()); - - if (D) { - GetRawInt = false; - os << D->getNameAsString(); - } - } - - if (GetRawInt) - os << LHS->EvaluateAsInt(PDB.getASTContext()); - - os << ":' at line " - << End.asLocation().getInstantiationLineNumber(); - break; - } - } - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - else { - os << "'Default' branch taken. "; - const PathDiagnosticLocation &End = PDB.ExecutionContinues(os, N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - - break; - } - - case Stmt::BreakStmtClass: - case Stmt::ContinueStmtClass: { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - break; - } - - // Determine control-flow for ternary '?'. - case Stmt::ConditionalOperatorClass: { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "'?' condition is "; - - if (*(Src->succ_begin()+1) == Dst) - os << "false"; - else - os << "true"; - - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - break; - } - - // Determine control-flow for short-circuited '&&' and '||'. - case Stmt::BinaryOperatorClass: { - if (!PDB.supportsLogicalOpControlFlow()) - break; - - BinaryOperator *B = cast(T); - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Left side of '"; - - if (B->getOpcode() == BinaryOperator::LAnd) { - os << "&&" << "' is "; - - if (*(Src->succ_begin()+1) == Dst) { - os << "false"; - PathDiagnosticLocation End(B->getLHS(), SMgr); - PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - else { - os << "true"; - PathDiagnosticLocation Start(B->getLHS(), SMgr); - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - } - else { - assert(B->getOpcode() == BinaryOperator::LOr); - os << "||" << "' is "; - - if (*(Src->succ_begin()+1) == Dst) { - os << "false"; - PathDiagnosticLocation Start(B->getLHS(), SMgr); - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - else { - os << "true"; - PathDiagnosticLocation End(B->getLHS(), SMgr); - PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - } - - break; - } - - case Stmt::DoStmtClass: { - if (*(Src->succ_begin()) == Dst) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Loop condition is true. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - else { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Loop condition is false. Exiting loop")); - } - - break; - } - - case Stmt::WhileStmtClass: - case Stmt::ForStmtClass: { - if (*(Src->succ_begin()+1) == Dst) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Loop condition is false. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - else { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Loop condition is true. Entering loop body")); - } - - break; - } - - case Stmt::IfStmtClass: { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - if (*(Src->succ_begin()+1) == Dst) - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Taking false branch")); - else - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Taking true branch")); - - break; - } - } - } - - if (NextNode) { - for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), - E = PDB.visitor_end(); I!=E; ++I) { - if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) - PD.push_front(p); - } - } - - if (const PostStmt* PS = dyn_cast(&P)) { - // Scan the region bindings, and see if a "notable" symbol has a new - // lval binding. - ScanNotableSymbols SNS(N, PS->getStmt(), PDB.getBugReporter(), PD); - PDB.getStateManager().iterBindings(N->getState(), SNS); - } - } - - // After constructing the full PathDiagnostic, do a pass over it to compact - // PathDiagnosticPieces that occur within a macro. - CompactPathDiagnostic(PD, PDB.getSourceManager()); -} - -//===----------------------------------------------------------------------===// -// "Extensive" PathDiagnostic generation. -//===----------------------------------------------------------------------===// - -static bool IsControlFlowExpr(const Stmt *S) { - const Expr *E = dyn_cast(S); - - if (!E) - return false; - - E = E->IgnoreParenCasts(); - - if (isa(E)) - return true; - - if (const BinaryOperator *B = dyn_cast(E)) - if (B->isLogicalOp()) - return true; - - return false; -} - -namespace { -class ContextLocation : public PathDiagnosticLocation { - bool IsDead; -public: - ContextLocation(const PathDiagnosticLocation &L, bool isdead = false) - : PathDiagnosticLocation(L), IsDead(isdead) {} - - void markDead() { IsDead = true; } - bool isDead() const { return IsDead; } -}; - -class EdgeBuilder { - std::vector CLocs; - typedef std::vector::iterator iterator; - PathDiagnostic &PD; - PathDiagnosticBuilder &PDB; - PathDiagnosticLocation PrevLoc; - - bool IsConsumedExpr(const PathDiagnosticLocation &L); - - bool containsLocation(const PathDiagnosticLocation &Container, - const PathDiagnosticLocation &Containee); - - PathDiagnosticLocation getContextLocation(const PathDiagnosticLocation &L); - - PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L, - bool firstCharOnly = false) { - if (const Stmt *S = L.asStmt()) { - const Stmt *Original = S; - while (1) { - // Adjust the location for some expressions that are best referenced - // by one of their subexpressions. - switch (S->getStmtClass()) { - default: - break; - case Stmt::ParenExprClass: - S = cast(S)->IgnoreParens(); - firstCharOnly = true; - continue; - case Stmt::ConditionalOperatorClass: - S = cast(S)->getCond(); - firstCharOnly = true; - continue; - case Stmt::ChooseExprClass: - S = cast(S)->getCond(); - firstCharOnly = true; - continue; - case Stmt::BinaryOperatorClass: - S = cast(S)->getLHS(); - firstCharOnly = true; - continue; - } - - break; - } - - if (S != Original) - L = PathDiagnosticLocation(S, L.getManager()); - } - - if (firstCharOnly) - L = PathDiagnosticLocation(L.asLocation()); - - return L; - } - - void popLocation() { - if (!CLocs.back().isDead() && CLocs.back().asLocation().isFileID()) { - // For contexts, we only one the first character as the range. - rawAddEdge(cleanUpLocation(CLocs.back(), true)); - } - CLocs.pop_back(); - } - - PathDiagnosticLocation IgnoreParens(const PathDiagnosticLocation &L); - -public: - EdgeBuilder(PathDiagnostic &pd, PathDiagnosticBuilder &pdb) - : PD(pd), PDB(pdb) { - - // If the PathDiagnostic already has pieces, add the enclosing statement - // of the first piece as a context as well. - if (!PD.empty()) { - PrevLoc = PD.begin()->getLocation(); - - if (const Stmt *S = PrevLoc.asStmt()) - addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); - } - } - - ~EdgeBuilder() { - while (!CLocs.empty()) popLocation(); - - // Finally, add an initial edge from the start location of the first - // statement (if it doesn't already exist). - // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. - if (const CompoundStmt *CS = - PDB.getCodeDecl().getCompoundBody()) - if (!CS->body_empty()) { - SourceLocation Loc = (*CS->body_begin())->getLocStart(); - rawAddEdge(PathDiagnosticLocation(Loc, PDB.getSourceManager())); - } - - } - - void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false); - - void addEdge(const Stmt *S, bool alwaysAdd = false) { - addEdge(PathDiagnosticLocation(S, PDB.getSourceManager()), alwaysAdd); - } - - void rawAddEdge(PathDiagnosticLocation NewLoc); - - void addContext(const Stmt *S); - void addExtendedContext(const Stmt *S); -}; -} // end anonymous namespace - - -PathDiagnosticLocation -EdgeBuilder::getContextLocation(const PathDiagnosticLocation &L) { - if (const Stmt *S = L.asStmt()) { - if (IsControlFlowExpr(S)) - return L; - - return PDB.getEnclosingStmtLocation(S); - } - - return L; -} - -bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, - const PathDiagnosticLocation &Containee) { - - if (Container == Containee) - return true; - - if (Container.asDecl()) - return true; - - if (const Stmt *S = Containee.asStmt()) - if (const Stmt *ContainerS = Container.asStmt()) { - while (S) { - if (S == ContainerS) - return true; - S = PDB.getParent(S); - } - return false; - } - - // Less accurate: compare using source ranges. - SourceRange ContainerR = Container.asRange(); - SourceRange ContaineeR = Containee.asRange(); - - SourceManager &SM = PDB.getSourceManager(); - SourceLocation ContainerRBeg = SM.getInstantiationLoc(ContainerR.getBegin()); - SourceLocation ContainerREnd = SM.getInstantiationLoc(ContainerR.getEnd()); - SourceLocation ContaineeRBeg = SM.getInstantiationLoc(ContaineeR.getBegin()); - SourceLocation ContaineeREnd = SM.getInstantiationLoc(ContaineeR.getEnd()); - - unsigned ContainerBegLine = SM.getInstantiationLineNumber(ContainerRBeg); - unsigned ContainerEndLine = SM.getInstantiationLineNumber(ContainerREnd); - unsigned ContaineeBegLine = SM.getInstantiationLineNumber(ContaineeRBeg); - unsigned ContaineeEndLine = SM.getInstantiationLineNumber(ContaineeREnd); - - assert(ContainerBegLine <= ContainerEndLine); - assert(ContaineeBegLine <= ContaineeEndLine); - - return (ContainerBegLine <= ContaineeBegLine && - ContainerEndLine >= ContaineeEndLine && - (ContainerBegLine != ContaineeBegLine || - SM.getInstantiationColumnNumber(ContainerRBeg) <= - SM.getInstantiationColumnNumber(ContaineeRBeg)) && - (ContainerEndLine != ContaineeEndLine || - SM.getInstantiationColumnNumber(ContainerREnd) >= - SM.getInstantiationColumnNumber(ContainerREnd))); -} - -PathDiagnosticLocation -EdgeBuilder::IgnoreParens(const PathDiagnosticLocation &L) { - if (const Expr* E = dyn_cast_or_null(L.asStmt())) - return PathDiagnosticLocation(E->IgnoreParenCasts(), - PDB.getSourceManager()); - return L; -} - -void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { - if (!PrevLoc.isValid()) { - PrevLoc = NewLoc; - return; - } - - const PathDiagnosticLocation &NewLocClean = cleanUpLocation(NewLoc); - const PathDiagnosticLocation &PrevLocClean = cleanUpLocation(PrevLoc); - - if (NewLocClean.asLocation() == PrevLocClean.asLocation()) - return; - - // FIXME: Ignore intra-macro edges for now. - if (NewLocClean.asLocation().getInstantiationLoc() == - PrevLocClean.asLocation().getInstantiationLoc()) - return; - - PD.push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean)); - PrevLoc = NewLoc; -} - -void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd) { - - if (!alwaysAdd && NewLoc.asLocation().isMacroID()) - return; - - const PathDiagnosticLocation &CLoc = getContextLocation(NewLoc); - - while (!CLocs.empty()) { - ContextLocation &TopContextLoc = CLocs.back(); - - // Is the top location context the same as the one for the new location? - if (TopContextLoc == CLoc) { - if (alwaysAdd) { - if (IsConsumedExpr(TopContextLoc) && - !IsControlFlowExpr(TopContextLoc.asStmt())) - TopContextLoc.markDead(); - - rawAddEdge(NewLoc); - } - - return; - } - - if (containsLocation(TopContextLoc, CLoc)) { - if (alwaysAdd) { - rawAddEdge(NewLoc); - - if (IsConsumedExpr(CLoc) && !IsControlFlowExpr(CLoc.asStmt())) { - CLocs.push_back(ContextLocation(CLoc, true)); - return; - } - } - - CLocs.push_back(CLoc); - return; - } - - // Context does not contain the location. Flush it. - popLocation(); - } - - // If we reach here, there is no enclosing context. Just add the edge. - rawAddEdge(NewLoc); -} - -bool EdgeBuilder::IsConsumedExpr(const PathDiagnosticLocation &L) { - if (const Expr *X = dyn_cast_or_null(L.asStmt())) - return PDB.getParentMap().isConsumedExpr(X) && !IsControlFlowExpr(X); - - return false; -} - -void EdgeBuilder::addExtendedContext(const Stmt *S) { - if (!S) - return; - - const Stmt *Parent = PDB.getParent(S); - while (Parent) { - if (isa(Parent)) - Parent = PDB.getParent(Parent); - else - break; - } - - if (Parent) { - switch (Parent->getStmtClass()) { - case Stmt::DoStmtClass: - case Stmt::ObjCAtSynchronizedStmtClass: - addContext(Parent); - default: - break; - } - } - - addContext(S); -} - -void EdgeBuilder::addContext(const Stmt *S) { - if (!S) - return; - - PathDiagnosticLocation L(S, PDB.getSourceManager()); - - while (!CLocs.empty()) { - const PathDiagnosticLocation &TopContextLoc = CLocs.back(); - - // Is the top location context the same as the one for the new location? - if (TopContextLoc == L) - return; - - if (containsLocation(TopContextLoc, L)) { - CLocs.push_back(L); - return; - } - - // Context does not contain the location. Flush it. - popLocation(); - } - - CLocs.push_back(L); -} - -static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode *N) { - - - EdgeBuilder EB(PD, PDB); - - const ExplodedNode* NextNode = N->pred_empty() - ? NULL : *(N->pred_begin()); - while (NextNode) { - N = NextNode; - NextNode = GetPredecessorNode(N); - ProgramPoint P = N->getLocation(); - - do { - // Block edges. - if (const BlockEdge *BE = dyn_cast(&P)) { - const CFGBlock &Blk = *BE->getSrc(); - const Stmt *Term = Blk.getTerminator(); - - // Are we jumping to the head of a loop? Add a special diagnostic. - if (const Stmt *Loop = BE->getDst()->getLoopTarget()) { - PathDiagnosticLocation L(Loop, PDB.getSourceManager()); - const CompoundStmt *CS = NULL; - - if (!Term) { - if (const ForStmt *FS = dyn_cast(Loop)) - CS = dyn_cast(FS->getBody()); - else if (const WhileStmt *WS = dyn_cast(Loop)) - CS = dyn_cast(WS->getBody()); - } - - PathDiagnosticEventPiece *p = - new PathDiagnosticEventPiece(L, - "Looping back to the head of the loop"); - - EB.addEdge(p->getLocation(), true); - PD.push_front(p); - - if (CS) { - PathDiagnosticLocation BL(CS->getRBracLoc(), - PDB.getSourceManager()); - BL = PathDiagnosticLocation(BL.asLocation()); - EB.addEdge(BL); - } - } - - if (Term) - EB.addContext(Term); - - break; - } - - if (const BlockEntrance *BE = dyn_cast(&P)) { - if (const Stmt* S = BE->getFirstStmt()) { - if (IsControlFlowExpr(S)) { - // Add the proper context for '&&', '||', and '?'. - EB.addContext(S); - } - else - EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); - } - - break; - } - } while (0); - - if (!NextNode) - continue; - - for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), - E = PDB.visitor_end(); I!=E; ++I) { - if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) { - const PathDiagnosticLocation &Loc = p->getLocation(); - EB.addEdge(Loc, true); - PD.push_front(p); - if (const Stmt *S = Loc.asStmt()) - EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); - } - } - } -} - -//===----------------------------------------------------------------------===// -// Methods for BugType and subclasses. -//===----------------------------------------------------------------------===// -BugType::~BugType() { - // Free up the equivalence class objects. Observe that we get a pointer to - // the object first before incrementing the iterator, as destroying the - // node before doing so means we will read from freed memory. - for (iterator I = begin(), E = end(); I !=E; ) { - BugReportEquivClass *EQ = &*I; - ++I; - delete EQ; - } -} -void BugType::FlushReports(BugReporter &BR) {} - -//===----------------------------------------------------------------------===// -// Methods for BugReport and subclasses. -//===----------------------------------------------------------------------===// -BugReport::~BugReport() {} -RangedBugReport::~RangedBugReport() {} - -const Stmt* BugReport::getStmt() const { - ProgramPoint ProgP = EndNode->getLocation(); - const Stmt *S = NULL; - - if (BlockEntrance* BE = dyn_cast(&ProgP)) { - CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); - if (BE->getBlock() == &Exit) - S = GetPreviousStmt(EndNode); - } - if (!S) - S = GetStmt(ProgP); - - return S; -} - -PathDiagnosticPiece* -BugReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndPathNode) { - - const Stmt* S = getStmt(); - - if (!S) - return NULL; - - const SourceRange *Beg, *End; - getRanges(Beg, End); - PathDiagnosticLocation L(S, BRC.getSourceManager()); - - // Only add the statement itself as a range if we didn't specify any - // special ranges for this report. - PathDiagnosticPiece* P = new PathDiagnosticEventPiece(L, getDescription(), - Beg == End); - - for (; Beg != End; ++Beg) - P->addRange(*Beg); - - return P; -} - -void BugReport::getRanges(const SourceRange*& beg, const SourceRange*& end) { - if (const Expr* E = dyn_cast_or_null(getStmt())) { - R = E->getSourceRange(); - assert(R.isValid()); - beg = &R; - end = beg+1; - } - else - beg = end = 0; -} - -SourceLocation BugReport::getLocation() const { - if (EndNode) - if (const Stmt* S = GetCurrentOrPreviousStmt(EndNode)) { - // For member expressions, return the location of the '.' or '->'. - if (const MemberExpr *ME = dyn_cast(S)) - return ME->getMemberLoc(); - // For binary operators, return the location of the operator. - if (const BinaryOperator *B = dyn_cast(S)) - return B->getOperatorLoc(); - - return S->getLocStart(); - } - - return FullSourceLoc(); -} - -PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext &BRC) { - return NULL; -} - -//===----------------------------------------------------------------------===// -// Methods for BugReporter and subclasses. -//===----------------------------------------------------------------------===// - -BugReportEquivClass::~BugReportEquivClass() { - for (iterator I=begin(), E=end(); I!=E; ++I) delete *I; -} - -GRBugReporter::~GRBugReporter() { } -BugReporterData::~BugReporterData() {} - -ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); } - -GRStateManager& -GRBugReporter::getStateManager() { return Eng.getStateManager(); } - -BugReporter::~BugReporter() { FlushReports(); } - -void BugReporter::FlushReports() { - if (BugTypes.isEmpty()) - return; - - // First flush the warnings for each BugType. This may end up creating new - // warnings and new BugTypes. Because ImmutableSet is a functional data - // structure, we do not need to worry about the iterators being invalidated. - for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) - const_cast(*I)->FlushReports(*this); - - // Iterate through BugTypes a second time. BugTypes may have been updated - // with new BugType objects and new warnings. - for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) { - BugType *BT = const_cast(*I); - - typedef llvm::FoldingSet SetTy; - SetTy& EQClasses = BT->EQClasses; - - for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){ - BugReportEquivClass& EQ = *EI; - FlushReport(EQ); - } - - // Delete the BugType object. - delete BT; - } - - // Remove all references to the BugType objects. - BugTypes = F.GetEmptySet(); -} - -//===----------------------------------------------------------------------===// -// PathDiagnostics generation. -//===----------------------------------------------------------------------===// - -static std::pair, - std::pair > -MakeReportGraph(const ExplodedGraph* G, - const ExplodedNode** NStart, - const ExplodedNode** NEnd) { - - // Create the trimmed graph. It will contain the shortest paths from the - // error nodes to the root. In the new graph we should only have one - // error node unless there are two or more error nodes with the same minimum - // path length. - ExplodedGraph* GTrim; - InterExplodedGraphMap* NMap; - - llvm::DenseMap InverseMap; - llvm::tie(GTrim, NMap) = G->Trim(NStart, NEnd, &InverseMap); - - // Create owning pointers for GTrim and NMap just to ensure that they are - // released when this function exists. - llvm::OwningPtr AutoReleaseGTrim(GTrim); - llvm::OwningPtr AutoReleaseNMap(NMap); - - // Find the (first) error node in the trimmed graph. We just need to consult - // the node map (NMap) which maps from nodes in the original graph to nodes - // in the new graph. - - std::queue WS; - typedef llvm::DenseMap IndexMapTy; - IndexMapTy IndexMap; - - for (const ExplodedNode** I = NStart; I != NEnd; ++I) - if (const ExplodedNode *N = NMap->getMappedNode(*I)) { - unsigned NodeIndex = (I - NStart) / sizeof(*I); - WS.push(N); - IndexMap[*I] = NodeIndex; - } - - assert(!WS.empty() && "No error node found in the trimmed graph."); - - // Create a new (third!) graph with a single path. This is the graph - // that will be returned to the caller. - ExplodedGraph *GNew = new ExplodedGraph(GTrim->getContext()); - - // Sometimes the trimmed graph can contain a cycle. Perform a reverse BFS - // to the root node, and then construct a new graph that contains only - // a single path. - llvm::DenseMap Visited; - - unsigned cnt = 0; - const ExplodedNode* Root = 0; - - while (!WS.empty()) { - const ExplodedNode* Node = WS.front(); - WS.pop(); - - if (Visited.find(Node) != Visited.end()) - continue; - - Visited[Node] = cnt++; - - if (Node->pred_empty()) { - Root = Node; - break; - } - - for (ExplodedNode::const_pred_iterator I=Node->pred_begin(), - E=Node->pred_end(); I!=E; ++I) - WS.push(*I); - } - - assert(Root); - - // Now walk from the root down the BFS path, always taking the successor - // with the lowest number. - ExplodedNode *Last = 0, *First = 0; - NodeBackMap *BM = new NodeBackMap(); - unsigned NodeIndex = 0; - - for ( const ExplodedNode *N = Root ;;) { - // Lookup the number associated with the current node. - llvm::DenseMap::iterator I = Visited.find(N); - assert(I != Visited.end()); - - // Create the equivalent node in the new graph with the same state - // and location. - ExplodedNode* NewN = GNew->getNode(N->getLocation(), N->getState()); - - // Store the mapping to the original node. - llvm::DenseMap::iterator IMitr=InverseMap.find(N); - assert(IMitr != InverseMap.end() && "No mapping to original node."); - (*BM)[NewN] = (const ExplodedNode*) IMitr->second; - - // Link up the new node with the previous node. - if (Last) - NewN->addPredecessor(Last, *GNew); - - Last = NewN; - - // Are we at the final node? - IndexMapTy::iterator IMI = - IndexMap.find((const ExplodedNode*)(IMitr->second)); - if (IMI != IndexMap.end()) { - First = NewN; - NodeIndex = IMI->second; - break; - } - - // Find the next successor node. We choose the node that is marked - // with the lowest DFS number. - ExplodedNode::const_succ_iterator SI = N->succ_begin(); - ExplodedNode::const_succ_iterator SE = N->succ_end(); - N = 0; - - for (unsigned MinVal = 0; SI != SE; ++SI) { - - I = Visited.find(*SI); - - if (I == Visited.end()) - continue; - - if (!N || I->second < MinVal) { - N = *SI; - MinVal = I->second; - } - } - - assert(N); - } - - assert(First); - - return std::make_pair(std::make_pair(GNew, BM), - std::make_pair(First, NodeIndex)); -} - -/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object -/// and collapses PathDiagosticPieces that are expanded by macros. -static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { - typedef std::vector > - MacroStackTy; - - typedef std::vector - PiecesTy; - - MacroStackTy MacroStack; - PiecesTy Pieces; - - for (PathDiagnostic::iterator I = PD.begin(), E = PD.end(); I!=E; ++I) { - // Get the location of the PathDiagnosticPiece. - const FullSourceLoc Loc = I->getLocation().asLocation(); - - // Determine the instantiation location, which is the location we group - // related PathDiagnosticPieces. - SourceLocation InstantiationLoc = Loc.isMacroID() ? - SM.getInstantiationLoc(Loc) : - SourceLocation(); - - if (Loc.isFileID()) { - MacroStack.clear(); - Pieces.push_back(&*I); - continue; - } - - assert(Loc.isMacroID()); - - // Is the PathDiagnosticPiece within the same macro group? - if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) { - MacroStack.back().first->push_back(&*I); - continue; - } - - // We aren't in the same group. Are we descending into a new macro - // or are part of an old one? - PathDiagnosticMacroPiece *MacroGroup = 0; - - SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ? - SM.getInstantiationLoc(Loc) : - SourceLocation(); - - // Walk the entire macro stack. - while (!MacroStack.empty()) { - if (InstantiationLoc == MacroStack.back().second) { - MacroGroup = MacroStack.back().first; - break; - } - - if (ParentInstantiationLoc == MacroStack.back().second) { - MacroGroup = MacroStack.back().first; - break; - } - - MacroStack.pop_back(); - } - - if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) { - // Create a new macro group and add it to the stack. - PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc); - - if (MacroGroup) - MacroGroup->push_back(NewGroup); - else { - assert(InstantiationLoc.isFileID()); - Pieces.push_back(NewGroup); - } - - MacroGroup = NewGroup; - MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc)); - } - - // Finally, add the PathDiagnosticPiece to the group. - MacroGroup->push_back(&*I); - } - - // Now take the pieces and construct a new PathDiagnostic. - PD.resetPath(false); - - for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) { - if (PathDiagnosticMacroPiece *MP=dyn_cast(*I)) - if (!MP->containsEvent()) { - delete MP; - continue; - } - - PD.push_back(*I); - } -} - -void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, - BugReportEquivClass& EQ) { - - std::vector Nodes; - - for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { - const ExplodedNode* N = I->getEndNode(); - if (N) Nodes.push_back(N); - } - - if (Nodes.empty()) - return; - - // Construct a new graph that contains only a single path from the error - // node to a root. - const std::pair, - std::pair >& - GPair = MakeReportGraph(&getGraph(), &Nodes[0], &Nodes[0] + Nodes.size()); - - // Find the BugReport with the original location. - BugReport *R = 0; - unsigned i = 0; - for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I, ++i) - if (i == GPair.second.second) { R = *I; break; } - - assert(R && "No original report found for sliced graph."); - - llvm::OwningPtr ReportGraph(GPair.first.first); - llvm::OwningPtr BackMap(GPair.first.second); - const ExplodedNode *N = GPair.second.first; - - // Start building the path diagnostic... - PathDiagnosticBuilder PDB(*this, R, BackMap.get(), getPathDiagnosticClient()); - - if (PathDiagnosticPiece* Piece = R->getEndPath(PDB, N)) - PD.push_back(Piece); - else - return; - - R->registerInitialVisitors(PDB, N); - - switch (PDB.getGenerationScheme()) { - case PathDiagnosticClient::Extensive: - GenerateExtensivePathDiagnostic(PD, PDB, N); - break; - case PathDiagnosticClient::Minimal: - GenerateMinimalPathDiagnostic(PD, PDB, N); - break; - } -} - -void BugReporter::Register(BugType *BT) { - BugTypes = F.Add(BugTypes, BT); -} - -void BugReporter::EmitReport(BugReport* R) { - // Compute the bug report's hash to determine its equivalence class. - llvm::FoldingSetNodeID ID; - R->Profile(ID); - - // Lookup the equivance class. If there isn't one, create it. - BugType& BT = R->getBugType(); - Register(&BT); - void *InsertPos; - BugReportEquivClass* EQ = BT.EQClasses.FindNodeOrInsertPos(ID, InsertPos); - - if (!EQ) { - EQ = new BugReportEquivClass(R); - BT.EQClasses.InsertNode(EQ, InsertPos); - } - else - EQ->AddReport(R); -} - - -//===----------------------------------------------------------------------===// -// Emitting reports in equivalence classes. -//===----------------------------------------------------------------------===// - -namespace { -struct FRIEC_WLItem { - const ExplodedNode *N; - ExplodedNode::const_succ_iterator I, E; - - FRIEC_WLItem(const ExplodedNode *n) - : N(n), I(N->succ_begin()), E(N->succ_end()) {} -}; -} - -static BugReport *FindReportInEquivalenceClass(BugReportEquivClass& EQ) { - BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); - assert(I != E); - BugReport *R = *I; - BugType& BT = R->getBugType(); - - if (!BT.isSuppressOnSink()) - return R; - - // For bug reports that should be suppressed when all paths are post-dominated - // by a sink node, iterate through the reports in the equivalence class - // until we find one that isn't post-dominated (if one exists). We use a - // DFS traversal of the ExplodedGraph to find a non-sink node. We could write - // this as a recursive function, but we don't want to risk blowing out the - // stack for very long paths. - for (; I != E; ++I) { - R = *I; - const ExplodedNode *N = R->getEndNode(); - - if (!N) - continue; - - if (N->isSink()) { - assert(false && - "BugType::isSuppressSink() should not be 'true' for sink end nodes"); - return R; - } - - if (N->succ_empty()) - return R; - - // At this point we know that 'N' is not a sink and it has at least one - // successor. Use a DFS worklist to find a non-sink end-of-path node. - typedef FRIEC_WLItem WLItem; - typedef llvm::SmallVector DFSWorkList; - llvm::DenseMap Visited; - - DFSWorkList WL; - WL.push_back(N); - Visited[N] = 1; - - while (!WL.empty()) { - WLItem &WI = WL.back(); - assert(!WI.N->succ_empty()); - - for (; WI.I != WI.E; ++WI.I) { - const ExplodedNode *Succ = *WI.I; - // End-of-path node? - if (Succ->succ_empty()) { - // If we found an end-of-path node that is not a sink, then return - // this report. - if (!Succ->isSink()) - return R; - - // Found a sink? Continue on to the next successor. - continue; - } - - // Mark the successor as visited. If it hasn't been explored, - // enqueue it to the DFS worklist. - unsigned &mark = Visited[Succ]; - if (!mark) { - mark = 1; - WL.push_back(Succ); - break; - } - } - - if (&WL.back() == &WI) - WL.pop_back(); - } - } - - // If we reach here, the end nodes for all reports in the equivalence - // class are post-dominated by a sink node. - return NULL; -} - - -//===----------------------------------------------------------------------===// -// DiagnosticCache. This is a hack to cache analyzer diagnostics. It -// uses global state, which eventually should go elsewhere. -//===----------------------------------------------------------------------===// -namespace { -class DiagCacheItem : public llvm::FoldingSetNode { - llvm::FoldingSetNodeID ID; -public: - DiagCacheItem(BugReport *R, PathDiagnostic *PD) { - ID.AddString(R->getBugType().getName()); - ID.AddString(R->getBugType().getCategory()); - ID.AddString(R->getDescription()); - ID.AddInteger(R->getLocation().getRawEncoding()); - PD->Profile(ID); - } - - void Profile(llvm::FoldingSetNodeID &id) { - id = ID; - } - - llvm::FoldingSetNodeID &getID() { return ID; } -}; -} - -static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) { - // FIXME: Eventually this diagnostic cache should reside in something - // like AnalysisManager instead of being a static variable. This is - // really unsafe in the long term. - typedef llvm::FoldingSet DiagnosticCache; - static DiagnosticCache DC; - - void *InsertPos; - DiagCacheItem *Item = new DiagCacheItem(R, PD); - - if (DC.FindNodeOrInsertPos(Item->getID(), InsertPos)) { - delete Item; - return true; - } - - DC.InsertNode(Item, InsertPos); - return false; -} - -void BugReporter::FlushReport(BugReportEquivClass& EQ) { - BugReport *R = FindReportInEquivalenceClass(EQ); - - if (!R) - return; - - PathDiagnosticClient* PD = getPathDiagnosticClient(); - - // FIXME: Make sure we use the 'R' for the path that was actually used. - // Probably doesn't make a difference in practice. - BugType& BT = R->getBugType(); - - llvm::OwningPtr - D(new PathDiagnostic(R->getBugType().getName(), - !PD || PD->useVerboseDescription() - ? R->getDescription() : R->getShortDescription(), - BT.getCategory())); - - GeneratePathDiagnostic(*D.get(), EQ); - - if (IsCachedDiagnostic(R, D.get())) - return; - - // Get the meta data. - std::pair Meta = R->getExtraDescriptiveText(); - for (const char** s = Meta.first; s != Meta.second; ++s) - D->addMeta(*s); - - // Emit a summary diagnostic to the regular Diagnostics engine. - const SourceRange *Beg = 0, *End = 0; - R->getRanges(Beg, End); - Diagnostic& Diag = getDiagnostic(); - FullSourceLoc L(R->getLocation(), getSourceManager()); - - // Search the description for '%', as that will be interpretted as a - // format character by FormatDiagnostics. - llvm::StringRef desc = R->getShortDescription(); - unsigned ErrorDiag; - { - llvm::SmallString<512> TmpStr; - llvm::raw_svector_ostream Out(TmpStr); - for (llvm::StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) - if (*I == '%') - Out << "%%"; - else - Out << *I; - - Out.flush(); - ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning, TmpStr); - } - - switch (End-Beg) { - default: assert(0 && "Don't handle this many ranges yet!"); - case 0: Diag.Report(L, ErrorDiag); break; - case 1: Diag.Report(L, ErrorDiag) << Beg[0]; break; - case 2: Diag.Report(L, ErrorDiag) << Beg[0] << Beg[1]; break; - case 3: Diag.Report(L, ErrorDiag) << Beg[0] << Beg[1] << Beg[2]; break; - } - - // Emit a full diagnostic for the path if we have a PathDiagnosticClient. - if (!PD) - return; - - if (D->empty()) { - PathDiagnosticPiece* piece = - new PathDiagnosticEventPiece(L, R->getDescription()); - - for ( ; Beg != End; ++Beg) piece->addRange(*Beg); - D->push_back(piece); - } - - PD->HandlePathDiagnostic(D.take()); -} - -void BugReporter::EmitBasicReport(llvm::StringRef name, llvm::StringRef str, - SourceLocation Loc, - SourceRange* RBeg, unsigned NumRanges) { - EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); -} - -void BugReporter::EmitBasicReport(llvm::StringRef name, - llvm::StringRef category, - llvm::StringRef str, SourceLocation Loc, - SourceRange* RBeg, unsigned NumRanges) { - - // 'BT' will be owned by BugReporter as soon as we call 'EmitReport'. - BugType *BT = new BugType(name, category); - FullSourceLoc L = getContext().getFullLoc(Loc); - RangedBugReport *R = new DiagBugReport(*BT, str, L); - for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg); - EmitReport(R); -} diff --git a/lib/Analysis/BugReporterVisitors.cpp b/lib/Analysis/BugReporterVisitors.cpp deleted file mode 100644 index 87de30ae7aec..000000000000 --- a/lib/Analysis/BugReporterVisitors.cpp +++ /dev/null @@ -1,349 +0,0 @@ -// BugReporterVisitors.cpp - Helpers for reporting bugs -----------*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a set of BugReporter "visitors" which can be used to -// enhance the diagnostics reported for a bug. -// -//===----------------------------------------------------------------------===// - -#include "clang/AST/Expr.h" -#include "clang/AST/ExprObjC.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/GRState.h" - -using namespace clang; - -//===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -const Stmt *clang::bugreporter::GetDerefExpr(const ExplodedNode *N) { - // Pattern match for a few useful cases (do something smarter later): - // a[0], p->f, *p - const Stmt *S = N->getLocationAs()->getStmt(); - - if (const UnaryOperator *U = dyn_cast(S)) { - if (U->getOpcode() == UnaryOperator::Deref) - return U->getSubExpr()->IgnoreParenCasts(); - } - else if (const MemberExpr *ME = dyn_cast(S)) { - return ME->getBase()->IgnoreParenCasts(); - } - else if (const ArraySubscriptExpr *AE = dyn_cast(S)) { - // Retrieve the base for arrays since BasicStoreManager doesn't know how - // to reason about them. - return AE->getBase(); - } - - return NULL; -} - -const Stmt* -clang::bugreporter::GetReceiverExpr(const ExplodedNode *N){ - const Stmt *S = N->getLocationAs()->getStmt(); - if (const ObjCMessageExpr *ME = dyn_cast(S)) - return ME->getReceiver(); - return NULL; -} - -const Stmt* -clang::bugreporter::GetDenomExpr(const ExplodedNode *N) { - const Stmt *S = N->getLocationAs()->getStmt(); - if (const BinaryOperator *BE = dyn_cast(S)) - return BE->getRHS(); - return NULL; -} - -const Stmt* -clang::bugreporter::GetCalleeExpr(const ExplodedNode *N) { - // Callee is checked as a PreVisit to the CallExpr. - const Stmt *S = N->getLocationAs()->getStmt(); - if (const CallExpr *CE = dyn_cast(S)) - return CE->getCallee(); - return NULL; -} - -const Stmt* -clang::bugreporter::GetRetValExpr(const ExplodedNode *N) { - const Stmt *S = N->getLocationAs()->getStmt(); - if (const ReturnStmt *RS = dyn_cast(S)) - return RS->getRetValue(); - return NULL; -} - -//===----------------------------------------------------------------------===// -// Definitions for bug reporter visitors. -//===----------------------------------------------------------------------===// - -namespace { -class FindLastStoreBRVisitor : public BugReporterVisitor { - const MemRegion *R; - SVal V; - bool satisfied; - const ExplodedNode *StoreSite; -public: - FindLastStoreBRVisitor(SVal v, const MemRegion *r) - : R(r), V(v), satisfied(false), StoreSite(0) {} - - PathDiagnosticPiece* VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext& BRC) { - - if (satisfied) - return NULL; - - if (!StoreSite) { - const ExplodedNode *Node = N, *Last = NULL; - - for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { - - if (const VarRegion *VR = dyn_cast(R)) { - if (const PostStmt *P = Node->getLocationAs()) - if (const DeclStmt *DS = P->getStmtAs()) - if (DS->getSingleDecl() == VR->getDecl()) { - Last = Node; - break; - } - } - - if (Node->getState()->getSVal(R) != V) - break; - } - - if (!Node || !Last) { - satisfied = true; - return NULL; - } - - StoreSite = Last; - } - - if (StoreSite != N) - return NULL; - - satisfied = true; - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - if (const PostStmt *PS = N->getLocationAs()) { - if (const DeclStmt *DS = PS->getStmtAs()) { - - if (const VarRegion *VR = dyn_cast(R)) { - os << "Variable '" << VR->getDecl()->getNameAsString() << "' "; - } - else - return NULL; - - if (isa(V)) { - bool b = false; - ASTContext &C = BRC.getASTContext(); - if (R->isBoundable()) { - if (const TypedRegion *TR = dyn_cast(R)) { - if (TR->getValueType(C)->isObjCObjectPointerType()) { - os << "initialized to nil"; - b = true; - } - } - } - - if (!b) - os << "initialized to a null pointer value"; - } - else if (isa(V)) { - os << "initialized to " << cast(V).getValue(); - } - else if (V.isUndef()) { - if (isa(R)) { - const VarDecl *VD = cast(DS->getSingleDecl()); - if (VD->getInit()) - os << "initialized to a garbage value"; - else - os << "declared without an initial value"; - } - } - } - } - - if (os.str().empty()) { - if (isa(V)) { - bool b = false; - ASTContext &C = BRC.getASTContext(); - if (R->isBoundable()) { - if (const TypedRegion *TR = dyn_cast(R)) { - if (TR->getValueType(C)->isObjCObjectPointerType()) { - os << "nil object reference stored to "; - b = true; - } - } - } - - if (!b) - os << "Null pointer value stored to "; - } - else if (V.isUndef()) { - os << "Uninitialized value stored to "; - } - else if (isa(V)) { - os << "The value " << cast(V).getValue() - << " is assigned to "; - } - else - return NULL; - - if (const VarRegion *VR = dyn_cast(R)) { - os << '\'' << VR->getDecl()->getNameAsString() << '\''; - } - else - return NULL; - } - - // FIXME: Refactor this into BugReporterContext. - const Stmt *S = 0; - ProgramPoint P = N->getLocation(); - - if (BlockEdge *BE = dyn_cast(&P)) { - CFGBlock *BSrc = BE->getSrc(); - S = BSrc->getTerminatorCondition(); - } - else if (PostStmt *PS = dyn_cast(&P)) { - S = PS->getStmt(); - } - - if (!S) - return NULL; - - // Construct a new PathDiagnosticPiece. - PathDiagnosticLocation L(S, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(L, os.str()); - } -}; - - -static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R, - SVal V) { - BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); -} - -class TrackConstraintBRVisitor : public BugReporterVisitor { - DefinedSVal Constraint; - const bool Assumption; - bool isSatisfied; -public: - TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) - : Constraint(constraint), Assumption(assumption), isSatisfied(false) {} - - PathDiagnosticPiece* VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext& BRC) { - if (isSatisfied) - return NULL; - - // Check if in the previous state it was feasible for this constraint - // to *not* be true. - if (PrevN->getState()->Assume(Constraint, !Assumption)) { - - isSatisfied = true; - - // As a sanity check, make sure that the negation of the constraint - // was infeasible in the current state. If it is feasible, we somehow - // missed the transition point. - if (N->getState()->Assume(Constraint, !Assumption)) - return NULL; - - // We found the transition point for the constraint. We now need to - // pretty-print the constraint. (work-in-progress) - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - if (isa(Constraint)) { - os << "Assuming pointer value is "; - os << (Assumption ? "non-null" : "null"); - } - - if (os.str().empty()) - return NULL; - - // FIXME: Refactor this into BugReporterContext. - const Stmt *S = 0; - ProgramPoint P = N->getLocation(); - - if (BlockEdge *BE = dyn_cast(&P)) { - CFGBlock *BSrc = BE->getSrc(); - S = BSrc->getTerminatorCondition(); - } - else if (PostStmt *PS = dyn_cast(&P)) { - S = PS->getStmt(); - } - - if (!S) - return NULL; - - // Construct a new PathDiagnosticPiece. - PathDiagnosticLocation L(S, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(L, os.str()); - } - - return NULL; - } -}; -} // end anonymous namespace - -static void registerTrackConstraint(BugReporterContext& BRC, - DefinedSVal Constraint, - bool Assumption) { - BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption)); -} - -void clang::bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, - const void *data, - const ExplodedNode* N) { - - const Stmt *S = static_cast(data); - - if (!S) - return; - - GRStateManager &StateMgr = BRC.getStateManager(); - const GRState *state = N->getState(); - - if (const DeclRefExpr *DR = dyn_cast(S)) { - if (const VarDecl *VD = dyn_cast(DR->getDecl())) { - const VarRegion *R = - StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); - - // What did we load? - SVal V = state->getSVal(S); - - if (isa(V) || isa(V) - || V.isUndef()) { - registerFindLastStore(BRC, R, V); - } - } - } - - SVal V = state->getSValAsScalarOrLoc(S); - - // Uncomment this to find cases where we aren't properly getting the - // base value that was dereferenced. - // assert(!V.isUnknownOrUndef()); - - // Is it a symbolic value? - if (loc::MemRegionVal *L = dyn_cast(&V)) { - const SubRegion *R = cast(L->getRegion()); - while (R && !isa(R)) { - R = dyn_cast(R->getSuperRegion()); - } - - if (R) { - assert(isa(R)); - registerTrackConstraint(BRC, loc::MemRegionVal(R), false); - } - } -} diff --git a/lib/Analysis/BuiltinFunctionChecker.cpp b/lib/Analysis/BuiltinFunctionChecker.cpp deleted file mode 100644 index a89ad2164b30..000000000000 --- a/lib/Analysis/BuiltinFunctionChecker.cpp +++ /dev/null @@ -1,76 +0,0 @@ -//=== BuiltinFunctionChecker.cpp --------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This checker evaluates clang builtin functions. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/Checker.h" -#include "clang/Basic/Builtins.h" -#include "llvm/ADT/StringSwitch.h" - -using namespace clang; - -namespace { - -class BuiltinFunctionChecker : public Checker { -public: - static void *getTag() { static int tag = 0; return &tag; } - virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); -}; - -} - -void clang::RegisterBuiltinFunctionChecker(GRExprEngine &Eng) { - Eng.registerCheck(new BuiltinFunctionChecker()); -} - -bool BuiltinFunctionChecker::EvalCallExpr(CheckerContext &C,const CallExpr *CE){ - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - const FunctionDecl *FD = L.getAsFunctionDecl(); - - if (!FD) - return false; - - unsigned id = FD->getBuiltinID(); - - if (!id) - return false; - - switch (id) { - case Builtin::BI__builtin_expect: { - // For __builtin_expect, just return the value of the subexpression. - assert (CE->arg_begin() != CE->arg_end()); - SVal X = state->getSVal(*(CE->arg_begin())); - C.GenerateNode(state->BindExpr(CE, X)); - return true; - } - - case Builtin::BI__builtin_alloca: { - // FIXME: Refactor into StoreManager itself? - MemRegionManager& RM = C.getStoreManager().getRegionManager(); - const MemRegion* R = - RM.getAllocaRegion(CE, C.getNodeBuilder().getCurrentBlockCount(), - C.getPredecessor()->getLocationContext()); - - // Set the extent of the region in bytes. This enables us to use the - // SVal of the argument directly. If we save the extent in bits, we - // cannot represent values like symbol*8. - SVal Extent = state->getSVal(*(CE->arg_begin())); - state = C.getStoreManager().setExtent(state, R, Extent); - C.GenerateNode(state->BindExpr(CE, loc::MemRegionVal(R))); - return true; - } - } - - return false; -} diff --git a/lib/Analysis/CFRefCount.cpp b/lib/Analysis/CFRefCount.cpp deleted file mode 100644 index 5a15fbfb1f05..000000000000 --- a/lib/Analysis/CFRefCount.cpp +++ /dev/null @@ -1,3790 +0,0 @@ -// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the methods for CFRefCount, which implements -// a reference count checker for Core Foundation (Mac OS X). -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/LangOptions.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Analysis/PathSensitive/GRExprEngineBuilders.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/SymbolManager.h" -#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/StmtVisitor.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/ImmutableMap.h" -#include "llvm/ADT/ImmutableList.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/STLExtras.h" -#include - -using namespace clang; - -//===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -// The "fundamental rule" for naming conventions of methods: -// (url broken into two lines) -// http://developer.apple.com/documentation/Cocoa/Conceptual/ -// MemoryMgmt/Tasks/MemoryManagementRules.html -// -// "You take ownership of an object if you create it using a method whose name -// begins with "alloc" or "new" or contains "copy" (for example, alloc, -// newObject, or mutableCopy), or if you send it a retain message. You are -// responsible for relinquishing ownership of objects you own using release -// or autorelease. Any other time you receive an object, you must -// not release it." -// - -using llvm::StrInStrNoCase; -using llvm::StringRef; - -enum NamingConvention { NoConvention, CreateRule, InitRule }; - -static inline bool isWordEnd(char ch, char prev, char next) { - return ch == '\0' - || (islower(prev) && isupper(ch)) // xxxC - || (isupper(prev) && isupper(ch) && islower(next)) // XXCreate - || !isalpha(ch); -} - -static inline const char* parseWord(const char* s) { - char ch = *s, prev = '\0'; - assert(ch != '\0'); - char next = *(s+1); - while (!isWordEnd(ch, prev, next)) { - prev = ch; - ch = next; - next = *((++s)+1); - } - return s; -} - -static NamingConvention deriveNamingConvention(Selector S) { - IdentifierInfo *II = S.getIdentifierInfoForSlot(0); - - if (!II) - return NoConvention; - - const char *s = II->getNameStart(); - - // A method/function name may contain a prefix. We don't know it is there, - // however, until we encounter the first '_'. - bool InPossiblePrefix = true; - bool AtBeginning = true; - NamingConvention C = NoConvention; - - while (*s != '\0') { - // Skip '_'. - if (*s == '_') { - if (InPossiblePrefix) { - // If we already have a convention, return it. Otherwise, skip - // the prefix as if it wasn't there. - if (C != NoConvention) - break; - - InPossiblePrefix = false; - AtBeginning = true; - assert(C == NoConvention); - } - ++s; - continue; - } - - // Skip numbers, ':', etc. - if (!isalpha(*s)) { - ++s; - continue; - } - - const char *wordEnd = parseWord(s); - assert(wordEnd > s); - unsigned len = wordEnd - s; - - switch (len) { - default: - break; - case 3: - // Methods starting with 'new' follow the create rule. - if (AtBeginning && StringRef(s, len).equals_lower("new")) - C = CreateRule; - break; - case 4: - // Methods starting with 'alloc' or contain 'copy' follow the - // create rule - if (C == NoConvention && StringRef(s, len).equals_lower("copy")) - C = CreateRule; - else // Methods starting with 'init' follow the init rule. - if (AtBeginning && StringRef(s, len).equals_lower("init")) - C = InitRule; - break; - case 5: - if (AtBeginning && StringRef(s, len).equals_lower("alloc")) - C = CreateRule; - break; - } - - // If we aren't in the prefix and have a derived convention then just - // return it now. - if (!InPossiblePrefix && C != NoConvention) - return C; - - AtBeginning = false; - s = wordEnd; - } - - // We will get here if there wasn't more than one word - // after the prefix. - return C; -} - -static bool followsFundamentalRule(Selector S) { - return deriveNamingConvention(S) == CreateRule; -} - -static const ObjCMethodDecl* -ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) { - ObjCInterfaceDecl *ID = - const_cast(MD->getClassInterface()); - - return MD->isInstanceMethod() - ? ID->lookupInstanceMethod(MD->getSelector()) - : ID->lookupClassMethod(MD->getSelector()); -} - -namespace { -class GenericNodeBuilder { - GRStmtNodeBuilder *SNB; - Stmt *S; - const void *tag; - GREndPathNodeBuilder *ENB; -public: - GenericNodeBuilder(GRStmtNodeBuilder &snb, Stmt *s, - const void *t) - : SNB(&snb), S(s), tag(t), ENB(0) {} - - GenericNodeBuilder(GREndPathNodeBuilder &enb) - : SNB(0), S(0), tag(0), ENB(&enb) {} - - ExplodedNode *MakeNode(const GRState *state, ExplodedNode *Pred) { - if (SNB) - return SNB->generateNode(PostStmt(S, Pred->getLocationContext(), tag), - state, Pred); - - assert(ENB); - return ENB->generateNode(state, Pred); - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Type querying functions. -//===----------------------------------------------------------------------===// - -static bool isRefType(QualType RetTy, const char* prefix, - ASTContext* Ctx = 0, const char* name = 0) { - - // Recursively walk the typedef stack, allowing typedefs of reference types. - while (TypedefType* TD = dyn_cast(RetTy.getTypePtr())) { - llvm::StringRef TDName = TD->getDecl()->getIdentifier()->getName(); - if (TDName.startswith(prefix) && TDName.endswith("Ref")) - return true; - - RetTy = TD->getDecl()->getUnderlyingType(); - } - - if (!Ctx || !name) - return false; - - // Is the type void*? - const PointerType* PT = RetTy->getAs(); - if (!(PT->getPointeeType().getUnqualifiedType() == Ctx->VoidTy)) - return false; - - // Does the name start with the prefix? - return llvm::StringRef(name).startswith(prefix); -} - -//===----------------------------------------------------------------------===// -// Primitives used for constructing summaries for function/method calls. -//===----------------------------------------------------------------------===// - -/// ArgEffect is used to summarize a function/method call's effect on a -/// particular argument. -enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing, - DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape, - NewAutoreleasePool, SelfOwn, StopTracking }; - -namespace llvm { -template <> struct FoldingSetTrait { -static inline void Profile(const ArgEffect X, FoldingSetNodeID& ID) { - ID.AddInteger((unsigned) X); -} -}; -} // end llvm namespace - -/// ArgEffects summarizes the effects of a function/method call on all of -/// its arguments. -typedef llvm::ImmutableMap ArgEffects; - -namespace { - -/// RetEffect is used to summarize a function/method call's behavior with -/// respect to its return value. -class RetEffect { -public: - enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, - NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias, - OwnedWhenTrackedReceiver }; - - enum ObjKind { CF, ObjC, AnyObj }; - -private: - Kind K; - ObjKind O; - unsigned index; - - RetEffect(Kind k, unsigned idx = 0) : K(k), O(AnyObj), index(idx) {} - RetEffect(Kind k, ObjKind o) : K(k), O(o), index(0) {} - -public: - Kind getKind() const { return K; } - - ObjKind getObjKind() const { return O; } - - unsigned getIndex() const { - assert(getKind() == Alias); - return index; - } - - bool isOwned() const { - return K == OwnedSymbol || K == OwnedAllocatedSymbol || - K == OwnedWhenTrackedReceiver; - } - - static RetEffect MakeOwnedWhenTrackedReceiver() { - return RetEffect(OwnedWhenTrackedReceiver, ObjC); - } - - static RetEffect MakeAlias(unsigned Idx) { - return RetEffect(Alias, Idx); - } - static RetEffect MakeReceiverAlias() { - return RetEffect(ReceiverAlias); - } - static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) { - return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o); - } - static RetEffect MakeNotOwned(ObjKind o) { - return RetEffect(NotOwnedSymbol, o); - } - static RetEffect MakeGCNotOwned() { - return RetEffect(GCNotOwnedSymbol, ObjC); - } - - static RetEffect MakeNoRet() { - return RetEffect(NoRet); - } - - void Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddInteger((unsigned)K); - ID.AddInteger((unsigned)O); - ID.AddInteger(index); - } -}; - -//===----------------------------------------------------------------------===// -// Reference-counting logic (typestate + counts). -//===----------------------------------------------------------------------===// - -class RefVal { -public: - enum Kind { - Owned = 0, // Owning reference. - NotOwned, // Reference is not owned by still valid (not freed). - Released, // Object has been released. - ReturnedOwned, // Returned object passes ownership to caller. - ReturnedNotOwned, // Return object does not pass ownership to caller. - ERROR_START, - ErrorDeallocNotOwned, // -dealloc called on non-owned object. - ErrorDeallocGC, // Calling -dealloc with GC enabled. - ErrorUseAfterRelease, // Object used after released. - ErrorReleaseNotOwned, // Release of an object that was not owned. - ERROR_LEAK_START, - ErrorLeak, // A memory leak due to excessive reference counts. - ErrorLeakReturned, // A memory leak due to the returning method not having - // the correct naming conventions. - ErrorGCLeakReturned, - ErrorOverAutorelease, - ErrorReturnedNotOwned - }; - -private: - Kind kind; - RetEffect::ObjKind okind; - unsigned Cnt; - unsigned ACnt; - QualType T; - - RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t) - : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {} - - RefVal(Kind k, unsigned cnt = 0) - : kind(k), okind(RetEffect::AnyObj), Cnt(cnt), ACnt(0) {} - -public: - Kind getKind() const { return kind; } - - RetEffect::ObjKind getObjKind() const { return okind; } - - unsigned getCount() const { return Cnt; } - unsigned getAutoreleaseCount() const { return ACnt; } - unsigned getCombinedCounts() const { return Cnt + ACnt; } - void clearCounts() { Cnt = 0; ACnt = 0; } - void setCount(unsigned i) { Cnt = i; } - void setAutoreleaseCount(unsigned i) { ACnt = i; } - - QualType getType() const { return T; } - - // Useful predicates. - - static bool isError(Kind k) { return k >= ERROR_START; } - - static bool isLeak(Kind k) { return k >= ERROR_LEAK_START; } - - bool isOwned() const { - return getKind() == Owned; - } - - bool isNotOwned() const { - return getKind() == NotOwned; - } - - bool isReturnedOwned() const { - return getKind() == ReturnedOwned; - } - - bool isReturnedNotOwned() const { - return getKind() == ReturnedNotOwned; - } - - bool isNonLeakError() const { - Kind k = getKind(); - return isError(k) && !isLeak(k); - } - - static RefVal makeOwned(RetEffect::ObjKind o, QualType t, - unsigned Count = 1) { - return RefVal(Owned, o, Count, 0, t); - } - - static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t, - unsigned Count = 0) { - return RefVal(NotOwned, o, Count, 0, t); - } - - // Comparison, profiling, and pretty-printing. - - bool operator==(const RefVal& X) const { - return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt; - } - - RefVal operator-(size_t i) const { - return RefVal(getKind(), getObjKind(), getCount() - i, - getAutoreleaseCount(), getType()); - } - - RefVal operator+(size_t i) const { - return RefVal(getKind(), getObjKind(), getCount() + i, - getAutoreleaseCount(), getType()); - } - - RefVal operator^(Kind k) const { - return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(), - getType()); - } - - RefVal autorelease() const { - return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1, - getType()); - } - - void Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddInteger((unsigned) kind); - ID.AddInteger(Cnt); - ID.AddInteger(ACnt); - ID.Add(T); - } - - void print(llvm::raw_ostream& Out) const; -}; - -void RefVal::print(llvm::raw_ostream& Out) const { - if (!T.isNull()) - Out << "Tracked Type:" << T.getAsString() << '\n'; - - switch (getKind()) { - default: assert(false); - case Owned: { - Out << "Owned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case NotOwned: { - Out << "NotOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case ReturnedOwned: { - Out << "ReturnedOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case ReturnedNotOwned: { - Out << "ReturnedNotOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case Released: - Out << "Released"; - break; - - case ErrorDeallocGC: - Out << "-dealloc (GC)"; - break; - - case ErrorDeallocNotOwned: - Out << "-dealloc (not-owned)"; - break; - - case ErrorLeak: - Out << "Leaked"; - break; - - case ErrorLeakReturned: - Out << "Leaked (Bad naming)"; - break; - - case ErrorGCLeakReturned: - Out << "Leaked (GC-ed at return)"; - break; - - case ErrorUseAfterRelease: - Out << "Use-After-Release [ERROR]"; - break; - - case ErrorReleaseNotOwned: - Out << "Release of Not-Owned [ERROR]"; - break; - - case RefVal::ErrorOverAutorelease: - Out << "Over autoreleased"; - break; - - case RefVal::ErrorReturnedNotOwned: - Out << "Non-owned object returned instead of owned"; - break; - } - - if (ACnt) { - Out << " [ARC +" << ACnt << ']'; - } -} -} //end anonymous namespace - -//===----------------------------------------------------------------------===// -// RefBindings - State used to track object reference counts. -//===----------------------------------------------------------------------===// - -typedef llvm::ImmutableMap RefBindings; - -namespace clang { - template<> - struct GRStateTrait : public GRStatePartialTrait { - static void* GDMIndex() { - static int RefBIndex = 0; - return &RefBIndex; - } - }; -} - -//===----------------------------------------------------------------------===// -// Summaries -//===----------------------------------------------------------------------===// - -namespace { -class RetainSummary { - /// Args - an ordered vector of (index, ArgEffect) pairs, where index - /// specifies the argument (starting from 0). This can be sparsely - /// populated; arguments with no entry in Args use 'DefaultArgEffect'. - ArgEffects Args; - - /// DefaultArgEffect - The default ArgEffect to apply to arguments that - /// do not have an entry in Args. - ArgEffect DefaultArgEffect; - - /// Receiver - If this summary applies to an Objective-C message expression, - /// this is the effect applied to the state of the receiver. - ArgEffect Receiver; - - /// Ret - The effect on the return value. Used to indicate if the - /// function/method call returns a new tracked symbol, returns an - /// alias of one of the arguments in the call, and so on. - RetEffect Ret; - - /// EndPath - Indicates that execution of this method/function should - /// terminate the simulation of a path. - bool EndPath; - -public: - RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff, - ArgEffect ReceiverEff, bool endpath = false) - : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R), - EndPath(endpath) {} - - /// getArg - Return the argument effect on the argument specified by - /// idx (starting from 0). - ArgEffect getArg(unsigned idx) const { - if (const ArgEffect *AE = Args.lookup(idx)) - return *AE; - - return DefaultArgEffect; - } - - /// setDefaultArgEffect - Set the default argument effect. - void setDefaultArgEffect(ArgEffect E) { - DefaultArgEffect = E; - } - - /// setArg - Set the argument effect on the argument specified by idx. - void setArgEffect(ArgEffects::Factory& AF, unsigned idx, ArgEffect E) { - Args = AF.Add(Args, idx, E); - } - - /// getRetEffect - Returns the effect on the return value of the call. - RetEffect getRetEffect() const { return Ret; } - - /// setRetEffect - Set the effect of the return value of the call. - void setRetEffect(RetEffect E) { Ret = E; } - - /// isEndPath - Returns true if executing the given method/function should - /// terminate the path. - bool isEndPath() const { return EndPath; } - - /// getReceiverEffect - Returns the effect on the receiver of the call. - /// This is only meaningful if the summary applies to an ObjCMessageExpr*. - ArgEffect getReceiverEffect() const { return Receiver; } - - /// setReceiverEffect - Set the effect on the receiver of the call. - void setReceiverEffect(ArgEffect E) { Receiver = E; } - - typedef ArgEffects::iterator ExprIterator; - - ExprIterator begin_args() const { return Args.begin(); } - ExprIterator end_args() const { return Args.end(); } - - static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects A, - RetEffect RetEff, ArgEffect DefaultEff, - ArgEffect ReceiverEff, bool EndPath) { - ID.Add(A); - ID.Add(RetEff); - ID.AddInteger((unsigned) DefaultEff); - ID.AddInteger((unsigned) ReceiverEff); - ID.AddInteger((unsigned) EndPath); - } - - void Profile(llvm::FoldingSetNodeID& ID) const { - Profile(ID, Args, Ret, DefaultArgEffect, Receiver, EndPath); - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Data structures for constructing summaries. -//===----------------------------------------------------------------------===// - -namespace { -class ObjCSummaryKey { - IdentifierInfo* II; - Selector S; -public: - ObjCSummaryKey(IdentifierInfo* ii, Selector s) - : II(ii), S(s) {} - - ObjCSummaryKey(const ObjCInterfaceDecl* d, Selector s) - : II(d ? d->getIdentifier() : 0), S(s) {} - - ObjCSummaryKey(const ObjCInterfaceDecl* d, IdentifierInfo *ii, Selector s) - : II(d ? d->getIdentifier() : ii), S(s) {} - - ObjCSummaryKey(Selector s) - : II(0), S(s) {} - - IdentifierInfo* getIdentifier() const { return II; } - Selector getSelector() const { return S; } -}; -} - -namespace llvm { -template <> struct DenseMapInfo { - static inline ObjCSummaryKey getEmptyKey() { - return ObjCSummaryKey(DenseMapInfo::getEmptyKey(), - DenseMapInfo::getEmptyKey()); - } - - static inline ObjCSummaryKey getTombstoneKey() { - return ObjCSummaryKey(DenseMapInfo::getTombstoneKey(), - DenseMapInfo::getTombstoneKey()); - } - - static unsigned getHashValue(const ObjCSummaryKey &V) { - return (DenseMapInfo::getHashValue(V.getIdentifier()) - & 0x88888888) - | (DenseMapInfo::getHashValue(V.getSelector()) - & 0x55555555); - } - - static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) { - return DenseMapInfo::isEqual(LHS.getIdentifier(), - RHS.getIdentifier()) && - DenseMapInfo::isEqual(LHS.getSelector(), - RHS.getSelector()); - } - -}; -template <> -struct isPodLike { static const bool value = true; }; -} // end llvm namespace - -namespace { -class ObjCSummaryCache { - typedef llvm::DenseMap MapTy; - MapTy M; -public: - ObjCSummaryCache() {} - - RetainSummary* find(const ObjCInterfaceDecl* D, IdentifierInfo *ClsName, - Selector S) { - // Lookup the method using the decl for the class @interface. If we - // have no decl, lookup using the class name. - return D ? find(D, S) : find(ClsName, S); - } - - RetainSummary* find(const ObjCInterfaceDecl* D, Selector S) { - // Do a lookup with the (D,S) pair. If we find a match return - // the iterator. - ObjCSummaryKey K(D, S); - MapTy::iterator I = M.find(K); - - if (I != M.end() || !D) - return I->second; - - // Walk the super chain. If we find a hit with a parent, we'll end - // up returning that summary. We actually allow that key (null,S), as - // we cache summaries for the null ObjCInterfaceDecl* to allow us to - // generate initial summaries without having to worry about NSObject - // being declared. - // FIXME: We may change this at some point. - for (ObjCInterfaceDecl* C=D->getSuperClass() ;; C=C->getSuperClass()) { - if ((I = M.find(ObjCSummaryKey(C, S))) != M.end()) - break; - - if (!C) - return NULL; - } - - // Cache the summary with original key to make the next lookup faster - // and return the iterator. - RetainSummary *Summ = I->second; - M[K] = Summ; - return Summ; - } - - - RetainSummary* find(Expr* Receiver, Selector S) { - return find(getReceiverDecl(Receiver), S); - } - - RetainSummary* find(IdentifierInfo* II, Selector S) { - // FIXME: Class method lookup. Right now we dont' have a good way - // of going between IdentifierInfo* and the class hierarchy. - MapTy::iterator I = M.find(ObjCSummaryKey(II, S)); - - if (I == M.end()) - I = M.find(ObjCSummaryKey(S)); - - return I == M.end() ? NULL : I->second; - } - - const ObjCInterfaceDecl* getReceiverDecl(Expr* E) { - if (const ObjCObjectPointerType* PT = - E->getType()->getAs()) - return PT->getInterfaceDecl(); - - return NULL; - } - - RetainSummary*& operator[](ObjCMessageExpr* ME) { - - Selector S = ME->getSelector(); - - if (Expr* Receiver = ME->getReceiver()) { - const ObjCInterfaceDecl* OD = getReceiverDecl(Receiver); - return OD ? M[ObjCSummaryKey(OD->getIdentifier(), S)] : M[S]; - } - - return M[ObjCSummaryKey(ME->getClassName(), S)]; - } - - RetainSummary*& operator[](ObjCSummaryKey K) { - return M[K]; - } - - RetainSummary*& operator[](Selector S) { - return M[ ObjCSummaryKey(S) ]; - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Data structures for managing collections of summaries. -//===----------------------------------------------------------------------===// - -namespace { -class RetainSummaryManager { - - //==-----------------------------------------------------------------==// - // Typedefs. - //==-----------------------------------------------------------------==// - - typedef llvm::DenseMap - FuncSummariesTy; - - typedef ObjCSummaryCache ObjCMethodSummariesTy; - - //==-----------------------------------------------------------------==// - // Data. - //==-----------------------------------------------------------------==// - - /// Ctx - The ASTContext object for the analyzed ASTs. - ASTContext& Ctx; - - /// CFDictionaryCreateII - An IdentifierInfo* representing the indentifier - /// "CFDictionaryCreate". - IdentifierInfo* CFDictionaryCreateII; - - /// GCEnabled - Records whether or not the analyzed code runs in GC mode. - const bool GCEnabled; - - /// FuncSummaries - A map from FunctionDecls to summaries. - FuncSummariesTy FuncSummaries; - - /// ObjCClassMethodSummaries - A map from selectors (for instance methods) - /// to summaries. - ObjCMethodSummariesTy ObjCClassMethodSummaries; - - /// ObjCMethodSummaries - A map from selectors to summaries. - ObjCMethodSummariesTy ObjCMethodSummaries; - - /// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects, - /// and all other data used by the checker. - llvm::BumpPtrAllocator BPAlloc; - - /// AF - A factory for ArgEffects objects. - ArgEffects::Factory AF; - - /// ScratchArgs - A holding buffer for construct ArgEffects. - ArgEffects ScratchArgs; - - /// ObjCAllocRetE - Default return effect for methods returning Objective-C - /// objects. - RetEffect ObjCAllocRetE; - - /// ObjCInitRetE - Default return effect for init methods returning - /// Objective-C objects. - RetEffect ObjCInitRetE; - - RetainSummary DefaultSummary; - RetainSummary* StopSummary; - - //==-----------------------------------------------------------------==// - // Methods. - //==-----------------------------------------------------------------==// - - /// getArgEffects - Returns a persistent ArgEffects object based on the - /// data in ScratchArgs. - ArgEffects getArgEffects(); - - enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable }; - -public: - RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } - - RetainSummary *getDefaultSummary() { - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate(); - return new (Summ) RetainSummary(DefaultSummary); - } - - RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func); - - RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD); - RetainSummary* getCFSummaryGetRule(FunctionDecl* FD); - RetainSummary* getCFCreateGetRuleSummary(FunctionDecl* FD, const char* FName); - - RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff, - ArgEffect ReceiverEff = DoNothing, - ArgEffect DefaultEff = MayEscape, - bool isEndPath = false); - - RetainSummary* getPersistentSummary(RetEffect RE, - ArgEffect ReceiverEff = DoNothing, - ArgEffect DefaultEff = MayEscape) { - return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff); - } - - RetainSummary *getPersistentStopSummary() { - if (StopSummary) - return StopSummary; - - StopSummary = getPersistentSummary(RetEffect::MakeNoRet(), - StopTracking, StopTracking); - - return StopSummary; - } - - RetainSummary *getInitMethodSummary(QualType RetTy); - - void InitializeClassMethodSummaries(); - void InitializeMethodSummaries(); - - bool isTrackedObjCObjectType(QualType T); - bool isTrackedCFObjectType(QualType T); - -private: - - void addClsMethSummary(IdentifierInfo* ClsII, Selector S, - RetainSummary* Summ) { - ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; - } - - void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) { - ObjCClassMethodSummaries[S] = Summ; - } - - void addNSObjectMethSummary(Selector S, RetainSummary *Summ) { - ObjCMethodSummaries[S] = Summ; - } - - void addClassMethSummary(const char* Cls, const char* nullaryName, - RetainSummary *Summ) { - IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); - Selector S = GetNullarySelector(nullaryName, Ctx); - ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; - } - - void addInstMethSummary(const char* Cls, const char* nullaryName, - RetainSummary *Summ) { - IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); - Selector S = GetNullarySelector(nullaryName, Ctx); - ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; - } - - Selector generateSelector(va_list argp) { - llvm::SmallVector II; - - while (const char* s = va_arg(argp, const char*)) - II.push_back(&Ctx.Idents.get(s)); - - return Ctx.Selectors.getSelector(II.size(), &II[0]); - } - - void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries, - RetainSummary* Summ, va_list argp) { - Selector S = generateSelector(argp); - Summaries[ObjCSummaryKey(ClsII, S)] = Summ; - } - - void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) { - va_list argp; - va_start(argp, Summ); - addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); - va_end(argp); - } - - void addClsMethSummary(const char* Cls, RetainSummary* Summ, ...) { - va_list argp; - va_start(argp, Summ); - addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp); - va_end(argp); - } - - void addClsMethSummary(IdentifierInfo *II, RetainSummary* Summ, ...) { - va_list argp; - va_start(argp, Summ); - addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp); - va_end(argp); - } - - void addPanicSummary(const char* Cls, ...) { - RetainSummary* Summ = getPersistentSummary(AF.GetEmptyMap(), - RetEffect::MakeNoRet(), - DoNothing, DoNothing, true); - va_list argp; - va_start (argp, Cls); - addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); - va_end(argp); - } - -public: - - RetainSummaryManager(ASTContext& ctx, bool gcenabled) - : Ctx(ctx), - CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")), - GCEnabled(gcenabled), AF(BPAlloc), ScratchArgs(AF.GetEmptyMap()), - ObjCAllocRetE(gcenabled ? RetEffect::MakeGCNotOwned() - : RetEffect::MakeOwned(RetEffect::ObjC, true)), - ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned() - : RetEffect::MakeOwnedWhenTrackedReceiver()), - DefaultSummary(AF.GetEmptyMap() /* per-argument effects (none) */, - RetEffect::MakeNoRet() /* return effect */, - MayEscape, /* default argument effect */ - DoNothing /* receiver effect */), - StopSummary(0) { - - InitializeClassMethodSummaries(); - InitializeMethodSummaries(); - } - - ~RetainSummaryManager(); - - RetainSummary* getSummary(FunctionDecl* FD); - - RetainSummary *getInstanceMethodSummary(const ObjCMessageExpr *ME, - const GRState *state, - const LocationContext *LC); - - RetainSummary* getInstanceMethodSummary(const ObjCMessageExpr* ME, - const ObjCInterfaceDecl* ID) { - return getInstanceMethodSummary(ME->getSelector(), ME->getClassName(), - ID, ME->getMethodDecl(), ME->getType()); - } - - RetainSummary* getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl* ID, - const ObjCMethodDecl *MD, - QualType RetTy); - - RetainSummary *getClassMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, - QualType RetTy); - - RetainSummary *getClassMethodSummary(const ObjCMessageExpr *ME) { - return getClassMethodSummary(ME->getSelector(), ME->getClassName(), - ME->getClassInfo().first, - ME->getMethodDecl(), ME->getType()); - } - - /// getMethodSummary - This version of getMethodSummary is used to query - /// the summary for the current method being analyzed. - RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) { - // FIXME: Eventually this should be unneeded. - const ObjCInterfaceDecl *ID = MD->getClassInterface(); - Selector S = MD->getSelector(); - IdentifierInfo *ClsName = ID->getIdentifier(); - QualType ResultTy = MD->getResultType(); - - // Resolve the method decl last. - if (const ObjCMethodDecl *InterfaceMD = ResolveToInterfaceMethodDecl(MD)) - MD = InterfaceMD; - - if (MD->isInstanceMethod()) - return getInstanceMethodSummary(S, ClsName, ID, MD, ResultTy); - else - return getClassMethodSummary(S, ClsName, ID, MD, ResultTy); - } - - RetainSummary* getCommonMethodSummary(const ObjCMethodDecl* MD, - Selector S, QualType RetTy); - - void updateSummaryFromAnnotations(RetainSummary &Summ, - const ObjCMethodDecl *MD); - - void updateSummaryFromAnnotations(RetainSummary &Summ, - const FunctionDecl *FD); - - bool isGCEnabled() const { return GCEnabled; } - - RetainSummary *copySummary(RetainSummary *OldSumm) { - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate(); - new (Summ) RetainSummary(*OldSumm); - return Summ; - } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Implementation of checker data structures. -//===----------------------------------------------------------------------===// - -RetainSummaryManager::~RetainSummaryManager() {} - -ArgEffects RetainSummaryManager::getArgEffects() { - ArgEffects AE = ScratchArgs; - ScratchArgs = AF.GetEmptyMap(); - return AE; -} - -RetainSummary* -RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff, - ArgEffect ReceiverEff, - ArgEffect DefaultEff, - bool isEndPath) { - // Create the summary and return it. - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate(); - new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff, isEndPath); - return Summ; -} - -//===----------------------------------------------------------------------===// -// Predicates. -//===----------------------------------------------------------------------===// - -bool RetainSummaryManager::isTrackedObjCObjectType(QualType Ty) { - if (!Ty->isObjCObjectPointerType()) - return false; - - const ObjCObjectPointerType *PT = Ty->getAs(); - - // Can be true for objects with the 'NSObject' attribute. - if (!PT) - return true; - - // We assume that id<..>, id, and "Class" all represent tracked objects. - if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() || - PT->isObjCClassType()) - return true; - - // Does the interface subclass NSObject? - // FIXME: We can memoize here if this gets too expensive. - const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); - - // Assume that anything declared with a forward declaration and no - // @interface subclasses NSObject. - if (ID->isForwardDecl()) - return true; - - IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); - - for ( ; ID ; ID = ID->getSuperClass()) - if (ID->getIdentifier() == NSObjectII) - return true; - - return false; -} - -bool RetainSummaryManager::isTrackedCFObjectType(QualType T) { - return isRefType(T, "CF") || // Core Foundation. - isRefType(T, "CG") || // Core Graphics. - isRefType(T, "DADisk") || // Disk Arbitration API. - isRefType(T, "DADissenter") || - isRefType(T, "DASessionRef"); -} - -//===----------------------------------------------------------------------===// -// Summary creation for functions (largely uses of Core Foundation). -//===----------------------------------------------------------------------===// - -static bool isRetain(FunctionDecl* FD, const char* FName) { - const char* loc = strstr(FName, "Retain"); - return loc && loc[sizeof("Retain")-1] == '\0'; -} - -static bool isRelease(FunctionDecl* FD, const char* FName) { - const char* loc = strstr(FName, "Release"); - return loc && loc[sizeof("Release")-1] == '\0'; -} - -RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) { - // Look up a summary in our cache of FunctionDecls -> Summaries. - FuncSummariesTy::iterator I = FuncSummaries.find(FD); - if (I != FuncSummaries.end()) - return I->second; - - // No summary? Generate one. - RetainSummary *S = 0; - - do { - // We generate "stop" summaries for implicitly defined functions. - if (FD->isImplicit()) { - S = getPersistentStopSummary(); - break; - } - - // [PR 3337] Use 'getAs' to strip away any typedefs on the - // function's type. - const FunctionType* FT = FD->getType()->getAs(); - const IdentifierInfo *II = FD->getIdentifier(); - if (!II) - break; - - const char* FName = II->getNameStart(); - - // Strip away preceding '_'. Doing this here will effect all the checks - // down below. - while (*FName == '_') ++FName; - - // Inspect the result type. - QualType RetTy = FT->getResultType(); - - // FIXME: This should all be refactored into a chain of "summary lookup" - // filters. - assert(ScratchArgs.isEmpty()); - - switch (strlen(FName)) { - default: break; - case 14: - if (!memcmp(FName, "pthread_create", 14)) { - // Part of: . This will be addressed - // better with IPA. - S = getPersistentStopSummary(); - } - break; - - case 17: - // Handle: id NSMakeCollectable(CFTypeRef) - if (!memcmp(FName, "NSMakeCollectable", 17)) { - S = (RetTy->isObjCIdType()) - ? getUnarySummary(FT, cfmakecollectable) - : getPersistentStopSummary(); - } - else if (!memcmp(FName, "IOBSDNameMatching", 17) || - !memcmp(FName, "IOServiceMatching", 17)) { - // Part of . (IOKit) - // This should be addressed using a API table. - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing, DoNothing); - } - break; - - case 21: - if (!memcmp(FName, "IOServiceNameMatching", 21)) { - // Part of . (IOKit) - // This should be addressed using a API table. - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing, DoNothing); - } - break; - - case 24: - if (!memcmp(FName, "IOServiceAddNotification", 24)) { - // Part of . (IOKit) - // This should be addressed using a API table. - ScratchArgs = AF.Add(ScratchArgs, 2, DecRef); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing,DoNothing); - } - break; - - case 25: - if (!memcmp(FName, "IORegistryEntryIDMatching", 25)) { - // Part of . (IOKit) - // This should be addressed using a API table. - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing, DoNothing); - } - break; - - case 26: - if (!memcmp(FName, "IOOpenFirmwarePathMatching", 26)) { - // Part of . (IOKit) - // This should be addressed using a API table. - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing, DoNothing); - } - break; - - case 27: - if (!memcmp(FName, "IOServiceGetMatchingService", 27)) { - // Part of . - // This should be addressed using a API table. - ScratchArgs = AF.Add(ScratchArgs, 1, DecRef); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } - break; - - case 28: - if (!memcmp(FName, "IOServiceGetMatchingServices", 28)) { - // FIXES: - // This should be addressed using a API table. This strcmp is also - // a little gross, but there is no need to super optimize here. - ScratchArgs = AF.Add(ScratchArgs, 1, DecRef); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, - DoNothing); - } - else if (!memcmp(FName, "CVPixelBufferCreateWithBytes", 28)) { - // FIXES: - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithBytes is released via - // a callback and doing full IPA to make sure this is done correctly. - // FIXME: This function has an out parameter that returns an - // allocated object. - ScratchArgs = AF.Add(ScratchArgs, 7, StopTracking); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, - DoNothing); - } - break; - - case 29: - if (!memcmp(FName, "CGBitmapContextCreateWithData", 29)) { - // FIXES: - // Eventually this can be improved by recognizing that 'releaseInfo' - // passed to CGBitmapContextCreateWithData is released via - // a callback and doing full IPA to make sure this is done correctly. - ScratchArgs = AF.Add(ScratchArgs, 8, StopTracking); - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing,DoNothing); - } - break; - - case 32: - if (!memcmp(FName, "IOServiceAddMatchingNotification", 32)) { - // Part of . - // This should be addressed using a API table. - ScratchArgs = AF.Add(ScratchArgs, 2, DecRef); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } - break; - - case 34: - if (!memcmp(FName, "CVPixelBufferCreateWithPlanarBytes", 34)) { - // FIXES: - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithPlanarBytes is released - // via a callback and doing full IPA to make sure this is done - // correctly. - ScratchArgs = AF.Add(ScratchArgs, 12, StopTracking); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, - DoNothing); - } - break; - } - - // Did we get a summary? - if (S) - break; - - // Enable this code once the semantics of NSDeallocateObject are resolved - // for GC. -#if 0 - // Handle: NSDeallocateObject(id anObject); - // This method does allow 'nil' (although we don't check it now). - if (strcmp(FName, "NSDeallocateObject") == 0) { - return RetTy == Ctx.VoidTy - ? getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Dealloc) - : getPersistentStopSummary(); - } -#endif - - if (RetTy->isPointerType()) { - // For CoreFoundation ('CF') types. - if (isRefType(RetTy, "CF", &Ctx, FName)) { - if (isRetain(FD, FName)) - S = getUnarySummary(FT, cfretain); - else if (strstr(FName, "MakeCollectable")) - S = getUnarySummary(FT, cfmakecollectable); - else - S = getCFCreateGetRuleSummary(FD, FName); - - break; - } - - // For CoreGraphics ('CG') types. - if (isRefType(RetTy, "CG", &Ctx, FName)) { - if (isRetain(FD, FName)) - S = getUnarySummary(FT, cfretain); - else - S = getCFCreateGetRuleSummary(FD, FName); - - break; - } - - // For the Disk Arbitration API (DiskArbitration/DADisk.h) - if (isRefType(RetTy, "DADisk") || - isRefType(RetTy, "DADissenter") || - isRefType(RetTy, "DASessionRef")) { - S = getCFCreateGetRuleSummary(FD, FName); - break; - } - - break; - } - - // Check for release functions, the only kind of functions that we care - // about that don't return a pointer type. - if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G')) { - // Test for 'CGCF'. - if (FName[1] == 'G' && FName[2] == 'C' && FName[3] == 'F') - FName += 4; - else - FName += 2; - - if (isRelease(FD, FName)) - S = getUnarySummary(FT, cfrelease); - else { - assert (ScratchArgs.isEmpty()); - // Remaining CoreFoundation and CoreGraphics functions. - // We use to assume that they all strictly followed the ownership idiom - // and that ownership cannot be transferred. While this is technically - // correct, many methods allow a tracked object to escape. For example: - // - // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...); - // CFDictionaryAddValue(y, key, x); - // CFRelease(x); - // ... it is okay to use 'x' since 'y' has a reference to it - // - // We handle this and similar cases with the follow heuristic. If the - // function name contains "InsertValue", "SetValue", "AddValue", - // "AppendValue", or "SetAttribute", then we assume that arguments may - // "escape." This means that something else holds on to the object, - // allowing it be used even after its local retain count drops to 0. - ArgEffect E = (StrInStrNoCase(FName, "InsertValue") != StringRef::npos|| - StrInStrNoCase(FName, "AddValue") != StringRef::npos || - StrInStrNoCase(FName, "SetValue") != StringRef::npos || - StrInStrNoCase(FName, "AppendValue") != StringRef::npos|| - StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) - ? MayEscape : DoNothing; - - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, E); - } - } - } - while (0); - - if (!S) - S = getDefaultSummary(); - - // Annotations override defaults. - assert(S); - updateSummaryFromAnnotations(*S, FD); - - FuncSummaries[FD] = S; - return S; -} - -RetainSummary* -RetainSummaryManager::getCFCreateGetRuleSummary(FunctionDecl* FD, - const char* FName) { - - if (strstr(FName, "Create") || strstr(FName, "Copy")) - return getCFSummaryCreateRule(FD); - - if (strstr(FName, "Get")) - return getCFSummaryGetRule(FD); - - return getDefaultSummary(); -} - -RetainSummary* -RetainSummaryManager::getUnarySummary(const FunctionType* FT, - UnaryFuncKind func) { - - // Sanity check that this is *really* a unary function. This can - // happen if people do weird things. - const FunctionProtoType* FTP = dyn_cast(FT); - if (!FTP || FTP->getNumArgs() != 1) - return getPersistentStopSummary(); - - assert (ScratchArgs.isEmpty()); - - switch (func) { - case cfretain: { - ScratchArgs = AF.Add(ScratchArgs, 0, IncRef); - return getPersistentSummary(RetEffect::MakeAlias(0), - DoNothing, DoNothing); - } - - case cfrelease: { - ScratchArgs = AF.Add(ScratchArgs, 0, DecRef); - return getPersistentSummary(RetEffect::MakeNoRet(), - DoNothing, DoNothing); - } - - case cfmakecollectable: { - ScratchArgs = AF.Add(ScratchArgs, 0, MakeCollectable); - return getPersistentSummary(RetEffect::MakeAlias(0),DoNothing, DoNothing); - } - - default: - assert (false && "Not a supported unary function."); - return getDefaultSummary(); - } -} - -RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) { - assert (ScratchArgs.isEmpty()); - - if (FD->getIdentifier() == CFDictionaryCreateII) { - ScratchArgs = AF.Add(ScratchArgs, 1, DoNothingByRef); - ScratchArgs = AF.Add(ScratchArgs, 2, DoNothingByRef); - } - - return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); -} - -RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) { - assert (ScratchArgs.isEmpty()); - return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF), - DoNothing, DoNothing); -} - -//===----------------------------------------------------------------------===// -// Summary creation for Selectors. -//===----------------------------------------------------------------------===// - -RetainSummary* -RetainSummaryManager::getInitMethodSummary(QualType RetTy) { - assert(ScratchArgs.isEmpty()); - // 'init' methods conceptually return a newly allocated object and claim - // the receiver. - if (isTrackedObjCObjectType(RetTy) || isTrackedCFObjectType(RetTy)) - return getPersistentSummary(ObjCInitRetE, DecRefMsg); - - return getDefaultSummary(); -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, - const FunctionDecl *FD) { - if (!FD) - return; - - QualType RetTy = FD->getResultType(); - - // Determine if there is a special return effect for this method. - if (isTrackedObjCObjectType(RetTy)) { - if (FD->getAttr()) { - Summ.setRetEffect(ObjCAllocRetE); - } - else if (FD->getAttr()) { - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); - } - } - else if (RetTy->getAs()) { - if (FD->getAttr()) { - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); - } - } -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, - const ObjCMethodDecl *MD) { - if (!MD) - return; - - bool isTrackedLoc = false; - - // Determine if there is a special return effect for this method. - if (isTrackedObjCObjectType(MD->getResultType())) { - if (MD->getAttr()) { - Summ.setRetEffect(ObjCAllocRetE); - return; - } - - isTrackedLoc = true; - } - - if (!isTrackedLoc) - isTrackedLoc = MD->getResultType()->getAs() != NULL; - - if (isTrackedLoc && MD->getAttr()) - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); -} - -RetainSummary* -RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, - Selector S, QualType RetTy) { - - if (MD) { - // Scan the method decl for 'void*' arguments. These should be treated - // as 'StopTracking' because they are often used with delegates. - // Delegates are a frequent form of false positives with the retain - // count checker. - unsigned i = 0; - for (ObjCMethodDecl::param_iterator I = MD->param_begin(), - E = MD->param_end(); I != E; ++I, ++i) - if (ParmVarDecl *PD = *I) { - QualType Ty = Ctx.getCanonicalType(PD->getType()); - if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy) - ScratchArgs = AF.Add(ScratchArgs, i, StopTracking); - } - } - - // Any special effect for the receiver? - ArgEffect ReceiverEff = DoNothing; - - // If one of the arguments in the selector has the keyword 'delegate' we - // should stop tracking the reference count for the receiver. This is - // because the reference count is quite possibly handled by a delegate - // method. - if (S.isKeywordSelector()) { - const std::string &str = S.getAsString(); - assert(!str.empty()); - if (StrInStrNoCase(str, "delegate:") != StringRef::npos) - ReceiverEff = StopTracking; - } - - // Look for methods that return an owned object. - if (isTrackedObjCObjectType(RetTy)) { - // EXPERIMENTAL: Assume the Cocoa conventions for all objects returned - // by instance methods. - RetEffect E = followsFundamentalRule(S) - ? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC); - - return getPersistentSummary(E, ReceiverEff, MayEscape); - } - - // Look for methods that return an owned core foundation object. - if (isTrackedCFObjectType(RetTy)) { - RetEffect E = followsFundamentalRule(S) - ? RetEffect::MakeOwned(RetEffect::CF, true) - : RetEffect::MakeNotOwned(RetEffect::CF); - - return getPersistentSummary(E, ReceiverEff, MayEscape); - } - - if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing) - return getDefaultSummary(); - - return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, MayEscape); -} - -RetainSummary* -RetainSummaryManager::getInstanceMethodSummary(const ObjCMessageExpr *ME, - const GRState *state, - const LocationContext *LC) { - - // We need the type-information of the tracked receiver object - // Retrieve it from the state. - const Expr *Receiver = ME->getReceiver(); - const ObjCInterfaceDecl* ID = 0; - - // FIXME: Is this really working as expected? There are cases where - // we just use the 'ID' from the message expression. - SVal receiverV = state->getSValAsScalarOrLoc(Receiver); - - // FIXME: Eventually replace the use of state->get with - // a generic API for reasoning about the Objective-C types of symbolic - // objects. - if (SymbolRef Sym = receiverV.getAsLocSymbol()) - if (const RefVal *T = state->get(Sym)) - if (const ObjCObjectPointerType* PT = - T->getType()->getAs()) - ID = PT->getInterfaceDecl(); - - // FIXME: this is a hack. This may or may not be the actual method - // that is called. - if (!ID) { - if (const ObjCObjectPointerType *PT = - Receiver->getType()->getAs()) - ID = PT->getInterfaceDecl(); - } - - // FIXME: The receiver could be a reference to a class, meaning that - // we should use the class method. - RetainSummary *Summ = getInstanceMethodSummary(ME, ID); - - // Special-case: are we sending a mesage to "self"? - // This is a hack. When we have full-IP this should be removed. - if (isa(LC->getDecl())) { - if (const loc::MemRegionVal *L = dyn_cast(&receiverV)) { - // Get the region associated with 'self'. - if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl()) { - SVal SelfVal = state->getSVal(state->getRegion(SelfDecl, LC)); - if (L->StripCasts() == SelfVal.getAsRegion()) { - // Update the summary to make the default argument effect - // 'StopTracking'. - Summ = copySummary(Summ); - Summ->setDefaultArgEffect(StopTracking); - } - } - } - } - - return Summ ? Summ : getDefaultSummary(); -} - -RetainSummary* -RetainSummaryManager::getInstanceMethodSummary(Selector S, - IdentifierInfo *ClsName, - const ObjCInterfaceDecl* ID, - const ObjCMethodDecl *MD, - QualType RetTy) { - - // Look up a summary in our summary cache. - RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S); - - if (!Summ) { - assert(ScratchArgs.isEmpty()); - - // "initXXX": pass-through for receiver. - if (deriveNamingConvention(S) == InitRule) - Summ = getInitMethodSummary(RetTy); - else - Summ = getCommonMethodSummary(MD, S, RetTy); - - // Annotations override defaults. - updateSummaryFromAnnotations(*Summ, MD); - - // Memoize the summary. - ObjCMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; - } - - return Summ; -} - -RetainSummary* -RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, - QualType RetTy) { - - assert(ClsName && "Class name must be specified."); - RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S); - - if (!Summ) { - Summ = getCommonMethodSummary(MD, S, RetTy); - // Annotations override defaults. - updateSummaryFromAnnotations(*Summ, MD); - // Memoize the summary. - ObjCClassMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; - } - - return Summ; -} - -void RetainSummaryManager::InitializeClassMethodSummaries() { - assert(ScratchArgs.isEmpty()); - RetainSummary* Summ = getPersistentSummary(ObjCAllocRetE); - - // Create the summaries for "alloc", "new", and "allocWithZone:" for - // NSObject and its derivatives. - addNSObjectClsMethSummary(GetNullarySelector("alloc", Ctx), Summ); - addNSObjectClsMethSummary(GetNullarySelector("new", Ctx), Summ); - addNSObjectClsMethSummary(GetUnarySelector("allocWithZone", Ctx), Summ); - - // Create the [NSAssertionHandler currentHander] summary. - addClassMethSummary("NSAssertionHandler", "currentHandler", - getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC))); - - // Create the [NSAutoreleasePool addObject:] summary. - ScratchArgs = AF.Add(ScratchArgs, 0, Autorelease); - addClassMethSummary("NSAutoreleasePool", "addObject", - getPersistentSummary(RetEffect::MakeNoRet(), - DoNothing, Autorelease)); - - // Create a summary for [NSCursor dragCopyCursor]. - addClassMethSummary("NSCursor", "dragCopyCursor", - getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, - DoNothing)); - - // Create the summaries for [NSObject performSelector...]. We treat - // these as 'stop tracking' for the arguments because they are often - // used for delegates that can release the object. When we have better - // inter-procedural analysis we can potentially do something better. This - // workaround is to remove false positives. - Summ = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking); - IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject"); - addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", - "afterDelay", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", - "afterDelay", "inModes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", - "withObject", "waitUntilDone", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", - "withObject", "waitUntilDone", "modes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", - "withObject", "waitUntilDone", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", - "withObject", "waitUntilDone", "modes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground", - "withObject", NULL); - - // Specially handle NSData. - RetainSummary *dataWithBytesNoCopySumm = - getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC), DoNothing, - DoNothing); - addClsMethSummary("NSData", dataWithBytesNoCopySumm, - "dataWithBytesNoCopy", "length", NULL); - addClsMethSummary("NSData", dataWithBytesNoCopySumm, - "dataWithBytesNoCopy", "length", "freeWhenDone", NULL); -} - -void RetainSummaryManager::InitializeMethodSummaries() { - - assert (ScratchArgs.isEmpty()); - - // Create the "init" selector. It just acts as a pass-through for the - // receiver. - RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg); - addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm); - - // awakeAfterUsingCoder: behaves basically like an 'init' method. It - // claims the receiver and returns a retained object. - addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx), - InitSumm); - - // The next methods are allocators. - RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE); - RetainSummary *CFAllocSumm = - getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); - - // Create the "copy" selector. - addNSObjectMethSummary(GetNullarySelector("copy", Ctx), AllocSumm); - - // Create the "mutableCopy" selector. - addNSObjectMethSummary(GetNullarySelector("mutableCopy", Ctx), AllocSumm); - - // Create the "retain" selector. - RetEffect E = RetEffect::MakeReceiverAlias(); - RetainSummary *Summ = getPersistentSummary(E, IncRefMsg); - addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); - - // Create the "release" selector. - Summ = getPersistentSummary(E, DecRefMsg); - addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); - - // Create the "drain" selector. - Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef); - addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ); - - // Create the -dealloc summary. - Summ = getPersistentSummary(RetEffect::MakeNoRet(), Dealloc); - addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); - - // Create the "autorelease" selector. - Summ = getPersistentSummary(E, Autorelease); - addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); - - // Specially handle NSAutoreleasePool. - addInstMethSummary("NSAutoreleasePool", "init", - getPersistentSummary(RetEffect::MakeReceiverAlias(), - NewAutoreleasePool)); - - // For NSWindow, allocated objects are (initially) self-owned. - // FIXME: For now we opt for false negatives with NSWindow, as these objects - // self-own themselves. However, they only do this once they are displayed. - // Thus, we need to track an NSWindow's display status. - // This is tracked in . - // See also http://llvm.org/bugs/show_bug.cgi?id=3714. - RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(), - StopTracking, - StopTracking); - - addClassMethSummary("NSWindow", "alloc", NoTrackYet); - -#if 0 - addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", NULL); - - addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", "screen", NULL); -#endif - - // For NSPanel (which subclasses NSWindow), allocated objects are not - // self-owned. - // FIXME: For now we don't track NSPanels. object for the same reason - // as for NSWindow objects. - addClassMethSummary("NSPanel", "alloc", NoTrackYet); - -#if 0 - addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", NULL); - - addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", "screen", NULL); -#endif - - // Don't track allocated autorelease pools yet, as it is okay to prematurely - // exit a method. - addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); - - // Create NSAssertionHandler summaries. - addPanicSummary("NSAssertionHandler", "handleFailureInFunction", "file", - "lineNumber", "description", NULL); - - addPanicSummary("NSAssertionHandler", "handleFailureInMethod", "object", - "file", "lineNumber", "description", NULL); - - // Create summaries QCRenderer/QCView -createSnapShotImageOfType: - addInstMethSummary("QCRenderer", AllocSumm, - "createSnapshotImageOfType", NULL); - addInstMethSummary("QCView", AllocSumm, - "createSnapshotImageOfType", NULL); - - // Create summaries for CIContext, 'createCGImage' and - // 'createCGLayerWithSize'. These objects are CF objects, and are not - // automatically garbage collected. - addInstMethSummary("CIContext", CFAllocSumm, - "createCGImage", "fromRect", NULL); - addInstMethSummary("CIContext", CFAllocSumm, - "createCGImage", "fromRect", "format", "colorSpace", NULL); - addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", - "info", NULL); -} - -//===----------------------------------------------------------------------===// -// AutoreleaseBindings - State used to track objects in autorelease pools. -//===----------------------------------------------------------------------===// - -typedef llvm::ImmutableMap ARCounts; -typedef llvm::ImmutableMap ARPoolContents; -typedef llvm::ImmutableList ARStack; - -static int AutoRCIndex = 0; -static int AutoRBIndex = 0; - -namespace { class AutoreleasePoolContents {}; } -namespace { class AutoreleaseStack {}; } - -namespace clang { -template<> struct GRStateTrait - : public GRStatePartialTrait { - static inline void* GDMIndex() { return &AutoRBIndex; } -}; - -template<> struct GRStateTrait - : public GRStatePartialTrait { - static inline void* GDMIndex() { return &AutoRCIndex; } -}; -} // end clang namespace - -static SymbolRef GetCurrentAutoreleasePool(const GRState* state) { - ARStack stack = state->get(); - return stack.isEmpty() ? SymbolRef() : stack.getHead(); -} - -static const GRState * SendAutorelease(const GRState *state, - ARCounts::Factory &F, SymbolRef sym) { - - SymbolRef pool = GetCurrentAutoreleasePool(state); - const ARCounts *cnts = state->get(pool); - ARCounts newCnts(0); - - if (cnts) { - const unsigned *cnt = (*cnts).lookup(sym); - newCnts = F.Add(*cnts, sym, cnt ? *cnt + 1 : 1); - } - else - newCnts = F.Add(F.GetEmptyMap(), sym, 1); - - return state->set(pool, newCnts); -} - -//===----------------------------------------------------------------------===// -// Transfer functions. -//===----------------------------------------------------------------------===// - -namespace { - -class CFRefCount : public GRTransferFuncs { -public: - class BindingsPrinter : public GRState::Printer { - public: - virtual void Print(llvm::raw_ostream& Out, const GRState* state, - const char* nl, const char* sep); - }; - -private: - typedef llvm::DenseMap - SummaryLogTy; - - RetainSummaryManager Summaries; - SummaryLogTy SummaryLog; - const LangOptions& LOpts; - ARCounts::Factory ARCountFactory; - - BugType *useAfterRelease, *releaseNotOwned; - BugType *deallocGC, *deallocNotOwned; - BugType *leakWithinFunction, *leakAtReturn; - BugType *overAutorelease; - BugType *returnNotOwnedForOwned; - BugReporter *BR; - - const GRState * Update(const GRState * state, SymbolRef sym, RefVal V, ArgEffect E, - RefVal::Kind& hasErr); - - void ProcessNonLeakError(ExplodedNodeSet& Dst, - GRStmtNodeBuilder& Builder, - Expr* NodeExpr, Expr* ErrorExpr, - ExplodedNode* Pred, - const GRState* St, - RefVal::Kind hasErr, SymbolRef Sym); - - const GRState * HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, - llvm::SmallVectorImpl &Leaked); - - ExplodedNode* ProcessLeaks(const GRState * state, - llvm::SmallVectorImpl &Leaked, - GenericNodeBuilder &Builder, - GRExprEngine &Eng, - ExplodedNode *Pred = 0); - -public: - CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts) - : Summaries(Ctx, gcenabled), - LOpts(lopts), useAfterRelease(0), releaseNotOwned(0), - deallocGC(0), deallocNotOwned(0), - leakWithinFunction(0), leakAtReturn(0), overAutorelease(0), - returnNotOwnedForOwned(0), BR(0) {} - - virtual ~CFRefCount() {} - - void RegisterChecks(GRExprEngine &Eng); - - virtual void RegisterPrinters(std::vector& Printers) { - Printers.push_back(new BindingsPrinter()); - } - - bool isGCEnabled() const { return Summaries.isGCEnabled(); } - const LangOptions& getLangOptions() const { return LOpts; } - - const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const { - SummaryLogTy::const_iterator I = SummaryLog.find(N); - return I == SummaryLog.end() ? 0 : I->second; - } - - // Calls. - - void EvalSummary(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - Expr* Ex, - Expr* Receiver, - const RetainSummary& Summ, - const MemRegion *Callee, - ExprIterator arg_beg, ExprIterator arg_end, - ExplodedNode* Pred, const GRState *state); - - virtual void EvalCall(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - CallExpr* CE, SVal L, - ExplodedNode* Pred); - - - virtual void EvalObjCMessageExpr(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - ObjCMessageExpr* ME, - ExplodedNode* Pred, - const GRState *state); - - bool EvalObjCMessageExprAux(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - ObjCMessageExpr* ME, - ExplodedNode* Pred); - - // Stores. - virtual void EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val); - - // End-of-path. - - virtual void EvalEndPath(GRExprEngine& Engine, - GREndPathNodeBuilder& Builder); - - virtual void EvalDeadSymbols(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - ExplodedNode* Pred, - Stmt* S, const GRState* state, - SymbolReaper& SymReaper); - - std::pair - HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd, - ExplodedNode* Pred, GRExprEngine &Eng, - SymbolRef Sym, RefVal V, bool &stop); - // Return statements. - - virtual void EvalReturn(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - ReturnStmt* S, - ExplodedNode* Pred); - - // Assumptions. - - virtual const GRState *EvalAssume(const GRState* state, SVal condition, - bool assumption); -}; - -} // end anonymous namespace - -static void PrintPool(llvm::raw_ostream &Out, SymbolRef Sym, - const GRState *state) { - Out << ' '; - if (Sym) - Out << Sym->getSymbolID(); - else - Out << ""; - Out << ":{"; - - // Get the contents of the pool. - if (const ARCounts *cnts = state->get(Sym)) - for (ARCounts::iterator J=cnts->begin(), EJ=cnts->end(); J != EJ; ++J) - Out << '(' << J.getKey() << ',' << J.getData() << ')'; - - Out << '}'; -} - -void CFRefCount::BindingsPrinter::Print(llvm::raw_ostream& Out, - const GRState* state, - const char* nl, const char* sep) { - - RefBindings B = state->get(); - - if (!B.isEmpty()) - Out << sep << nl; - - for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { - Out << (*I).first << " : "; - (*I).second.print(Out); - Out << nl; - } - - // Print the autorelease stack. - Out << sep << nl << "AR pool stack:"; - ARStack stack = state->get(); - - PrintPool(Out, SymbolRef(), state); // Print the caller's pool. - for (ARStack::iterator I=stack.begin(), E=stack.end(); I!=E; ++I) - PrintPool(Out, *I, state); - - Out << nl; -} - -//===----------------------------------------------------------------------===// -// Error reporting. -//===----------------------------------------------------------------------===// - -namespace { - - //===-------------===// - // Bug Descriptions. // - //===-------------===// - - class CFRefBug : public BugType { - protected: - CFRefCount& TF; - - CFRefBug(CFRefCount* tf, llvm::StringRef name) - : BugType(name, "Memory (Core Foundation/Objective-C)"), TF(*tf) {} - public: - - CFRefCount& getTF() { return TF; } - const CFRefCount& getTF() const { return TF; } - - // FIXME: Eventually remove. - virtual const char* getDescription() const = 0; - - virtual bool isLeak() const { return false; } - }; - - class UseAfterRelease : public CFRefBug { - public: - UseAfterRelease(CFRefCount* tf) - : CFRefBug(tf, "Use-after-release") {} - - const char* getDescription() const { - return "Reference-counted object is used after it is released"; - } - }; - - class BadRelease : public CFRefBug { - public: - BadRelease(CFRefCount* tf) : CFRefBug(tf, "Bad release") {} - - const char* getDescription() const { - return "Incorrect decrement of the reference count of an object that is " - "not owned at this point by the caller"; - } - }; - - class DeallocGC : public CFRefBug { - public: - DeallocGC(CFRefCount *tf) - : CFRefBug(tf, "-dealloc called while using garbage collection") {} - - const char *getDescription() const { - return "-dealloc called while using garbage collection"; - } - }; - - class DeallocNotOwned : public CFRefBug { - public: - DeallocNotOwned(CFRefCount *tf) - : CFRefBug(tf, "-dealloc sent to non-exclusively owned object") {} - - const char *getDescription() const { - return "-dealloc sent to object that may be referenced elsewhere"; - } - }; - - class OverAutorelease : public CFRefBug { - public: - OverAutorelease(CFRefCount *tf) : - CFRefBug(tf, "Object sent -autorelease too many times") {} - - const char *getDescription() const { - return "Object sent -autorelease too many times"; - } - }; - - class ReturnedNotOwnedForOwned : public CFRefBug { - public: - ReturnedNotOwnedForOwned(CFRefCount *tf) : - CFRefBug(tf, "Method should return an owned object") {} - - const char *getDescription() const { - return "Object with +0 retain counts returned to caller where a +1 " - "(owning) retain count is expected"; - } - }; - - class Leak : public CFRefBug { - const bool isReturn; - protected: - Leak(CFRefCount* tf, llvm::StringRef name, bool isRet) - : CFRefBug(tf, name), isReturn(isRet) {} - public: - - const char* getDescription() const { return ""; } - - bool isLeak() const { return true; } - }; - - class LeakAtReturn : public Leak { - public: - LeakAtReturn(CFRefCount* tf, llvm::StringRef name) - : Leak(tf, name, true) {} - }; - - class LeakWithinFunction : public Leak { - public: - LeakWithinFunction(CFRefCount* tf, llvm::StringRef name) - : Leak(tf, name, false) {} - }; - - //===---------===// - // Bug Reports. // - //===---------===// - - class CFRefReport : public RangedBugReport { - protected: - SymbolRef Sym; - const CFRefCount &TF; - public: - CFRefReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym) - : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {} - - CFRefReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym, llvm::StringRef endText) - : RangedBugReport(D, D.getDescription(), endText, n), Sym(sym), TF(tf) {} - - virtual ~CFRefReport() {} - - CFRefBug& getBugType() { - return (CFRefBug&) RangedBugReport::getBugType(); - } - const CFRefBug& getBugType() const { - return (const CFRefBug&) RangedBugReport::getBugType(); - } - - virtual void getRanges(const SourceRange*& beg, const SourceRange*& end) { - if (!getBugType().isLeak()) - RangedBugReport::getRanges(beg, end); - else - beg = end = 0; - } - - SymbolRef getSymbol() const { return Sym; } - - PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, - const ExplodedNode* N); - - std::pair getExtraDescriptiveText(); - - PathDiagnosticPiece* VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext& BRC); - }; - - class CFRefLeakReport : public CFRefReport { - SourceLocation AllocSite; - const MemRegion* AllocBinding; - public: - CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym, - GRExprEngine& Eng); - - PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, - const ExplodedNode* N); - - SourceLocation getLocation() const { return AllocSite; } - }; -} // end anonymous namespace - - - -static const char* Msgs[] = { - // GC only - "Code is compiled to only use garbage collection", - // No GC. - "Code is compiled to use reference counts", - // Hybrid, with GC. - "Code is compiled to use either garbage collection (GC) or reference counts" - " (non-GC). The bug occurs with GC enabled", - // Hybrid, without GC - "Code is compiled to use either garbage collection (GC) or reference counts" - " (non-GC). The bug occurs in non-GC mode" -}; - -std::pair CFRefReport::getExtraDescriptiveText() { - CFRefCount& TF = static_cast(getBugType()).getTF(); - - switch (TF.getLangOptions().getGCMode()) { - default: - assert(false); - - case LangOptions::GCOnly: - assert (TF.isGCEnabled()); - return std::make_pair(&Msgs[0], &Msgs[0]+1); - - case LangOptions::NonGC: - assert (!TF.isGCEnabled()); - return std::make_pair(&Msgs[1], &Msgs[1]+1); - - case LangOptions::HybridGC: - if (TF.isGCEnabled()) - return std::make_pair(&Msgs[2], &Msgs[2]+1); - else - return std::make_pair(&Msgs[3], &Msgs[3]+1); - } -} - -static inline bool contains(const llvm::SmallVectorImpl& V, - ArgEffect X) { - for (llvm::SmallVectorImpl::const_iterator I=V.begin(), E=V.end(); - I!=E; ++I) - if (*I == X) return true; - - return false; -} - -PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext& BRC) { - - if (!isa(N->getLocation())) - return NULL; - - // Check if the type state has changed. - const GRState *PrevSt = PrevN->getState(); - const GRState *CurrSt = N->getState(); - - const RefVal* CurrT = CurrSt->get(Sym); - if (!CurrT) return NULL; - - const RefVal &CurrV = *CurrT; - const RefVal *PrevT = PrevSt->get(Sym); - - // Create a string buffer to constain all the useful things we want - // to tell the user. - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - // This is the allocation site since the previous node had no bindings - // for this symbol. - if (!PrevT) { - const Stmt* S = cast(N->getLocation()).getStmt(); - - if (const CallExpr *CE = dyn_cast(S)) { - // Get the name of the callee (if it is available). - SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee()); - if (const FunctionDecl* FD = X.getAsFunctionDecl()) - os << "Call to function '" << FD->getNameAsString() <<'\''; - else - os << "function call"; - } - else { - assert (isa(S)); - os << "Method"; - } - - if (CurrV.getObjKind() == RetEffect::CF) { - os << " returns a Core Foundation object with a "; - } - else { - assert (CurrV.getObjKind() == RetEffect::ObjC); - os << " returns an Objective-C object with a "; - } - - if (CurrV.isOwned()) { - os << "+1 retain count (owning reference)."; - - if (static_cast(getBugType()).getTF().isGCEnabled()) { - assert(CurrV.getObjKind() == RetEffect::CF); - os << " " - "Core Foundation objects are not automatically garbage collected."; - } - } - else { - assert (CurrV.isNotOwned()); - os << "+0 retain count (non-owning reference)."; - } - - PathDiagnosticLocation Pos(S, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(Pos, os.str()); - } - - // Gather up the effects that were performed on the object at this - // program point - llvm::SmallVector AEffects; - - if (const RetainSummary *Summ = - TF.getSummaryOfNode(BRC.getNodeResolver().getOriginalNode(N))) { - // We only have summaries attached to nodes after evaluating CallExpr and - // ObjCMessageExprs. - const Stmt* S = cast(N->getLocation()).getStmt(); - - if (const CallExpr *CE = dyn_cast(S)) { - // Iterate through the parameter expressions and see if the symbol - // was ever passed as an argument. - unsigned i = 0; - - for (CallExpr::const_arg_iterator AI=CE->arg_begin(), AE=CE->arg_end(); - AI!=AE; ++AI, ++i) { - - // Retrieve the value of the argument. Is it the symbol - // we are interested in? - if (CurrSt->getSValAsScalarOrLoc(*AI).getAsLocSymbol() != Sym) - continue; - - // We have an argument. Get the effect! - AEffects.push_back(Summ->getArg(i)); - } - } - else if (const ObjCMessageExpr *ME = dyn_cast(S)) { - if (const Expr *receiver = ME->getReceiver()) - if (CurrSt->getSValAsScalarOrLoc(receiver).getAsLocSymbol() == Sym) { - // The symbol we are tracking is the receiver. - AEffects.push_back(Summ->getReceiverEffect()); - } - } - } - - do { - // Get the previous type state. - RefVal PrevV = *PrevT; - - // Specially handle -dealloc. - if (!TF.isGCEnabled() && contains(AEffects, Dealloc)) { - // Determine if the object's reference count was pushed to zero. - assert(!(PrevV == CurrV) && "The typestate *must* have changed."); - // We may not have transitioned to 'release' if we hit an error. - // This case is handled elsewhere. - if (CurrV.getKind() == RefVal::Released) { - assert(CurrV.getCombinedCounts() == 0); - os << "Object released by directly sending the '-dealloc' message"; - break; - } - } - - // Specially handle CFMakeCollectable and friends. - if (contains(AEffects, MakeCollectable)) { - // Get the name of the function. - const Stmt* S = cast(N->getLocation()).getStmt(); - SVal X = CurrSt->getSValAsScalarOrLoc(cast(S)->getCallee()); - const FunctionDecl* FD = X.getAsFunctionDecl(); - const std::string& FName = FD->getNameAsString(); - - if (TF.isGCEnabled()) { - // Determine if the object's reference count was pushed to zero. - assert(!(PrevV == CurrV) && "The typestate *must* have changed."); - - os << "In GC mode a call to '" << FName - << "' decrements an object's retain count and registers the " - "object with the garbage collector. "; - - if (CurrV.getKind() == RefVal::Released) { - assert(CurrV.getCount() == 0); - os << "Since it now has a 0 retain count the object can be " - "automatically collected by the garbage collector."; - } - else - os << "An object must have a 0 retain count to be garbage collected. " - "After this call its retain count is +" << CurrV.getCount() - << '.'; - } - else - os << "When GC is not enabled a call to '" << FName - << "' has no effect on its argument."; - - // Nothing more to say. - break; - } - - // Determine if the typestate has changed. - if (!(PrevV == CurrV)) - switch (CurrV.getKind()) { - case RefVal::Owned: - case RefVal::NotOwned: - - if (PrevV.getCount() == CurrV.getCount()) { - // Did an autorelease message get sent? - if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount()) - return 0; - - assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); - os << "Object sent -autorelease message"; - break; - } - - if (PrevV.getCount() > CurrV.getCount()) - os << "Reference count decremented."; - else - os << "Reference count incremented."; - - if (unsigned Count = CurrV.getCount()) - os << " The object now has a +" << Count << " retain count."; - - if (PrevV.getKind() == RefVal::Released) { - assert(TF.isGCEnabled() && CurrV.getCount() > 0); - os << " The object is not eligible for garbage collection until the " - "retain count reaches 0 again."; - } - - break; - - case RefVal::Released: - os << "Object released."; - break; - - case RefVal::ReturnedOwned: - os << "Object returned to caller as an owning reference (single retain " - "count transferred to caller)."; - break; - - case RefVal::ReturnedNotOwned: - os << "Object returned to caller with a +0 (non-owning) retain count."; - break; - - default: - return NULL; - } - - // Emit any remaining diagnostics for the argument effects (if any). - for (llvm::SmallVectorImpl::iterator I=AEffects.begin(), - E=AEffects.end(); I != E; ++I) { - - // A bunch of things have alternate behavior under GC. - if (TF.isGCEnabled()) - switch (*I) { - default: break; - case Autorelease: - os << "In GC mode an 'autorelease' has no effect."; - continue; - case IncRefMsg: - os << "In GC mode the 'retain' message has no effect."; - continue; - case DecRefMsg: - os << "In GC mode the 'release' message has no effect."; - continue; - } - } - } while (0); - - if (os.str().empty()) - return 0; // We have nothing to say! - - const Stmt* S = cast(N->getLocation()).getStmt(); - PathDiagnosticLocation Pos(S, BRC.getSourceManager()); - PathDiagnosticPiece* P = new PathDiagnosticEventPiece(Pos, os.str()); - - // Add the range by scanning the children of the statement for any bindings - // to Sym. - for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); - I!=E; ++I) - if (const Expr* Exp = dyn_cast_or_null(*I)) - if (CurrSt->getSValAsScalarOrLoc(Exp).getAsLocSymbol() == Sym) { - P->addRange(Exp->getSourceRange()); - break; - } - - return P; -} - -namespace { - class FindUniqueBinding : - public StoreManager::BindingsHandler { - SymbolRef Sym; - const MemRegion* Binding; - bool First; - - public: - FindUniqueBinding(SymbolRef sym) : Sym(sym), Binding(0), First(true) {} - - bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, - SVal val) { - - SymbolRef SymV = val.getAsSymbol(); - if (!SymV || SymV != Sym) - return true; - - if (Binding) { - First = false; - return false; - } - else - Binding = R; - - return true; - } - - operator bool() { return First && Binding; } - const MemRegion* getRegion() { return Binding; } - }; -} - -static std::pair -GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode* N, - SymbolRef Sym) { - - // Find both first node that referred to the tracked symbol and the - // memory location that value was store to. - const ExplodedNode* Last = N; - const MemRegion* FirstBinding = 0; - - while (N) { - const GRState* St = N->getState(); - RefBindings B = St->get(); - - if (!B.lookup(Sym)) - break; - - FindUniqueBinding FB(Sym); - StateMgr.iterBindings(St, FB); - if (FB) FirstBinding = FB.getRegion(); - - Last = N; - N = N->pred_empty() ? NULL : *(N->pred_begin()); - } - - return std::make_pair(Last, FirstBinding); -} - -PathDiagnosticPiece* -CFRefReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndN) { - // Tell the BugReporterContext to report cases when the tracked symbol is - // assigned to different variables, etc. - BRC.addNotableSymbol(Sym); - return RangedBugReport::getEndPath(BRC, EndN); -} - -PathDiagnosticPiece* -CFRefLeakReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndN){ - - // Tell the BugReporterContext to report cases when the tracked symbol is - // assigned to different variables, etc. - BRC.addNotableSymbol(Sym); - - // We are reporting a leak. Walk up the graph to get to the first node where - // the symbol appeared, and also get the first VarDecl that tracked object - // is stored to. - const ExplodedNode* AllocNode = 0; - const MemRegion* FirstBinding = 0; - - llvm::tie(AllocNode, FirstBinding) = - GetAllocationSite(BRC.getStateManager(), EndN, Sym); - - // Get the allocate site. - assert(AllocNode); - const Stmt* FirstStmt = cast(AllocNode->getLocation()).getStmt(); - - SourceManager& SMgr = BRC.getSourceManager(); - unsigned AllocLine =SMgr.getInstantiationLineNumber(FirstStmt->getLocStart()); - - // Compute an actual location for the leak. Sometimes a leak doesn't - // occur at an actual statement (e.g., transition between blocks; end - // of function) so we need to walk the graph and compute a real location. - const ExplodedNode* LeakN = EndN; - PathDiagnosticLocation L; - - while (LeakN) { - ProgramPoint P = LeakN->getLocation(); - - if (const PostStmt *PS = dyn_cast(&P)) { - L = PathDiagnosticLocation(PS->getStmt()->getLocStart(), SMgr); - break; - } - else if (const BlockEdge *BE = dyn_cast(&P)) { - if (const Stmt* Term = BE->getSrc()->getTerminator()) { - L = PathDiagnosticLocation(Term->getLocStart(), SMgr); - break; - } - } - - LeakN = LeakN->succ_empty() ? 0 : *(LeakN->succ_begin()); - } - - if (!L.isValid()) { - const Decl &D = EndN->getCodeDecl(); - L = PathDiagnosticLocation(D.getBodyRBrace(), SMgr); - } - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Object allocated on line " << AllocLine; - - if (FirstBinding) - os << " and stored into '" << FirstBinding->getString() << '\''; - - // Get the retain count. - const RefVal* RV = EndN->getState()->get(Sym); - - if (RV->getKind() == RefVal::ErrorLeakReturned) { - // FIXME: Per comments in rdar://6320065, "create" only applies to CF - // ojbects. Only "copy", "alloc", "retain" and "new" transfer ownership - // to the caller for NS objects. - ObjCMethodDecl& MD = cast(EndN->getCodeDecl()); - os << " is returned from a method whose name ('" - << MD.getSelector().getAsString() - << "') does not contain 'copy' or otherwise starts with" - " 'new' or 'alloc'. This violates the naming convention rules given" - " in the Memory Management Guide for Cocoa (object leaked)"; - } - else if (RV->getKind() == RefVal::ErrorGCLeakReturned) { - ObjCMethodDecl& MD = cast(EndN->getCodeDecl()); - os << " and returned from method '" << MD.getSelector().getAsString() - << "' is potentially leaked when using garbage collection. Callers " - "of this method do not expect a returned object with a +1 retain " - "count since they expect the object to be managed by the garbage " - "collector"; - } - else - os << " is no longer referenced after this point and has a retain count of" - " +" << RV->getCount() << " (object leaked)"; - - return new PathDiagnosticEventPiece(L, os.str()); -} - -CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, - SymbolRef sym, GRExprEngine& Eng) -: CFRefReport(D, tf, n, sym) { - - // Most bug reports are cached at the location where they occured. - // With leaks, we want to unique them by the location where they were - // allocated, and only report a single path. To do this, we need to find - // the allocation site of a piece of tracked memory, which we do via a - // call to GetAllocationSite. This will walk the ExplodedGraph backwards. - // Note that this is *not* the trimmed graph; we are guaranteed, however, - // that all ancestor nodes that represent the allocation site have the - // same SourceLocation. - const ExplodedNode* AllocNode = 0; - - llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. - GetAllocationSite(Eng.getStateManager(), getEndNode(), getSymbol()); - - // Get the SourceLocation for the allocation site. - ProgramPoint P = AllocNode->getLocation(); - AllocSite = cast(P).getStmt()->getLocStart(); - - // Fill in the description of the bug. - Description.clear(); - llvm::raw_string_ostream os(Description); - SourceManager& SMgr = Eng.getContext().getSourceManager(); - unsigned AllocLine = SMgr.getInstantiationLineNumber(AllocSite); - os << "Potential leak "; - if (tf.isGCEnabled()) { - os << "(when using garbage collection) "; - } - os << "of an object allocated on line " << AllocLine; - - // FIXME: AllocBinding doesn't get populated for RegionStore yet. - if (AllocBinding) - os << " and stored into '" << AllocBinding->getString() << '\''; -} - -//===----------------------------------------------------------------------===// -// Main checker logic. -//===----------------------------------------------------------------------===// - -/// GetReturnType - Used to get the return type of a message expression or -/// function call with the intention of affixing that type to a tracked symbol. -/// While the the return type can be queried directly from RetEx, when -/// invoking class methods we augment to the return type to be that of -/// a pointer to the class (as opposed it just being id). -static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) { - QualType RetTy = RetE->getType(); - // If RetE is not a message expression just return its type. - // If RetE is a message expression, return its types if it is something - /// more specific than id. - if (const ObjCMessageExpr *ME = dyn_cast(RetE)) - if (const ObjCObjectPointerType *PT = RetTy->getAs()) - if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() || - PT->isObjCClassType()) { - // At this point we know the return type of the message expression is - // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this - // is a call to a class method whose type we can resolve. In such - // cases, promote the return type to XXX* (where XXX is the class). - const ObjCInterfaceDecl *D = ME->getClassInfo().first; - return !D ? RetTy : Ctx.getPointerType(Ctx.getObjCInterfaceType(D)); - } - - return RetTy; -} - -void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - Expr* Ex, - Expr* Receiver, - const RetainSummary& Summ, - const MemRegion *Callee, - ExprIterator arg_beg, ExprIterator arg_end, - ExplodedNode* Pred, const GRState *state) { - - // Evaluate the effect of the arguments. - RefVal::Kind hasErr = (RefVal::Kind) 0; - unsigned idx = 0; - Expr* ErrorExpr = NULL; - SymbolRef ErrorSym = 0; - - llvm::SmallVector RegionsToInvalidate; - - for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) { - SVal V = state->getSValAsScalarOrLoc(*I); - SymbolRef Sym = V.getAsLocSymbol(); - - if (Sym) - if (RefBindings::data_type* T = state->get(Sym)) { - state = Update(state, Sym, *T, Summ.getArg(idx), hasErr); - if (hasErr) { - ErrorExpr = *I; - ErrorSym = Sym; - break; - } - continue; - } - - tryAgain: - if (isa(V)) { - if (loc::MemRegionVal* MR = dyn_cast(&V)) { - if (Summ.getArg(idx) == DoNothingByRef) - continue; - - // Invalidate the value of the variable passed by reference. - const MemRegion *R = MR->getRegion(); - - // Are we dealing with an ElementRegion? If the element type is - // a basic integer type (e.g., char, int) and the underying region - // is a variable region then strip off the ElementRegion. - // FIXME: We really need to think about this for the general case - // as sometimes we are reasoning about arrays and other times - // about (char*), etc., is just a form of passing raw bytes. - // e.g., void *p = alloca(); foo((char*)p); - if (const ElementRegion *ER = dyn_cast(R)) { - // Checking for 'integral type' is probably too promiscuous, but - // we'll leave it in for now until we have a systematic way of - // handling all of these cases. Eventually we need to come up - // with an interface to StoreManager so that this logic can be - // approriately delegated to the respective StoreManagers while - // still allowing us to do checker-specific logic (e.g., - // invalidating reference counts), probably via callbacks. - if (ER->getElementType()->isIntegralType()) { - const MemRegion *superReg = ER->getSuperRegion(); - if (isa(superReg) || isa(superReg) || - isa(superReg)) - R = cast(superReg); - } - // FIXME: What about layers of ElementRegions? - } - - // Mark this region for invalidation. We batch invalidate regions - // below for efficiency. - RegionsToInvalidate.push_back(R); - continue; - } - else { - // Nuke all other arguments passed by reference. - // FIXME: is this necessary or correct? This handles the non-Region - // cases. Is it ever valid to store to these? - state = state->unbindLoc(cast(V)); - } - } - else if (isa(V)) { - // If we are passing a location wrapped as an integer, unwrap it and - // invalidate the values referred by the location. - V = cast(V).getLoc(); - goto tryAgain; - } - } - - // Block calls result in all captured values passed-via-reference to be - // invalidated. - if (const BlockDataRegion *BR = dyn_cast_or_null(Callee)) { - RegionsToInvalidate.push_back(BR); - } - - // Invalidate regions we designed for invalidation use the batch invalidation - // API. - if (!RegionsToInvalidate.empty()) { - // FIXME: We can have collisions on the conjured symbol if the - // expression *I also creates conjured symbols. We probably want - // to identify conjured symbols by an expression pair: the enclosing - // expression (the context) and the expression itself. This should - // disambiguate conjured symbols. - unsigned Count = Builder.getCurrentBlockCount(); - StoreManager& StoreMgr = Eng.getStateManager().getStoreManager(); - - - StoreManager::InvalidatedSymbols IS; - state = StoreMgr.InvalidateRegions(state, RegionsToInvalidate.data(), - RegionsToInvalidate.data() + - RegionsToInvalidate.size(), - Ex, Count, &IS); - for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(), - E = IS.end(); I!=E; ++I) { - // Remove any existing reference-count binding. - state = state->remove(*I); - } - } - - // Evaluate the effect on the message receiver. - if (!ErrorExpr && Receiver) { - SymbolRef Sym = state->getSValAsScalarOrLoc(Receiver).getAsLocSymbol(); - if (Sym) { - if (const RefVal* T = state->get(Sym)) { - state = Update(state, Sym, *T, Summ.getReceiverEffect(), hasErr); - if (hasErr) { - ErrorExpr = Receiver; - ErrorSym = Sym; - } - } - } - } - - // Process any errors. - if (hasErr) { - ProcessNonLeakError(Dst, Builder, Ex, ErrorExpr, Pred, state, - hasErr, ErrorSym); - return; - } - - // Consult the summary for the return value. - RetEffect RE = Summ.getRetEffect(); - - if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) { - assert(Receiver); - SVal V = state->getSValAsScalarOrLoc(Receiver); - bool found = false; - if (SymbolRef Sym = V.getAsLocSymbol()) - if (state->get(Sym)) { - found = true; - RE = Summaries.getObjAllocRetEffect(); - } - - if (!found) - RE = RetEffect::MakeNoRet(); - } - - switch (RE.getKind()) { - default: - assert (false && "Unhandled RetEffect."); break; - - case RetEffect::NoRet: { - // Make up a symbol for the return value (not reference counted). - // FIXME: Most of this logic is not specific to the retain/release - // checker. - - // FIXME: We eventually should handle structs and other compound types - // that are returned by value. - - QualType T = Ex->getType(); - - // For CallExpr, use the result type to know if it returns a reference. - if (const CallExpr *CE = dyn_cast(Ex)) { - const Expr *Callee = CE->getCallee(); - if (const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl()) - T = FD->getResultType(); - } - else if (const ObjCMessageExpr *ME = dyn_cast(Ex)) { - if (const ObjCMethodDecl *MD = ME->getMethodDecl()) - T = MD->getResultType(); - } - - if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) { - unsigned Count = Builder.getCurrentBlockCount(); - ValueManager &ValMgr = Eng.getValueManager(); - SVal X = ValMgr.getConjuredSymbolVal(NULL, Ex, T, Count); - state = state->BindExpr(Ex, X, false); - } - - break; - } - - case RetEffect::Alias: { - unsigned idx = RE.getIndex(); - assert (arg_end >= arg_beg); - assert (idx < (unsigned) (arg_end - arg_beg)); - SVal V = state->getSValAsScalarOrLoc(*(arg_beg+idx)); - state = state->BindExpr(Ex, V, false); - break; - } - - case RetEffect::ReceiverAlias: { - assert (Receiver); - SVal V = state->getSValAsScalarOrLoc(Receiver); - state = state->BindExpr(Ex, V, false); - break; - } - - case RetEffect::OwnedAllocatedSymbol: - case RetEffect::OwnedSymbol: { - unsigned Count = Builder.getCurrentBlockCount(); - ValueManager &ValMgr = Eng.getValueManager(); - SymbolRef Sym = ValMgr.getConjuredSymbol(Ex, Count); - QualType RetT = GetReturnType(Ex, ValMgr.getContext()); - state = state->set(Sym, RefVal::makeOwned(RE.getObjKind(), - RetT)); - state = state->BindExpr(Ex, ValMgr.makeLoc(Sym), false); - - // FIXME: Add a flag to the checker where allocations are assumed to - // *not fail. -#if 0 - if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) { - bool isFeasible; - state = state.Assume(loc::SymbolVal(Sym), true, isFeasible); - assert(isFeasible && "Cannot assume fresh symbol is non-null."); - } -#endif - - break; - } - - case RetEffect::GCNotOwnedSymbol: - case RetEffect::NotOwnedSymbol: { - unsigned Count = Builder.getCurrentBlockCount(); - ValueManager &ValMgr = Eng.getValueManager(); - SymbolRef Sym = ValMgr.getConjuredSymbol(Ex, Count); - QualType RetT = GetReturnType(Ex, ValMgr.getContext()); - state = state->set(Sym, RefVal::makeNotOwned(RE.getObjKind(), - RetT)); - state = state->BindExpr(Ex, ValMgr.makeLoc(Sym), false); - break; - } - } - - // Generate a sink node if we are at the end of a path. - ExplodedNode *NewNode = - Summ.isEndPath() ? Builder.MakeSinkNode(Dst, Ex, Pred, state) - : Builder.MakeNode(Dst, Ex, Pred, state); - - // Annotate the edge with summary we used. - if (NewNode) SummaryLog[NewNode] = &Summ; -} - - -void CFRefCount::EvalCall(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - CallExpr* CE, SVal L, - ExplodedNode* Pred) { - - RetainSummary *Summ = 0; - - // FIXME: Better support for blocks. For now we stop tracking anything - // that is passed to blocks. - // FIXME: Need to handle variables that are "captured" by the block. - if (dyn_cast_or_null(L.getAsRegion())) { - Summ = Summaries.getPersistentStopSummary(); - } - else { - const FunctionDecl* FD = L.getAsFunctionDecl(); - Summ = !FD ? Summaries.getDefaultSummary() : - Summaries.getSummary(const_cast(FD)); - } - - assert(Summ); - EvalSummary(Dst, Eng, Builder, CE, 0, *Summ, L.getAsRegion(), - CE->arg_begin(), CE->arg_end(), Pred, Builder.GetState(Pred)); -} - -void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - ObjCMessageExpr* ME, - ExplodedNode* Pred, - const GRState *state) { - RetainSummary *Summ = - ME->getReceiver() - ? Summaries.getInstanceMethodSummary(ME, state,Pred->getLocationContext()) - : Summaries.getClassMethodSummary(ME); - - assert(Summ && "RetainSummary is null"); - EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), *Summ, NULL, - ME->arg_begin(), ME->arg_end(), Pred, state); -} - -namespace { -class StopTrackingCallback : public SymbolVisitor { - const GRState *state; -public: - StopTrackingCallback(const GRState *st) : state(st) {} - const GRState *getState() const { return state; } - - bool VisitSymbol(SymbolRef sym) { - state = state->remove(sym); - return true; - } -}; -} // end anonymous namespace - - -void CFRefCount::EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val) { - // Are we storing to something that causes the value to "escape"? - bool escapes = false; - - // A value escapes in three possible cases (this may change): - // - // (1) we are binding to something that is not a memory region. - // (2) we are binding to a memregion that does not have stack storage - // (3) we are binding to a memregion with stack storage that the store - // does not understand. - const GRState *state = B.getState(); - - if (!isa(location)) - escapes = true; - else { - const MemRegion* R = cast(location).getRegion(); - escapes = !R->hasStackStorage(); - - if (!escapes) { - // To test (3), generate a new state with the binding removed. If it is - // the same state, then it escapes (since the store cannot represent - // the binding). - escapes = (state == (state->bindLoc(cast(location), UnknownVal()))); - } - } - - // If our store can represent the binding and we aren't storing to something - // that doesn't have local storage then just return and have the simulation - // state continue as is. - if (!escapes) - return; - - // Otherwise, find all symbols referenced by 'val' that we are tracking - // and stop tracking them. - B.MakeNode(state->scanReachableSymbols(val).getState()); -} - - // Return statements. - -void CFRefCount::EvalReturn(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - ReturnStmt* S, - ExplodedNode* Pred) { - - Expr* RetE = S->getRetValue(); - if (!RetE) - return; - - const GRState *state = Builder.GetState(Pred); - SymbolRef Sym = state->getSValAsScalarOrLoc(RetE).getAsLocSymbol(); - - if (!Sym) - return; - - // Get the reference count binding (if any). - const RefVal* T = state->get(Sym); - - if (!T) - return; - - // Change the reference count. - RefVal X = *T; - - switch (X.getKind()) { - case RefVal::Owned: { - unsigned cnt = X.getCount(); - assert (cnt > 0); - X.setCount(cnt - 1); - X = X ^ RefVal::ReturnedOwned; - break; - } - - case RefVal::NotOwned: { - unsigned cnt = X.getCount(); - if (cnt) { - X.setCount(cnt - 1); - X = X ^ RefVal::ReturnedOwned; - } - else { - X = X ^ RefVal::ReturnedNotOwned; - } - break; - } - - default: - return; - } - - // Update the binding. - state = state->set(Sym, X); - Pred = Builder.MakeNode(Dst, S, Pred, state); - - // Did we cache out? - if (!Pred) - return; - - // Update the autorelease counts. - static unsigned autoreleasetag = 0; - GenericNodeBuilder Bd(Builder, S, &autoreleasetag); - bool stop = false; - llvm::tie(Pred, state) = HandleAutoreleaseCounts(state , Bd, Pred, Eng, Sym, - X, stop); - - // Did we cache out? - if (!Pred || stop) - return; - - // Get the updated binding. - T = state->get(Sym); - assert(T); - X = *T; - - // Any leaks or other errors? - if (X.isReturnedOwned() && X.getCount() == 0) { - Decl const *CD = &Pred->getCodeDecl(); - if (const ObjCMethodDecl* MD = dyn_cast(CD)) { - const RetainSummary &Summ = *Summaries.getMethodSummary(MD); - RetEffect RE = Summ.getRetEffect(); - bool hasError = false; - - if (RE.getKind() != RetEffect::NoRet) { - if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { - // Things are more complicated with garbage collection. If the - // returned object is suppose to be an Objective-C object, we have - // a leak (as the caller expects a GC'ed object) because no - // method should return ownership unless it returns a CF object. - hasError = true; - X = X ^ RefVal::ErrorGCLeakReturned; - } - else if (!RE.isOwned()) { - // Either we are using GC and the returned object is a CF type - // or we aren't using GC. In either case, we expect that the - // enclosing method is expected to return ownership. - hasError = true; - X = X ^ RefVal::ErrorLeakReturned; - } - } - - if (hasError) { - // Generate an error node. - static int ReturnOwnLeakTag = 0; - state = state->set(Sym, X); - ExplodedNode *N = - Builder.generateNode(PostStmt(S, Pred->getLocationContext(), - &ReturnOwnLeakTag), state, Pred); - if (N) { - CFRefReport *report = - new CFRefLeakReport(*static_cast(leakAtReturn), *this, - N, Sym, Eng); - BR->EmitReport(report); - } - } - } - } - else if (X.isReturnedNotOwned()) { - Decl const *CD = &Pred->getCodeDecl(); - if (const ObjCMethodDecl* MD = dyn_cast(CD)) { - const RetainSummary &Summ = *Summaries.getMethodSummary(MD); - if (Summ.getRetEffect().isOwned()) { - // Trying to return a not owned object to a caller expecting an - // owned object. - - static int ReturnNotOwnedForOwnedTag = 0; - state = state->set(Sym, X ^ RefVal::ErrorReturnedNotOwned); - if (ExplodedNode *N = - Builder.generateNode(PostStmt(S, Pred->getLocationContext(), - &ReturnNotOwnedForOwnedTag), - state, Pred)) { - CFRefReport *report = - new CFRefReport(*static_cast(returnNotOwnedForOwned), - *this, N, Sym); - BR->EmitReport(report); - } - } - } - } -} - -// Assumptions. - -const GRState* CFRefCount::EvalAssume(const GRState *state, - SVal Cond, bool Assumption) { - - // FIXME: We may add to the interface of EvalAssume the list of symbols - // whose assumptions have changed. For now we just iterate through the - // bindings and check if any of the tracked symbols are NULL. This isn't - // too bad since the number of symbols we will track in practice are - // probably small and EvalAssume is only called at branches and a few - // other places. - RefBindings B = state->get(); - - if (B.isEmpty()) - return state; - - bool changed = false; - RefBindings::Factory& RefBFactory = state->get_context(); - - for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { - // Check if the symbol is null (or equal to any constant). - // If this is the case, stop tracking the symbol. - if (state->getSymVal(I.getKey())) { - changed = true; - B = RefBFactory.Remove(B, I.getKey()); - } - } - - if (changed) - state = state->set(B); - - return state; -} - -const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, - RefVal V, ArgEffect E, - RefVal::Kind& hasErr) { - - // In GC mode [... release] and [... retain] do nothing. - switch (E) { - default: break; - case IncRefMsg: E = isGCEnabled() ? DoNothing : IncRef; break; - case DecRefMsg: E = isGCEnabled() ? DoNothing : DecRef; break; - case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break; - case NewAutoreleasePool: E = isGCEnabled() ? DoNothing : - NewAutoreleasePool; break; - } - - // Handle all use-after-releases. - if (!isGCEnabled() && V.getKind() == RefVal::Released) { - V = V ^ RefVal::ErrorUseAfterRelease; - hasErr = V.getKind(); - return state->set(sym, V); - } - - switch (E) { - default: - assert (false && "Unhandled CFRef transition."); - - case Dealloc: - // Any use of -dealloc in GC is *bad*. - if (isGCEnabled()) { - V = V ^ RefVal::ErrorDeallocGC; - hasErr = V.getKind(); - break; - } - - switch (V.getKind()) { - default: - assert(false && "Invalid case."); - case RefVal::Owned: - // The object immediately transitions to the released state. - V = V ^ RefVal::Released; - V.clearCounts(); - return state->set(sym, V); - case RefVal::NotOwned: - V = V ^ RefVal::ErrorDeallocNotOwned; - hasErr = V.getKind(); - break; - } - break; - - case NewAutoreleasePool: - assert(!isGCEnabled()); - return state->add(sym); - - case MayEscape: - if (V.getKind() == RefVal::Owned) { - V = V ^ RefVal::NotOwned; - break; - } - - // Fall-through. - - case DoNothingByRef: - case DoNothing: - return state; - - case Autorelease: - if (isGCEnabled()) - return state; - - // Update the autorelease counts. - state = SendAutorelease(state, ARCountFactory, sym); - V = V.autorelease(); - break; - - case StopTracking: - return state->remove(sym); - - case IncRef: - switch (V.getKind()) { - default: - assert(false); - - case RefVal::Owned: - case RefVal::NotOwned: - V = V + 1; - break; - case RefVal::Released: - // Non-GC cases are handled above. - assert(isGCEnabled()); - V = (V ^ RefVal::Owned) + 1; - break; - } - break; - - case SelfOwn: - V = V ^ RefVal::NotOwned; - // Fall-through. - case DecRef: - switch (V.getKind()) { - default: - // case 'RefVal::Released' handled above. - assert (false); - - case RefVal::Owned: - assert(V.getCount() > 0); - if (V.getCount() == 1) V = V ^ RefVal::Released; - V = V - 1; - break; - - case RefVal::NotOwned: - if (V.getCount() > 0) - V = V - 1; - else { - V = V ^ RefVal::ErrorReleaseNotOwned; - hasErr = V.getKind(); - } - break; - - case RefVal::Released: - // Non-GC cases are handled above. - assert(isGCEnabled()); - V = V ^ RefVal::ErrorUseAfterRelease; - hasErr = V.getKind(); - break; - } - break; - } - return state->set(sym, V); -} - -//===----------------------------------------------------------------------===// -// Handle dead symbols and end-of-path. -//===----------------------------------------------------------------------===// - -std::pair -CFRefCount::HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd, - ExplodedNode* Pred, - GRExprEngine &Eng, - SymbolRef Sym, RefVal V, bool &stop) { - - unsigned ACnt = V.getAutoreleaseCount(); - stop = false; - - // No autorelease counts? Nothing to be done. - if (!ACnt) - return std::make_pair(Pred, state); - - assert(!isGCEnabled() && "Autorelease counts in GC mode?"); - unsigned Cnt = V.getCount(); - - // FIXME: Handle sending 'autorelease' to already released object. - - if (V.getKind() == RefVal::ReturnedOwned) - ++Cnt; - - if (ACnt <= Cnt) { - if (ACnt == Cnt) { - V.clearCounts(); - if (V.getKind() == RefVal::ReturnedOwned) - V = V ^ RefVal::ReturnedNotOwned; - else - V = V ^ RefVal::NotOwned; - } - else { - V.setCount(Cnt - ACnt); - V.setAutoreleaseCount(0); - } - state = state->set(Sym, V); - ExplodedNode *N = Bd.MakeNode(state, Pred); - stop = (N == 0); - return std::make_pair(N, state); - } - - // Woah! More autorelease counts then retain counts left. - // Emit hard error. - stop = true; - V = V ^ RefVal::ErrorOverAutorelease; - state = state->set(Sym, V); - - if (ExplodedNode *N = Bd.MakeNode(state, Pred)) { - N->markAsSink(); - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Object over-autoreleased: object was sent -autorelease"; - if (V.getAutoreleaseCount() > 1) - os << V.getAutoreleaseCount() << " times"; - os << " but the object has "; - if (V.getCount() == 0) - os << "zero (locally visible)"; - else - os << "+" << V.getCount(); - os << " retain counts"; - - CFRefReport *report = - new CFRefReport(*static_cast(overAutorelease), - *this, N, Sym, os.str()); - BR->EmitReport(report); - } - - return std::make_pair((ExplodedNode*)0, state); -} - -const GRState * -CFRefCount::HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, - llvm::SmallVectorImpl &Leaked) { - - bool hasLeak = V.isOwned() || - ((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0); - - if (!hasLeak) - return state->remove(sid); - - Leaked.push_back(sid); - return state->set(sid, V ^ RefVal::ErrorLeak); -} - -ExplodedNode* -CFRefCount::ProcessLeaks(const GRState * state, - llvm::SmallVectorImpl &Leaked, - GenericNodeBuilder &Builder, - GRExprEngine& Eng, - ExplodedNode *Pred) { - - if (Leaked.empty()) - return Pred; - - // Generate an intermediate node representing the leak point. - ExplodedNode *N = Builder.MakeNode(state, Pred); - - if (N) { - for (llvm::SmallVectorImpl::iterator - I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { - - CFRefBug *BT = static_cast(Pred ? leakWithinFunction - : leakAtReturn); - assert(BT && "BugType not initialized."); - CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, *I, Eng); - BR->EmitReport(report); - } - } - - return N; -} - -void CFRefCount::EvalEndPath(GRExprEngine& Eng, - GREndPathNodeBuilder& Builder) { - - const GRState *state = Builder.getState(); - GenericNodeBuilder Bd(Builder); - RefBindings B = state->get(); - ExplodedNode *Pred = 0; - - for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - bool stop = false; - llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng, - (*I).first, - (*I).second, stop); - - if (stop) - return; - } - - B = state->get(); - llvm::SmallVector Leaked; - - for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) - state = HandleSymbolDeath(state, (*I).first, (*I).second, Leaked); - - ProcessLeaks(state, Leaked, Bd, Eng, Pred); -} - -void CFRefCount::EvalDeadSymbols(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - ExplodedNode* Pred, - Stmt* S, - const GRState* state, - SymbolReaper& SymReaper) { - - RefBindings B = state->get(); - - // Update counts from autorelease pools - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) { - SymbolRef Sym = *I; - if (const RefVal* T = B.lookup(Sym)){ - // Use the symbol as the tag. - // FIXME: This might not be as unique as we would like. - GenericNodeBuilder Bd(Builder, S, Sym); - bool stop = false; - llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng, - Sym, *T, stop); - if (stop) - return; - } - } - - B = state->get(); - llvm::SmallVector Leaked; - - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) { - if (const RefVal* T = B.lookup(*I)) - state = HandleSymbolDeath(state, *I, *T, Leaked); - } - - static unsigned LeakPPTag = 0; - { - GenericNodeBuilder Bd(Builder, S, &LeakPPTag); - Pred = ProcessLeaks(state, Leaked, Bd, Eng, Pred); - } - - // Did we cache out? - if (!Pred) - return; - - // Now generate a new node that nukes the old bindings. - RefBindings::Factory& F = state->get_context(); - - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I!=E; ++I) B = F.Remove(B, *I); - - state = state->set(B); - Builder.MakeNode(Dst, S, Pred, state); -} - -void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, - GRStmtNodeBuilder& Builder, - Expr* NodeExpr, Expr* ErrorExpr, - ExplodedNode* Pred, - const GRState* St, - RefVal::Kind hasErr, SymbolRef Sym) { - Builder.BuildSinks = true; - ExplodedNode *N = Builder.MakeNode(Dst, NodeExpr, Pred, St); - - if (!N) - return; - - CFRefBug *BT = 0; - - switch (hasErr) { - default: - assert(false && "Unhandled error."); - return; - case RefVal::ErrorUseAfterRelease: - BT = static_cast(useAfterRelease); - break; - case RefVal::ErrorReleaseNotOwned: - BT = static_cast(releaseNotOwned); - break; - case RefVal::ErrorDeallocGC: - BT = static_cast(deallocGC); - break; - case RefVal::ErrorDeallocNotOwned: - BT = static_cast(deallocNotOwned); - break; - } - - CFRefReport *report = new CFRefReport(*BT, *this, N, Sym); - report->addRange(ErrorExpr->getSourceRange()); - BR->EmitReport(report); -} - -//===----------------------------------------------------------------------===// -// Pieces of the retain/release checker implemented using a CheckerVisitor. -// More pieces of the retain/release checker will be migrated to this interface -// (ideally, all of it some day). -//===----------------------------------------------------------------------===// - -namespace { -class RetainReleaseChecker - : public CheckerVisitor { - CFRefCount *TF; -public: - RetainReleaseChecker(CFRefCount *tf) : TF(tf) {} - static void* getTag() { static int x = 0; return &x; } - - void PostVisitBlockExpr(CheckerContext &C, const BlockExpr *BE); -}; -} // end anonymous namespace - - -void RetainReleaseChecker::PostVisitBlockExpr(CheckerContext &C, - const BlockExpr *BE) { - - // Scan the BlockDecRefExprs for any object the retain/release checker - // may be tracking. - if (!BE->hasBlockDeclRefExprs()) - return; - - const GRState *state = C.getState(); - const BlockDataRegion *R = - cast(state->getSVal(BE).getAsRegion()); - - BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), - E = R->referenced_vars_end(); - - if (I == E) - return; - - // FIXME: For now we invalidate the tracking of all symbols passed to blocks - // via captured variables, even though captured variables result in a copy - // and in implicit increment/decrement of a retain count. - llvm::SmallVector Regions; - const LocationContext *LC = C.getPredecessor()->getLocationContext(); - MemRegionManager &MemMgr = C.getValueManager().getRegionManager(); - - for ( ; I != E; ++I) { - const VarRegion *VR = *I; - if (VR->getSuperRegion() == R) { - VR = MemMgr.getVarRegion(VR->getDecl(), LC); - } - Regions.push_back(VR); - } - - state = - state->scanReachableSymbols(Regions.data(), - Regions.data() + Regions.size()).getState(); - C.addTransition(state); -} - -//===----------------------------------------------------------------------===// -// Transfer function creation for external clients. -//===----------------------------------------------------------------------===// - -void CFRefCount::RegisterChecks(GRExprEngine& Eng) { - BugReporter &BR = Eng.getBugReporter(); - - useAfterRelease = new UseAfterRelease(this); - BR.Register(useAfterRelease); - - releaseNotOwned = new BadRelease(this); - BR.Register(releaseNotOwned); - - deallocGC = new DeallocGC(this); - BR.Register(deallocGC); - - deallocNotOwned = new DeallocNotOwned(this); - BR.Register(deallocNotOwned); - - overAutorelease = new OverAutorelease(this); - BR.Register(overAutorelease); - - returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this); - BR.Register(returnNotOwnedForOwned); - - // First register "return" leaks. - const char* name = 0; - - if (isGCEnabled()) - name = "Leak of returned object when using garbage collection"; - else if (getLangOptions().getGCMode() == LangOptions::HybridGC) - name = "Leak of returned object when not using garbage collection (GC) in " - "dual GC/non-GC code"; - else { - assert(getLangOptions().getGCMode() == LangOptions::NonGC); - name = "Leak of returned object"; - } - - // Leaks should not be reported if they are post-dominated by a sink. - leakAtReturn = new LeakAtReturn(this, name); - leakAtReturn->setSuppressOnSink(true); - BR.Register(leakAtReturn); - - // Second, register leaks within a function/method. - if (isGCEnabled()) - name = "Leak of object when using garbage collection"; - else if (getLangOptions().getGCMode() == LangOptions::HybridGC) - name = "Leak of object when not using garbage collection (GC) in " - "dual GC/non-GC code"; - else { - assert(getLangOptions().getGCMode() == LangOptions::NonGC); - name = "Leak"; - } - - // Leaks should not be reported if they are post-dominated by sinks. - leakWithinFunction = new LeakWithinFunction(this, name); - leakWithinFunction->setSuppressOnSink(true); - BR.Register(leakWithinFunction); - - // Save the reference to the BugReporter. - this->BR = &BR; - - // Register the RetainReleaseChecker with the GRExprEngine object. - // Functionality in CFRefCount will be migrated to RetainReleaseChecker - // over time. - Eng.registerCheck(new RetainReleaseChecker(this)); -} - -GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, - const LangOptions& lopts) { - return new CFRefCount(Ctx, GCEnabled, lopts); -} diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 521f1be6ec8b..4f8259e44939 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -2,67 +2,10 @@ set(LLVM_NO_RTTI 1) add_clang_library(clangAnalysis AnalysisContext.cpp - ArrayBoundChecker.cpp - AttrNonNullChecker.cpp - BasicConstraintManager.cpp - BasicObjCFoundationChecks.cpp - BasicStore.cpp - BasicValueFactory.cpp - BugReporter.cpp - BugReporterVisitors.cpp - BuiltinFunctionChecker.cpp CFG.cpp - CFRefCount.cpp - CallAndMessageChecker.cpp - CallInliner.cpp - CastToStructChecker.cpp - CheckDeadStores.cpp - CheckObjCDealloc.cpp - CheckObjCInstMethSignature.cpp - CheckObjCUnusedIVars.cpp - CheckSecuritySyntaxOnly.cpp - CheckSizeofPointer.cpp - Checker.cpp - DereferenceChecker.cpp - DivZeroChecker.cpp - Environment.cpp - ExplodedGraph.cpp - FixedAddressChecker.cpp - GRBlockCounter.cpp - GRCoreEngine.cpp - GRExprEngine.cpp - GRExprEngineExperimentalChecks.cpp - GRState.cpp LiveVariables.cpp - MallocChecker.cpp - ManagerRegistry.cpp - MemRegion.cpp - NoReturnFunctionChecker.cpp - NSAutoreleasePoolChecker.cpp - NSErrorChecker.cpp - OSAtomicChecker.cpp - PathDiagnostic.cpp - PointerArithChecker.cpp - PointerSubChecker.cpp - PthreadLockChecker.cpp - RangeConstraintManager.cpp - RegionStore.cpp - ReturnPointerRangeChecker.cpp - ReturnStackAddressChecker.cpp - ReturnUndefChecker.cpp - SVals.cpp - SValuator.cpp - SimpleConstraintManager.cpp - SimpleSValuator.cpp - Store.cpp - SymbolManager.cpp - UndefBranchChecker.cpp - UndefResultChecker.cpp - UndefinedArraySubscriptChecker.cpp - UndefinedAssignmentChecker.cpp + PrintfFormatString.cpp UninitializedValues.cpp - VLASizeChecker.cpp - ValueManager.cpp ) add_dependencies(clangAnalysis ClangDiagnosticAnalysis) diff --git a/lib/Analysis/CallAndMessageChecker.cpp b/lib/Analysis/CallAndMessageChecker.cpp deleted file mode 100644 index c287354650b8..000000000000 --- a/lib/Analysis/CallAndMessageChecker.cpp +++ /dev/null @@ -1,252 +0,0 @@ -//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines CallAndMessageChecker, a builtin checker that checks for various -// errors of call and objc message expressions. -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/TargetInfo.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/AST/ParentMap.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class CallAndMessageChecker - : public CheckerVisitor { - BugType *BT_call_null; - BugType *BT_call_undef; - BugType *BT_call_arg; - BugType *BT_msg_undef; - BugType *BT_msg_arg; - BugType *BT_msg_ret; -public: - CallAndMessageChecker() : - BT_call_null(0), BT_call_undef(0), BT_call_arg(0), - BT_msg_undef(0), BT_msg_arg(0), BT_msg_ret(0) {} - - static void *getTag() { - static int x = 0; - return &x; - } - - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); - bool EvalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME); - -private: - void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); - void EmitNilReceiverBug(CheckerContext &C, const ObjCMessageExpr *ME, - ExplodedNode *N); - - void HandleNilReceiver(CheckerContext &C, const GRState *state, - const ObjCMessageExpr *ME); -}; -} // end anonymous namespace - -void clang::RegisterCallAndMessageChecker(GRExprEngine &Eng) { - Eng.registerCheck(new CallAndMessageChecker()); -} - -void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, - const CallExpr *CE) { - ExplodedNode *N = C.GenerateSink(); - if (!N) - return; - - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetCalleeExpr(N)); - C.EmitReport(R); -} - -void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C, - const CallExpr *CE){ - - const Expr *Callee = CE->getCallee()->IgnoreParens(); - SVal L = C.getState()->getSVal(Callee); - - if (L.isUndef()) { - if (!BT_call_undef) - BT_call_undef = - new BuiltinBug("Called function pointer is an undefined pointer value"); - EmitBadCall(BT_call_undef, C, CE); - return; - } - - if (isa(L)) { - if (!BT_call_null) - BT_call_null = - new BuiltinBug("Called function pointer is null (null dereference)"); - EmitBadCall(BT_call_null, C, CE); - } - - for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); - I != E; ++I) { - if (C.getState()->getSVal(*I).isUndef()) { - if (ExplodedNode *N = C.GenerateSink()) { - if (!BT_call_arg) - BT_call_arg = new BuiltinBug("Pass-by-value argument in function call" - " is undefined"); - // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT_call_arg, - BT_call_arg->getName(), N); - R->addRange((*I)->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); - C.EmitReport(R); - return; - } - } - } -} - -void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) { - - const GRState *state = C.getState(); - - if (const Expr *receiver = ME->getReceiver()) - if (state->getSVal(receiver).isUndef()) { - if (ExplodedNode *N = C.GenerateSink()) { - if (!BT_msg_undef) - BT_msg_undef = - new BuiltinBug("Receiver in message expression is a garbage value"); - EnhancedBugReport *R = - new EnhancedBugReport(*BT_msg_undef, BT_msg_undef->getName(), N); - R->addRange(receiver->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - receiver); - C.EmitReport(R); - } - return; - } - - // Check for any arguments that are uninitialized/undefined. - for (ObjCMessageExpr::const_arg_iterator I = ME->arg_begin(), - E = ME->arg_end(); I != E; ++I) { - if (state->getSVal(*I).isUndef()) { - if (ExplodedNode *N = C.GenerateSink()) { - if (!BT_msg_arg) - BT_msg_arg = - new BuiltinBug("Pass-by-value argument in message expression" - " is undefined"); - // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT_msg_arg, - BT_msg_arg->getName(), N); - R->addRange((*I)->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); - C.EmitReport(R); - return; - } - } - } -} - -bool CallAndMessageChecker::EvalNilReceiver(CheckerContext &C, - const ObjCMessageExpr *ME) { - HandleNilReceiver(C, C.getState(), ME); - return true; // Nil receiver is not handled elsewhere. -} - -void CallAndMessageChecker::EmitNilReceiverBug(CheckerContext &C, - const ObjCMessageExpr *ME, - ExplodedNode *N) { - - if (!BT_msg_ret) - BT_msg_ret = - new BuiltinBug("Receiver in message expression is " - "'nil' and returns a garbage value"); - - llvm::SmallString<200> buf; - llvm::raw_svector_ostream os(buf); - os << "The receiver of message '" << ME->getSelector().getAsString() - << "' is nil and returns a value of type '" - << ME->getType().getAsString() << "' that will be garbage"; - - EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N); - const Expr *receiver = ME->getReceiver(); - report->addRange(receiver->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - receiver); - C.EmitReport(report); -} - -static bool SupportsNilWithFloatRet(const llvm::Triple &triple) { - return triple.getVendor() == llvm::Triple::Apple && - triple.getDarwinMajorNumber() >= 9; -} - -void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, - const GRState *state, - const ObjCMessageExpr *ME) { - - // Check the return type of the message expression. A message to nil will - // return different values depending on the return type and the architecture. - QualType RetTy = ME->getType(); - - ASTContext &Ctx = C.getASTContext(); - CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); - - if (CanRetTy->isStructureType()) { - // FIXME: At some point we shouldn't rely on isConsumedExpr(), but instead - // have the "use of undefined value" be smarter about where the - // undefined value came from. - if (C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { - if (ExplodedNode* N = C.GenerateSink(state)) - EmitNilReceiverBug(C, ME, N); - return; - } - - // The result is not consumed by a surrounding expression. Just propagate - // the current state. - C.addTransition(state); - return; - } - - // Other cases: check if the return type is smaller than void*. - if (CanRetTy != Ctx.VoidTy && - C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { - // Compute: sizeof(void *) and sizeof(return type) - const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); - const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); - - if (voidPtrSize < returnTypeSize && - !(SupportsNilWithFloatRet(Ctx.Target.getTriple()) && - (Ctx.FloatTy == CanRetTy || - Ctx.DoubleTy == CanRetTy || - Ctx.LongDoubleTy == CanRetTy || - Ctx.LongLongTy == CanRetTy))) { - if (ExplodedNode* N = C.GenerateSink(state)) - EmitNilReceiverBug(C, ME, N); - return; - } - - // Handle the safe cases where the return value is 0 if the - // receiver is nil. - // - // FIXME: For now take the conservative approach that we only - // return null values if we *know* that the receiver is nil. - // This is because we can have surprises like: - // - // ... = [[NSScreens screens] objectAtIndex:0]; - // - // What can happen is that [... screens] could return nil, but - // it most likely isn't nil. We should assume the semantics - // of this case unless we have *a lot* more knowledge. - // - SVal V = C.getValueManager().makeZeroVal(ME->getType()); - C.GenerateNode(state->BindExpr(ME, V)); - return; - } - - C.addTransition(state); -} diff --git a/lib/Analysis/CallInliner.cpp b/lib/Analysis/CallInliner.cpp deleted file mode 100644 index d18bbcc0174a..000000000000 --- a/lib/Analysis/CallInliner.cpp +++ /dev/null @@ -1,113 +0,0 @@ -//===--- CallInliner.cpp - Transfer function that inlines callee ----------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the callee inlining transfer function. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/LocalCheckers.h" - -using namespace clang; - -namespace { -class CallInliner : public Checker { -public: - static void *getTag() { - static int x; - return &x; - } - - virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); - virtual void EvalEndPath(GREndPathNodeBuilder &B,void *tag,GRExprEngine &Eng); -}; -} - -void clang::RegisterCallInliner(GRExprEngine &Eng) { - Eng.registerCheck(new CallInliner()); -} - -bool CallInliner::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - - const FunctionDecl *FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - if (!FD->isThisDeclarationADefinition()) - return false; - - GRStmtNodeBuilder &Builder = C.getNodeBuilder(); - // Make a new LocationContext. - const StackFrameContext *LocCtx = C.getAnalysisManager().getStackFrame(FD, - C.getPredecessor()->getLocationContext(), CE, - Builder.getBlock(), Builder.getIndex()); - - CFGBlock const *Entry = &(LocCtx->getCFG()->getEntry()); - - assert (Entry->empty() && "Entry block must be empty."); - - assert (Entry->succ_size() == 1 && "Entry block must have 1 successor."); - - // Get the solitary successor. - CFGBlock const *SuccB = *(Entry->succ_begin()); - - // Construct an edge representing the starting location in the function. - BlockEdge Loc(Entry, SuccB, LocCtx); - - state = C.getStoreManager().EnterStackFrame(state, LocCtx); - // This is a hack. We really should not use the GRStmtNodeBuilder. - bool isNew; - GRExprEngine &Eng = C.getEngine(); - ExplodedNode *Pred = C.getPredecessor(); - - - ExplodedNode *SuccN = Eng.getGraph().getNode(Loc, state, &isNew); - SuccN->addPredecessor(Pred, Eng.getGraph()); - C.getNodeBuilder().Deferred.erase(Pred); - - if (isNew) - Builder.getWorkList()->Enqueue(SuccN); - - Builder.HasGeneratedNode = true; - - return true; -} - -void CallInliner::EvalEndPath(GREndPathNodeBuilder &B, void *tag, - GRExprEngine &Eng) { - const GRState *state = B.getState(); - ExplodedNode *Pred = B.getPredecessor(); - const StackFrameContext *LocCtx = - cast(Pred->getLocationContext()); - - const Stmt *CE = LocCtx->getCallSite(); - - // Check if this is the top level stack frame. - if (!LocCtx->getParent()) - return; - - PostStmt NodeLoc(CE, LocCtx->getParent()); - - bool isNew; - ExplodedNode *Succ = Eng.getGraph().getNode(NodeLoc, state, &isNew); - Succ->addPredecessor(Pred, Eng.getGraph()); - - // When creating the new work list unit, increment the statement index to - // point to the statement after the CallExpr. - if (isNew) - B.getWorkList().Enqueue(Succ, - *const_cast(LocCtx->getCallSiteBlock()), - LocCtx->getIndex() + 1); - - B.HasGeneratedNode = true; -} diff --git a/lib/Analysis/CastToStructChecker.cpp b/lib/Analysis/CastToStructChecker.cpp deleted file mode 100644 index 219c09f6ab7a..000000000000 --- a/lib/Analysis/CastToStructChecker.cpp +++ /dev/null @@ -1,77 +0,0 @@ -//=== CastToStructChecker.cpp - Fixed address usage checker ----*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This files defines CastToStructChecker, a builtin checker that checks for -// cast from non-struct pointer to struct pointer. -// This check corresponds to CWE-588. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class CastToStructChecker - : public CheckerVisitor { - BuiltinBug *BT; -public: - CastToStructChecker() : BT(0) {} - static void *getTag(); - void PreVisitCastExpr(CheckerContext &C, const CastExpr *B); -}; -} - -void *CastToStructChecker::getTag() { - static int x; - return &x; -} - -void CastToStructChecker::PreVisitCastExpr(CheckerContext &C, - const CastExpr *CE) { - const Expr *E = CE->getSubExpr(); - ASTContext &Ctx = C.getASTContext(); - QualType OrigTy = Ctx.getCanonicalType(E->getType()); - QualType ToTy = Ctx.getCanonicalType(CE->getType()); - - PointerType *OrigPTy = dyn_cast(OrigTy.getTypePtr()); - PointerType *ToPTy = dyn_cast(ToTy.getTypePtr()); - - if (!ToPTy || !OrigPTy) - return; - - QualType OrigPointeeTy = OrigPTy->getPointeeType(); - QualType ToPointeeTy = ToPTy->getPointeeType(); - - if (!ToPointeeTy->isStructureType()) - return; - - // We allow cast from void*. - if (OrigPointeeTy->isVoidType()) - return; - - // Now the cast-to-type is struct pointer, the original type is not void*. - if (!OrigPointeeTy->isRecordType()) { - if (ExplodedNode *N = C.GenerateNode()) { - if (!BT) - BT = new BuiltinBug("Cast from non-struct type to struct type", - "Casting a non-structure type to a structure type " - "and accessing a field can lead to memory access " - "errors or data corruption."); - RangedBugReport *R = new RangedBugReport(*BT,BT->getDescription(), N); - R->addRange(CE->getSourceRange()); - C.EmitReport(R); - } - } -} - -void clang::RegisterCastToStructChecker(GRExprEngine &Eng) { - Eng.registerCheck(new CastToStructChecker()); -} diff --git a/lib/Analysis/CheckDeadStores.cpp b/lib/Analysis/CheckDeadStores.cpp deleted file mode 100644 index 6e4d8998620d..000000000000 --- a/lib/Analysis/CheckDeadStores.cpp +++ /dev/null @@ -1,279 +0,0 @@ -//==- DeadStores.cpp - Check for stores to dead variables --------*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a DeadStores, a flow-sensitive checker that looks for -// stores to variables that are no longer live. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" -#include "clang/Basic/Diagnostic.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/ParentMap.h" -#include "llvm/ADT/SmallPtrSet.h" - -using namespace clang; - -namespace { - -class DeadStoreObs : public LiveVariables::ObserverTy { - ASTContext &Ctx; - BugReporter& BR; - ParentMap& Parents; - llvm::SmallPtrSet Escaped; - - enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit }; - -public: - DeadStoreObs(ASTContext &ctx, BugReporter& br, ParentMap& parents, - llvm::SmallPtrSet &escaped) - : Ctx(ctx), BR(br), Parents(parents), Escaped(escaped) {} - - virtual ~DeadStoreObs() {} - - void Report(VarDecl* V, DeadStoreKind dsk, SourceLocation L, SourceRange R) { - if (Escaped.count(V)) - return; - - std::string name = V->getNameAsString(); - - const char* BugType = 0; - std::string msg; - - switch (dsk) { - default: - assert(false && "Impossible dead store type."); - - case DeadInit: - BugType = "Dead initialization"; - msg = "Value stored to '" + name + - "' during its initialization is never read"; - break; - - case DeadIncrement: - BugType = "Dead increment"; - case Standard: - if (!BugType) BugType = "Dead assignment"; - msg = "Value stored to '" + name + "' is never read"; - break; - - case Enclosing: - BugType = "Dead nested assignment"; - msg = "Although the value stored to '" + name + - "' is used in the enclosing expression, the value is never actually" - " read from '" + name + "'"; - break; - } - - BR.EmitBasicReport(BugType, "Dead store", msg, L, R); - } - - void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val, - DeadStoreKind dsk, - const LiveVariables::AnalysisDataTy& AD, - const LiveVariables::ValTy& Live) { - - if (!VD->hasLocalStorage()) - return; - // Reference types confuse the dead stores checker. Skip them - // for now. - if (VD->getType()->getAs()) - return; - - if (!Live(VD, AD) && - !(VD->getAttr() || VD->getAttr())) - Report(VD, dsk, Ex->getSourceRange().getBegin(), - Val->getSourceRange()); - } - - void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk, - const LiveVariables::AnalysisDataTy& AD, - const LiveVariables::ValTy& Live) { - if (VarDecl* VD = dyn_cast(DR->getDecl())) - CheckVarDecl(VD, DR, Val, dsk, AD, Live); - } - - bool isIncrement(VarDecl* VD, BinaryOperator* B) { - if (B->isCompoundAssignmentOp()) - return true; - - Expr* RHS = B->getRHS()->IgnoreParenCasts(); - BinaryOperator* BRHS = dyn_cast(RHS); - - if (!BRHS) - return false; - - DeclRefExpr *DR; - - if ((DR = dyn_cast(BRHS->getLHS()->IgnoreParenCasts()))) - if (DR->getDecl() == VD) - return true; - - if ((DR = dyn_cast(BRHS->getRHS()->IgnoreParenCasts()))) - if (DR->getDecl() == VD) - return true; - - return false; - } - - virtual void ObserveStmt(Stmt* S, - const LiveVariables::AnalysisDataTy& AD, - const LiveVariables::ValTy& Live) { - - // Skip statements in macros. - if (S->getLocStart().isMacroID()) - return; - - if (BinaryOperator* B = dyn_cast(S)) { - if (!B->isAssignmentOp()) return; // Skip non-assignments. - - if (DeclRefExpr* DR = dyn_cast(B->getLHS())) - if (VarDecl *VD = dyn_cast(DR->getDecl())) { - // Special case: check for assigning null to a pointer. - // This is a common form of defensive programming. - if (VD->getType()->isPointerType()) { - if (B->getRHS()->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) - return; - } - - Expr* RHS = B->getRHS()->IgnoreParenCasts(); - // Special case: self-assignments. These are often used to shut up - // "unused variable" compiler warnings. - if (DeclRefExpr* RhsDR = dyn_cast(RHS)) - if (VD == dyn_cast(RhsDR->getDecl())) - return; - - // Otherwise, issue a warning. - DeadStoreKind dsk = Parents.isConsumedExpr(B) - ? Enclosing - : (isIncrement(VD,B) ? DeadIncrement : Standard); - - CheckVarDecl(VD, DR, B->getRHS(), dsk, AD, Live); - } - } - else if (UnaryOperator* U = dyn_cast(S)) { - if (!U->isIncrementOp()) - return; - - // Handle: ++x within a subexpression. The solution is not warn - // about preincrements to dead variables when the preincrement occurs - // as a subexpression. This can lead to false negatives, e.g. "(++x);" - // A generalized dead code checker should find such issues. - if (U->isPrefix() && Parents.isConsumedExpr(U)) - return; - - Expr *Ex = U->getSubExpr()->IgnoreParenCasts(); - - if (DeclRefExpr* DR = dyn_cast(Ex)) - CheckDeclRef(DR, U, DeadIncrement, AD, Live); - } - else if (DeclStmt* DS = dyn_cast(S)) - // Iterate through the decls. Warn if any initializers are complex - // expressions that are not live (never used). - for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end(); - DI != DE; ++DI) { - - VarDecl* V = dyn_cast(*DI); - - if (!V) - continue; - - if (V->hasLocalStorage()) { - // Reference types confuse the dead stores checker. Skip them - // for now. - if (V->getType()->getAs()) - return; - - if (Expr* E = V->getInit()) { - // Don't warn on C++ objects (yet) until we can show that their - // constructors/destructors don't have side effects. - if (isa(E)) - return; - - if (isa(E)) - return; - - // A dead initialization is a variable that is dead after it - // is initialized. We don't flag warnings for those variables - // marked 'unused'. - if (!Live(V, AD) && V->getAttr() == 0) { - // Special case: check for initializations with constants. - // - // e.g. : int x = 0; - // - // If x is EVER assigned a new value later, don't issue - // a warning. This is because such initialization can be - // due to defensive programming. - if (E->isConstantInitializer(Ctx)) - return; - - // Special case: check for initializations from constant - // variables. - // - // e.g. extern const int MyConstant; - // int x = MyConstant; - // - if (DeclRefExpr *DRE=dyn_cast(E->IgnoreParenCasts())) - if (VarDecl *VD = dyn_cast(DRE->getDecl())) - if (VD->hasGlobalStorage() && - VD->getType().isConstQualified()) return; - - Report(V, DeadInit, V->getLocation(), E->getSourceRange()); - } - } - } - } - } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Driver function to invoke the Dead-Stores checker on a CFG. -//===----------------------------------------------------------------------===// - -namespace { -class FindEscaped : public CFGRecStmtDeclVisitor{ - CFG *cfg; -public: - FindEscaped(CFG *c) : cfg(c) {} - - CFG& getCFG() { return *cfg; } - - llvm::SmallPtrSet Escaped; - - void VisitUnaryOperator(UnaryOperator* U) { - // Check for '&'. Any VarDecl whose value has its address-taken we - // treat as escaped. - Expr* E = U->getSubExpr()->IgnoreParenCasts(); - if (U->getOpcode() == UnaryOperator::AddrOf) - if (DeclRefExpr* DR = dyn_cast(E)) - if (VarDecl* VD = dyn_cast(DR->getDecl())) { - Escaped.insert(VD); - return; - } - Visit(E); - } -}; -} // end anonymous namespace - - -void clang::CheckDeadStores(CFG &cfg, LiveVariables &L, ParentMap &pmap, - BugReporter& BR) { - FindEscaped FS(&cfg); - FS.getCFG().VisitBlockStmts(FS); - DeadStoreObs A(BR.getContext(), BR, pmap, FS.Escaped); - L.runOnAllBlocks(cfg, &A); -} diff --git a/lib/Analysis/CheckObjCDealloc.cpp b/lib/Analysis/CheckObjCDealloc.cpp deleted file mode 100644 index 87c1f270a65c..000000000000 --- a/lib/Analysis/CheckObjCDealloc.cpp +++ /dev/null @@ -1,258 +0,0 @@ -//==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a CheckObjCDealloc, a checker that -// analyzes an Objective-C class's implementation to determine if it -// correctly implements -dealloc. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/Expr.h" -#include "clang/AST/DeclObjC.h" -#include "clang/Basic/LangOptions.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -static bool scan_dealloc(Stmt* S, Selector Dealloc) { - - if (ObjCMessageExpr* ME = dyn_cast(S)) - if (ME->getSelector() == Dealloc) - if (ME->getReceiver()) - if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts()) - return isa(Receiver); - - // Recurse to children. - - for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) - if (*I && scan_dealloc(*I, Dealloc)) - return true; - - return false; -} - -static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID, - const ObjCPropertyDecl* PD, - Selector Release, - IdentifierInfo* SelfII, - ASTContext& Ctx) { - - // [mMyIvar release] - if (ObjCMessageExpr* ME = dyn_cast(S)) - if (ME->getSelector() == Release) - if (ME->getReceiver()) - if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts()) - if (ObjCIvarRefExpr* E = dyn_cast(Receiver)) - if (E->getDecl() == ID) - return true; - - // [self setMyIvar:nil]; - if (ObjCMessageExpr* ME = dyn_cast(S)) - if (ME->getReceiver()) - if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts()) - if (DeclRefExpr* E = dyn_cast(Receiver)) - if (E->getDecl()->getIdentifier() == SelfII) - if (ME->getMethodDecl() == PD->getSetterMethodDecl() && - ME->getNumArgs() == 1 && - ME->getArg(0)->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) - return true; - - // self.myIvar = nil; - if (BinaryOperator* BO = dyn_cast(S)) - if (BO->isAssignmentOp()) - if (ObjCPropertyRefExpr* PRE = - dyn_cast(BO->getLHS()->IgnoreParenCasts())) - if (PRE->getProperty() == PD) - if (BO->getRHS()->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) { - // This is only a 'release' if the property kind is not - // 'assign'. - return PD->getSetterKind() != ObjCPropertyDecl::Assign;; - } - - // Recurse to children. - for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) - if (*I && scan_ivar_release(*I, ID, PD, Release, SelfII, Ctx)) - return true; - - return false; -} - -void clang::CheckObjCDealloc(const ObjCImplementationDecl* D, - const LangOptions& LOpts, BugReporter& BR) { - - assert (LOpts.getGCMode() != LangOptions::GCOnly); - - ASTContext& Ctx = BR.getContext(); - const ObjCInterfaceDecl* ID = D->getClassInterface(); - - // Does the class contain any ivars that are pointers (or id<...>)? - // If not, skip the check entirely. - // NOTE: This is motivated by PR 2517: - // http://llvm.org/bugs/show_bug.cgi?id=2517 - - bool containsPointerIvar = false; - - for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end(); - I!=E; ++I) { - - ObjCIvarDecl* ID = *I; - QualType T = ID->getType(); - - if (!T->isObjCObjectPointerType() || - ID->getAttr()) // Skip IBOutlets. - continue; - - containsPointerIvar = true; - break; - } - - if (!containsPointerIvar) - return; - - // Determine if the class subclasses NSObject. - IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); - IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); - - - for ( ; ID ; ID = ID->getSuperClass()) { - IdentifierInfo *II = ID->getIdentifier(); - - if (II == NSObjectII) - break; - - // FIXME: For now, ignore classes that subclass SenTestCase, as these don't - // need to implement -dealloc. They implement tear down in another way, - // which we should try and catch later. - // http://llvm.org/bugs/show_bug.cgi?id=3187 - if (II == SenTestCaseII) - return; - } - - if (!ID) - return; - - // Get the "dealloc" selector. - IdentifierInfo* II = &Ctx.Idents.get("dealloc"); - Selector S = Ctx.Selectors.getSelector(0, &II); - ObjCMethodDecl* MD = 0; - - // Scan the instance methods for "dealloc". - for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), - E = D->instmeth_end(); I!=E; ++I) { - - if ((*I)->getSelector() == S) { - MD = *I; - break; - } - } - - if (!MD) { // No dealloc found. - - const char* name = LOpts.getGCMode() == LangOptions::NonGC - ? "missing -dealloc" - : "missing -dealloc (Hybrid MM, non-GC)"; - - std::string buf; - llvm::raw_string_ostream os(buf); - os << "Objective-C class '" << D->getNameAsString() - << "' lacks a 'dealloc' instance method"; - - BR.EmitBasicReport(name, os.str(), D->getLocStart()); - return; - } - - // dealloc found. Scan for missing [super dealloc]. - if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) { - - const char* name = LOpts.getGCMode() == LangOptions::NonGC - ? "missing [super dealloc]" - : "missing [super dealloc] (Hybrid MM, non-GC)"; - - std::string buf; - llvm::raw_string_ostream os(buf); - os << "The 'dealloc' instance method in Objective-C class '" - << D->getNameAsString() - << "' does not send a 'dealloc' message to its super class" - " (missing [super dealloc])"; - - BR.EmitBasicReport(name, os.str(), D->getLocStart()); - return; - } - - // Get the "release" selector. - IdentifierInfo* RII = &Ctx.Idents.get("release"); - Selector RS = Ctx.Selectors.getSelector(0, &RII); - - // Get the "self" identifier - IdentifierInfo* SelfII = &Ctx.Idents.get("self"); - - // Scan for missing and extra releases of ivars used by implementations - // of synthesized properties - for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(), - E = D->propimpl_end(); I!=E; ++I) { - - // We can only check the synthesized properties - if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) - continue; - - ObjCIvarDecl* ID = (*I)->getPropertyIvarDecl(); - if (!ID) - continue; - - QualType T = ID->getType(); - if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars - continue; - - const ObjCPropertyDecl* PD = (*I)->getPropertyDecl(); - if (!PD) - continue; - - // ivars cannot be set via read-only properties, so we'll skip them - if (PD->isReadOnly()) - continue; - - // ivar must be released if and only if the kind of setter was not 'assign' - bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; - if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) - != requiresRelease) { - const char *name; - const char* category = "Memory (Core Foundation/Objective-C)"; - - std::string buf; - llvm::raw_string_ostream os(buf); - - if (requiresRelease) { - name = LOpts.getGCMode() == LangOptions::NonGC - ? "missing ivar release (leak)" - : "missing ivar release (Hybrid MM, non-GC)"; - - os << "The '" << ID->getNameAsString() - << "' instance variable was retained by a synthesized property but " - "wasn't released in 'dealloc'"; - } else { - name = LOpts.getGCMode() == LangOptions::NonGC - ? "extra ivar release (use-after-release)" - : "extra ivar release (Hybrid MM, non-GC)"; - - os << "The '" << ID->getNameAsString() - << "' instance variable was not retained by a synthesized property " - "but was released in 'dealloc'"; - } - - BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation()); - } - } -} - diff --git a/lib/Analysis/CheckObjCInstMethSignature.cpp b/lib/Analysis/CheckObjCInstMethSignature.cpp deleted file mode 100644 index 10ba896557df..000000000000 --- a/lib/Analysis/CheckObjCInstMethSignature.cpp +++ /dev/null @@ -1,119 +0,0 @@ -//=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a CheckObjCInstMethSignature, a flow-insenstive check -// that determines if an Objective-C class interface incorrectly redefines -// the method signature in a subclass. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Type.h" -#include "clang/AST/ASTContext.h" - -#include "llvm/ADT/DenseMap.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -static bool AreTypesCompatible(QualType Derived, QualType Ancestor, - ASTContext& C) { - - // Right now don't compare the compatibility of pointers. That involves - // looking at subtyping relationships. FIXME: Future patch. - if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType()) - return true; - - return C.typesAreCompatible(Derived, Ancestor); -} - -static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, - const ObjCMethodDecl *MethAncestor, - BugReporter &BR, ASTContext &Ctx, - const ObjCImplementationDecl *ID) { - - QualType ResDerived = MethDerived->getResultType(); - QualType ResAncestor = MethAncestor->getResultType(); - - if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "The Objective-C class '" - << MethDerived->getClassInterface()->getNameAsString() - << "', which is derived from class '" - << MethAncestor->getClassInterface()->getNameAsString() - << "', defines the instance method '" - << MethDerived->getSelector().getAsString() - << "' whose return type is '" - << ResDerived.getAsString() - << "'. A method with the same name (same selector) is also defined in " - "class '" - << MethAncestor->getClassInterface()->getNameAsString() - << "' and has a return type of '" - << ResAncestor.getAsString() - << "'. These two types are incompatible, and may result in undefined " - "behavior for clients of these classes."; - - BR.EmitBasicReport("Incompatible instance method return type", - os.str(), MethDerived->getLocStart()); - } -} - -void clang::CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, - BugReporter& BR) { - - const ObjCInterfaceDecl* D = ID->getClassInterface(); - const ObjCInterfaceDecl* C = D->getSuperClass(); - - if (!C) - return; - - ASTContext& Ctx = BR.getContext(); - - // Build a DenseMap of the methods for quick querying. - typedef llvm::DenseMap MapTy; - MapTy IMeths; - unsigned NumMethods = 0; - - for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), - E=ID->instmeth_end(); I!=E; ++I) { - - ObjCMethodDecl* M = *I; - IMeths[M->getSelector()] = M; - ++NumMethods; - } - - // Now recurse the class hierarchy chain looking for methods with the - // same signatures. - while (C && NumMethods) { - for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), - E=C->instmeth_end(); I!=E; ++I) { - - ObjCMethodDecl* M = *I; - Selector S = M->getSelector(); - - MapTy::iterator MI = IMeths.find(S); - - if (MI == IMeths.end() || MI->second == 0) - continue; - - --NumMethods; - ObjCMethodDecl* MethDerived = MI->second; - MI->second = 0; - - CompareReturnTypes(MethDerived, M, BR, Ctx, ID); - } - - C = C->getSuperClass(); - } -} diff --git a/lib/Analysis/CheckObjCUnusedIVars.cpp b/lib/Analysis/CheckObjCUnusedIVars.cpp deleted file mode 100644 index d4067c900f3f..000000000000 --- a/lib/Analysis/CheckObjCUnusedIVars.cpp +++ /dev/null @@ -1,162 +0,0 @@ -//==- CheckObjCUnusedIVars.cpp - Check for unused ivars ----------*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a CheckObjCUnusedIvars, a checker that -// analyzes an Objective-C class's interface/implementation to determine if it -// has any ivars that are never accessed. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/Expr.h" -#include "clang/AST/DeclObjC.h" -#include "clang/Basic/LangOptions.h" -#include "clang/Basic/SourceManager.h" - -using namespace clang; - -enum IVarState { Unused, Used }; -typedef llvm::DenseMap IvarUsageMap; - -static void Scan(IvarUsageMap& M, const Stmt* S) { - if (!S) - return; - - if (const ObjCIvarRefExpr *Ex = dyn_cast(S)) { - const ObjCIvarDecl *D = Ex->getDecl(); - IvarUsageMap::iterator I = M.find(D); - if (I != M.end()) - I->second = Used; - return; - } - - // Blocks can reference an instance variable of a class. - if (const BlockExpr *BE = dyn_cast(S)) { - Scan(M, BE->getBody()); - return; - } - - for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) - Scan(M, *I); -} - -static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) { - if (!D) - return; - - const ObjCIvarDecl* ID = D->getPropertyIvarDecl(); - - if (!ID) - return; - - IvarUsageMap::iterator I = M.find(ID); - if (I != M.end()) - I->second = Used; -} - -static void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) { - // Scan the methods for accesses. - for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), - E = D->instmeth_end(); I!=E; ++I) - Scan(M, (*I)->getBody()); - - if (const ObjCImplementationDecl *ID = dyn_cast(D)) { - // Scan for @synthesized property methods that act as setters/getters - // to an ivar. - for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), - E = ID->propimpl_end(); I!=E; ++I) - Scan(M, *I); - - // Scan the associated categories as well. - for (const ObjCCategoryDecl *CD = - ID->getClassInterface()->getCategoryList(); CD ; - CD = CD->getNextClassCategory()) { - if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) - Scan(M, CID); - } - } -} - -static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, - SourceManager &SM) { - for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); - I!=E; ++I) - if (const FunctionDecl *FD = dyn_cast(*I)) { - SourceLocation L = FD->getLocStart(); - if (SM.getFileID(L) == FID) - Scan(M, FD->getBody()); - } -} - -void clang::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, - BugReporter &BR) { - - const ObjCInterfaceDecl* ID = D->getClassInterface(); - IvarUsageMap M; - - // Iterate over the ivars. - for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), - E=ID->ivar_end(); I!=E; ++I) { - - const ObjCIvarDecl* ID = *I; - - // Ignore ivars that aren't private. - if (ID->getAccessControl() != ObjCIvarDecl::Private) - continue; - - // Skip IB Outlets. - if (ID->getAttr()) - continue; - - M[ID] = Unused; - } - - if (M.empty()) - return; - - // Now scan the implementation declaration. - Scan(M, D); - - - // Any potentially unused ivars? - bool hasUnused = false; - for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) - if (I->second == Unused) { - hasUnused = true; - break; - } - - if (!hasUnused) - return; - - // We found some potentially unused ivars. Scan the entire translation unit - // for functions inside the @implementation that reference these ivars. - // FIXME: In the future hopefully we can just use the lexical DeclContext - // to go from the ObjCImplementationDecl to the lexically "nested" - // C functions. - SourceManager &SM = BR.getSourceManager(); - Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); - - // Find ivars that are unused. - for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) - if (I->second == Unused) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Instance variable '" << I->first->getNameAsString() - << "' in class '" << ID->getNameAsString() - << "' is never used by the methods in its @implementation " - "(although it may be used by category methods)."; - - BR.EmitBasicReport("Unused instance variable", "Optimization", - os.str(), I->first->getLocation()); - } -} diff --git a/lib/Analysis/CheckSecuritySyntaxOnly.cpp b/lib/Analysis/CheckSecuritySyntaxOnly.cpp deleted file mode 100644 index f4874a5dfe4c..000000000000 --- a/lib/Analysis/CheckSecuritySyntaxOnly.cpp +++ /dev/null @@ -1,502 +0,0 @@ -//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a set of flow-insensitive security checks. -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/TargetInfo.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/LocalCheckers.h" -#include "clang/AST/StmtVisitor.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -static bool isArc4RandomAvailable(const ASTContext &Ctx) { - const llvm::Triple &T = Ctx.Target.getTriple(); - return T.getVendor() == llvm::Triple::Apple || - T.getOS() == llvm::Triple::FreeBSD; -} - -namespace { -class WalkAST : public StmtVisitor { - BugReporter &BR; - IdentifierInfo *II_gets; - IdentifierInfo *II_getpw; - IdentifierInfo *II_mktemp; - enum { num_rands = 9 }; - IdentifierInfo *II_rand[num_rands]; - IdentifierInfo *II_random; - enum { num_setids = 6 }; - IdentifierInfo *II_setid[num_setids]; - - const bool CheckRand; - -public: - WalkAST(BugReporter &br) : BR(br), - II_gets(0), II_getpw(0), II_mktemp(0), - II_rand(), II_random(0), II_setid(), - CheckRand(isArc4RandomAvailable(BR.getContext())) {} - - // Statement visitor methods. - void VisitCallExpr(CallExpr *CE); - void VisitForStmt(ForStmt *S); - void VisitCompoundStmt (CompoundStmt *S); - void VisitStmt(Stmt *S) { VisitChildren(S); } - - void VisitChildren(Stmt *S); - - // Helpers. - IdentifierInfo *GetIdentifier(IdentifierInfo *& II, const char *str); - - // Checker-specific methods. - void CheckLoopConditionForFloat(const ForStmt *FS); - void CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD); - void CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD); - void CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); - void CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD); - void CheckCall_random(const CallExpr *CE, const FunctionDecl *FD); - void CheckUncheckedReturnValue(CallExpr *CE); -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Helper methods. -//===----------------------------------------------------------------------===// - -IdentifierInfo *WalkAST::GetIdentifier(IdentifierInfo *& II, const char *str) { - if (!II) - II = &BR.getContext().Idents.get(str); - - return II; -} - -//===----------------------------------------------------------------------===// -// AST walking. -//===----------------------------------------------------------------------===// - -void WalkAST::VisitChildren(Stmt *S) { - for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) - if (Stmt *child = *I) - Visit(child); -} - -void WalkAST::VisitCallExpr(CallExpr *CE) { - if (const FunctionDecl *FD = CE->getDirectCallee()) { - CheckCall_gets(CE, FD); - CheckCall_getpw(CE, FD); - CheckCall_mktemp(CE, FD); - if (CheckRand) { - CheckCall_rand(CE, FD); - CheckCall_random(CE, FD); - } - } - - // Recurse and check children. - VisitChildren(CE); -} - -void WalkAST::VisitCompoundStmt(CompoundStmt *S) { - for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) - if (Stmt *child = *I) { - if (CallExpr *CE = dyn_cast(child)) - CheckUncheckedReturnValue(CE); - Visit(child); - } -} - -void WalkAST::VisitForStmt(ForStmt *FS) { - CheckLoopConditionForFloat(FS); - - // Recurse and check children. - VisitChildren(FS); -} - -//===----------------------------------------------------------------------===// -// Check: floating poing variable used as loop counter. -// Originally: -// Implements: CERT security coding advisory FLP-30. -//===----------------------------------------------------------------------===// - -static const DeclRefExpr* -GetIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { - expr = expr->IgnoreParenCasts(); - - if (const BinaryOperator *B = dyn_cast(expr)) { - if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || - B->getOpcode() == BinaryOperator::Comma)) - return NULL; - - if (const DeclRefExpr *lhs = GetIncrementedVar(B->getLHS(), x, y)) - return lhs; - - if (const DeclRefExpr *rhs = GetIncrementedVar(B->getRHS(), x, y)) - return rhs; - - return NULL; - } - - if (const DeclRefExpr *DR = dyn_cast(expr)) { - const NamedDecl *ND = DR->getDecl(); - return ND == x || ND == y ? DR : NULL; - } - - if (const UnaryOperator *U = dyn_cast(expr)) - return U->isIncrementDecrementOp() - ? GetIncrementedVar(U->getSubExpr(), x, y) : NULL; - - return NULL; -} - -/// CheckLoopConditionForFloat - This check looks for 'for' statements that -/// use a floating point variable as a loop counter. -/// CERT: FLP30-C, FLP30-CPP. -/// -void WalkAST::CheckLoopConditionForFloat(const ForStmt *FS) { - // Does the loop have a condition? - const Expr *condition = FS->getCond(); - - if (!condition) - return; - - // Does the loop have an increment? - const Expr *increment = FS->getInc(); - - if (!increment) - return; - - // Strip away '()' and casts. - condition = condition->IgnoreParenCasts(); - increment = increment->IgnoreParenCasts(); - - // Is the loop condition a comparison? - const BinaryOperator *B = dyn_cast(condition); - - if (!B) - return; - - // Is this a comparison? - if (!(B->isRelationalOp() || B->isEqualityOp())) - return; - - // Are we comparing variables? - const DeclRefExpr *drLHS = dyn_cast(B->getLHS()->IgnoreParens()); - const DeclRefExpr *drRHS = dyn_cast(B->getRHS()->IgnoreParens()); - - // Does at least one of the variables have a floating point type? - drLHS = drLHS && drLHS->getType()->isFloatingType() ? drLHS : NULL; - drRHS = drRHS && drRHS->getType()->isFloatingType() ? drRHS : NULL; - - if (!drLHS && !drRHS) - return; - - const VarDecl *vdLHS = drLHS ? dyn_cast(drLHS->getDecl()) : NULL; - const VarDecl *vdRHS = drRHS ? dyn_cast(drRHS->getDecl()) : NULL; - - if (!vdLHS && !vdRHS) - return; - - // Does either variable appear in increment? - const DeclRefExpr *drInc = GetIncrementedVar(increment, vdLHS, vdRHS); - - if (!drInc) - return; - - // Emit the error. First figure out which DeclRefExpr in the condition - // referenced the compared variable. - const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; - - llvm::SmallVector ranges; - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Variable '" << drCond->getDecl()->getNameAsCString() - << "' with floating point type '" << drCond->getType().getAsString() - << "' should not be used as a loop counter"; - - ranges.push_back(drCond->getSourceRange()); - ranges.push_back(drInc->getSourceRange()); - - const char *bugType = "Floating point variable used as loop counter"; - BR.EmitBasicReport(bugType, "Security", os.str(), - FS->getLocStart(), ranges.data(), ranges.size()); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'gets' is insecure. -// Originally: -// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) -// CWE-242: Use of Inherently Dangerous Function -//===----------------------------------------------------------------------===// - -void WalkAST::CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD) { - if (FD->getIdentifier() != GetIdentifier(II_gets, "gets")) - return; - - const FunctionProtoType *FPT = dyn_cast(FD->getType()); - if (!FPT) - return; - - // Verify that the function takes a single argument. - if (FPT->getNumArgs() != 1) - return; - - // Is the argument a 'char*'? - const PointerType *PT = dyn_cast(FPT->getArgType(0)); - if (!PT) - return; - - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) - return; - - // Issue a warning. - SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport("Potential buffer overflow in call to 'gets'", - "Security", - "Call to function 'gets' is extremely insecure as it can " - "always result in a buffer overflow", - CE->getLocStart(), &R, 1); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'getpwd' is insecure. -// CWE-477: Use of Obsolete Functions -//===----------------------------------------------------------------------===// - -void WalkAST::CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { - if (FD->getIdentifier() != GetIdentifier(II_getpw, "getpw")) - return; - - const FunctionProtoType *FPT = dyn_cast(FD->getType()); - if (!FPT) - return; - - // Verify that the function takes two arguments. - if (FPT->getNumArgs() != 2) - return; - - // Verify the first argument type is integer. - if (!FPT->getArgType(0)->isIntegerType()) - return; - - // Verify the second argument type is char*. - const PointerType *PT = dyn_cast(FPT->getArgType(1)); - if (!PT) - return; - - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) - return; - - // Issue a warning. - SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'", - "Security", - "The getpw() function is dangerous as it may overflow the " - "provided buffer. It is obsoleted by getpwuid().", - CE->getLocStart(), &R, 1); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'mktemp' is insecure.It is obsoleted by mkstemp(). -// CWE-377: Insecure Temporary File -//===----------------------------------------------------------------------===// - -void WalkAST::CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { - if (FD->getIdentifier() != GetIdentifier(II_mktemp, "mktemp")) - return; - - const FunctionProtoType *FPT = dyn_cast(FD->getType()); - if(!FPT) - return; - - // Verify that the funcion takes a single argument. - if (FPT->getNumArgs() != 1) - return; - - // Verify that the argument is Pointer Type. - const PointerType *PT = dyn_cast(FPT->getArgType(0)); - if (!PT) - return; - - // Verify that the argument is a 'char*'. - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) - return; - - // Issue a waring. - SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'", - "Security", - "Call to function 'mktemp' is insecure as it always " - "creates or uses insecure temporary file", - CE->getLocStart(), &R, 1); -} - - -//===----------------------------------------------------------------------===// -// Check: Linear congruent random number generators should not be used -// Originally: -// CWE-338: Use of cryptographically weak prng -//===----------------------------------------------------------------------===// - -void WalkAST::CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD) { - if (II_rand[0] == NULL) { - // This check applies to these functions - static const char * const identifiers[num_rands] = { - "drand48", "erand48", "jrand48", "lrand48", "mrand48", "nrand48", - "lcong48", - "rand", "rand_r" - }; - - for (size_t i = 0; i < num_rands; i++) - II_rand[i] = &BR.getContext().Idents.get(identifiers[i]); - } - - const IdentifierInfo *id = FD->getIdentifier(); - size_t identifierid; - - for (identifierid = 0; identifierid < num_rands; identifierid++) - if (id == II_rand[identifierid]) - break; - - if (identifierid >= num_rands) - return; - - const FunctionProtoType *FTP = dyn_cast(FD->getType()); - if (!FTP) - return; - - if (FTP->getNumArgs() == 1) { - // Is the argument an 'unsigned short *'? - // (Actually any integer type is allowed.) - const PointerType *PT = dyn_cast(FTP->getArgType(0)); - if (!PT) - return; - - if (! PT->getPointeeType()->isIntegerType()) - return; - } - else if (FTP->getNumArgs() != 0) - return; - - // Issue a warning. - std::string buf1; - llvm::raw_string_ostream os1(buf1); - os1 << "'" << FD->getNameAsString() << "' is a poor random number generator"; - - std::string buf2; - llvm::raw_string_ostream os2(buf2); - os2 << "Function '" << FD->getNameAsString() - << "' is obsolete because it implements a poor random number generator." - << " Use 'arc4random' instead"; - - SourceRange R = CE->getCallee()->getSourceRange(); - - BR.EmitBasicReport(os1.str(), "Security", os2.str(), - CE->getLocStart(), &R, 1); -} - -//===----------------------------------------------------------------------===// -// Check: 'random' should not be used -// Originally: -//===----------------------------------------------------------------------===// - -void WalkAST::CheckCall_random(const CallExpr *CE, const FunctionDecl *FD) { - if (FD->getIdentifier() != GetIdentifier(II_random, "random")) - return; - - const FunctionProtoType *FTP = dyn_cast(FD->getType()); - if (!FTP) - return; - - // Verify that the function takes no argument. - if (FTP->getNumArgs() != 0) - return; - - // Issue a warning. - SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport("'random' is not a secure random number generator", - "Security", - "The 'random' function produces a sequence of values that " - "an adversary may be able to predict. Use 'arc4random' " - "instead", - CE->getLocStart(), &R, 1); -} - -//===----------------------------------------------------------------------===// -// Check: Should check whether privileges are dropped successfully. -// Originally: -//===----------------------------------------------------------------------===// - -void WalkAST::CheckUncheckedReturnValue(CallExpr *CE) { - const FunctionDecl *FD = CE->getDirectCallee(); - if (!FD) - return; - - if (II_setid[0] == NULL) { - static const char * const identifiers[num_setids] = { - "setuid", "setgid", "seteuid", "setegid", - "setreuid", "setregid" - }; - - for (size_t i = 0; i < num_setids; i++) - II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); - } - - const IdentifierInfo *id = FD->getIdentifier(); - size_t identifierid; - - for (identifierid = 0; identifierid < num_setids; identifierid++) - if (id == II_setid[identifierid]) - break; - - if (identifierid >= num_setids) - return; - - const FunctionProtoType *FTP = dyn_cast(FD->getType()); - if (!FTP) - return; - - // Verify that the function takes one or two arguments (depending on - // the function). - if (FTP->getNumArgs() != (identifierid < 4 ? 1 : 2)) - return; - - // The arguments must be integers. - for (unsigned i = 0; i < FTP->getNumArgs(); i++) - if (! FTP->getArgType(i)->isIntegerType()) - return; - - // Issue a warning. - std::string buf1; - llvm::raw_string_ostream os1(buf1); - os1 << "Return value is not checked in call to '" << FD->getNameAsString() - << "'"; - - std::string buf2; - llvm::raw_string_ostream os2(buf2); - os2 << "The return value from the call to '" << FD->getNameAsString() - << "' is not checked. If an error occurs in '" - << FD->getNameAsString() - << "', the following code may execute with unexpected privileges"; - - SourceRange R = CE->getCallee()->getSourceRange(); - - BR.EmitBasicReport(os1.str(), "Security", os2.str(), - CE->getLocStart(), &R, 1); -} - -//===----------------------------------------------------------------------===// -// Entry point for check. -//===----------------------------------------------------------------------===// - -void clang::CheckSecuritySyntaxOnly(const Decl *D, BugReporter &BR) { - WalkAST walker(BR); - walker.Visit(D->getBody()); -} diff --git a/lib/Analysis/CheckSizeofPointer.cpp b/lib/Analysis/CheckSizeofPointer.cpp deleted file mode 100644 index 4f5da9f5a716..000000000000 --- a/lib/Analysis/CheckSizeofPointer.cpp +++ /dev/null @@ -1,71 +0,0 @@ -//==- CheckSizeofPointer.cpp - Check for sizeof on pointers ------*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a check for unintended use of sizeof() on pointer -// expressions. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/AST/StmtVisitor.h" -#include "clang/Analysis/LocalCheckers.h" - -using namespace clang; - -namespace { -class WalkAST : public StmtVisitor { - BugReporter &BR; - -public: - WalkAST(BugReporter &br) : BR(br) {} - void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); - void VisitStmt(Stmt *S) { VisitChildren(S); } - void VisitChildren(Stmt *S); -}; -} - -void WalkAST::VisitChildren(Stmt *S) { - for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) - if (Stmt *child = *I) - Visit(child); -} - -// CWE-467: Use of sizeof() on a Pointer Type -void WalkAST::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { - if (!E->isSizeOf()) - return; - - // If an explicit type is used in the code, usually the coder knows what he is - // doing. - if (E->isArgumentType()) - return; - - QualType T = E->getTypeOfArgument(); - if (T->isPointerType()) { - - // Many false positives have the form 'sizeof *p'. This is reasonable - // because people know what they are doing when they intentionally - // dereference the pointer. - Expr *ArgEx = E->getArgumentExpr(); - if (!isa(ArgEx->IgnoreParens())) - return; - - SourceRange R = ArgEx->getSourceRange(); - BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type", - "Logic", - "The code calls sizeof() on a pointer type. " - "This can produce an unexpected result.", - E->getLocStart(), &R, 1); - } -} - -void clang::CheckSizeofPointer(const Decl *D, BugReporter &BR) { - WalkAST walker(BR); - walker.Visit(D->getBody()); -} diff --git a/lib/Analysis/Checker.cpp b/lib/Analysis/Checker.cpp deleted file mode 100644 index fb9d04d947b7..000000000000 --- a/lib/Analysis/Checker.cpp +++ /dev/null @@ -1,35 +0,0 @@ -//== Checker.h - Abstract interface for checkers -----------------*- C++ -*--=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines Checker and CheckerVisitor, classes used for creating -// domain-specific checks. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/Checker.h" -using namespace clang; - -Checker::~Checker() {} - -CheckerContext::~CheckerContext() { - // Do we need to autotransition? 'Dst' can get populated in a variety of - // ways, including 'addTransition()' adding the predecessor node to Dst - // without actually generated a new node. We also shouldn't autotransition - // if we are building sinks or we generated a node and decided to not - // add it as a transition. - if (Dst.size() == size && !B.BuildSinks && !B.HasGeneratedNode) { - if (ST && ST != B.GetState(Pred)) { - static int autoTransitionTag = 0; - B.Tag = &autoTransitionTag; - addTransition(ST); - } - else - Dst.Add(Pred); - } -} diff --git a/lib/Analysis/DereferenceChecker.cpp b/lib/Analysis/DereferenceChecker.cpp deleted file mode 100644 index 98243874d7d9..000000000000 --- a/lib/Analysis/DereferenceChecker.cpp +++ /dev/null @@ -1,135 +0,0 @@ -//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines NullDerefChecker, a builtin check in GRExprEngine that performs -// checks for null pointers at loads and stores. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/Checkers/DereferenceChecker.h" -#include "clang/Analysis/PathSensitive/Checker.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class DereferenceChecker : public Checker { - BuiltinBug *BT_null; - BuiltinBug *BT_undef; - llvm::SmallVector ImplicitNullDerefNodes; -public: - DereferenceChecker() : BT_null(0), BT_undef(0) {} - static void *getTag() { static int tag = 0; return &tag; } - void VisitLocation(CheckerContext &C, const Stmt *S, SVal location); - - std::pair - getImplicitNodes() const { - return std::make_pair(ImplicitNullDerefNodes.data(), - ImplicitNullDerefNodes.data() + - ImplicitNullDerefNodes.size()); - } -}; -} // end anonymous namespace - -void clang::RegisterDereferenceChecker(GRExprEngine &Eng) { - Eng.registerCheck(new DereferenceChecker()); -} - -std::pair -clang::GetImplicitNullDereferences(GRExprEngine &Eng) { - DereferenceChecker *checker = Eng.getChecker(); - if (!checker) - return std::make_pair((ExplodedNode * const *) 0, - (ExplodedNode * const *) 0); - return checker->getImplicitNodes(); -} - -void DereferenceChecker::VisitLocation(CheckerContext &C, const Stmt *S, - SVal l) { - // Check for dereference of an undefined value. - if (l.isUndef()) { - if (ExplodedNode *N = C.GenerateSink()) { - if (!BT_undef) - BT_undef = new BuiltinBug("Dereference of undefined pointer value"); - - EnhancedBugReport *report = - new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDerefExpr(N)); - C.EmitReport(report); - } - return; - } - - DefinedOrUnknownSVal location = cast(l); - - // Check for null dereferences. - if (!isa(location)) - return; - - const GRState *state = C.getState(); - const GRState *notNullState, *nullState; - llvm::tie(notNullState, nullState) = state->Assume(location); - - // The explicit NULL case. - if (nullState) { - if (!notNullState) { - // Generate an error node. - ExplodedNode *N = C.GenerateSink(nullState); - if (!N) - return; - - // We know that 'location' cannot be non-null. This is what - // we call an "explicit" null dereference. - if (!BT_null) - BT_null = new BuiltinBug("Dereference of null pointer"); - - llvm::SmallString<100> buf; - - switch (S->getStmtClass()) { - case Stmt::UnaryOperatorClass: { - const UnaryOperator *U = cast(S); - const Expr *SU = U->getSubExpr()->IgnoreParens(); - if (const DeclRefExpr *DR = dyn_cast(SU)) { - if (const VarDecl *VD = dyn_cast(DR->getDecl())) { - llvm::raw_svector_ostream os(buf); - os << "Dereference of null pointer loaded from variable '" - << VD->getName() << '\''; - } - } - } - default: - break; - } - - EnhancedBugReport *report = - new EnhancedBugReport(*BT_null, - buf.empty() ? BT_null->getDescription():buf.str(), - N); - - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDerefExpr(N)); - - C.EmitReport(report); - return; - } - else { - // Otherwise, we have the case where the location could either be - // null or not-null. Record the error node as an "implicit" null - // dereference. - if (ExplodedNode *N = C.GenerateSink(nullState)) - ImplicitNullDerefNodes.push_back(N); - } - } - - // From this point forward, we know that the location is not null. - C.addTransition(notNullState); -} diff --git a/lib/Analysis/DivZeroChecker.cpp b/lib/Analysis/DivZeroChecker.cpp deleted file mode 100644 index 266c23609422..000000000000 --- a/lib/Analysis/DivZeroChecker.cpp +++ /dev/null @@ -1,84 +0,0 @@ -//== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines DivZeroChecker, a builtin check in GRExprEngine that performs -// checks for division by zeros. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class DivZeroChecker : public CheckerVisitor { - BuiltinBug *BT; -public: - DivZeroChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; -} // end anonymous namespace - -void clang::RegisterDivZeroChecker(GRExprEngine &Eng) { - Eng.registerCheck(new DivZeroChecker()); -} - -void *DivZeroChecker::getTag() { - static int x; - return &x; -} - -void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - BinaryOperator::Opcode Op = B->getOpcode(); - if (Op != BinaryOperator::Div && - Op != BinaryOperator::Rem && - Op != BinaryOperator::DivAssign && - Op != BinaryOperator::RemAssign) - return; - - if (!B->getRHS()->getType()->isIntegerType() || - !B->getRHS()->getType()->isScalarType()) - return; - - SVal Denom = C.getState()->getSVal(B->getRHS()); - const DefinedSVal *DV = dyn_cast(&Denom); - - // Divide-by-undefined handled in the generic checking for uses of - // undefined values. - if (!DV) - return; - - // Check for divide by zero. - ConstraintManager &CM = C.getConstraintManager(); - const GRState *stateNotZero, *stateZero; - llvm::tie(stateNotZero, stateZero) = CM.AssumeDual(C.getState(), *DV); - - if (stateZero && !stateNotZero) { - if (ExplodedNode *N = C.GenerateSink(stateZero)) { - if (!BT) - BT = new BuiltinBug("Division by zero"); - - EnhancedBugReport *R = - new EnhancedBugReport(*BT, BT->getDescription(), N); - - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDenomExpr(N)); - - C.EmitReport(R); - } - return; - } - - // If we get here, then the denom should not be zero. We abandon the implicit - // zero denom case for now. - C.addTransition(stateNotZero); -} diff --git a/lib/Analysis/Environment.cpp b/lib/Analysis/Environment.cpp deleted file mode 100644 index f04cf7b05fed..000000000000 --- a/lib/Analysis/Environment.cpp +++ /dev/null @@ -1,164 +0,0 @@ -//== Environment.cpp - Map from Stmt* to Locations/Values -------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defined the Environment and EnvironmentManager classes. -// -//===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "llvm/ADT/ImmutableMap.h" - -using namespace clang; - -SVal Environment::GetSVal(const Stmt *E, ValueManager& ValMgr) const { - - for (;;) { - - switch (E->getStmtClass()) { - - case Stmt::AddrLabelExprClass: - return ValMgr.makeLoc(cast(E)); - - // ParenExprs are no-ops. - - case Stmt::ParenExprClass: - E = cast(E)->getSubExpr(); - continue; - - case Stmt::CharacterLiteralClass: { - const CharacterLiteral* C = cast(E); - return ValMgr.makeIntVal(C->getValue(), C->getType()); - } - - case Stmt::IntegerLiteralClass: { - // In C++, this expression may have been bound to a temporary object. - SVal const *X = ExprBindings.lookup(E); - if (X) - return *X; - else - return ValMgr.makeIntVal(cast(E)); - } - - // Casts where the source and target type are the same - // are no-ops. We blast through these to get the descendant - // subexpression that has a value. - - case Stmt::ImplicitCastExprClass: - case Stmt::CStyleCastExprClass: { - const CastExpr* C = cast(E); - QualType CT = C->getType(); - - if (CT->isVoidType()) - return UnknownVal(); - - break; - } - - // Handle all other Stmt* using a lookup. - - default: - break; - }; - - break; - } - - return LookupExpr(E); -} - -Environment EnvironmentManager::BindExpr(Environment Env, const Stmt *S, - SVal V, bool Invalidate) { - assert(S); - - if (V.isUnknown()) { - if (Invalidate) - return Environment(F.Remove(Env.ExprBindings, S), Env.ACtx); - else - return Env; - } - - return Environment(F.Add(Env.ExprBindings, S, V), Env.ACtx); -} - -namespace { -class MarkLiveCallback : public SymbolVisitor { - SymbolReaper &SymReaper; -public: - MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} - bool VisitSymbol(SymbolRef sym) { SymReaper.markLive(sym); return true; } -}; -} // end anonymous namespace - -// RemoveDeadBindings: -// - Remove subexpression bindings. -// - Remove dead block expression bindings. -// - Keep live block expression bindings: -// - Mark their reachable symbols live in SymbolReaper, -// see ScanReachableSymbols. -// - Mark the region in DRoots if the binding is a loc::MemRegionVal. - -Environment -EnvironmentManager::RemoveDeadBindings(Environment Env, const Stmt *S, - SymbolReaper &SymReaper, - const GRState *ST, - llvm::SmallVectorImpl &DRoots) { - - CFG &C = *Env.getAnalysisContext().getCFG(); - - // We construct a new Environment object entirely, as this is cheaper than - // individually removing all the subexpression bindings (which will greatly - // outnumber block-level expression bindings). - Environment NewEnv = getInitialEnvironment(&Env.getAnalysisContext()); - - // Iterate over the block-expr bindings. - for (Environment::iterator I = Env.begin(), E = Env.end(); - I != E; ++I) { - - const Stmt *BlkExpr = I.getKey(); - - // Not a block-level expression? - if (!C.isBlkExpr(BlkExpr)) - continue; - - const SVal &X = I.getData(); - - if (SymReaper.isLive(S, BlkExpr)) { - // Copy the binding to the new map. - NewEnv.ExprBindings = F.Add(NewEnv.ExprBindings, BlkExpr, X); - - // If the block expr's value is a memory region, then mark that region. - if (isa(X)) { - const MemRegion* R = cast(X).getRegion(); - DRoots.push_back(R); - // Mark the super region of the RX as live. - // e.g.: int x; char *y = (char*) &x; if (*y) ... - // 'y' => element region. 'x' is its super region. - // We only add one level super region for now. - - // FIXME: maybe multiple level of super regions should be added. - if (const SubRegion *SR = dyn_cast(R)) - DRoots.push_back(SR->getSuperRegion()); - } - - // Mark all symbols in the block expr's value live. - MarkLiveCallback cb(SymReaper); - ST->scanReachableSymbols(X, cb); - continue; - } - - // Otherwise the expression is dead with a couple exceptions. - // Do not misclean LogicalExpr or ConditionalOperator. It is dead at the - // beginning of itself, but we need its UndefinedVal to determine its - // SVal. - if (X.isUndef() && cast(X).getData()) - NewEnv.ExprBindings = F.Add(NewEnv.ExprBindings, BlkExpr, X); - } - - return NewEnv; -} diff --git a/lib/Analysis/ExplodedGraph.cpp b/lib/Analysis/ExplodedGraph.cpp deleted file mode 100644 index 3b339ffc0dfe..000000000000 --- a/lib/Analysis/ExplodedGraph.cpp +++ /dev/null @@ -1,281 +0,0 @@ -//=-- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -*- C++ -*------=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the template classes ExplodedNode and ExplodedGraph, -// which represent a path-sensitive, intra-procedural "exploded graph." -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/ExplodedGraph.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/AST/Stmt.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/SmallVector.h" -#include - -using namespace clang; - -//===----------------------------------------------------------------------===// -// Node auditing. -//===----------------------------------------------------------------------===// - -// An out of line virtual method to provide a home for the class vtable. -ExplodedNode::Auditor::~Auditor() {} - -#ifndef NDEBUG -static ExplodedNode::Auditor* NodeAuditor = 0; -#endif - -void ExplodedNode::SetAuditor(ExplodedNode::Auditor* A) { -#ifndef NDEBUG - NodeAuditor = A; -#endif -} - -//===----------------------------------------------------------------------===// -// ExplodedNode. -//===----------------------------------------------------------------------===// - -static inline BumpVector& getVector(void* P) { - return *reinterpret_cast*>(P); -} - -void ExplodedNode::addPredecessor(ExplodedNode* V, ExplodedGraph &G) { - assert (!V->isSink()); - Preds.addNode(V, G); - V->Succs.addNode(this, G); -#ifndef NDEBUG - if (NodeAuditor) NodeAuditor->AddEdge(V, this); -#endif -} - -void ExplodedNode::NodeGroup::addNode(ExplodedNode* N, ExplodedGraph &G) { - assert((reinterpret_cast(N) & Mask) == 0x0); - assert(!getFlag()); - - if (getKind() == Size1) { - if (ExplodedNode* NOld = getNode()) { - BumpVectorContext &Ctx = G.getNodeAllocator(); - BumpVector *V = - G.getAllocator().Allocate >(); - new (V) BumpVector(Ctx, 4); - - assert((reinterpret_cast(V) & Mask) == 0x0); - V->push_back(NOld, Ctx); - V->push_back(N, Ctx); - P = reinterpret_cast(V) | SizeOther; - assert(getPtr() == (void*) V); - assert(getKind() == SizeOther); - } - else { - P = reinterpret_cast(N); - assert(getKind() == Size1); - } - } - else { - assert(getKind() == SizeOther); - getVector(getPtr()).push_back(N, G.getNodeAllocator()); - } -} - -unsigned ExplodedNode::NodeGroup::size() const { - if (getFlag()) - return 0; - - if (getKind() == Size1) - return getNode() ? 1 : 0; - else - return getVector(getPtr()).size(); -} - -ExplodedNode **ExplodedNode::NodeGroup::begin() const { - if (getFlag()) - return NULL; - - if (getKind() == Size1) - return (ExplodedNode**) (getPtr() ? &P : NULL); - else - return const_cast(&*(getVector(getPtr()).begin())); -} - -ExplodedNode** ExplodedNode::NodeGroup::end() const { - if (getFlag()) - return NULL; - - if (getKind() == Size1) - return (ExplodedNode**) (getPtr() ? &P+1 : NULL); - else { - // Dereferencing end() is undefined behaviour. The vector is not empty, so - // we can dereference the last elem and then add 1 to the result. - return const_cast(getVector(getPtr()).end()); - } -} - -ExplodedNode *ExplodedGraph::getNode(const ProgramPoint& L, - const GRState* State, bool* IsNew) { - // Profile 'State' to determine if we already have an existing node. - llvm::FoldingSetNodeID profile; - void* InsertPos = 0; - - NodeTy::Profile(profile, L, State); - NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos); - - if (!V) { - // Allocate a new node. - V = (NodeTy*) getAllocator().Allocate(); - new (V) NodeTy(L, State); - - // Insert the node into the node set and return it. - Nodes.InsertNode(V, InsertPos); - - ++NumNodes; - - if (IsNew) *IsNew = true; - } - else - if (IsNew) *IsNew = false; - - return V; -} - -std::pair -ExplodedGraph::Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd, - llvm::DenseMap *InverseMap) const { - - if (NBeg == NEnd) - return std::make_pair((ExplodedGraph*) 0, - (InterExplodedGraphMap*) 0); - - assert (NBeg < NEnd); - - llvm::OwningPtr M(new InterExplodedGraphMap()); - - ExplodedGraph* G = TrimInternal(NBeg, NEnd, M.get(), InverseMap); - - return std::make_pair(static_cast(G), M.take()); -} - -ExplodedGraph* -ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, - const ExplodedNode* const* EndSources, - InterExplodedGraphMap* M, - llvm::DenseMap *InverseMap) const { - - typedef llvm::DenseSet Pass1Ty; - Pass1Ty Pass1; - - typedef llvm::DenseMap Pass2Ty; - Pass2Ty& Pass2 = M->M; - - llvm::SmallVector WL1, WL2; - - // ===- Pass 1 (reverse DFS) -=== - for (const ExplodedNode* const* I = BeginSources; I != EndSources; ++I) { - assert(*I); - WL1.push_back(*I); - } - - // Process the first worklist until it is empty. Because it is a std::list - // it acts like a FIFO queue. - while (!WL1.empty()) { - const ExplodedNode *N = WL1.back(); - WL1.pop_back(); - - // Have we already visited this node? If so, continue to the next one. - if (Pass1.count(N)) - continue; - - // Otherwise, mark this node as visited. - Pass1.insert(N); - - // If this is a root enqueue it to the second worklist. - if (N->Preds.empty()) { - WL2.push_back(N); - continue; - } - - // Visit our predecessors and enqueue them. - for (ExplodedNode** I=N->Preds.begin(), **E=N->Preds.end(); I!=E; ++I) - WL1.push_back(*I); - } - - // We didn't hit a root? Return with a null pointer for the new graph. - if (WL2.empty()) - return 0; - - // Create an empty graph. - ExplodedGraph* G = MakeEmptyGraph(); - - // ===- Pass 2 (forward DFS to construct the new graph) -=== - while (!WL2.empty()) { - const ExplodedNode* N = WL2.back(); - WL2.pop_back(); - - // Skip this node if we have already processed it. - if (Pass2.find(N) != Pass2.end()) - continue; - - // Create the corresponding node in the new graph and record the mapping - // from the old node to the new node. - ExplodedNode* NewN = G->getNode(N->getLocation(), N->State, NULL); - Pass2[N] = NewN; - - // Also record the reverse mapping from the new node to the old node. - if (InverseMap) (*InverseMap)[NewN] = N; - - // If this node is a root, designate it as such in the graph. - if (N->Preds.empty()) - G->addRoot(NewN); - - // In the case that some of the intended predecessors of NewN have already - // been created, we should hook them up as predecessors. - - // Walk through the predecessors of 'N' and hook up their corresponding - // nodes in the new graph (if any) to the freshly created node. - for (ExplodedNode **I=N->Preds.begin(), **E=N->Preds.end(); I!=E; ++I) { - Pass2Ty::iterator PI = Pass2.find(*I); - if (PI == Pass2.end()) - continue; - - NewN->addPredecessor(PI->second, *G); - } - - // In the case that some of the intended successors of NewN have already - // been created, we should hook them up as successors. Otherwise, enqueue - // the new nodes from the original graph that should have nodes created - // in the new graph. - for (ExplodedNode **I=N->Succs.begin(), **E=N->Succs.end(); I!=E; ++I) { - Pass2Ty::iterator PI = Pass2.find(*I); - if (PI != Pass2.end()) { - PI->second->addPredecessor(NewN, *G); - continue; - } - - // Enqueue nodes to the worklist that were marked during pass 1. - if (Pass1.count(*I)) - WL2.push_back(*I); - } - - // Finally, explictly mark all nodes without any successors as sinks. - if (N->isSink()) - NewN->markAsSink(); - } - - return G; -} - -ExplodedNode* -InterExplodedGraphMap::getMappedNode(const ExplodedNode* N) const { - llvm::DenseMap::const_iterator I = - M.find(N); - - return I == M.end() ? 0 : I->second; -} - diff --git a/lib/Analysis/FixedAddressChecker.cpp b/lib/Analysis/FixedAddressChecker.cpp deleted file mode 100644 index 031ca44b602e..000000000000 --- a/lib/Analysis/FixedAddressChecker.cpp +++ /dev/null @@ -1,70 +0,0 @@ -//=== FixedAddressChecker.cpp - Fixed address usage checker ----*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This files defines FixedAddressChecker, a builtin checker that checks for -// assignment of a fixed address to a pointer. -// This check corresponds to CWE-587. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class FixedAddressChecker - : public CheckerVisitor { - BuiltinBug *BT; -public: - FixedAddressChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; -} - -void *FixedAddressChecker::getTag() { - static int x; - return &x; -} - -void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - // Using a fixed address is not portable because that address will probably - // not be valid in all environments or platforms. - - if (B->getOpcode() != BinaryOperator::Assign) - return; - - QualType T = B->getType(); - if (!T->isPointerType()) - return; - - const GRState *state = C.getState(); - - SVal RV = state->getSVal(B->getRHS()); - - if (!RV.isConstant() || RV.isZeroConstant()) - return; - - if (ExplodedNode *N = C.GenerateNode()) { - if (!BT) - BT = new BuiltinBug("Use fixed address", - "Using a fixed address is not portable because that " - "address will probably not be valid in all " - "environments or platforms."); - RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); - R->addRange(B->getRHS()->getSourceRange()); - C.EmitReport(R); - } -} - -void clang::RegisterFixedAddressChecker(GRExprEngine &Eng) { - Eng.registerCheck(new FixedAddressChecker()); -} diff --git a/lib/Analysis/GRBlockCounter.cpp b/lib/Analysis/GRBlockCounter.cpp deleted file mode 100644 index 4f4103ac45b4..000000000000 --- a/lib/Analysis/GRBlockCounter.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//==- GRBlockCounter.h - ADT for counting block visits -------------*- C++ -*-// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines GRBlockCounter, an abstract data type used to count -// the number of times a given block has been visited along a path -// analyzed by GRCoreEngine. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/GRBlockCounter.h" -#include "llvm/ADT/ImmutableMap.h" - -using namespace clang; - -typedef llvm::ImmutableMap CountMap; - -static inline CountMap GetMap(void* D) { - return CountMap(static_cast(D)); -} - -static inline CountMap::Factory& GetFactory(void* F) { - return *static_cast(F); -} - -unsigned GRBlockCounter::getNumVisited(unsigned BlockID) const { - CountMap M = GetMap(Data); - CountMap::data_type* T = M.lookup(BlockID); - return T ? *T : 0; -} - -GRBlockCounter::Factory::Factory(llvm::BumpPtrAllocator& Alloc) { - F = new CountMap::Factory(Alloc); -} - -GRBlockCounter::Factory::~Factory() { - delete static_cast(F); -} - -GRBlockCounter -GRBlockCounter::Factory::IncrementCount(GRBlockCounter BC, unsigned BlockID) { - return GRBlockCounter(GetFactory(F).Add(GetMap(BC.Data), BlockID, - BC.getNumVisited(BlockID)+1).getRoot()); -} - -GRBlockCounter -GRBlockCounter::Factory::GetEmptyCounter() { - return GRBlockCounter(GetFactory(F).GetEmptyMap().getRoot()); -} diff --git a/lib/Analysis/GRCoreEngine.cpp b/lib/Analysis/GRCoreEngine.cpp deleted file mode 100644 index 209452a3927a..000000000000 --- a/lib/Analysis/GRCoreEngine.cpp +++ /dev/null @@ -1,599 +0,0 @@ -//==- GRCoreEngine.cpp - Path-Sensitive Dataflow Engine ------------*- C++ -*-// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a generic engine for intraprocedural, path-sensitive, -// dataflow analysis via graph reachability engine. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/GRCoreEngine.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/AST/Expr.h" -#include "llvm/Support/Casting.h" -#include "llvm/ADT/DenseMap.h" -#include -#include - -using llvm::cast; -using llvm::isa; -using namespace clang; - -//===----------------------------------------------------------------------===// -// Worklist classes for exploration of reachable states. -//===----------------------------------------------------------------------===// - -namespace { -class DFS : public GRWorkList { - llvm::SmallVector Stack; -public: - virtual bool hasWork() const { - return !Stack.empty(); - } - - virtual void Enqueue(const GRWorkListUnit& U) { - Stack.push_back(U); - } - - virtual GRWorkListUnit Dequeue() { - assert (!Stack.empty()); - const GRWorkListUnit& U = Stack.back(); - Stack.pop_back(); // This technically "invalidates" U, but we are fine. - return U; - } -}; - -class BFS : public GRWorkList { - std::queue Queue; -public: - virtual bool hasWork() const { - return !Queue.empty(); - } - - virtual void Enqueue(const GRWorkListUnit& U) { - Queue.push(U); - } - - virtual GRWorkListUnit Dequeue() { - // Don't use const reference. The subsequent pop_back() might make it - // unsafe. - GRWorkListUnit U = Queue.front(); - Queue.pop(); - return U; - } -}; - -} // end anonymous namespace - -// Place the dstor for GRWorkList here because it contains virtual member -// functions, and we the code for the dstor generated in one compilation unit. -GRWorkList::~GRWorkList() {} - -GRWorkList *GRWorkList::MakeDFS() { return new DFS(); } -GRWorkList *GRWorkList::MakeBFS() { return new BFS(); } - -namespace { - class BFSBlockDFSContents : public GRWorkList { - std::queue Queue; - llvm::SmallVector Stack; - public: - virtual bool hasWork() const { - return !Queue.empty() || !Stack.empty(); - } - - virtual void Enqueue(const GRWorkListUnit& U) { - if (isa(U.getNode()->getLocation())) - Queue.push(U); - else - Stack.push_back(U); - } - - virtual GRWorkListUnit Dequeue() { - // Process all basic blocks to completion. - if (!Stack.empty()) { - const GRWorkListUnit& U = Stack.back(); - Stack.pop_back(); // This technically "invalidates" U, but we are fine. - return U; - } - - assert(!Queue.empty()); - // Don't use const reference. The subsequent pop_back() might make it - // unsafe. - GRWorkListUnit U = Queue.front(); - Queue.pop(); - return U; - } - }; -} // end anonymous namespace - -GRWorkList* GRWorkList::MakeBFSBlockDFSContents() { - return new BFSBlockDFSContents(); -} - -//===----------------------------------------------------------------------===// -// Core analysis engine. -//===----------------------------------------------------------------------===// -void GRCoreEngine::ProcessEndPath(GREndPathNodeBuilder& Builder) { - SubEngine.ProcessEndPath(Builder); -} - -void GRCoreEngine::ProcessStmt(CFGElement E, GRStmtNodeBuilder& Builder) { - SubEngine.ProcessStmt(E, Builder); -} - -bool GRCoreEngine::ProcessBlockEntrance(CFGBlock* Blk, const GRState* State, - GRBlockCounter BC) { - return SubEngine.ProcessBlockEntrance(Blk, State, BC); -} - -void GRCoreEngine::ProcessBranch(Stmt* Condition, Stmt* Terminator, - GRBranchNodeBuilder& Builder) { - SubEngine.ProcessBranch(Condition, Terminator, Builder); -} - -void GRCoreEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& Builder) { - SubEngine.ProcessIndirectGoto(Builder); -} - -void GRCoreEngine::ProcessSwitch(GRSwitchNodeBuilder& Builder) { - SubEngine.ProcessSwitch(Builder); -} - -/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. -bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) { - - if (G->num_roots() == 0) { // Initialize the analysis by constructing - // the root if none exists. - - CFGBlock* Entry = &(L->getCFG()->getEntry()); - - assert (Entry->empty() && - "Entry block must be empty."); - - assert (Entry->succ_size() == 1 && - "Entry block must have 1 successor."); - - // Get the solitary successor. - CFGBlock* Succ = *(Entry->succ_begin()); - - // Construct an edge representing the - // starting location in the function. - BlockEdge StartLoc(Entry, Succ, L); - - // Set the current block counter to being empty. - WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); - - // Generate the root. - GenerateNode(StartLoc, getInitialState(L), 0); - } - - while (Steps && WList->hasWork()) { - --Steps; - const GRWorkListUnit& WU = WList->Dequeue(); - - // Set the current block counter. - WList->setBlockCounter(WU.getBlockCounter()); - - // Retrieve the node. - ExplodedNode* Node = WU.getNode(); - - // Dispatch on the location type. - switch (Node->getLocation().getKind()) { - case ProgramPoint::BlockEdgeKind: - HandleBlockEdge(cast(Node->getLocation()), Node); - break; - - case ProgramPoint::BlockEntranceKind: - HandleBlockEntrance(cast(Node->getLocation()), Node); - break; - - case ProgramPoint::BlockExitKind: - assert (false && "BlockExit location never occur in forward analysis."); - break; - - default: - assert(isa(Node->getLocation())); - HandlePostStmt(cast(Node->getLocation()), WU.getBlock(), - WU.getIndex(), Node); - break; - } - } - - return WList->hasWork(); -} - - -void GRCoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { - - CFGBlock* Blk = L.getDst(); - - // Check if we are entering the EXIT block. - if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { - - assert (L.getLocationContext()->getCFG()->getExit().size() == 0 - && "EXIT block cannot contain Stmts."); - - // Process the final state transition. - GREndPathNodeBuilder Builder(Blk, Pred, this); - ProcessEndPath(Builder); - - // This path is done. Don't enqueue any more nodes. - return; - } - - // FIXME: Should we allow ProcessBlockEntrance to also manipulate state? - - if (ProcessBlockEntrance(Blk, Pred->State, WList->getBlockCounter())) - GenerateNode(BlockEntrance(Blk, Pred->getLocationContext()), Pred->State, Pred); -} - -void GRCoreEngine::HandleBlockEntrance(const BlockEntrance& L, - ExplodedNode* Pred) { - - // Increment the block counter. - GRBlockCounter Counter = WList->getBlockCounter(); - Counter = BCounterFactory.IncrementCount(Counter, L.getBlock()->getBlockID()); - WList->setBlockCounter(Counter); - - // Process the entrance of the block. - if (CFGElement E = L.getFirstElement()) { - GRStmtNodeBuilder Builder(L.getBlock(), 0, Pred, this, - SubEngine.getStateManager()); - ProcessStmt(E, Builder); - } - else - HandleBlockExit(L.getBlock(), Pred); -} - -void GRCoreEngine::HandleBlockExit(CFGBlock * B, ExplodedNode* Pred) { - - if (Stmt* Term = B->getTerminator()) { - switch (Term->getStmtClass()) { - default: - assert(false && "Analysis for this terminator not implemented."); - break; - - case Stmt::BinaryOperatorClass: // '&&' and '||' - HandleBranch(cast(Term)->getLHS(), Term, B, Pred); - return; - - case Stmt::ConditionalOperatorClass: - HandleBranch(cast(Term)->getCond(), Term, B, Pred); - return; - - // FIXME: Use constant-folding in CFG construction to simplify this - // case. - - case Stmt::ChooseExprClass: - HandleBranch(cast(Term)->getCond(), Term, B, Pred); - return; - - case Stmt::DoStmtClass: - HandleBranch(cast(Term)->getCond(), Term, B, Pred); - return; - - case Stmt::ForStmtClass: - HandleBranch(cast(Term)->getCond(), Term, B, Pred); - return; - - case Stmt::ContinueStmtClass: - case Stmt::BreakStmtClass: - case Stmt::GotoStmtClass: - break; - - case Stmt::IfStmtClass: - HandleBranch(cast(Term)->getCond(), Term, B, Pred); - return; - - case Stmt::IndirectGotoStmtClass: { - // Only 1 successor: the indirect goto dispatch block. - assert (B->succ_size() == 1); - - GRIndirectGotoNodeBuilder - builder(Pred, B, cast(Term)->getTarget(), - *(B->succ_begin()), this); - - ProcessIndirectGoto(builder); - return; - } - - case Stmt::ObjCForCollectionStmtClass: { - // In the case of ObjCForCollectionStmt, it appears twice in a CFG: - // - // (1) inside a basic block, which represents the binding of the - // 'element' variable to a value. - // (2) in a terminator, which represents the branch. - // - // For (1), subengines will bind a value (i.e., 0 or 1) indicating - // whether or not collection contains any more elements. We cannot - // just test to see if the element is nil because a container can - // contain nil elements. - HandleBranch(Term, Term, B, Pred); - return; - } - - case Stmt::SwitchStmtClass: { - GRSwitchNodeBuilder builder(Pred, B, cast(Term)->getCond(), - this); - - ProcessSwitch(builder); - return; - } - - case Stmt::WhileStmtClass: - HandleBranch(cast(Term)->getCond(), Term, B, Pred); - return; - } - } - - assert (B->succ_size() == 1 && - "Blocks with no terminator should have at most 1 successor."); - - GenerateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()), - Pred->State, Pred); -} - -void GRCoreEngine::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B, - ExplodedNode* Pred) { - assert (B->succ_size() == 2); - - GRBranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), - Pred, this); - - ProcessBranch(Cond, Term, Builder); -} - -void GRCoreEngine::HandlePostStmt(const PostStmt& L, CFGBlock* B, - unsigned StmtIdx, ExplodedNode* Pred) { - - assert (!B->empty()); - - if (StmtIdx == B->size()) - HandleBlockExit(B, Pred); - else { - GRStmtNodeBuilder Builder(B, StmtIdx, Pred, this, - SubEngine.getStateManager()); - ProcessStmt((*B)[StmtIdx], Builder); - } -} - -/// GenerateNode - Utility method to generate nodes, hook up successors, -/// and add nodes to the worklist. -void GRCoreEngine::GenerateNode(const ProgramPoint& Loc, - const GRState* State, ExplodedNode* Pred) { - - bool IsNew; - ExplodedNode* Node = G->getNode(Loc, State, &IsNew); - - if (Pred) - Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. - else { - assert (IsNew); - G->addRoot(Node); // 'Node' has no predecessor. Make it a root. - } - - // Only add 'Node' to the worklist if it was freshly generated. - if (IsNew) WList->Enqueue(Node); -} - -GRStmtNodeBuilder::GRStmtNodeBuilder(CFGBlock* b, unsigned idx, - ExplodedNode* N, GRCoreEngine* e, - GRStateManager &mgr) - : Eng(*e), B(*b), Idx(idx), Pred(N), LastNode(N), Mgr(mgr), Auditor(0), - PurgingDeadSymbols(false), BuildSinks(false), HasGeneratedNode(false), - PointKind(ProgramPoint::PostStmtKind), Tag(0) { - Deferred.insert(N); - CleanedState = getLastNode()->getState(); -} - -GRStmtNodeBuilder::~GRStmtNodeBuilder() { - for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) - if (!(*I)->isSink()) - GenerateAutoTransition(*I); -} - -void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { - assert (!N->isSink()); - - PostStmt Loc(getStmt(), N->getLocationContext()); - - if (Loc == N->getLocation()) { - // Note: 'N' should be a fresh node because otherwise it shouldn't be - // a member of Deferred. - Eng.WList->Enqueue(N, B, Idx+1); - return; - } - - bool IsNew; - ExplodedNode* Succ = Eng.G->getNode(Loc, N->State, &IsNew); - Succ->addPredecessor(N, *Eng.G); - - if (IsNew) - Eng.WList->Enqueue(Succ, B, Idx+1); -} - -static ProgramPoint GetProgramPoint(const Stmt *S, ProgramPoint::Kind K, - const LocationContext *LC, const void *tag){ - switch (K) { - default: - assert(false && "Unhandled ProgramPoint kind"); - case ProgramPoint::PreStmtKind: - return PreStmt(S, LC, tag); - case ProgramPoint::PostStmtKind: - return PostStmt(S, LC, tag); - case ProgramPoint::PreLoadKind: - return PreLoad(S, LC, tag); - case ProgramPoint::PostLoadKind: - return PostLoad(S, LC, tag); - case ProgramPoint::PreStoreKind: - return PreStore(S, LC, tag); - case ProgramPoint::PostStoreKind: - return PostStore(S, LC, tag); - case ProgramPoint::PostLValueKind: - return PostLValue(S, LC, tag); - case ProgramPoint::PostPurgeDeadSymbolsKind: - return PostPurgeDeadSymbols(S, LC, tag); - } -} - -ExplodedNode* -GRStmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* state, - ExplodedNode* Pred, - ProgramPoint::Kind K, - const void *tag) { - - const ProgramPoint &L = GetProgramPoint(S, K, Pred->getLocationContext(),tag); - return generateNodeInternal(L, state, Pred); -} - -ExplodedNode* -GRStmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, - const GRState* State, - ExplodedNode* Pred) { - bool IsNew; - ExplodedNode* N = Eng.G->getNode(Loc, State, &IsNew); - N->addPredecessor(Pred, *Eng.G); - Deferred.erase(Pred); - - if (IsNew) { - Deferred.insert(N); - LastNode = N; - return N; - } - - LastNode = NULL; - return NULL; -} - -ExplodedNode* GRBranchNodeBuilder::generateNode(const GRState* State, - bool branch) { - - // If the branch has been marked infeasible we should not generate a node. - if (!isFeasible(branch)) - return NULL; - - bool IsNew; - - ExplodedNode* Succ = - Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), - State, &IsNew); - - Succ->addPredecessor(Pred, *Eng.G); - - if (branch) - GeneratedTrue = true; - else - GeneratedFalse = true; - - if (IsNew) { - Deferred.push_back(Succ); - return Succ; - } - - return NULL; -} - -GRBranchNodeBuilder::~GRBranchNodeBuilder() { - if (!GeneratedTrue) generateNode(Pred->State, true); - if (!GeneratedFalse) generateNode(Pred->State, false); - - for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) - if (!(*I)->isSink()) Eng.WList->Enqueue(*I); -} - - -ExplodedNode* -GRIndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, - bool isSink) { - bool IsNew; - - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), St, &IsNew); - - Succ->addPredecessor(Pred, *Eng.G); - - if (IsNew) { - - if (isSink) - Succ->markAsSink(); - else - Eng.WList->Enqueue(Succ); - - return Succ; - } - - return NULL; -} - - -ExplodedNode* -GRSwitchNodeBuilder::generateCaseStmtNode(const iterator& I, const GRState* St){ - - bool IsNew; - - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), St, &IsNew); - Succ->addPredecessor(Pred, *Eng.G); - - if (IsNew) { - Eng.WList->Enqueue(Succ); - return Succ; - } - - return NULL; -} - - -ExplodedNode* -GRSwitchNodeBuilder::generateDefaultCaseNode(const GRState* St, bool isSink) { - - // Get the block for the default case. - assert (Src->succ_rbegin() != Src->succ_rend()); - CFGBlock* DefaultBlock = *Src->succ_rbegin(); - - bool IsNew; - - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, - Pred->getLocationContext()), St, &IsNew); - Succ->addPredecessor(Pred, *Eng.G); - - if (IsNew) { - if (isSink) - Succ->markAsSink(); - else - Eng.WList->Enqueue(Succ); - - return Succ; - } - - return NULL; -} - -GREndPathNodeBuilder::~GREndPathNodeBuilder() { - // Auto-generate an EOP node if one has not been generated. - if (!HasGeneratedNode) generateNode(Pred->State); -} - -ExplodedNode* -GREndPathNodeBuilder::generateNode(const GRState* State, const void *tag, - ExplodedNode* P) { - HasGeneratedNode = true; - bool IsNew; - - ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B, - Pred->getLocationContext(), tag), State, &IsNew); - - Node->addPredecessor(P ? P : Pred, *Eng.G); - - if (IsNew) { - Eng.G->addEndOfPath(Node); - return Node; - } - - return NULL; -} diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp deleted file mode 100644 index 8f8d859e0ca0..000000000000 --- a/lib/Analysis/GRExprEngine.cpp +++ /dev/null @@ -1,3325 +0,0 @@ -//=-- GRExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- C++ -*-= -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a meta-engine for path-sensitive dataflow analysis that -// is built on GREngine, but provides the boilerplate to execute transfer -// functions and build the ExplodedGraph at the expression level. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/GRExprEngineBuilders.h" -#include "clang/Analysis/PathSensitive/Checker.h" -#include "clang/AST/CharUnits.h" -#include "clang/AST/ParentMap.h" -#include "clang/AST/StmtObjC.h" -#include "clang/Basic/Builtins.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Basic/PrettyStackTrace.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/ADT/ImmutableList.h" -#include "llvm/ADT/StringSwitch.h" - -#ifndef NDEBUG -#include "llvm/Support/GraphWriter.h" -#endif - -using namespace clang; -using llvm::dyn_cast; -using llvm::dyn_cast_or_null; -using llvm::cast; -using llvm::APSInt; - -//===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { - IdentifierInfo* II = &Ctx.Idents.get(name); - return Ctx.Selectors.getSelector(0, &II); -} - - -static QualType GetCalleeReturnType(const CallExpr *CE) { - const Expr *Callee = CE->getCallee(); - QualType T = Callee->getType(); - if (const PointerType *PT = T->getAs()) { - const FunctionType *FT = PT->getPointeeType()->getAs(); - T = FT->getResultType(); - } - else { - const BlockPointerType *BT = T->getAs(); - T = BT->getPointeeType()->getAs()->getResultType(); - } - return T; -} - -static bool CalleeReturnsReference(const CallExpr *CE) { - return (bool) GetCalleeReturnType(CE)->getAs(); -} - -static bool ReceiverReturnsReference(const ObjCMessageExpr *ME) { - const ObjCMethodDecl *MD = ME->getMethodDecl(); - if (!MD) - return false; - return MD->getResultType()->getAs(); -} - -#ifndef NDEBUG -static bool ReceiverReturnsReferenceOrRecord(const ObjCMessageExpr *ME) { - const ObjCMethodDecl *MD = ME->getMethodDecl(); - if (!MD) - return false; - QualType T = MD->getResultType(); - return T->getAs() || T->getAs(); -} - -static bool CalleeReturnsReferenceOrRecord(const CallExpr *CE) { - QualType T = GetCalleeReturnType(CE); - return T->getAs() || T->getAs(); -} -#endif - -//===----------------------------------------------------------------------===// -// Batch auditor. DEPRECATED. -//===----------------------------------------------------------------------===// - -namespace { - -class MappedBatchAuditor : public GRSimpleAPICheck { - typedef llvm::ImmutableList Checks; - typedef llvm::DenseMap MapTy; - - MapTy M; - Checks::Factory F; - Checks AllStmts; - -public: - MappedBatchAuditor(llvm::BumpPtrAllocator& Alloc) : - F(Alloc), AllStmts(F.GetEmptyList()) {} - - virtual ~MappedBatchAuditor() { - llvm::DenseSet AlreadyVisited; - - for (MapTy::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI) - for (Checks::iterator I=MI->second.begin(), E=MI->second.end(); I!=E;++I){ - - GRSimpleAPICheck* check = *I; - - if (AlreadyVisited.count(check)) - continue; - - AlreadyVisited.insert(check); - delete check; - } - } - - void AddCheck(GRSimpleAPICheck *A, Stmt::StmtClass C) { - assert (A && "Check cannot be null."); - void* key = reinterpret_cast((uintptr_t) C); - MapTy::iterator I = M.find(key); - M[key] = F.Concat(A, I == M.end() ? F.GetEmptyList() : I->second); - } - - void AddCheck(GRSimpleAPICheck *A) { - assert (A && "Check cannot be null."); - AllStmts = F.Concat(A, AllStmts); - } - - virtual bool Audit(ExplodedNode* N, GRStateManager& VMgr) { - // First handle the auditors that accept all statements. - bool isSink = false; - for (Checks::iterator I = AllStmts.begin(), E = AllStmts.end(); I!=E; ++I) - isSink |= (*I)->Audit(N, VMgr); - - // Next handle the auditors that accept only specific statements. - const Stmt* S = cast(N->getLocation()).getStmt(); - void* key = reinterpret_cast((uintptr_t) S->getStmtClass()); - MapTy::iterator MI = M.find(key); - if (MI != M.end()) { - for (Checks::iterator I=MI->second.begin(), E=MI->second.end(); I!=E; ++I) - isSink |= (*I)->Audit(N, VMgr); - } - - return isSink; - } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Checker worklist routines. -//===----------------------------------------------------------------------===// - -void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, - ExplodedNodeSet &Src, bool isPrevisit) { - - if (Checkers.empty()) { - Dst.insert(Src); - return; - } - - ExplodedNodeSet Tmp; - ExplodedNodeSet *PrevSet = &Src; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ - ExplodedNodeSet *CurrSet = 0; - if (I+1 == E) - CurrSet = &Dst; - else { - CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; - CurrSet->clear(); - } - void *tag = I->first; - Checker *checker = I->second; - - for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) - checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit); - PrevSet = CurrSet; - } - - // Don't autotransition. The CheckerContext objects should do this - // automatically. -} - -void GRExprEngine::CheckerEvalNilReceiver(const ObjCMessageExpr *ME, - ExplodedNodeSet &Dst, - const GRState *state, - ExplodedNode *Pred) { - bool Evaluated = false; - ExplodedNodeSet DstTmp; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { - void *tag = I->first; - Checker *checker = I->second; - - if (checker->GR_EvalNilReceiver(DstTmp, *Builder, *this, ME, Pred, state, - tag)) { - Evaluated = true; - break; - } else - // The checker didn't evaluate the expr. Restore the Dst. - DstTmp.clear(); - } - - if (Evaluated) - Dst.insert(DstTmp); - else - Dst.insert(Pred); -} - -// CheckerEvalCall returns true if one of the checkers processed the node. -// This may return void when all call evaluation logic goes to some checker -// in the future. -bool GRExprEngine::CheckerEvalCall(const CallExpr *CE, - ExplodedNodeSet &Dst, - ExplodedNode *Pred) { - bool Evaluated = false; - ExplodedNodeSet DstTmp; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { - void *tag = I->first; - Checker *checker = I->second; - - if (checker->GR_EvalCallExpr(DstTmp, *Builder, *this, CE, Pred, tag)) { - Evaluated = true; - break; - } else - // The checker didn't evaluate the expr. Restore the DstTmp set. - DstTmp.clear(); - } - - if (Evaluated) - Dst.insert(DstTmp); - else - Dst.insert(Pred); - - return Evaluated; -} - -// FIXME: This is largely copy-paste from CheckerVisit(). Need to -// unify. -void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE, - ExplodedNodeSet &Dst, - ExplodedNodeSet &Src, - SVal location, SVal val, bool isPrevisit) { - - if (Checkers.empty()) { - Dst.insert(Src); - return; - } - - ExplodedNodeSet Tmp; - ExplodedNodeSet *PrevSet = &Src; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) - { - ExplodedNodeSet *CurrSet = 0; - if (I+1 == E) - CurrSet = &Dst; - else { - CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; - CurrSet->clear(); - } - - void *tag = I->first; - Checker *checker = I->second; - - for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) - checker->GR_VisitBind(*CurrSet, *Builder, *this, AssignE, StoreE, - *NI, tag, location, val, isPrevisit); - - // Update which NodeSet is the current one. - PrevSet = CurrSet; - } - - // Don't autotransition. The CheckerContext objects should do this - // automatically. -} -//===----------------------------------------------------------------------===// -// Engine construction and deletion. -//===----------------------------------------------------------------------===// - -static void RegisterInternalChecks(GRExprEngine &Eng) { - // Register internal "built-in" BugTypes with the BugReporter. These BugTypes - // are different than what probably many checks will do since they don't - // create BugReports on-the-fly but instead wait until GRExprEngine finishes - // analyzing a function. Generation of BugReport objects is done via a call - // to 'FlushReports' from BugReporter. - // The following checks do not need to have their associated BugTypes - // explicitly registered with the BugReporter. If they issue any BugReports, - // their associated BugType will get registered with the BugReporter - // automatically. Note that the check itself is owned by the GRExprEngine - // object. - RegisterAttrNonNullChecker(Eng); - RegisterCallAndMessageChecker(Eng); - RegisterDereferenceChecker(Eng); - RegisterVLASizeChecker(Eng); - RegisterDivZeroChecker(Eng); - RegisterReturnStackAddressChecker(Eng); - RegisterReturnUndefChecker(Eng); - RegisterUndefinedArraySubscriptChecker(Eng); - RegisterUndefinedAssignmentChecker(Eng); - RegisterUndefBranchChecker(Eng); - RegisterUndefResultChecker(Eng); - - // This is not a checker yet. - RegisterNoReturnFunctionChecker(Eng); - RegisterBuiltinFunctionChecker(Eng); - RegisterOSAtomicChecker(Eng); -} - -GRExprEngine::GRExprEngine(AnalysisManager &mgr, GRTransferFuncs *tf) - : AMgr(mgr), - CoreEngine(mgr.getASTContext(), *this), - G(CoreEngine.getGraph()), - Builder(NULL), - StateMgr(G.getContext(), mgr.getStoreManagerCreator(), - mgr.getConstraintManagerCreator(), G.getAllocator(), - *this), - SymMgr(StateMgr.getSymbolManager()), - ValMgr(StateMgr.getValueManager()), - SVator(ValMgr.getSValuator()), - CurrentStmt(NULL), - NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), - RaiseSel(GetNullarySelector("raise", G.getContext())), - BR(mgr, *this), TF(tf) { - // Register internal checks. - RegisterInternalChecks(*this); - - // FIXME: Eventually remove the TF object entirely. - TF->RegisterChecks(*this); - TF->RegisterPrinters(getStateManager().Printers); -} - -GRExprEngine::~GRExprEngine() { - BR.FlushReports(); - delete [] NSExceptionInstanceRaiseSelectors; - for (CheckersOrdered::iterator I=Checkers.begin(), E=Checkers.end(); I!=E;++I) - delete I->second; -} - -//===----------------------------------------------------------------------===// -// Utility methods. -//===----------------------------------------------------------------------===// - -void GRExprEngine::AddCheck(GRSimpleAPICheck* A, Stmt::StmtClass C) { - if (!BatchAuditor) - BatchAuditor.reset(new MappedBatchAuditor(getGraph().getAllocator())); - - ((MappedBatchAuditor*) BatchAuditor.get())->AddCheck(A, C); -} - -void GRExprEngine::AddCheck(GRSimpleAPICheck *A) { - if (!BatchAuditor) - BatchAuditor.reset(new MappedBatchAuditor(getGraph().getAllocator())); - - ((MappedBatchAuditor*) BatchAuditor.get())->AddCheck(A); -} - -const GRState* GRExprEngine::getInitialState(const LocationContext *InitLoc) { - const GRState *state = StateMgr.getInitialState(InitLoc); - - // Preconditions. - - // FIXME: It would be nice if we had a more general mechanism to add - // such preconditions. Some day. - do { - const Decl *D = InitLoc->getDecl(); - if (const FunctionDecl *FD = dyn_cast(D)) { - // Precondition: the first argument of 'main' is an integer guaranteed - // to be > 0. - const IdentifierInfo *II = FD->getIdentifier(); - if (!II || !(II->getName() == "main" && FD->getNumParams() > 0)) - break; - - const ParmVarDecl *PD = FD->getParamDecl(0); - QualType T = PD->getType(); - if (!T->isIntegerType()) - break; - - const MemRegion *R = state->getRegion(PD, InitLoc); - if (!R) - break; - - SVal V = state->getSVal(loc::MemRegionVal(R)); - SVal Constraint_untested = EvalBinOp(state, BinaryOperator::GT, V, - ValMgr.makeZeroVal(T), - getContext().IntTy); - - DefinedOrUnknownSVal *Constraint = - dyn_cast(&Constraint_untested); - - if (!Constraint) - break; - - if (const GRState *newState = state->Assume(*Constraint, true)) - state = newState; - - break; - } - - if (const ObjCMethodDecl *MD = dyn_cast(D)) { - // Precondition: 'self' is always non-null upon entry to an Objective-C - // method. - const ImplicitParamDecl *SelfD = MD->getSelfDecl(); - const MemRegion *R = state->getRegion(SelfD, InitLoc); - SVal V = state->getSVal(loc::MemRegionVal(R)); - - if (const Loc *LV = dyn_cast(&V)) { - // Assume that the pointer value in 'self' is non-null. - state = state->Assume(*LV, true); - assert(state && "'self' cannot be null"); - } - } - } while (0); - - return state; -} - -//===----------------------------------------------------------------------===// -// Top-level transfer function logic (Dispatcher). -//===----------------------------------------------------------------------===// - -/// EvalAssume - Called by ConstraintManager. Used to call checker-specific -/// logic for handling assumptions on symbolic values. -const GRState *GRExprEngine::ProcessAssume(const GRState *state, SVal cond, - bool assumption) { - for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); - I != E; ++I) { - - if (!state) - return NULL; - - state = I->second->EvalAssume(state, cond, assumption); - } - - if (!state) - return NULL; - - return TF->EvalAssume(state, cond, assumption); -} - -void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) { - CurrentStmt = CE.getStmt(); - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - CurrentStmt->getLocStart(), - "Error evaluating statement"); - - Builder = &builder; - EntryNode = builder.getLastNode(); - - // Set up our simple checks. - if (BatchAuditor) - Builder->setAuditor(BatchAuditor.get()); - - // Create the cleaned state. - const ExplodedNode *BasePred = Builder->getBasePredecessor(); - SymbolReaper SymReaper(BasePred->getLiveVariables(), SymMgr, - BasePred->getLocationContext()->getCurrentStackFrame()); - CleanedState = AMgr.shouldPurgeDead() - ? StateMgr.RemoveDeadBindings(EntryNode->getState(), CurrentStmt, SymReaper) - : EntryNode->getState(); - - // Process any special transfer function for dead symbols. - ExplodedNodeSet Tmp; - - if (!SymReaper.hasDeadSymbols()) - Tmp.Add(EntryNode); - else { - SaveAndRestore OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->HasGeneratedNode); - - SaveAndRestore OldPurgeDeadSymbols(Builder->PurgingDeadSymbols); - Builder->PurgingDeadSymbols = true; - - // FIXME: This should soon be removed. - ExplodedNodeSet Tmp2; - getTF().EvalDeadSymbols(Tmp2, *this, *Builder, EntryNode, CurrentStmt, - CleanedState, SymReaper); - - if (Checkers.empty()) - Tmp.insert(Tmp2); - else { - ExplodedNodeSet Tmp3; - ExplodedNodeSet *SrcSet = &Tmp2; - for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); - I != E; ++I) { - ExplodedNodeSet *DstSet = 0; - if (I+1 == E) - DstSet = &Tmp; - else { - DstSet = (SrcSet == &Tmp2) ? &Tmp3 : &Tmp2; - DstSet->clear(); - } - - void *tag = I->first; - Checker *checker = I->second; - for (ExplodedNodeSet::iterator NI = SrcSet->begin(), NE = SrcSet->end(); - NI != NE; ++NI) - checker->GR_EvalDeadSymbols(*DstSet, *Builder, *this, CurrentStmt, - *NI, SymReaper, tag); - SrcSet = DstSet; - } - } - - if (!Builder->BuildSinks && !Builder->HasGeneratedNode) - Tmp.Add(EntryNode); - } - - bool HasAutoGenerated = false; - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - - ExplodedNodeSet Dst; - - // Set the cleaned state. - Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I)); - - // Visit the statement. - if (CE.asLValue()) - VisitLValue(cast(CurrentStmt), *I, Dst); - else - Visit(CurrentStmt, *I, Dst); - - // Do we need to auto-generate a node? We only need to do this to generate - // a node with a "cleaned" state; GRCoreEngine will actually handle - // auto-transitions for other cases. - if (Dst.size() == 1 && *Dst.begin() == EntryNode - && !Builder->HasGeneratedNode && !HasAutoGenerated) { - HasAutoGenerated = true; - builder.generateNode(CurrentStmt, GetState(EntryNode), *I); - } - } - - // NULL out these variables to cleanup. - CleanedState = NULL; - EntryNode = NULL; - - CurrentStmt = 0; - - Builder = NULL; -} - -void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - S->getLocStart(), - "Error evaluating statement"); - - // FIXME: add metadata to the CFG so that we can disable - // this check when we KNOW that there is no block-level subexpression. - // The motivation is that this check requires a hashtable lookup. - - if (S != CurrentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) { - Dst.Add(Pred); - return; - } - - switch (S->getStmtClass()) { - // C++ stuff we don't support yet. - case Stmt::CXXMemberCallExprClass: - case Stmt::CXXNamedCastExprClass: - case Stmt::CXXStaticCastExprClass: - case Stmt::CXXDynamicCastExprClass: - case Stmt::CXXReinterpretCastExprClass: - case Stmt::CXXConstCastExprClass: - case Stmt::CXXFunctionalCastExprClass: - case Stmt::CXXTypeidExprClass: - case Stmt::CXXBoolLiteralExprClass: - case Stmt::CXXNullPtrLiteralExprClass: - case Stmt::CXXThrowExprClass: - case Stmt::CXXDefaultArgExprClass: - case Stmt::CXXZeroInitValueExprClass: - case Stmt::CXXNewExprClass: - case Stmt::CXXDeleteExprClass: - case Stmt::CXXPseudoDestructorExprClass: - case Stmt::UnresolvedLookupExprClass: - case Stmt::UnaryTypeTraitExprClass: - case Stmt::DependentScopeDeclRefExprClass: - case Stmt::CXXConstructExprClass: - case Stmt::CXXBindTemporaryExprClass: - case Stmt::CXXExprWithTemporariesClass: - case Stmt::CXXTemporaryObjectExprClass: - case Stmt::CXXUnresolvedConstructExprClass: - case Stmt::CXXDependentScopeMemberExprClass: - case Stmt::UnresolvedMemberExprClass: - case Stmt::CXXCatchStmtClass: - case Stmt::CXXTryStmtClass: { - SaveAndRestore OldSink(Builder->BuildSinks); - Builder->BuildSinks = true; - MakeNode(Dst, S, Pred, GetState(Pred)); - break; - } - - default: - // Cases we intentionally have "default" handle: - // AddrLabelExpr, IntegerLiteral, CharacterLiteral - - Dst.Add(Pred); // No-op. Simply propagate the current state unchanged. - break; - - case Stmt::ArraySubscriptExprClass: - VisitArraySubscriptExpr(cast(S), Pred, Dst, false); - break; - - case Stmt::AsmStmtClass: - VisitAsmStmt(cast(S), Pred, Dst); - break; - - case Stmt::BlockDeclRefExprClass: - VisitBlockDeclRefExpr(cast(S), Pred, Dst, false); - break; - - case Stmt::BlockExprClass: - VisitBlockExpr(cast(S), Pred, Dst); - break; - - case Stmt::BinaryOperatorClass: { - BinaryOperator* B = cast(S); - - if (B->isLogicalOp()) { - VisitLogicalExpr(B, Pred, Dst); - break; - } - else if (B->getOpcode() == BinaryOperator::Comma) { - const GRState* state = GetState(Pred); - MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS()))); - break; - } - - if (AMgr.shouldEagerlyAssume() && - (B->isRelationalOp() || B->isEqualityOp())) { - ExplodedNodeSet Tmp; - VisitBinaryOperator(cast(S), Pred, Tmp, false); - EvalEagerlyAssume(Dst, Tmp, cast(S)); - } - else - VisitBinaryOperator(cast(S), Pred, Dst, false); - - break; - } - - case Stmt::CallExprClass: - case Stmt::CXXOperatorCallExprClass: { - CallExpr* C = cast(S); - VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst, false); - break; - } - - // FIXME: ChooseExpr is really a constant. We need to fix - // the CFG do not model them as explicit control-flow. - - case Stmt::ChooseExprClass: { // __builtin_choose_expr - ChooseExpr* C = cast(S); - VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); - break; - } - - case Stmt::CompoundAssignOperatorClass: - VisitBinaryOperator(cast(S), Pred, Dst, false); - break; - - case Stmt::CompoundLiteralExprClass: - VisitCompoundLiteralExpr(cast(S), Pred, Dst, false); - break; - - case Stmt::ConditionalOperatorClass: { // '?' operator - ConditionalOperator* C = cast(S); - VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); - break; - } - - case Stmt::CXXThisExprClass: - VisitCXXThisExpr(cast(S), Pred, Dst); - break; - - case Stmt::DeclRefExprClass: - VisitDeclRefExpr(cast(S), Pred, Dst, false); - break; - - case Stmt::DeclStmtClass: - VisitDeclStmt(cast(S), Pred, Dst); - break; - - case Stmt::ForStmtClass: - // This case isn't for branch processing, but for handling the - // initialization of a condition variable. - VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); - break; - - case Stmt::ImplicitCastExprClass: - case Stmt::CStyleCastExprClass: { - CastExpr* C = cast(S); - VisitCast(C, C->getSubExpr(), Pred, Dst, false); - break; - } - - case Stmt::IfStmtClass: - // This case isn't for branch processing, but for handling the - // initialization of a condition variable. - VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); - break; - - case Stmt::InitListExprClass: - VisitInitListExpr(cast(S), Pred, Dst); - break; - - case Stmt::MemberExprClass: - VisitMemberExpr(cast(S), Pred, Dst, false); - break; - - case Stmt::ObjCIvarRefExprClass: - VisitObjCIvarRefExpr(cast(S), Pred, Dst, false); - break; - - case Stmt::ObjCForCollectionStmtClass: - VisitObjCForCollectionStmt(cast(S), Pred, Dst); - break; - - case Stmt::ObjCMessageExprClass: - VisitObjCMessageExpr(cast(S), Pred, Dst, false); - break; - - case Stmt::ObjCAtThrowStmtClass: { - // FIXME: This is not complete. We basically treat @throw as - // an abort. - SaveAndRestore OldSink(Builder->BuildSinks); - Builder->BuildSinks = true; - MakeNode(Dst, S, Pred, GetState(Pred)); - break; - } - - case Stmt::ParenExprClass: - Visit(cast(S)->getSubExpr()->IgnoreParens(), Pred, Dst); - break; - - case Stmt::ReturnStmtClass: - VisitReturnStmt(cast(S), Pred, Dst); - break; - - case Stmt::SizeOfAlignOfExprClass: - VisitSizeOfAlignOfExpr(cast(S), Pred, Dst); - break; - - case Stmt::StmtExprClass: { - StmtExpr* SE = cast(S); - - if (SE->getSubStmt()->body_empty()) { - // Empty statement expression. - assert(SE->getType() == getContext().VoidTy - && "Empty statement expression must have void type."); - Dst.Add(Pred); - break; - } - - if (Expr* LastExpr = dyn_cast(*SE->getSubStmt()->body_rbegin())) { - const GRState* state = GetState(Pred); - MakeNode(Dst, SE, Pred, state->BindExpr(SE, state->getSVal(LastExpr))); - } - else - Dst.Add(Pred); - - break; - } - - case Stmt::StringLiteralClass: - VisitLValue(cast(S), Pred, Dst); - break; - - case Stmt::SwitchStmtClass: - // This case isn't for branch processing, but for handling the - // initialization of a condition variable. - VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); - break; - - case Stmt::UnaryOperatorClass: { - UnaryOperator *U = cast(S); - if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UnaryOperator::LNot)) { - ExplodedNodeSet Tmp; - VisitUnaryOperator(U, Pred, Tmp, false); - EvalEagerlyAssume(Dst, Tmp, U); - } - else - VisitUnaryOperator(U, Pred, Dst, false); - break; - } - - case Stmt::WhileStmtClass: - // This case isn't for branch processing, but for handling the - // initialization of a condition variable. - VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); - break; - } -} - -void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - Ex->getLocStart(), - "Error evaluating statement"); - - - Ex = Ex->IgnoreParens(); - - if (Ex != CurrentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(Ex)){ - Dst.Add(Pred); - return; - } - - switch (Ex->getStmtClass()) { - // C++ stuff we don't support yet. - case Stmt::CXXExprWithTemporariesClass: - case Stmt::CXXMemberCallExprClass: - case Stmt::CXXZeroInitValueExprClass: { - SaveAndRestore OldSink(Builder->BuildSinks); - Builder->BuildSinks = true; - MakeNode(Dst, Ex, Pred, GetState(Pred)); - break; - } - - case Stmt::ArraySubscriptExprClass: - VisitArraySubscriptExpr(cast(Ex), Pred, Dst, true); - return; - - case Stmt::BinaryOperatorClass: - case Stmt::CompoundAssignOperatorClass: - VisitBinaryOperator(cast(Ex), Pred, Dst, true); - return; - - case Stmt::BlockDeclRefExprClass: - VisitBlockDeclRefExpr(cast(Ex), Pred, Dst, true); - return; - - case Stmt::CallExprClass: - case Stmt::CXXOperatorCallExprClass: { - CallExpr *C = cast(Ex); - assert(CalleeReturnsReferenceOrRecord(C)); - VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst, true); - break; - } - - case Stmt::CompoundLiteralExprClass: - VisitCompoundLiteralExpr(cast(Ex), Pred, Dst, true); - return; - - case Stmt::DeclRefExprClass: - VisitDeclRefExpr(cast(Ex), Pred, Dst, true); - return; - - case Stmt::ImplicitCastExprClass: - case Stmt::CStyleCastExprClass: { - CastExpr *C = cast(Ex); - QualType T = Ex->getType(); - VisitCast(C, C->getSubExpr(), Pred, Dst, true); - break; - } - - case Stmt::MemberExprClass: - VisitMemberExpr(cast(Ex), Pred, Dst, true); - return; - - case Stmt::ObjCIvarRefExprClass: - VisitObjCIvarRefExpr(cast(Ex), Pred, Dst, true); - return; - - case Stmt::ObjCMessageExprClass: { - ObjCMessageExpr *ME = cast(Ex); - assert(ReceiverReturnsReferenceOrRecord(ME)); - VisitObjCMessageExpr(ME, Pred, Dst, true); - return; - } - - case Stmt::ObjCPropertyRefExprClass: - case Stmt::ObjCImplicitSetterGetterRefExprClass: - // FIXME: Property assignments are lvalues, but not really "locations". - // e.g.: self.x = something; - // Here the "self.x" really can translate to a method call (setter) when - // the assignment is made. Moreover, the entire assignment expression - // evaluate to whatever "something" is, not calling the "getter" for - // the property (which would make sense since it can have side effects). - // We'll probably treat this as a location, but not one that we can - // take the address of. Perhaps we need a new SVal class for cases - // like thsis? - // Note that we have a similar problem for bitfields, since they don't - // have "locations" in the sense that we can take their address. - Dst.Add(Pred); - return; - - case Stmt::StringLiteralClass: { - const GRState* state = GetState(Pred); - SVal V = state->getLValue(cast(Ex)); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); - return; - } - - case Stmt::UnaryOperatorClass: - VisitUnaryOperator(cast(Ex), Pred, Dst, true); - return; - - // In C++, binding an rvalue to a reference requires to create an object. - case Stmt::IntegerLiteralClass: - CreateCXXTemporaryObject(Ex, Pred, Dst); - return; - - default: - // Arbitrary subexpressions can return aggregate temporaries that - // can be used in a lvalue context. We need to enhance our support - // of such temporaries in both the environment and the store, so right - // now we just do a regular visit. - assert ((Ex->getType()->isAggregateType()) && - "Other kinds of expressions with non-aggregate/union types do" - " not have lvalues."); - - Visit(Ex, Pred, Dst); - } -} - -//===----------------------------------------------------------------------===// -// Block entrance. (Update counters). -//===----------------------------------------------------------------------===// - -bool GRExprEngine::ProcessBlockEntrance(CFGBlock* B, const GRState*, - GRBlockCounter BC) { - - return BC.getNumVisited(B->getBlockID()) < 3; -} - -//===----------------------------------------------------------------------===// -// Generic node creation. -//===----------------------------------------------------------------------===// - -ExplodedNode* GRExprEngine::MakeNode(ExplodedNodeSet& Dst, Stmt* S, - ExplodedNode* Pred, const GRState* St, - ProgramPoint::Kind K, const void *tag) { - assert (Builder && "GRStmtNodeBuilder not present."); - SaveAndRestore OldTag(Builder->Tag); - Builder->Tag = tag; - return Builder->MakeNode(Dst, S, Pred, St, K); -} - -//===----------------------------------------------------------------------===// -// Branch processing. -//===----------------------------------------------------------------------===// - -const GRState* GRExprEngine::MarkBranch(const GRState* state, - Stmt* Terminator, - bool branchTaken) { - - switch (Terminator->getStmtClass()) { - default: - return state; - - case Stmt::BinaryOperatorClass: { // '&&' and '||' - - BinaryOperator* B = cast(Terminator); - BinaryOperator::Opcode Op = B->getOpcode(); - - assert (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr); - - // For &&, if we take the true branch, then the value of the whole - // expression is that of the RHS expression. - // - // For ||, if we take the false branch, then the value of the whole - // expression is that of the RHS expression. - - Expr* Ex = (Op == BinaryOperator::LAnd && branchTaken) || - (Op == BinaryOperator::LOr && !branchTaken) - ? B->getRHS() : B->getLHS(); - - return state->BindExpr(B, UndefinedVal(Ex)); - } - - case Stmt::ConditionalOperatorClass: { // ?: - - ConditionalOperator* C = cast(Terminator); - - // For ?, if branchTaken == true then the value is either the LHS or - // the condition itself. (GNU extension). - - Expr* Ex; - - if (branchTaken) - Ex = C->getLHS() ? C->getLHS() : C->getCond(); - else - Ex = C->getRHS(); - - return state->BindExpr(C, UndefinedVal(Ex)); - } - - case Stmt::ChooseExprClass: { // ?: - - ChooseExpr* C = cast(Terminator); - - Expr* Ex = branchTaken ? C->getLHS() : C->getRHS(); - return state->BindExpr(C, UndefinedVal(Ex)); - } - } -} - -/// RecoverCastedSymbol - A helper function for ProcessBranch that is used -/// to try to recover some path-sensitivity for casts of symbolic -/// integers that promote their values (which are currently not tracked well). -/// This function returns the SVal bound to Condition->IgnoreCasts if all the -// cast(s) did was sign-extend the original value. -static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state, - Stmt* Condition, ASTContext& Ctx) { - - Expr *Ex = dyn_cast(Condition); - if (!Ex) - return UnknownVal(); - - uint64_t bits = 0; - bool bitsInit = false; - - while (CastExpr *CE = dyn_cast(Ex)) { - QualType T = CE->getType(); - - if (!T->isIntegerType()) - return UnknownVal(); - - uint64_t newBits = Ctx.getTypeSize(T); - if (!bitsInit || newBits < bits) { - bitsInit = true; - bits = newBits; - } - - Ex = CE->getSubExpr(); - } - - // We reached a non-cast. Is it a symbolic value? - QualType T = Ex->getType(); - - if (!bitsInit || !T->isIntegerType() || Ctx.getTypeSize(T) > bits) - return UnknownVal(); - - return state->getSVal(Ex); -} - -void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term, - GRBranchNodeBuilder& builder) { - - // Check for NULL conditions; e.g. "for(;;)" - if (!Condition) { - builder.markInfeasible(false); - return; - } - - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - Condition->getLocStart(), - "Error evaluating branch"); - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { - void *tag = I->first; - Checker *checker = I->second; - checker->VisitBranchCondition(builder, *this, Condition, tag); - } - - // If the branch condition is undefined, return; - if (!builder.isFeasible(true) && !builder.isFeasible(false)) - return; - - const GRState* PrevState = builder.getState(); - SVal X = PrevState->getSVal(Condition); - - if (X.isUnknown()) { - // Give it a chance to recover from unknown. - if (const Expr *Ex = dyn_cast(Condition)) { - if (Ex->getType()->isIntegerType()) { - // Try to recover some path-sensitivity. Right now casts of symbolic - // integers that promote their values are currently not tracked well. - // If 'Condition' is such an expression, try and recover the - // underlying value and use that instead. - SVal recovered = RecoverCastedSymbol(getStateManager(), - builder.getState(), Condition, - getContext()); - - if (!recovered.isUnknown()) { - X = recovered; - } - } - } - // If the condition is still unknown, give up. - if (X.isUnknown()) { - builder.generateNode(MarkBranch(PrevState, Term, true), true); - builder.generateNode(MarkBranch(PrevState, Term, false), false); - return; - } - } - - DefinedSVal V = cast(X); - - // Process the true branch. - if (builder.isFeasible(true)) { - if (const GRState *state = PrevState->Assume(V, true)) - builder.generateNode(MarkBranch(state, Term, true), true); - else - builder.markInfeasible(true); - } - - // Process the false branch. - if (builder.isFeasible(false)) { - if (const GRState *state = PrevState->Assume(V, false)) - builder.generateNode(MarkBranch(state, Term, false), false); - else - builder.markInfeasible(false); - } -} - -/// ProcessIndirectGoto - Called by GRCoreEngine. Used to generate successor -/// nodes by processing the 'effects' of a computed goto jump. -void GRExprEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& builder) { - - const GRState *state = builder.getState(); - SVal V = state->getSVal(builder.getTarget()); - - // Three possibilities: - // - // (1) We know the computed label. - // (2) The label is NULL (or some other constant), or Undefined. - // (3) We have no clue about the label. Dispatch to all targets. - // - - typedef GRIndirectGotoNodeBuilder::iterator iterator; - - if (isa(V)) { - LabelStmt* L = cast(V).getLabel(); - - for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) { - if (I.getLabel() == L) { - builder.generateNode(I, state); - return; - } - } - - assert (false && "No block with label."); - return; - } - - if (isa(V) || isa(V)) { - // Dispatch to the first target and mark it as a sink. - //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); - // FIXME: add checker visit. - // UndefBranches.insert(N); - return; - } - - // This is really a catch-all. We don't support symbolics yet. - // FIXME: Implement dispatch for symbolic pointers. - - for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) - builder.generateNode(I, state); -} - - -void GRExprEngine::VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R, - ExplodedNode* Pred, ExplodedNodeSet& Dst) { - - assert(Ex == CurrentStmt && - Pred->getLocationContext()->getCFG()->isBlkExpr(Ex)); - - const GRState* state = GetState(Pred); - SVal X = state->getSVal(Ex); - - assert (X.isUndef()); - - Expr *SE = (Expr*) cast(X).getData(); - assert(SE); - X = state->getSVal(SE); - - // Make sure that we invalidate the previous binding. - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true)); -} - -/// ProcessEndPath - Called by GRCoreEngine. Used to generate end-of-path -/// nodes when the control reaches the end of a function. -void GRExprEngine::ProcessEndPath(GREndPathNodeBuilder& builder) { - getTF().EvalEndPath(*this, builder); - StateMgr.EndPath(builder.getState()); - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ - void *tag = I->first; - Checker *checker = I->second; - checker->EvalEndPath(builder, tag, *this); - } -} - -/// ProcessSwitch - Called by GRCoreEngine. Used to generate successor -/// nodes by processing the 'effects' of a switch statement. -void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) { - typedef GRSwitchNodeBuilder::iterator iterator; - const GRState* state = builder.getState(); - Expr* CondE = builder.getCondition(); - SVal CondV_untested = state->getSVal(CondE); - - if (CondV_untested.isUndef()) { - //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); - // FIXME: add checker - //UndefBranches.insert(N); - - return; - } - DefinedOrUnknownSVal CondV = cast(CondV_untested); - - const GRState *DefaultSt = state; - bool defaultIsFeasible = false; - - for (iterator I = builder.begin(), EI = builder.end(); I != EI; ++I) { - CaseStmt* Case = cast(I.getCase()); - - // Evaluate the LHS of the case value. - Expr::EvalResult V1; - bool b = Case->getLHS()->Evaluate(V1, getContext()); - - // Sanity checks. These go away in Release builds. - assert(b && V1.Val.isInt() && !V1.HasSideEffects - && "Case condition must evaluate to an integer constant."); - b = b; // silence unused variable warning - assert(V1.Val.getInt().getBitWidth() == - getContext().getTypeSize(CondE->getType())); - - // Get the RHS of the case, if it exists. - Expr::EvalResult V2; - - if (Expr* E = Case->getRHS()) { - b = E->Evaluate(V2, getContext()); - assert(b && V2.Val.isInt() && !V2.HasSideEffects - && "Case condition must evaluate to an integer constant."); - b = b; // silence unused variable warning - } - else - V2 = V1; - - // FIXME: Eventually we should replace the logic below with a range - // comparison, rather than concretize the values within the range. - // This should be easy once we have "ranges" for NonLVals. - - do { - nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1.Val.getInt())); - DefinedOrUnknownSVal Res = SVator.EvalEQ(DefaultSt ? DefaultSt : state, - CondV, CaseVal); - - // Now "assume" that the case matches. - if (const GRState* stateNew = state->Assume(Res, true)) { - builder.generateCaseStmtNode(I, stateNew); - - // If CondV evaluates to a constant, then we know that this - // is the *only* case that we can take, so stop evaluating the - // others. - if (isa(CondV)) - return; - } - - // Now "assume" that the case doesn't match. Add this state - // to the default state (if it is feasible). - if (DefaultSt) { - if (const GRState *stateNew = DefaultSt->Assume(Res, false)) { - defaultIsFeasible = true; - DefaultSt = stateNew; - } - else { - defaultIsFeasible = false; - DefaultSt = NULL; - } - } - - // Concretize the next value in the range. - if (V1.Val.getInt() == V2.Val.getInt()) - break; - - ++V1.Val.getInt(); - assert (V1.Val.getInt() <= V2.Val.getInt()); - - } while (true); - } - - // If we reach here, than we know that the default branch is - // possible. - if (defaultIsFeasible) builder.generateDefaultCaseNode(DefaultSt); -} - -//===----------------------------------------------------------------------===// -// Transfer functions: logical operations ('&&', '||'). -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - assert(B->getOpcode() == BinaryOperator::LAnd || - B->getOpcode() == BinaryOperator::LOr); - - assert(B==CurrentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(B)); - - const GRState* state = GetState(Pred); - SVal X = state->getSVal(B); - assert(X.isUndef()); - - const Expr *Ex = (const Expr*) cast(X).getData(); - assert(Ex); - - if (Ex == B->getRHS()) { - X = state->getSVal(Ex); - - // Handle undefined values. - if (X.isUndef()) { - MakeNode(Dst, B, Pred, state->BindExpr(B, X)); - return; - } - - DefinedOrUnknownSVal XD = cast(X); - - // We took the RHS. Because the value of the '&&' or '||' expression must - // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0 - // or 1. Alternatively, we could take a lazy approach, and calculate this - // value later when necessary. We don't have the machinery in place for - // this right now, and since most logical expressions are used for branches, - // the payoff is not likely to be large. Instead, we do eager evaluation. - if (const GRState *newState = state->Assume(XD, true)) - MakeNode(Dst, B, Pred, - newState->BindExpr(B, ValMgr.makeIntVal(1U, B->getType()))); - - if (const GRState *newState = state->Assume(XD, false)) - MakeNode(Dst, B, Pred, - newState->BindExpr(B, ValMgr.makeIntVal(0U, B->getType()))); - } - else { - // We took the LHS expression. Depending on whether we are '&&' or - // '||' we know what the value of the expression is via properties of - // the short-circuiting. - X = ValMgr.makeIntVal(B->getOpcode() == BinaryOperator::LAnd ? 0U : 1U, - B->getType()); - MakeNode(Dst, B, Pred, state->BindExpr(B, X)); - } -} - -//===----------------------------------------------------------------------===// -// Transfer functions: Loads and stores. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitBlockExpr(BlockExpr *BE, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - - ExplodedNodeSet Tmp; - - CanQualType T = getContext().getCanonicalType(BE->getType()); - SVal V = ValMgr.getBlockPointer(BE->getBlockDecl(), T, - Pred->getLocationContext()); - - MakeNode(Tmp, BE, Pred, GetState(Pred)->BindExpr(BE, V), - ProgramPoint::PostLValueKind); - - // Post-visit the BlockExpr. - CheckerVisit(BE, Dst, Tmp, false); -} - -void GRExprEngine::VisitDeclRefExpr(DeclRefExpr *Ex, ExplodedNode *Pred, - ExplodedNodeSet &Dst, bool asLValue) { - VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue); -} - -void GRExprEngine::VisitBlockDeclRefExpr(BlockDeclRefExpr *Ex, - ExplodedNode *Pred, - ExplodedNodeSet &Dst, bool asLValue) { - VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue); -} - -void GRExprEngine::VisitCommonDeclRefExpr(Expr *Ex, const NamedDecl *D, - ExplodedNode *Pred, - ExplodedNodeSet &Dst, bool asLValue) { - - const GRState *state = GetState(Pred); - - if (const VarDecl* VD = dyn_cast(D)) { - - SVal V = state->getLValue(VD, Pred->getLocationContext()); - - if (asLValue) { - // For references, the 'lvalue' is the pointer address stored in the - // reference region. - if (VD->getType()->isReferenceType()) { - if (const MemRegion *R = V.getAsRegion()) - V = state->getSVal(R); - else - V = UnknownVal(); - } - - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), - ProgramPoint::PostLValueKind); - } - else - EvalLoad(Dst, Ex, Pred, state, V); - - return; - } else if (const EnumConstantDecl* ED = dyn_cast(D)) { - assert(!asLValue && "EnumConstantDecl does not have lvalue."); - - SVal V = ValMgr.makeIntVal(ED->getInitVal()); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); - return; - - } else if (const FunctionDecl* FD = dyn_cast(D)) { - // This code is valid regardless of the value of 'isLValue'. - SVal V = ValMgr.getFunctionPointer(FD); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), - ProgramPoint::PostLValueKind); - return; - } - - assert (false && - "ValueDecl support for this ValueDecl not implemented."); -} - -/// VisitArraySubscriptExpr - Transfer function for array accesses -void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A, - ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue){ - - Expr* Base = A->getBase()->IgnoreParens(); - Expr* Idx = A->getIdx()->IgnoreParens(); - ExplodedNodeSet Tmp; - - if (Base->getType()->isVectorType()) { - // For vector types get its lvalue. - // FIXME: This may not be correct. Is the rvalue of a vector its location? - // In fact, I think this is just a hack. We need to get the right - // semantics. - VisitLValue(Base, Pred, Tmp); - } - else - Visit(Base, Pred, Tmp); // Get Base's rvalue, which should be an LocVal. - - for (ExplodedNodeSet::iterator I1=Tmp.begin(), E1=Tmp.end(); I1!=E1; ++I1) { - ExplodedNodeSet Tmp2; - Visit(Idx, *I1, Tmp2); // Evaluate the index. - - ExplodedNodeSet Tmp3; - CheckerVisit(A, Tmp3, Tmp2, true); - - for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) { - const GRState* state = GetState(*I2); - SVal V = state->getLValue(A->getType(), state->getSVal(Idx), - state->getSVal(Base)); - - if (asLValue) - MakeNode(Dst, A, *I2, state->BindExpr(A, V), - ProgramPoint::PostLValueKind); - else - EvalLoad(Dst, A, *I2, state, V); - } - } -} - -/// VisitMemberExpr - Transfer function for member expressions. -void GRExprEngine::VisitMemberExpr(MemberExpr* M, ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue) { - - Expr* Base = M->getBase()->IgnoreParens(); - ExplodedNodeSet Tmp; - - if (M->isArrow()) - Visit(Base, Pred, Tmp); // p->f = ... or ... = p->f - else - VisitLValue(Base, Pred, Tmp); // x.f = ... or ... = x.f - - FieldDecl *Field = dyn_cast(M->getMemberDecl()); - if (!Field) // FIXME: skipping member expressions for non-fields - return; - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { - const GRState* state = GetState(*I); - // FIXME: Should we insert some assumption logic in here to determine - // if "Base" is a valid piece of memory? Before we put this assumption - // later when using FieldOffset lvals (which we no longer have). - SVal L = state->getLValue(Field, state->getSVal(Base)); - - if (asLValue) - MakeNode(Dst, M, *I, state->BindExpr(M, L), ProgramPoint::PostLValueKind); - else - EvalLoad(Dst, M, *I, state, L); - } -} - -/// EvalBind - Handle the semantics of binding a value to a specific location. -/// This method is used by EvalStore and (soon) VisitDeclStmt, and others. -void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt *AssignE, - Stmt* StoreE, ExplodedNode* Pred, - const GRState* state, SVal location, SVal Val, - bool atDeclInit) { - - - // Do a previsit of the bind. - ExplodedNodeSet CheckedSet, Src; - Src.Add(Pred); - CheckerVisitBind(AssignE, StoreE, CheckedSet, Src, location, Val, true); - - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I!=E; ++I) { - - if (Pred != *I) - state = GetState(*I); - - const GRState* newState = 0; - - if (atDeclInit) { - const VarRegion *VR = - cast(cast(location).getRegion()); - - newState = state->bindDecl(VR, Val); - } - else { - if (location.isUnknown()) { - // We know that the new state will be the same as the old state since - // the location of the binding is "unknown". Consequently, there - // is no reason to just create a new node. - newState = state; - } - else { - // We are binding to a value other than 'unknown'. Perform the binding - // using the StoreManager. - newState = state->bindLoc(cast(location), Val); - } - } - - // The next thing to do is check if the GRTransferFuncs object wants to - // update the state based on the new binding. If the GRTransferFunc object - // doesn't do anything, just auto-propagate the current state. - GRStmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, StoreE, - newState != state); - - getTF().EvalBind(BuilderRef, location, Val); - } -} - -/// EvalStore - Handle the semantics of a store via an assignment. -/// @param Dst The node set to store generated state nodes -/// @param Ex The expression representing the location of the store -/// @param state The current simulation state -/// @param location The location to store the value -/// @param Val The value to be stored -void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, Expr *AssignE, - Expr* StoreE, - ExplodedNode* Pred, - const GRState* state, SVal location, SVal Val, - const void *tag) { - - assert(Builder && "GRStmtNodeBuilder must be defined."); - - // Evaluate the location (checks for bad dereferences). - ExplodedNodeSet Tmp; - EvalLocation(Tmp, StoreE, Pred, state, location, tag, false); - - if (Tmp.empty()) - return; - - assert(!location.isUndef()); - - SaveAndRestore OldSPointKind(Builder->PointKind, - ProgramPoint::PostStoreKind); - SaveAndRestore OldTag(Builder->Tag, tag); - - // Proceed with the store. - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - EvalBind(Dst, AssignE, StoreE, *NI, GetState(*NI), location, Val); -} - -void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred, - const GRState* state, SVal location, - const void *tag, QualType LoadTy) { - - // Are we loading from a region? This actually results in two loads; one - // to fetch the address of the referenced value and one to fetch the - // referenced value. - if (const TypedRegion *TR = - dyn_cast_or_null(location.getAsRegion())) { - - QualType ValTy = TR->getValueType(getContext()); - if (const ReferenceType *RT = ValTy->getAs()) { - static int loadReferenceTag = 0; - ExplodedNodeSet Tmp; - EvalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag, - getContext().getPointerType(RT->getPointeeType())); - - // Perform the load from the referenced value. - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { - state = GetState(*I); - location = state->getSVal(Ex); - EvalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy); - } - return; - } - } - - EvalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy); -} - -void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, Expr *Ex, - ExplodedNode* Pred, - const GRState* state, SVal location, - const void *tag, QualType LoadTy) { - - // Evaluate the location (checks for bad dereferences). - ExplodedNodeSet Tmp; - EvalLocation(Tmp, Ex, Pred, state, location, tag, true); - - if (Tmp.empty()) - return; - - assert(!location.isUndef()); - - SaveAndRestore OldSPointKind(Builder->PointKind); - SaveAndRestore OldTag(Builder->Tag); - - // Proceed with the load. - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { - state = GetState(*NI); - if (location.isUnknown()) { - // This is important. We must nuke the old binding. - MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, UnknownVal()), - ProgramPoint::PostLoadKind, tag); - } - else { - SVal V = state->getSVal(cast(location), LoadTy.isNull() ? - Ex->getType() : LoadTy); - MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, V), ProgramPoint::PostLoadKind, - tag); - } - } -} - -void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S, - ExplodedNode* Pred, - const GRState* state, SVal location, - const void *tag, bool isLoad) { - // Early checks for performance reason. - if (location.isUnknown() || Checkers.empty()) { - Dst.Add(Pred); - return; - } - - ExplodedNodeSet Src, Tmp; - Src.Add(Pred); - ExplodedNodeSet *PrevSet = &Src; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) - { - ExplodedNodeSet *CurrSet = 0; - if (I+1 == E) - CurrSet = &Dst; - else { - CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; - CurrSet->clear(); - } - - void *tag = I->first; - Checker *checker = I->second; - - for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) { - // Use the 'state' argument only when the predecessor node is the - // same as Pred. This allows us to catch updates to the state. - checker->GR_VisitLocation(*CurrSet, *Builder, *this, S, *NI, - *NI == Pred ? state : GetState(*NI), - location, tag, isLoad); - } - - // Update which NodeSet is the current one. - PrevSet = CurrSet; - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Function calls. -//===----------------------------------------------------------------------===// - -namespace { -class CallExprWLItem { -public: - CallExpr::arg_iterator I; - ExplodedNode *N; - - CallExprWLItem(const CallExpr::arg_iterator &i, ExplodedNode *n) - : I(i), N(n) {} -}; -} // end anonymous namespace - -void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, - CallExpr::arg_iterator AI, - CallExpr::arg_iterator AE, - ExplodedNodeSet& Dst, bool asLValue) { - - // Determine the type of function we're calling (if available). - const FunctionProtoType *Proto = NULL; - QualType FnType = CE->getCallee()->IgnoreParens()->getType(); - if (const PointerType *FnTypePtr = FnType->getAs()) - Proto = FnTypePtr->getPointeeType()->getAs(); - - // Create a worklist to process the arguments. - llvm::SmallVector WorkList; - WorkList.reserve(AE - AI); - WorkList.push_back(CallExprWLItem(AI, Pred)); - - ExplodedNodeSet ArgsEvaluated; - - while (!WorkList.empty()) { - CallExprWLItem Item = WorkList.back(); - WorkList.pop_back(); - - if (Item.I == AE) { - ArgsEvaluated.insert(Item.N); - continue; - } - - // Evaluate the argument. - ExplodedNodeSet Tmp; - const unsigned ParamIdx = Item.I - AI; - - bool VisitAsLvalue = false; - if (Proto && ParamIdx < Proto->getNumArgs()) - VisitAsLvalue = Proto->getArgType(ParamIdx)->isReferenceType(); - - if (VisitAsLvalue) - VisitLValue(*Item.I, Item.N, Tmp); - else - Visit(*Item.I, Item.N, Tmp); - - // Enqueue evaluating the next argument on the worklist. - ++(Item.I); - - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - WorkList.push_back(CallExprWLItem(Item.I, *NI)); - } - - // Now process the call itself. - ExplodedNodeSet DstTmp; - Expr* Callee = CE->getCallee()->IgnoreParens(); - - for (ExplodedNodeSet::iterator NI=ArgsEvaluated.begin(), - NE=ArgsEvaluated.end(); NI != NE; ++NI) { - // Evaluate the callee. - ExplodedNodeSet DstTmp2; - Visit(Callee, *NI, DstTmp2); - // Perform the previsit of the CallExpr, storing the results in DstTmp. - CheckerVisit(CE, DstTmp, DstTmp2, true); - } - - // Finally, evaluate the function call. We try each of the checkers - // to see if the can evaluate the function call. - ExplodedNodeSet DstTmp3; - - - for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); - DI != DE; ++DI) { - - const GRState* state = GetState(*DI); - SVal L = state->getSVal(Callee); - - // FIXME: Add support for symbolic function calls (calls involving - // function pointer values that are symbolic). - SaveAndRestore OldSink(Builder->BuildSinks); - ExplodedNodeSet DstChecker; - - // If the callee is processed by a checker, skip the rest logic. - if (CheckerEvalCall(CE, DstChecker, *DI)) - DstTmp3.insert(DstChecker); - else { - for (ExplodedNodeSet::iterator DI_Checker = DstChecker.begin(), - DE_Checker = DstChecker.end(); - DI_Checker != DE_Checker; ++DI_Checker) { - - // Dispatch to the plug-in transfer function. - unsigned OldSize = DstTmp3.size(); - SaveOr OldHasGen(Builder->HasGeneratedNode); - Pred = *DI_Checker; - - // Dispatch to transfer function logic to handle the call itself. - // FIXME: Allow us to chain together transfer functions. - assert(Builder && "GRStmtNodeBuilder must be defined."); - getTF().EvalCall(DstTmp3, *this, *Builder, CE, L, Pred); - - // Handle the case where no nodes where generated. Auto-generate that - // contains the updated state if we aren't generating sinks. - if (!Builder->BuildSinks && DstTmp3.size() == OldSize && - !Builder->HasGeneratedNode) - MakeNode(DstTmp3, CE, Pred, state); - } - } - } - - // Finally, perform the post-condition check of the CallExpr and store - // the created nodes in 'Dst'. - - if (!(!asLValue && CalleeReturnsReference(CE))) { - CheckerVisit(CE, Dst, DstTmp3, false); - return; - } - - // Handle the case where the called function returns a reference but - // we expect an rvalue. For such cases, convert the reference to - // an rvalue. - // FIXME: This conversion doesn't actually happen unless the result - // of CallExpr is consumed by another expression. - ExplodedNodeSet DstTmp4; - CheckerVisit(CE, DstTmp4, DstTmp3, false); - QualType LoadTy = CE->getType(); - - static int *ConvertToRvalueTag = 0; - for (ExplodedNodeSet::iterator NI = DstTmp4.begin(), NE = DstTmp4.end(); - NI!=NE; ++NI) { - const GRState *state = GetState(*NI); - EvalLoad(Dst, CE, *NI, state, state->getSVal(CE), - &ConvertToRvalueTag, LoadTy); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C ivar references. -//===----------------------------------------------------------------------===// - -static std::pair EagerlyAssumeTag - = std::pair(&EagerlyAssumeTag,0); - -void GRExprEngine::EvalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, - Expr *Ex) { - for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { - ExplodedNode *Pred = *I; - - // Test if the previous node was as the same expression. This can happen - // when the expression fails to evaluate to anything meaningful and - // (as an optimization) we don't generate a node. - ProgramPoint P = Pred->getLocation(); - if (!isa(P) || cast(P).getStmt() != Ex) { - Dst.Add(Pred); - continue; - } - - const GRState* state = Pred->getState(); - SVal V = state->getSVal(Ex); - if (nonloc::SymExprVal *SEV = dyn_cast(&V)) { - // First assume that the condition is true. - if (const GRState *stateTrue = state->Assume(*SEV, true)) { - stateTrue = stateTrue->BindExpr(Ex, - ValMgr.makeIntVal(1U, Ex->getType())); - Dst.Add(Builder->generateNode(PostStmtCustom(Ex, - &EagerlyAssumeTag, Pred->getLocationContext()), - stateTrue, Pred)); - } - - // Next, assume that the condition is false. - if (const GRState *stateFalse = state->Assume(*SEV, false)) { - stateFalse = stateFalse->BindExpr(Ex, - ValMgr.makeIntVal(0U, Ex->getType())); - Dst.Add(Builder->generateNode(PostStmtCustom(Ex, &EagerlyAssumeTag, - Pred->getLocationContext()), - stateFalse, Pred)); - } - } - else - Dst.Add(Pred); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C ivar references. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitObjCIvarRefExpr(ObjCIvarRefExpr* Ex, ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue) { - - Expr* Base = cast(Ex->getBase()); - ExplodedNodeSet Tmp; - Visit(Base, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - SVal BaseVal = state->getSVal(Base); - SVal location = state->getLValue(Ex->getDecl(), BaseVal); - - if (asLValue) - MakeNode(Dst, Ex, *I, state->BindExpr(Ex, location)); - else - EvalLoad(Dst, Ex, *I, state, location); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C fast enumeration 'for' statements. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S, - ExplodedNode* Pred, ExplodedNodeSet& Dst) { - - // ObjCForCollectionStmts are processed in two places. This method - // handles the case where an ObjCForCollectionStmt* occurs as one of the - // statements within a basic block. This transfer function does two things: - // - // (1) binds the next container value to 'element'. This creates a new - // node in the ExplodedGraph. - // - // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating - // whether or not the container has any more elements. This value - // will be tested in ProcessBranch. We need to explicitly bind - // this value because a container can contain nil elements. - // - // FIXME: Eventually this logic should actually do dispatches to - // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration). - // This will require simulating a temporary NSFastEnumerationState, either - // through an SVal or through the use of MemRegions. This value can - // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop - // terminates we reclaim the temporary (it goes out of scope) and we - // we can test if the SVal is 0 or if the MemRegion is null (depending - // on what approach we take). - // - // For now: simulate (1) by assigning either a symbol or nil if the - // container is empty. Thus this transfer function will by default - // result in state splitting. - - Stmt* elem = S->getElement(); - SVal ElementV; - - if (DeclStmt* DS = dyn_cast(elem)) { - VarDecl* ElemD = cast(DS->getSingleDecl()); - assert (ElemD->getInit() == 0); - ElementV = GetState(Pred)->getLValue(ElemD, Pred->getLocationContext()); - VisitObjCForCollectionStmtAux(S, Pred, Dst, ElementV); - return; - } - - ExplodedNodeSet Tmp; - VisitLValue(cast(elem), Pred, Tmp); - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - VisitObjCForCollectionStmtAux(S, *I, Dst, state->getSVal(elem)); - } -} - -void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S, - ExplodedNode* Pred, ExplodedNodeSet& Dst, - SVal ElementV) { - - // Check if the location we are writing back to is a null pointer. - Stmt* elem = S->getElement(); - ExplodedNodeSet Tmp; - EvalLocation(Tmp, elem, Pred, GetState(Pred), ElementV, NULL, false); - - if (Tmp.empty()) - return; - - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { - Pred = *NI; - const GRState *state = GetState(Pred); - - // Handle the case where the container still has elements. - SVal TrueV = ValMgr.makeTruthVal(1); - const GRState *hasElems = state->BindExpr(S, TrueV); - - // Handle the case where the container has no elements. - SVal FalseV = ValMgr.makeTruthVal(0); - const GRState *noElems = state->BindExpr(S, FalseV); - - if (loc::MemRegionVal* MV = dyn_cast(&ElementV)) - if (const TypedRegion* R = dyn_cast(MV->getRegion())) { - // FIXME: The proper thing to do is to really iterate over the - // container. We will do this with dispatch logic to the store. - // For now, just 'conjure' up a symbolic value. - QualType T = R->getValueType(getContext()); - assert(Loc::IsLocType(T)); - unsigned Count = Builder->getCurrentBlockCount(); - SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); - SVal V = ValMgr.makeLoc(Sym); - hasElems = hasElems->bindLoc(ElementV, V); - - // Bind the location to 'nil' on the false branch. - SVal nilV = ValMgr.makeIntVal(0, T); - noElems = noElems->bindLoc(ElementV, nilV); - } - - // Create the new nodes. - MakeNode(Dst, S, Pred, hasElems); - MakeNode(Dst, S, Pred, noElems); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C message expressions. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue){ - - VisitObjCMessageExprArgHelper(ME, ME->arg_begin(), ME->arg_end(), - Pred, Dst, asLValue); -} - -void GRExprEngine::VisitObjCMessageExprArgHelper(ObjCMessageExpr* ME, - ObjCMessageExpr::arg_iterator AI, - ObjCMessageExpr::arg_iterator AE, - ExplodedNode* Pred, - ExplodedNodeSet& Dst, - bool asLValue) { - if (AI == AE) { - - // Process the receiver. - - if (Expr* Receiver = ME->getReceiver()) { - ExplodedNodeSet Tmp; - Visit(Receiver, Pred, Tmp); - - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI != NE; - ++NI) - VisitObjCMessageExprDispatchHelper(ME, *NI, Dst, asLValue); - - return; - } - - VisitObjCMessageExprDispatchHelper(ME, Pred, Dst, asLValue); - return; - } - - ExplodedNodeSet Tmp; - Visit(*AI, Pred, Tmp); - - ++AI; - - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end();NI != NE;++NI) - VisitObjCMessageExprArgHelper(ME, AI, AE, *NI, Dst, asLValue); -} - -void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME, - ExplodedNode* Pred, - ExplodedNodeSet& Dst, - bool asLValue) { - - // Handle previsits checks. - ExplodedNodeSet Src, DstTmp; - Src.Add(Pred); - - CheckerVisit(ME, DstTmp, Src, true); - - ExplodedNodeSet PostVisitSrc; - - for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); - DI!=DE; ++DI) { - - Pred = *DI; - bool RaisesException = false; - - unsigned OldSize = PostVisitSrc.size(); - SaveAndRestore OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->HasGeneratedNode); - - if (const Expr *Receiver = ME->getReceiver()) { - const GRState *state = Pred->getState(); - - // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = - cast(state->getSVal(Receiver)); - - const GRState *notNilState, *nilState; - llvm::tie(notNilState, nilState) = state->Assume(receiverVal); - - // There are three cases: can be nil or non-nil, must be nil, must be - // non-nil. We handle must be nil, and merge the rest two into non-nil. - if (nilState && !notNilState) { - CheckerEvalNilReceiver(ME, PostVisitSrc, nilState, Pred); - continue; - } - - assert(notNilState); - - // Check if the "raise" message was sent. - if (ME->getSelector() == RaiseSel) - RaisesException = true; - - // Check if we raise an exception. For now treat these as sinks. - // Eventually we will want to handle exceptions properly. - if (RaisesException) - Builder->BuildSinks = true; - - // Dispatch to plug-in transfer function. - EvalObjCMessageExpr(PostVisitSrc, ME, Pred, notNilState); - } - else { - IdentifierInfo* ClsName = ME->getClassName(); - Selector S = ME->getSelector(); - - // Check for special instance methods. - if (!NSExceptionII) { - ASTContext& Ctx = getContext(); - NSExceptionII = &Ctx.Idents.get("NSException"); - } - - if (ClsName == NSExceptionII) { - enum { NUM_RAISE_SELECTORS = 2 }; - - // Lazily create a cache of the selectors. - if (!NSExceptionInstanceRaiseSelectors) { - ASTContext& Ctx = getContext(); - NSExceptionInstanceRaiseSelectors = new Selector[NUM_RAISE_SELECTORS]; - llvm::SmallVector II; - unsigned idx = 0; - - // raise:format: - II.push_back(&Ctx.Idents.get("raise")); - II.push_back(&Ctx.Idents.get("format")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); - - // raise:format::arguments: - II.push_back(&Ctx.Idents.get("arguments")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); - } - - for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) - if (S == NSExceptionInstanceRaiseSelectors[i]) { - RaisesException = true; - break; - } - } - - // Check if we raise an exception. For now treat these as sinks. - // Eventually we will want to handle exceptions properly. - if (RaisesException) - Builder->BuildSinks = true; - - // Dispatch to plug-in transfer function. - EvalObjCMessageExpr(PostVisitSrc, ME, Pred, Builder->GetState(Pred)); - } - - // Handle the case where no nodes where generated. Auto-generate that - // contains the updated state if we aren't generating sinks. - if (!Builder->BuildSinks && PostVisitSrc.size() == OldSize && - !Builder->HasGeneratedNode) - MakeNode(PostVisitSrc, ME, Pred, GetState(Pred)); - } - - // Finally, perform the post-condition check of the ObjCMessageExpr and store - // the created nodes in 'Dst'. - if (!(!asLValue && ReceiverReturnsReference(ME))) { - CheckerVisit(ME, Dst, PostVisitSrc, false); - return; - } - - // Handle the case where the message expression returns a reference but - // we expect an rvalue. For such cases, convert the reference to - // an rvalue. - // FIXME: This conversion doesn't actually happen unless the result - // of ObjCMessageExpr is consumed by another expression. - ExplodedNodeSet DstRValueConvert; - CheckerVisit(ME, DstRValueConvert, PostVisitSrc, false); - QualType LoadTy = ME->getType(); - - static int *ConvertToRvalueTag = 0; - for (ExplodedNodeSet::iterator NI = DstRValueConvert.begin(), - NE = DstRValueConvert.end(); - NI!=NE; ++NI) { - const GRState *state = GetState(*NI); - EvalLoad(Dst, ME, *NI, state, state->getSVal(ME), - &ConvertToRvalueTag, LoadTy); - } -} - -//===----------------------------------------------------------------------===// -// Transfer functions: Miscellaneous statements. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred, - ExplodedNodeSet &Dst, bool asLValue) { - ExplodedNodeSet S1; - QualType T = CastE->getType(); - QualType ExTy = Ex->getType(); - - if (const ExplicitCastExpr *ExCast=dyn_cast_or_null(CastE)) - T = ExCast->getTypeAsWritten(); - - if (ExTy->isArrayType() || ExTy->isFunctionType() || T->isReferenceType() || - asLValue) - VisitLValue(Ex, Pred, S1); - else - Visit(Ex, Pred, S1); - - ExplodedNodeSet S2; - CheckerVisit(CastE, S2, S1, true); - - // If we are evaluating the cast in an lvalue context, we implicitly want - // the cast to evaluate to a location. - if (asLValue) { - ASTContext &Ctx = getContext(); - T = Ctx.getPointerType(Ctx.getCanonicalType(T)); - ExTy = Ctx.getPointerType(Ctx.getCanonicalType(ExTy)); - } - - switch (CastE->getCastKind()) { - case CastExpr::CK_ToVoid: - assert(!asLValue); - for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) - Dst.Add(*I); - return; - - case CastExpr::CK_NoOp: - case CastExpr::CK_FunctionToPointerDecay: - for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { - // Copy the SVal of Ex to CastE. - ExplodedNode *N = *I; - const GRState *state = GetState(N); - SVal V = state->getSVal(Ex); - state = state->BindExpr(CastE, V); - MakeNode(Dst, CastE, N, state); - } - return; - - case CastExpr::CK_Unknown: - case CastExpr::CK_ArrayToPointerDecay: - case CastExpr::CK_BitCast: - case CastExpr::CK_IntegralCast: - case CastExpr::CK_IntegralToPointer: - case CastExpr::CK_PointerToIntegral: - case CastExpr::CK_IntegralToFloating: - case CastExpr::CK_FloatingToIntegral: - case CastExpr::CK_FloatingCast: - case CastExpr::CK_AnyPointerToObjCPointerCast: - case CastExpr::CK_AnyPointerToBlockPointerCast: - case CastExpr::CK_DerivedToBase: - // Delegate to SValuator to process. - for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { - ExplodedNode* N = *I; - const GRState* state = GetState(N); - SVal V = state->getSVal(Ex); - const SValuator::CastResult &Res = SVator.EvalCast(V, state, T, ExTy); - state = Res.getState()->BindExpr(CastE, Res.getSVal()); - MakeNode(Dst, CastE, N, state); - } - return; - - default: - llvm::errs() << "Cast kind " << CastE->getCastKind() << " not handled.\n"; - assert(0); - } -} - -void GRExprEngine::VisitCompoundLiteralExpr(CompoundLiteralExpr* CL, - ExplodedNode* Pred, - ExplodedNodeSet& Dst, - bool asLValue) { - InitListExpr* ILE = cast(CL->getInitializer()->IgnoreParens()); - ExplodedNodeSet Tmp; - Visit(ILE, Pred, Tmp); - - for (ExplodedNodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I!=EI; ++I) { - const GRState* state = GetState(*I); - SVal ILV = state->getSVal(ILE); - const LocationContext *LC = (*I)->getLocationContext(); - state = state->bindCompoundLiteral(CL, LC, ILV); - - if (asLValue) { - MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL, LC))); - } - else - MakeNode(Dst, CL, *I, state->BindExpr(CL, ILV)); - } -} - -void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, - ExplodedNodeSet& Dst) { - - // The CFG has one DeclStmt per Decl. - Decl* D = *DS->decl_begin(); - - if (!D || !isa(D)) - return; - - const VarDecl* VD = dyn_cast(D); - Expr* InitEx = const_cast(VD->getInit()); - - // FIXME: static variables may have an initializer, but the second - // time a function is called those values may not be current. - ExplodedNodeSet Tmp; - - if (InitEx) { - if (VD->getType()->isReferenceType()) - VisitLValue(InitEx, Pred, Tmp); - else - Visit(InitEx, Pred, Tmp); - } - else - Tmp.Add(Pred); - - ExplodedNodeSet Tmp2; - CheckerVisit(DS, Tmp2, Tmp, true); - - for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) { - ExplodedNode *N = *I; - const GRState *state = GetState(N); - - // Decls without InitExpr are not initialized explicitly. - const LocationContext *LC = N->getLocationContext(); - - if (InitEx) { - SVal InitVal = state->getSVal(InitEx); - - // Recover some path-sensitivity if a scalar value evaluated to - // UnknownVal. - if (InitVal.isUnknown() || - !getConstraintManager().canReasonAbout(InitVal)) { - InitVal = ValMgr.getConjuredSymbolVal(NULL, InitEx, - Builder->getCurrentBlockCount()); - } - - EvalBind(Dst, DS, DS, *I, state, - loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); - } - else { - state = state->bindDeclWithNoInit(state->getRegion(VD, LC)); - MakeNode(Dst, DS, *I, state); - } - } -} - -void GRExprEngine::VisitCondInit(VarDecl *VD, Stmt *S, - ExplodedNode *Pred, ExplodedNodeSet& Dst) { - - Expr* InitEx = VD->getInit(); - ExplodedNodeSet Tmp; - Visit(InitEx, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - ExplodedNode *N = *I; - const GRState *state = GetState(N); - - const LocationContext *LC = N->getLocationContext(); - SVal InitVal = state->getSVal(InitEx); - - // Recover some path-sensitivity if a scalar value evaluated to - // UnknownVal. - if (InitVal.isUnknown() || - !getConstraintManager().canReasonAbout(InitVal)) { - InitVal = ValMgr.getConjuredSymbolVal(NULL, InitEx, - Builder->getCurrentBlockCount()); - } - - EvalBind(Dst, S, S, N, state, - loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); - } -} - -namespace { - // This class is used by VisitInitListExpr as an item in a worklist - // for processing the values contained in an InitListExpr. -class InitListWLItem { -public: - llvm::ImmutableList Vals; - ExplodedNode* N; - InitListExpr::reverse_iterator Itr; - - InitListWLItem(ExplodedNode* n, llvm::ImmutableList vals, - InitListExpr::reverse_iterator itr) - : Vals(vals), N(n), Itr(itr) {} -}; -} - - -void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - const GRState* state = GetState(Pred); - QualType T = getContext().getCanonicalType(E->getType()); - unsigned NumInitElements = E->getNumInits(); - - if (T->isArrayType() || T->isStructureType() || - T->isUnionType() || T->isVectorType()) { - - llvm::ImmutableList StartVals = getBasicVals().getEmptySValList(); - - // Handle base case where the initializer has no elements. - // e.g: static int* myArray[] = {}; - if (NumInitElements == 0) { - SVal V = ValMgr.makeCompoundVal(T, StartVals); - MakeNode(Dst, E, Pred, state->BindExpr(E, V)); - return; - } - - // Create a worklist to process the initializers. - llvm::SmallVector WorkList; - WorkList.reserve(NumInitElements); - WorkList.push_back(InitListWLItem(Pred, StartVals, E->rbegin())); - InitListExpr::reverse_iterator ItrEnd = E->rend(); - assert(!(E->rbegin() == E->rend())); - - // Process the worklist until it is empty. - while (!WorkList.empty()) { - InitListWLItem X = WorkList.back(); - WorkList.pop_back(); - - ExplodedNodeSet Tmp; - Visit(*X.Itr, X.N, Tmp); - - InitListExpr::reverse_iterator NewItr = X.Itr + 1; - - for (ExplodedNodeSet::iterator NI=Tmp.begin(),NE=Tmp.end();NI!=NE;++NI) { - // Get the last initializer value. - state = GetState(*NI); - SVal InitV = state->getSVal(cast(*X.Itr)); - - // Construct the new list of values by prepending the new value to - // the already constructed list. - llvm::ImmutableList NewVals = - getBasicVals().consVals(InitV, X.Vals); - - if (NewItr == ItrEnd) { - // Now we have a list holding all init values. Make CompoundValData. - SVal V = ValMgr.makeCompoundVal(T, NewVals); - - // Make final state and node. - MakeNode(Dst, E, *NI, state->BindExpr(E, V)); - } - else { - // Still some initializer values to go. Push them onto the worklist. - WorkList.push_back(InitListWLItem(*NI, NewVals, NewItr)); - } - } - } - - return; - } - - if (Loc::IsLocType(T) || T->isIntegerType()) { - assert (E->getNumInits() == 1); - ExplodedNodeSet Tmp; - Expr* Init = E->getInit(0); - Visit(Init, Pred, Tmp); - for (ExplodedNodeSet::iterator I=Tmp.begin(), EI=Tmp.end(); I != EI; ++I) { - state = GetState(*I); - MakeNode(Dst, E, *I, state->BindExpr(E, state->getSVal(Init))); - } - return; - } - - assert(0 && "unprocessed InitListExpr type"); -} - -/// VisitSizeOfAlignOfExpr - Transfer function for sizeof(type). -void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex, - ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - QualType T = Ex->getTypeOfArgument(); - CharUnits amt; - - if (Ex->isSizeOf()) { - if (T == getContext().VoidTy) { - // sizeof(void) == 1 byte. - amt = CharUnits::One(); - } - else if (!T.getTypePtr()->isConstantSizeType()) { - // FIXME: Add support for VLAs. - return; - } - else if (T->isObjCInterfaceType()) { - // Some code tries to take the sizeof an ObjCInterfaceType, relying that - // the compiler has laid out its representation. Just report Unknown - // for these. - return; - } - else { - // All other cases. - amt = getContext().getTypeSizeInChars(T); - } - } - else // Get alignment of the type. - amt = CharUnits::fromQuantity(getContext().getTypeAlign(T) / 8); - - MakeNode(Dst, Ex, Pred, - GetState(Pred)->BindExpr(Ex, - ValMgr.makeIntVal(amt.getQuantity(), Ex->getType()))); -} - - -void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue) { - - switch (U->getOpcode()) { - - default: - break; - - case UnaryOperator::Deref: { - - Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - - const GRState* state = GetState(*I); - SVal location = state->getSVal(Ex); - - if (asLValue) - MakeNode(Dst, U, *I, state->BindExpr(U, location), - ProgramPoint::PostLValueKind); - else - EvalLoad(Dst, U, *I, state, location); - } - - return; - } - - case UnaryOperator::Real: { - - Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - - // FIXME: We don't have complex SValues yet. - if (Ex->getType()->isAnyComplexType()) { - // Just report "Unknown." - Dst.Add(*I); - continue; - } - - // For all other types, UnaryOperator::Real is an identity operation. - assert (U->getType() == Ex->getType()); - const GRState* state = GetState(*I); - MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); - } - - return; - } - - case UnaryOperator::Imag: { - - Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - // FIXME: We don't have complex SValues yet. - if (Ex->getType()->isAnyComplexType()) { - // Just report "Unknown." - Dst.Add(*I); - continue; - } - - // For all other types, UnaryOperator::Float returns 0. - assert (Ex->getType()->isIntegerType()); - const GRState* state = GetState(*I); - SVal X = ValMgr.makeZeroVal(Ex->getType()); - MakeNode(Dst, U, *I, state->BindExpr(U, X)); - } - - return; - } - - case UnaryOperator::OffsetOf: { - Expr::EvalResult Res; - if (U->Evaluate(Res, getContext()) && Res.Val.isInt()) { - const APSInt &IV = Res.Val.getInt(); - assert(IV.getBitWidth() == getContext().getTypeSize(U->getType())); - assert(U->getType()->isIntegerType()); - assert(IV.isSigned() == U->getType()->isSignedIntegerType()); - SVal X = ValMgr.makeIntVal(IV); - MakeNode(Dst, U, Pred, GetState(Pred)->BindExpr(U, X)); - return; - } - // FIXME: Handle the case where __builtin_offsetof is not a constant. - Dst.Add(Pred); - return; - } - - case UnaryOperator::Plus: assert (!asLValue); // FALL-THROUGH. - case UnaryOperator::Extension: { - - // Unary "+" is a no-op, similar to a parentheses. We still have places - // where it may be a block-level expression, so we need to - // generate an extra node that just propagates the value of the - // subexpression. - - Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); - } - - return; - } - - case UnaryOperator::AddrOf: { - - assert(!asLValue); - Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - VisitLValue(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - SVal V = state->getSVal(Ex); - state = state->BindExpr(U, V); - MakeNode(Dst, U, *I, state); - } - - return; - } - - case UnaryOperator::LNot: - case UnaryOperator::Minus: - case UnaryOperator::Not: { - - assert (!asLValue); - Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - - // Get the value of the subexpression. - SVal V = state->getSVal(Ex); - - if (V.isUnknownOrUndef()) { - MakeNode(Dst, U, *I, state->BindExpr(U, V)); - continue; - } - -// QualType DstT = getContext().getCanonicalType(U->getType()); -// QualType SrcT = getContext().getCanonicalType(Ex->getType()); -// -// if (DstT != SrcT) // Perform promotions. -// V = EvalCast(V, DstT); -// -// if (V.isUnknownOrUndef()) { -// MakeNode(Dst, U, *I, BindExpr(St, U, V)); -// continue; -// } - - switch (U->getOpcode()) { - default: - assert(false && "Invalid Opcode."); - break; - - case UnaryOperator::Not: - // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, EvalComplement(cast(V))); - break; - - case UnaryOperator::Minus: - // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, EvalMinus(cast(V))); - break; - - case UnaryOperator::LNot: - - // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." - // - // Note: technically we do "E == 0", but this is the same in the - // transfer functions as "0 == E". - SVal Result; - - if (isa(V)) { - Loc X = ValMgr.makeNull(); - Result = EvalBinOp(state, BinaryOperator::EQ, cast(V), X, - U->getType()); - } - else { - nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); - Result = EvalBinOp(state, BinaryOperator::EQ, cast(V), X, - U->getType()); - } - - state = state->BindExpr(U, Result); - - break; - } - - MakeNode(Dst, U, *I, state); - } - - return; - } - } - - // Handle ++ and -- (both pre- and post-increment). - - assert (U->isIncrementDecrementOp()); - ExplodedNodeSet Tmp; - Expr* Ex = U->getSubExpr()->IgnoreParens(); - VisitLValue(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { - - const GRState* state = GetState(*I); - SVal V1 = state->getSVal(Ex); - - // Perform a load. - ExplodedNodeSet Tmp2; - EvalLoad(Tmp2, Ex, *I, state, V1); - - for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) { - - state = GetState(*I2); - SVal V2_untested = state->getSVal(Ex); - - // Propagate unknown and undefined values. - if (V2_untested.isUnknownOrUndef()) { - MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested)); - continue; - } - DefinedSVal V2 = cast(V2_untested); - - // Handle all other values. - BinaryOperator::Opcode Op = U->isIncrementOp() ? BinaryOperator::Add - : BinaryOperator::Sub; - - // If the UnaryOperator has non-location type, use its type to create the - // constant value. If the UnaryOperator has location type, create the - // constant with int type and pointer width. - SVal RHS; - - if (U->getType()->isAnyPointerType()) - RHS = ValMgr.makeIntValWithPtrWidth(1, false); - else - RHS = ValMgr.makeIntVal(1, U->getType()); - - SVal Result = EvalBinOp(state, Op, V2, RHS, U->getType()); - - // Conjure a new symbol if necessary to recover precision. - if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){ - DefinedOrUnknownSVal SymVal = - ValMgr.getConjuredSymbolVal(NULL, Ex, - Builder->getCurrentBlockCount()); - Result = SymVal; - - // If the value is a location, ++/-- should always preserve - // non-nullness. Check if the original value was non-null, and if so - // propagate that constraint. - if (Loc::IsLocType(U->getType())) { - DefinedOrUnknownSVal Constraint = - SVator.EvalEQ(state, V2, ValMgr.makeZeroVal(U->getType())); - - if (!state->Assume(Constraint, true)) { - // It isn't feasible for the original value to be null. - // Propagate this constraint. - Constraint = SVator.EvalEQ(state, SymVal, - ValMgr.makeZeroVal(U->getType())); - - - state = state->Assume(Constraint, false); - assert(state); - } - } - } - - state = state->BindExpr(U, U->isPostfix() ? V2 : Result); - - // Perform the store. - EvalStore(Dst, NULL, U, *I2, state, V1, Result); - } - } -} - - -void GRExprEngine::VisitCXXThisExpr(CXXThisExpr *TE, ExplodedNode *Pred, - ExplodedNodeSet & Dst) { - // Get the this object region from StoreManager. - const MemRegion *R = - ValMgr.getRegionManager().getCXXThisRegion(TE->getType(), - Pred->getLocationContext()); - - const GRState *state = GetState(Pred); - SVal V = state->getSVal(loc::MemRegionVal(R)); - MakeNode(Dst, TE, Pred, state->BindExpr(TE, V)); -} - -void GRExprEngine::VisitAsmStmt(AsmStmt* A, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst); -} - -void GRExprEngine::VisitAsmStmtHelperOutputs(AsmStmt* A, - AsmStmt::outputs_iterator I, - AsmStmt::outputs_iterator E, - ExplodedNode* Pred, ExplodedNodeSet& Dst) { - if (I == E) { - VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst); - return; - } - - ExplodedNodeSet Tmp; - VisitLValue(*I, Pred, Tmp); - - ++I; - - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end();NI != NE;++NI) - VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst); -} - -void GRExprEngine::VisitAsmStmtHelperInputs(AsmStmt* A, - AsmStmt::inputs_iterator I, - AsmStmt::inputs_iterator E, - ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - if (I == E) { - - // We have processed both the inputs and the outputs. All of the outputs - // should evaluate to Locs. Nuke all of their values. - - // FIXME: Some day in the future it would be nice to allow a "plug-in" - // which interprets the inline asm and stores proper results in the - // outputs. - - const GRState* state = GetState(Pred); - - for (AsmStmt::outputs_iterator OI = A->begin_outputs(), - OE = A->end_outputs(); OI != OE; ++OI) { - - SVal X = state->getSVal(*OI); - assert (!isa(X)); // Should be an Lval, or unknown, undef. - - if (isa(X)) - state = state->bindLoc(cast(X), UnknownVal()); - } - - MakeNode(Dst, A, Pred, state); - return; - } - - ExplodedNodeSet Tmp; - Visit(*I, Pred, Tmp); - - ++I; - - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI!=NE; ++NI) - VisitAsmStmtHelperInputs(A, I, E, *NI, Dst); -} - -void GRExprEngine::VisitReturnStmt(ReturnStmt *RS, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - - ExplodedNodeSet Src; - if (Expr *RetE = RS->getRetValue()) { - Visit(RetE, Pred, Src); - } - else { - Src.Add(Pred); - } - - ExplodedNodeSet CheckedSet; - CheckerVisit(RS, CheckedSet, Src, true); - - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I != E; ++I) { - - assert(Builder && "GRStmtNodeBuilder must be defined."); - - Pred = *I; - unsigned size = Dst.size(); - - SaveAndRestore OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->HasGeneratedNode); - - getTF().EvalReturn(Dst, *this, *Builder, RS, Pred); - - // Handle the case where no nodes where generated. - if (!Builder->BuildSinks && Dst.size() == size && - !Builder->HasGeneratedNode) - MakeNode(Dst, RS, Pred, GetState(Pred)); - } -} - -//===----------------------------------------------------------------------===// -// Transfer functions: Binary operators. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, - ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue) { - - ExplodedNodeSet Tmp1; - Expr* LHS = B->getLHS()->IgnoreParens(); - Expr* RHS = B->getRHS()->IgnoreParens(); - - // FIXME: Add proper support for ObjCImplicitSetterGetterRefExpr. - if (isa(LHS)) { - Visit(RHS, Pred, Dst); - return; - } - - if (B->isAssignmentOp()) - VisitLValue(LHS, Pred, Tmp1); - else - Visit(LHS, Pred, Tmp1); - - ExplodedNodeSet Tmp3; - - for (ExplodedNodeSet::iterator I1=Tmp1.begin(), E1=Tmp1.end(); I1!=E1; ++I1) { - SVal LeftV = (*I1)->getState()->getSVal(LHS); - ExplodedNodeSet Tmp2; - Visit(RHS, *I1, Tmp2); - - ExplodedNodeSet CheckedSet; - CheckerVisit(B, CheckedSet, Tmp2, true); - - // With both the LHS and RHS evaluated, process the operation itself. - - for (ExplodedNodeSet::iterator I2=CheckedSet.begin(), E2=CheckedSet.end(); - I2 != E2; ++I2) { - - const GRState *state = GetState(*I2); - const GRState *OldSt = state; - SVal RightV = state->getSVal(RHS); - - BinaryOperator::Opcode Op = B->getOpcode(); - - if (Op == BinaryOperator::Assign) { - // EXPERIMENTAL: "Conjured" symbols. - // FIXME: Handle structs. - QualType T = RHS->getType(); - - if ((RightV.isUnknown()||!getConstraintManager().canReasonAbout(RightV)) - && (Loc::IsLocType(T) || (T->isScalarType()&&T->isIntegerType()))) { - unsigned Count = Builder->getCurrentBlockCount(); - RightV = ValMgr.getConjuredSymbolVal(NULL, B->getRHS(), Count); - } - - SVal ExprVal = asLValue ? LeftV : RightV; - - // Simulate the effects of a "store": bind the value of the RHS - // to the L-Value represented by the LHS. - EvalStore(Tmp3, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV,RightV); - continue; - } - - if (!B->isAssignmentOp()) { - // Process non-assignments except commas or short-circuited - // logical expressions (LAnd and LOr). - SVal Result = EvalBinOp(state, Op, LeftV, RightV, B->getType()); - - if (Result.isUnknown()) { - if (OldSt != state) { - // Generate a new node if we have already created a new state. - MakeNode(Tmp3, B, *I2, state); - } - else - Tmp3.Add(*I2); - - continue; - } - - state = state->BindExpr(B, Result); - - MakeNode(Tmp3, B, *I2, state); - continue; - } - - assert (B->isCompoundAssignmentOp()); - - switch (Op) { - default: - assert(0 && "Invalid opcode for compound assignment."); - case BinaryOperator::MulAssign: Op = BinaryOperator::Mul; break; - case BinaryOperator::DivAssign: Op = BinaryOperator::Div; break; - case BinaryOperator::RemAssign: Op = BinaryOperator::Rem; break; - case BinaryOperator::AddAssign: Op = BinaryOperator::Add; break; - case BinaryOperator::SubAssign: Op = BinaryOperator::Sub; break; - case BinaryOperator::ShlAssign: Op = BinaryOperator::Shl; break; - case BinaryOperator::ShrAssign: Op = BinaryOperator::Shr; break; - case BinaryOperator::AndAssign: Op = BinaryOperator::And; break; - case BinaryOperator::XorAssign: Op = BinaryOperator::Xor; break; - case BinaryOperator::OrAssign: Op = BinaryOperator::Or; break; - } - - // Perform a load (the LHS). This performs the checks for - // null dereferences, and so on. - ExplodedNodeSet Tmp4; - SVal location = state->getSVal(LHS); - EvalLoad(Tmp4, LHS, *I2, state, location); - - for (ExplodedNodeSet::iterator I4=Tmp4.begin(), E4=Tmp4.end(); I4!=E4; - ++I4) { - state = GetState(*I4); - SVal V = state->getSVal(LHS); - - // Get the computation type. - QualType CTy = - cast(B)->getComputationResultType(); - CTy = getContext().getCanonicalType(CTy); - - QualType CLHSTy = - cast(B)->getComputationLHSType(); - CLHSTy = getContext().getCanonicalType(CLHSTy); - - QualType LTy = getContext().getCanonicalType(LHS->getType()); - QualType RTy = getContext().getCanonicalType(RHS->getType()); - - // Promote LHS. - llvm::tie(state, V) = SVator.EvalCast(V, state, CLHSTy, LTy); - - // Compute the result of the operation. - SVal Result; - llvm::tie(state, Result) = SVator.EvalCast(EvalBinOp(state, Op, V, - RightV, CTy), - state, B->getType(), CTy); - - // EXPERIMENTAL: "Conjured" symbols. - // FIXME: Handle structs. - - SVal LHSVal; - - if ((Result.isUnknown() || - !getConstraintManager().canReasonAbout(Result)) - && (Loc::IsLocType(CTy) - || (CTy->isScalarType() && CTy->isIntegerType()))) { - - unsigned Count = Builder->getCurrentBlockCount(); - - // The symbolic value is actually for the type of the left-hand side - // expression, not the computation type, as this is the value the - // LValue on the LHS will bind to. - LHSVal = ValMgr.getConjuredSymbolVal(NULL, B->getRHS(), LTy, Count); - - // However, we need to convert the symbol to the computation type. - llvm::tie(state, Result) = SVator.EvalCast(LHSVal, state, CTy, LTy); - } - else { - // The left-hand side may bind to a different value then the - // computation type. - llvm::tie(state, LHSVal) = SVator.EvalCast(Result, state, LTy, CTy); - } - - EvalStore(Tmp3, B, LHS, *I4, state->BindExpr(B, Result), - location, LHSVal); - } - } - } - - CheckerVisit(B, Dst, Tmp3, false); -} - -void GRExprEngine::CreateCXXTemporaryObject(Expr *Ex, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { - const GRState *state = GetState(*I); - - // Bind the temporary object to the value of the expression. Then bind - // the expression to the location of the object. - SVal V = state->getSVal(Ex); - - const MemRegion *R = - ValMgr.getRegionManager().getCXXObjectRegion(Ex, - Pred->getLocationContext()); - - state = state->bindLoc(loc::MemRegionVal(R), V); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, loc::MemRegionVal(R))); - } -} - -//===----------------------------------------------------------------------===// -// Checker registration/lookup. -//===----------------------------------------------------------------------===// - -Checker *GRExprEngine::lookupChecker(void *tag) const { - CheckerMap::const_iterator I = CheckerM.find(tag); - return (I == CheckerM.end()) ? NULL : Checkers[I->second].second; -} - -//===----------------------------------------------------------------------===// -// Visualization. -//===----------------------------------------------------------------------===// - -#ifndef NDEBUG -static GRExprEngine* GraphPrintCheckerState; -static SourceManager* GraphPrintSourceManager; - -namespace llvm { -template<> -struct DOTGraphTraits : - public DefaultDOTGraphTraits { - - DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} - - // FIXME: Since we do not cache error nodes in GRExprEngine now, this does not - // work. - static std::string getNodeAttributes(const ExplodedNode* N, void*) { - -#if 0 - // FIXME: Replace with a general scheme to tell if the node is - // an error node. - if (GraphPrintCheckerState->isImplicitNullDeref(N) || - GraphPrintCheckerState->isExplicitNullDeref(N) || - GraphPrintCheckerState->isUndefDeref(N) || - GraphPrintCheckerState->isUndefStore(N) || - GraphPrintCheckerState->isUndefControlFlow(N) || - GraphPrintCheckerState->isUndefResult(N) || - GraphPrintCheckerState->isBadCall(N) || - GraphPrintCheckerState->isUndefArg(N)) - return "color=\"red\",style=\"filled\""; - - if (GraphPrintCheckerState->isNoReturnCall(N)) - return "color=\"blue\",style=\"filled\""; -#endif - return ""; - } - - static std::string getNodeLabel(const ExplodedNode* N, void*){ - - std::string sbuf; - llvm::raw_string_ostream Out(sbuf); - - // Program Location. - ProgramPoint Loc = N->getLocation(); - - switch (Loc.getKind()) { - case ProgramPoint::BlockEntranceKind: - Out << "Block Entrance: B" - << cast(Loc).getBlock()->getBlockID(); - break; - - case ProgramPoint::BlockExitKind: - assert (false); - break; - - default: { - if (StmtPoint *L = dyn_cast(&Loc)) { - const Stmt* S = L->getStmt(); - SourceLocation SLoc = S->getLocStart(); - - Out << S->getStmtClassName() << ' ' << (void*) S << ' '; - LangOptions LO; // FIXME. - S->printPretty(Out, 0, PrintingPolicy(LO)); - - if (SLoc.isFileID()) { - Out << "\\lline=" - << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) - << " col=" - << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc) - << "\\l"; - } - - if (isa(Loc)) - Out << "\\lPreStmt\\l;"; - else if (isa(Loc)) - Out << "\\lPostLoad\\l;"; - else if (isa(Loc)) - Out << "\\lPostStore\\l"; - else if (isa(Loc)) - Out << "\\lPostLValue\\l"; - -#if 0 - // FIXME: Replace with a general scheme to determine - // the name of the check. - if (GraphPrintCheckerState->isImplicitNullDeref(N)) - Out << "\\|Implicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isExplicitNullDeref(N)) - Out << "\\|Explicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isUndefDeref(N)) - Out << "\\|Dereference of undefialied value.\\l"; - else if (GraphPrintCheckerState->isUndefStore(N)) - Out << "\\|Store to Undefined Loc."; - else if (GraphPrintCheckerState->isUndefResult(N)) - Out << "\\|Result of operation is undefined."; - else if (GraphPrintCheckerState->isNoReturnCall(N)) - Out << "\\|Call to function marked \"noreturn\"."; - else if (GraphPrintCheckerState->isBadCall(N)) - Out << "\\|Call to NULL/Undefined."; - else if (GraphPrintCheckerState->isUndefArg(N)) - Out << "\\|Argument in call is undefined"; -#endif - - break; - } - - const BlockEdge& E = cast(Loc); - Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" - << E.getDst()->getBlockID() << ')'; - - if (Stmt* T = E.getSrc()->getTerminator()) { - - SourceLocation SLoc = T->getLocStart(); - - Out << "\\|Terminator: "; - LangOptions LO; // FIXME. - E.getSrc()->printTerminator(Out, LO); - - if (SLoc.isFileID()) { - Out << "\\lline=" - << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) - << " col=" - << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc); - } - - if (isa(T)) { - Stmt* Label = E.getDst()->getLabel(); - - if (Label) { - if (CaseStmt* C = dyn_cast(Label)) { - Out << "\\lcase "; - LangOptions LO; // FIXME. - C->getLHS()->printPretty(Out, 0, PrintingPolicy(LO)); - - if (Stmt* RHS = C->getRHS()) { - Out << " .. "; - RHS->printPretty(Out, 0, PrintingPolicy(LO)); - } - - Out << ":"; - } - else { - assert (isa(Label)); - Out << "\\ldefault:"; - } - } - else - Out << "\\l(implicit) default:"; - } - else if (isa(T)) { - // FIXME - } - else { - Out << "\\lCondition: "; - if (*E.getSrc()->succ_begin() == E.getDst()) - Out << "true"; - else - Out << "false"; - } - - Out << "\\l"; - } - -#if 0 - // FIXME: Replace with a general scheme to determine - // the name of the check. - if (GraphPrintCheckerState->isUndefControlFlow(N)) { - Out << "\\|Control-flow based on\\lUndefined value.\\l"; - } -#endif - } - } - - Out << "\\|StateID: " << (void*) N->getState() << "\\|"; - - const GRState *state = N->getState(); - state->printDOT(Out); - - Out << "\\l"; - return Out.str(); - } -}; -} // end llvm namespace -#endif - -#ifndef NDEBUG -template -ExplodedNode* GetGraphNode(ITERATOR I) { return *I; } - -template <> ExplodedNode* -GetGraphNode::iterator> - (llvm::DenseMap::iterator I) { - return I->first; -} -#endif - -void GRExprEngine::ViewGraph(bool trim) { -#ifndef NDEBUG - if (trim) { - std::vector Src; - - // Flush any outstanding reports to make sure we cover all the nodes. - // This does not cause them to get displayed. - for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) - const_cast(*I)->FlushReports(BR); - - // Iterate through the reports and get their nodes. - for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) { - for (BugType::const_iterator I2=(*I)->begin(), E2=(*I)->end(); - I2!=E2; ++I2) { - const BugReportEquivClass& EQ = *I2; - const BugReport &R = **EQ.begin(); - ExplodedNode *N = const_cast(R.getEndNode()); - if (N) Src.push_back(N); - } - } - - ViewGraph(&Src[0], &Src[0]+Src.size()); - } - else { - GraphPrintCheckerState = this; - GraphPrintSourceManager = &getContext().getSourceManager(); - - llvm::ViewGraph(*G.roots_begin(), "GRExprEngine"); - - GraphPrintCheckerState = NULL; - GraphPrintSourceManager = NULL; - } -#endif -} - -void GRExprEngine::ViewGraph(ExplodedNode** Beg, ExplodedNode** End) { -#ifndef NDEBUG - GraphPrintCheckerState = this; - GraphPrintSourceManager = &getContext().getSourceManager(); - - std::auto_ptr TrimmedG(G.Trim(Beg, End).first); - - if (!TrimmedG.get()) - llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; - else - llvm::ViewGraph(*TrimmedG->roots_begin(), "TrimmedGRExprEngine"); - - GraphPrintCheckerState = NULL; - GraphPrintSourceManager = NULL; -#endif -} diff --git a/lib/Analysis/GRExprEngineExperimentalChecks.cpp b/lib/Analysis/GRExprEngineExperimentalChecks.cpp deleted file mode 100644 index 33479b0cb7e5..000000000000 --- a/lib/Analysis/GRExprEngineExperimentalChecks.cpp +++ /dev/null @@ -1,40 +0,0 @@ -//=-- GRExprEngineExperimentalChecks.h ------------------------------*- C++ -*-= -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines functions to instantiate and register experimental -// checks in GRExprEngine. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "GRExprEngineExperimentalChecks.h" -#include "clang/Analysis/LocalCheckers.h" - -using namespace clang; - -void clang::RegisterExperimentalChecks(GRExprEngine &Eng) { - // These are checks that never belong as internal checks - // within GRExprEngine. - RegisterPthreadLockChecker(Eng); - RegisterMallocChecker(Eng); -} - -void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) { - // These are internal checks that should eventually migrate to - // RegisterInternalChecks() once they have been further tested. - - // Note that this must be registered after ReturnStackAddresEngsChecker. - RegisterReturnPointerRangeChecker(Eng); - - RegisterFixedAddressChecker(Eng); - RegisterPointerSubChecker(Eng); - RegisterPointerArithChecker(Eng); - RegisterCastToStructChecker(Eng); - RegisterArrayBoundChecker(Eng); -} diff --git a/lib/Analysis/GRExprEngineExperimentalChecks.h b/lib/Analysis/GRExprEngineExperimentalChecks.h deleted file mode 100644 index 9a9da32e556e..000000000000 --- a/lib/Analysis/GRExprEngineExperimentalChecks.h +++ /dev/null @@ -1,26 +0,0 @@ -//=-- GRExprEngineExperimentalChecks.h ------------------------------*- C++ -*-= -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines functions to instantiate and register experimental -// checks in GRExprEngine. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_GREXPRENGINE_EXPERIMENTAL_CHECKS -#define LLVM_CLANG_GREXPRENGINE_EXPERIMENTAL_CHECKS - -namespace clang { - -class GRExprEngine; - -void RegisterPthreadLockChecker(GRExprEngine &Eng); -void RegisterMallocChecker(GRExprEngine &Eng); - -} // end clang namespace -#endif diff --git a/lib/Analysis/GRExprEngineInternalChecks.h b/lib/Analysis/GRExprEngineInternalChecks.h deleted file mode 100644 index e2354ed09888..000000000000 --- a/lib/Analysis/GRExprEngineInternalChecks.h +++ /dev/null @@ -1,44 +0,0 @@ -//=-- GRExprEngineInternalChecks.h- Builtin GRExprEngine Checks -----*- C++ -*-= -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines functions to instantiate and register the "built-in" -// checks in GRExprEngine. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_GREXPRENGINE_INTERNAL_CHECKS -#define LLVM_CLANG_GREXPRENGINE_INTERNAL_CHECKS - -namespace clang { - -class GRExprEngine; - -void RegisterAttrNonNullChecker(GRExprEngine &Eng); -void RegisterDereferenceChecker(GRExprEngine &Eng); -void RegisterDivZeroChecker(GRExprEngine &Eng); -void RegisterReturnPointerRangeChecker(GRExprEngine &Eng); -void RegisterReturnStackAddressChecker(GRExprEngine &Eng); -void RegisterReturnUndefChecker(GRExprEngine &Eng); -void RegisterVLASizeChecker(GRExprEngine &Eng); -void RegisterPointerSubChecker(GRExprEngine &Eng); -void RegisterPointerArithChecker(GRExprEngine &Eng); -void RegisterFixedAddressChecker(GRExprEngine &Eng); -void RegisterCastToStructChecker(GRExprEngine &Eng); -void RegisterCallAndMessageChecker(GRExprEngine &Eng); -void RegisterArrayBoundChecker(GRExprEngine &Eng); -void RegisterUndefinedArraySubscriptChecker(GRExprEngine &Eng); -void RegisterUndefinedAssignmentChecker(GRExprEngine &Eng); -void RegisterUndefBranchChecker(GRExprEngine &Eng); -void RegisterUndefResultChecker(GRExprEngine &Eng); - -void RegisterNoReturnFunctionChecker(GRExprEngine &Eng); -void RegisterBuiltinFunctionChecker(GRExprEngine &Eng); -void RegisterOSAtomicChecker(GRExprEngine &Eng); -} // end clang namespace -#endif diff --git a/lib/Analysis/GRState.cpp b/lib/Analysis/GRState.cpp deleted file mode 100644 index 051d465f41b7..000000000000 --- a/lib/Analysis/GRState.cpp +++ /dev/null @@ -1,358 +0,0 @@ -//= GRState.cpp - Path-Sensitive "State" for tracking values -----*- C++ -*--=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements GRState and GRStateManager. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -// Give the vtable for ConstraintManager somewhere to live. -// FIXME: Move this elsewhere. -ConstraintManager::~ConstraintManager() {} - -GRStateManager::~GRStateManager() { - for (std::vector::iterator I=Printers.begin(), - E=Printers.end(); I!=E; ++I) - delete *I; - - for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end(); - I!=E; ++I) - I->second.second(I->second.first); -} - -const GRState* -GRStateManager::RemoveDeadBindings(const GRState* state, Stmt* Loc, - SymbolReaper& SymReaper) { - - // This code essentially performs a "mark-and-sweep" of the VariableBindings. - // The roots are any Block-level exprs and Decls that our liveness algorithm - // tells us are live. We then see what Decls they may reference, and keep - // those around. This code more than likely can be made faster, and the - // frequency of which this method is called should be experimented with - // for optimum performance. - llvm::SmallVector RegionRoots; - GRState NewState = *state; - - NewState.Env = EnvMgr.RemoveDeadBindings(NewState.Env, Loc, SymReaper, - state, RegionRoots); - - // Clean up the store. - StoreMgr->RemoveDeadBindings(NewState, Loc, SymReaper, RegionRoots); - - return ConstraintMgr->RemoveDeadBindings(getPersistentState(NewState), - SymReaper); -} - -const GRState *GRState::unbindLoc(Loc LV) const { - Store OldStore = getStore(); - Store NewStore = getStateManager().StoreMgr->Remove(OldStore, LV); - - if (NewStore == OldStore) - return this; - - GRState NewSt = *this; - NewSt.St = NewStore; - return getStateManager().getPersistentState(NewSt); -} - -SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const { - // We only want to do fetches from regions that we can actually bind - // values. For example, SymbolicRegions of type 'id<...>' cannot - // have direct bindings (but their can be bindings on their subregions). - if (!R->isBoundable()) - return UnknownVal(); - - if (const TypedRegion *TR = dyn_cast(R)) { - QualType T = TR->getValueType(getStateManager().getContext()); - if (Loc::IsLocType(T) || T->isIntegerType()) - return getSVal(R); - } - - return UnknownVal(); -} - - -const GRState *GRState::BindExpr(const Stmt* Ex, SVal V, bool Invalidate) const{ - Environment NewEnv = getStateManager().EnvMgr.BindExpr(Env, Ex, V, - Invalidate); - if (NewEnv == Env) - return this; - - GRState NewSt = *this; - NewSt.Env = NewEnv; - return getStateManager().getPersistentState(NewSt); -} - -const GRState* GRStateManager::getInitialState(const LocationContext *InitLoc) { - GRState State(this, - EnvMgr.getInitialEnvironment(InitLoc->getAnalysisContext()), - StoreMgr->getInitialStore(InitLoc), - GDMFactory.GetEmptyMap()); - - return getPersistentState(State); -} - -const GRState* GRStateManager::getPersistentState(GRState& State) { - - llvm::FoldingSetNodeID ID; - State.Profile(ID); - void* InsertPos; - - if (GRState* I = StateSet.FindNodeOrInsertPos(ID, InsertPos)) - return I; - - GRState* I = (GRState*) Alloc.Allocate(); - new (I) GRState(State); - StateSet.InsertNode(I, InsertPos); - return I; -} - -const GRState* GRState::makeWithStore(Store store) const { - GRState NewSt = *this; - NewSt.St = store; - return getStateManager().getPersistentState(NewSt); -} - -//===----------------------------------------------------------------------===// -// State pretty-printing. -//===----------------------------------------------------------------------===// - -void GRState::print(llvm::raw_ostream& Out, const char* nl, - const char* sep) const { - // Print the store. - GRStateManager &Mgr = getStateManager(); - Mgr.getStoreManager().print(getStore(), Out, nl, sep); - - CFG &C = *getAnalysisContext().getCFG(); - - // Print Subexpression bindings. - bool isFirst = true; - - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (C.isBlkExpr(I.getKey())) - continue; - - if (isFirst) { - Out << nl << nl << "Sub-Expressions:" << nl; - isFirst = false; - } - else { Out << nl; } - - Out << " (" << (void*) I.getKey() << ") "; - LangOptions LO; // FIXME. - I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); - Out << " : " << I.getData(); - } - - // Print block-expression bindings. - isFirst = true; - - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (!C.isBlkExpr(I.getKey())) - continue; - - if (isFirst) { - Out << nl << nl << "Block-level Expressions:" << nl; - isFirst = false; - } - else { Out << nl; } - - Out << " (" << (void*) I.getKey() << ") "; - LangOptions LO; // FIXME. - I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); - Out << " : " << I.getData(); - } - - Mgr.getConstraintManager().print(this, Out, nl, sep); - - // Print checker-specific data. - for (std::vector::iterator I = Mgr.Printers.begin(), - E = Mgr.Printers.end(); I != E; ++I) { - (*I)->Print(Out, this, nl, sep); - } -} - -void GRState::printDOT(llvm::raw_ostream& Out) const { - print(Out, "\\l", "\\|"); -} - -void GRState::printStdErr() const { - print(llvm::errs()); -} - -//===----------------------------------------------------------------------===// -// Generic Data Map. -//===----------------------------------------------------------------------===// - -void* const* GRState::FindGDM(void* K) const { - return GDM.lookup(K); -} - -void* -GRStateManager::FindGDMContext(void* K, - void* (*CreateContext)(llvm::BumpPtrAllocator&), - void (*DeleteContext)(void*)) { - - std::pair& p = GDMContexts[K]; - if (!p.first) { - p.first = CreateContext(Alloc); - p.second = DeleteContext; - } - - return p.first; -} - -const GRState* GRStateManager::addGDM(const GRState* St, void* Key, void* Data){ - GRState::GenericDataMap M1 = St->getGDM(); - GRState::GenericDataMap M2 = GDMFactory.Add(M1, Key, Data); - - if (M1 == M2) - return St; - - GRState NewSt = *St; - NewSt.GDM = M2; - return getPersistentState(NewSt); -} - -//===----------------------------------------------------------------------===// -// Utility. -//===----------------------------------------------------------------------===// - -namespace { -class ScanReachableSymbols : public SubRegionMap::Visitor { - typedef llvm::DenseSet VisitedRegionsTy; - - VisitedRegionsTy visited; - const GRState *state; - SymbolVisitor &visitor; - llvm::OwningPtr SRM; -public: - - ScanReachableSymbols(const GRState *st, SymbolVisitor& v) - : state(st), visitor(v) {} - - bool scan(nonloc::CompoundVal val); - bool scan(SVal val); - bool scan(const MemRegion *R); - - // From SubRegionMap::Visitor. - bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) { - return scan(SubRegion); - } -}; -} - -bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { - for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I) - if (!scan(*I)) - return false; - - return true; -} - -bool ScanReachableSymbols::scan(SVal val) { - if (loc::MemRegionVal *X = dyn_cast(&val)) - return scan(X->getRegion()); - - if (nonloc::LocAsInteger *X = dyn_cast(&val)) - return scan(X->getLoc()); - - if (SymbolRef Sym = val.getAsSymbol()) - return visitor.VisitSymbol(Sym); - - if (nonloc::CompoundVal *X = dyn_cast(&val)) - return scan(*X); - - return true; -} - -bool ScanReachableSymbols::scan(const MemRegion *R) { - if (isa(R) || visited.count(R)) - return true; - - visited.insert(R); - - // If this is a symbolic region, visit the symbol for the region. - if (const SymbolicRegion *SR = dyn_cast(R)) - if (!visitor.VisitSymbol(SR->getSymbol())) - return false; - - // If this is a subregion, also visit the parent regions. - if (const SubRegion *SR = dyn_cast(R)) - if (!scan(SR->getSuperRegion())) - return false; - - // Now look at the binding to this region (if any). - if (!scan(state->getSValAsScalarOrLoc(R))) - return false; - - // Now look at the subregions. - if (!SRM.get()) - SRM.reset(state->getStateManager().getStoreManager().getSubRegionMap(state)); - - return SRM->iterSubRegions(R, *this); -} - -bool GRState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { - ScanReachableSymbols S(this, visitor); - return S.scan(val); -} - -bool GRState::scanReachableSymbols(const SVal *I, const SVal *E, - SymbolVisitor &visitor) const { - ScanReachableSymbols S(this, visitor); - for ( ; I != E; ++I) { - if (!S.scan(*I)) - return false; - } - return true; -} - -bool GRState::scanReachableSymbols(const MemRegion * const *I, - const MemRegion * const *E, - SymbolVisitor &visitor) const { - ScanReachableSymbols S(this, visitor); - for ( ; I != E; ++I) { - if (!S.scan(*I)) - return false; - } - return true; -} - -//===----------------------------------------------------------------------===// -// Queries. -//===----------------------------------------------------------------------===// - -bool GRStateManager::isEqual(const GRState* state, const Expr* Ex, - const llvm::APSInt& Y) { - - SVal V = state->getSVal(Ex); - - if (loc::ConcreteInt* X = dyn_cast(&V)) - return X->getValue() == Y; - - if (nonloc::ConcreteInt* X = dyn_cast(&V)) - return X->getValue() == Y; - - if (SymbolRef Sym = V.getAsSymbol()) - return ConstraintMgr->isEqual(state, Sym, Y); - - return false; -} - -bool GRStateManager::isEqual(const GRState* state, const Expr* Ex, uint64_t x) { - return isEqual(state, Ex, getBasicVals().getValue(x, Ex->getType())); -} diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp index 0b2620e609c2..94ed75286dee 100644 --- a/lib/Analysis/LiveVariables.cpp +++ b/lib/Analysis/LiveVariables.cpp @@ -19,7 +19,7 @@ #include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" #include "clang/Analysis/FlowSensitive/DataflowSolver.h" #include "clang/Analysis/Support/SaveAndRestore.h" -#include "clang/Analysis/PathSensitive/AnalysisContext.h" +#include "clang/Analysis/AnalysisContext.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/Analysis/Makefile b/lib/Analysis/Makefile index c597254fd2d7..d6411122e322 100644 --- a/lib/Analysis/Makefile +++ b/lib/Analysis/Makefile @@ -14,7 +14,6 @@ LEVEL = ../../../.. LIBRARYNAME := clangAnalysis BUILD_ARCHIVE = 1 -CXXFLAGS = -fno-rtti CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include diff --git a/lib/Analysis/MallocChecker.cpp b/lib/Analysis/MallocChecker.cpp deleted file mode 100644 index 28f4db788068..000000000000 --- a/lib/Analysis/MallocChecker.cpp +++ /dev/null @@ -1,336 +0,0 @@ -//=== MallocChecker.cpp - A malloc/free checker -------------------*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines malloc/free checker, which checks for potential memory -// leaks, double free, and use-after-free problems. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineExperimentalChecks.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/PathSensitive/SymbolManager.h" -#include "llvm/ADT/ImmutableMap.h" -using namespace clang; - -namespace { - -class RefState { - enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped } K; - const Stmt *S; - -public: - RefState(Kind k, const Stmt *s) : K(k), S(s) {} - - bool isAllocated() const { return K == AllocateUnchecked; } - bool isReleased() const { return K == Released; } - bool isEscaped() const { return K == Escaped; } - - bool operator==(const RefState &X) const { - return K == X.K && S == X.S; - } - - static RefState getAllocateUnchecked(const Stmt *s) { - return RefState(AllocateUnchecked, s); - } - static RefState getAllocateFailed() { - return RefState(AllocateFailed, 0); - } - static RefState getReleased(const Stmt *s) { return RefState(Released, s); } - static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); } - - void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(K); - ID.AddPointer(S); - } -}; - -class RegionState {}; - -class MallocChecker : public CheckerVisitor { - BuiltinBug *BT_DoubleFree; - BuiltinBug *BT_Leak; - IdentifierInfo *II_malloc, *II_free, *II_realloc; - -public: - MallocChecker() - : BT_DoubleFree(0), BT_Leak(0), II_malloc(0), II_free(0), II_realloc(0) {} - static void *getTag(); - bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); - void EvalDeadSymbols(CheckerContext &C,const Stmt *S,SymbolReaper &SymReaper); - void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); - const GRState *EvalAssume(const GRState *state, SVal Cond, bool Assumption); - -private: - void MallocMem(CheckerContext &C, const CallExpr *CE); - const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, - const Expr *SizeEx, const GRState *state); - void FreeMem(CheckerContext &C, const CallExpr *CE); - const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE, - const GRState *state); - - void ReallocMem(CheckerContext &C, const CallExpr *CE); -}; -} // end anonymous namespace - -typedef llvm::ImmutableMap RegionStateTy; - -namespace clang { - template <> - struct GRStateTrait - : public GRStatePartialTrait > { - static void *GDMIndex() { return MallocChecker::getTag(); } - }; -} - -void clang::RegisterMallocChecker(GRExprEngine &Eng) { - Eng.registerCheck(new MallocChecker()); -} - -void *MallocChecker::getTag() { - static int x; - return &x; -} - -bool MallocChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - - const FunctionDecl *FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - ASTContext &Ctx = C.getASTContext(); - if (!II_malloc) - II_malloc = &Ctx.Idents.get("malloc"); - if (!II_free) - II_free = &Ctx.Idents.get("free"); - if (!II_realloc) - II_realloc = &Ctx.Idents.get("realloc"); - - if (FD->getIdentifier() == II_malloc) { - MallocMem(C, CE); - return true; - } - - if (FD->getIdentifier() == II_free) { - FreeMem(C, CE); - return true; - } - - if (FD->getIdentifier() == II_realloc) { - ReallocMem(C, CE); - return true; - } - - return false; -} - -void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = MallocMemAux(C, CE, CE->getArg(0), C.getState()); - C.addTransition(state); -} - -const GRState *MallocChecker::MallocMemAux(CheckerContext &C, - const CallExpr *CE, - const Expr *SizeEx, - const GRState *state) { - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); - ValueManager &ValMgr = C.getValueManager(); - - SVal RetVal = ValMgr.getConjuredSymbolVal(NULL, CE, CE->getType(), Count); - - SVal Size = state->getSVal(SizeEx); - - state = C.getEngine().getStoreManager().setExtent(state, RetVal.getAsRegion(), - Size); - - state = state->BindExpr(CE, RetVal); - - SymbolRef Sym = RetVal.getAsLocSymbol(); - assert(Sym); - // Set the symbol's state to Allocated. - return state->set(Sym, RefState::getAllocateUnchecked(CE)); -} - -void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = FreeMemAux(C, CE, C.getState()); - - if (state) - C.addTransition(state); -} - -const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, - const GRState *state) { - SVal ArgVal = state->getSVal(CE->getArg(0)); - SymbolRef Sym = ArgVal.getAsLocSymbol(); - assert(Sym); - - const RefState *RS = state->get(Sym); - - // If the symbol has not been tracked, return. This is possible when free() is - // called on a pointer that does not get its pointee directly from malloc(). - // Full support of this requires inter-procedural analysis. - if (!RS) - return state; - - // Check double free. - if (RS->isReleased()) { - ExplodedNode *N = C.GenerateSink(); - if (N) { - if (!BT_DoubleFree) - BT_DoubleFree = new BuiltinBug("Double free", - "Try to free a memory block that has been released"); - // FIXME: should find where it's freed last time. - BugReport *R = new BugReport(*BT_DoubleFree, - BT_DoubleFree->getDescription(), N); - C.EmitReport(R); - } - return NULL; - } - - // Normal free. - return state->set(Sym, RefState::getReleased(CE)); -} - -void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Arg0 = CE->getArg(0); - DefinedOrUnknownSVal Arg0Val=cast(state->getSVal(Arg0)); - - ValueManager &ValMgr = C.getValueManager(); - SValuator &SVator = C.getSValuator(); - - DefinedOrUnknownSVal PtrEQ = SVator.EvalEQ(state, Arg0Val, ValMgr.makeNull()); - - // If the ptr is NULL, the call is equivalent to malloc(size). - if (const GRState *stateEqual = state->Assume(PtrEQ, true)) { - // Hack: set the NULL symbolic region to released to suppress false warning. - // In the future we should add more states for allocated regions, e.g., - // CheckedNull, CheckedNonNull. - - SymbolRef Sym = Arg0Val.getAsLocSymbol(); - if (Sym) - stateEqual = stateEqual->set(Sym, RefState::getReleased(CE)); - - const GRState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1), stateEqual); - C.addTransition(stateMalloc); - } - - if (const GRState *stateNotEqual = state->Assume(PtrEQ, false)) { - const Expr *Arg1 = CE->getArg(1); - DefinedOrUnknownSVal Arg1Val = - cast(stateNotEqual->getSVal(Arg1)); - DefinedOrUnknownSVal SizeZero = SVator.EvalEQ(stateNotEqual, Arg1Val, - ValMgr.makeIntValWithPtrWidth(0, false)); - - if (const GRState *stateSizeZero = stateNotEqual->Assume(SizeZero, true)) { - const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero); - if (stateFree) - C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true)); - } - - if (const GRState *stateSizeNotZero=stateNotEqual->Assume(SizeZero,false)) { - const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero); - if (stateFree) { - // FIXME: We should copy the content of the original buffer. - const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1), - stateFree); - C.addTransition(stateRealloc); - } - } - } -} - -void MallocChecker::EvalDeadSymbols(CheckerContext &C, const Stmt *S, - SymbolReaper &SymReaper) { - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) { - SymbolRef Sym = *I; - const GRState *state = C.getState(); - const RefState *RS = state->get(Sym); - if (!RS) - return; - - if (RS->isAllocated()) { - ExplodedNode *N = C.GenerateSink(); - if (N) { - if (!BT_Leak) - BT_Leak = new BuiltinBug("Memory leak", - "Allocated memory never released. Potential memory leak."); - // FIXME: where it is allocated. - BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); - C.EmitReport(R); - } - } - } -} - -void MallocChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag, - GRExprEngine &Eng) { - SaveAndRestore OldHasGen(B.HasGeneratedNode); - const GRState *state = B.getState(); - typedef llvm::ImmutableMap SymMap; - SymMap M = state->get(); - - for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { - RefState RS = I->second; - if (RS.isAllocated()) { - ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); - if (N) { - if (!BT_Leak) - BT_Leak = new BuiltinBug("Memory leak", - "Allocated memory never released. Potential memory leak."); - BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); - Eng.getBugReporter().EmitReport(R); - } - } - } -} - -void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { - const Expr *RetE = S->getRetValue(); - if (!RetE) - return; - - const GRState *state = C.getState(); - - SymbolRef Sym = state->getSVal(RetE).getAsSymbol(); - - if (!Sym) - return; - - const RefState *RS = state->get(Sym); - if (!RS) - return; - - // FIXME: check other cases. - if (RS->isAllocated()) - state = state->set(Sym, RefState::getEscaped(S)); - - C.addTransition(state); -} - -const GRState *MallocChecker::EvalAssume(const GRState *state, SVal Cond, - bool Assumption) { - // If a symblic region is assumed to NULL, set its state to AllocateFailed. - // FIXME: should also check symbols assumed to non-null. - - RegionStateTy RS = state->get(); - - for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { - if (state->getSymVal(I.getKey())) - state = state->set(I.getKey(),RefState::getAllocateFailed()); - } - - return state; -} diff --git a/lib/Analysis/ManagerRegistry.cpp b/lib/Analysis/ManagerRegistry.cpp deleted file mode 100644 index 8943db2a2343..000000000000 --- a/lib/Analysis/ManagerRegistry.cpp +++ /dev/null @@ -1,20 +0,0 @@ -//===- ManagerRegistry.cpp - Pluggble Analyzer module creators --*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the pluggable analyzer module creators. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/ManagerRegistry.h" - -using namespace clang; - -StoreManagerCreator ManagerRegistry::StoreMgrCreator = 0; - -ConstraintManagerCreator ManagerRegistry::ConstraintMgrCreator = 0; diff --git a/lib/Analysis/MemRegion.cpp b/lib/Analysis/MemRegion.cpp deleted file mode 100644 index 87d60d340934..000000000000 --- a/lib/Analysis/MemRegion.cpp +++ /dev/null @@ -1,800 +0,0 @@ -//== MemRegion.cpp - Abstract memory regions for static analysis --*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines MemRegion and its subclasses. MemRegion defines a -// partially-typed abstraction of memory useful for path-sensitive dataflow -// analyses. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Support/raw_ostream.h" -#include "clang/Analysis/PathSensitive/MemRegion.h" -#include "clang/Analysis/PathSensitive/ValueManager.h" -#include "clang/Analysis/PathSensitive/AnalysisContext.h" -#include "clang/AST/CharUnits.h" -#include "clang/AST/StmtVisitor.h" - -using namespace clang; - -//===----------------------------------------------------------------------===// -// MemRegion Construction. -//===----------------------------------------------------------------------===// - -template struct MemRegionManagerTrait; - -template -RegionTy* MemRegionManager::getRegion(const A1 a1) { - - const typename MemRegionManagerTrait::SuperRegionTy *superRegion = - MemRegionManagerTrait::getSuperRegion(*this, a1); - - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, superRegion); - void* InsertPos; - RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = (RegionTy*) A.Allocate(); - new (R) RegionTy(a1, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -template -RegionTy* MemRegionManager::getSubRegion(const A1 a1, - const MemRegion *superRegion) { - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, superRegion); - void* InsertPos; - RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = (RegionTy*) A.Allocate(); - new (R) RegionTy(a1, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -template -RegionTy* MemRegionManager::getRegion(const A1 a1, const A2 a2) { - - const typename MemRegionManagerTrait::SuperRegionTy *superRegion = - MemRegionManagerTrait::getSuperRegion(*this, a1, a2); - - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, a2, superRegion); - void* InsertPos; - RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = (RegionTy*) A.Allocate(); - new (R) RegionTy(a1, a2, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -template -RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, - const MemRegion *superRegion) { - - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, a2, superRegion); - void* InsertPos; - RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = (RegionTy*) A.Allocate(); - new (R) RegionTy(a1, a2, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -template -RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3, - const MemRegion *superRegion) { - - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, a2, a3, superRegion); - void* InsertPos; - RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = (RegionTy*) A.Allocate(); - new (R) RegionTy(a1, a2, a3, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -//===----------------------------------------------------------------------===// -// Object destruction. -//===----------------------------------------------------------------------===// - -MemRegion::~MemRegion() {} - -MemRegionManager::~MemRegionManager() { - // All regions and their data are BumpPtrAllocated. No need to call - // their destructors. -} - -//===----------------------------------------------------------------------===// -// Basic methods. -//===----------------------------------------------------------------------===// - -bool SubRegion::isSubRegionOf(const MemRegion* R) const { - const MemRegion* r = getSuperRegion(); - while (r != 0) { - if (r == R) - return true; - if (const SubRegion* sr = dyn_cast(r)) - r = sr->getSuperRegion(); - else - break; - } - return false; -} - -MemRegionManager* SubRegion::getMemRegionManager() const { - const SubRegion* r = this; - do { - const MemRegion *superRegion = r->getSuperRegion(); - if (const SubRegion *sr = dyn_cast(superRegion)) { - r = sr; - continue; - } - return superRegion->getMemRegionManager(); - } while (1); -} - -const StackFrameContext *VarRegion::getStackFrame() const { - const StackSpaceRegion *SSR = dyn_cast(getMemorySpace()); - return SSR ? SSR->getStackFrame() : NULL; -} - -//===----------------------------------------------------------------------===// -// FoldingSet profiling. -//===----------------------------------------------------------------------===// - -void MemSpaceRegion::Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddInteger((unsigned)getKind()); -} - -void StackSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned)getKind()); - ID.AddPointer(getStackFrame()); -} - -void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const StringLiteral* Str, - const MemRegion* superRegion) { - ID.AddInteger((unsigned) StringRegionKind); - ID.AddPointer(Str); - ID.AddPointer(superRegion); -} - -void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const Expr* Ex, unsigned cnt, - const MemRegion *) { - ID.AddInteger((unsigned) AllocaRegionKind); - ID.AddPointer(Ex); - ID.AddInteger(cnt); -} - -void AllocaRegion::Profile(llvm::FoldingSetNodeID& ID) const { - ProfileRegion(ID, Ex, Cnt, superRegion); -} - -void CompoundLiteralRegion::Profile(llvm::FoldingSetNodeID& ID) const { - CompoundLiteralRegion::ProfileRegion(ID, CL, superRegion); -} - -void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const CompoundLiteralExpr* CL, - const MemRegion* superRegion) { - ID.AddInteger((unsigned) CompoundLiteralRegionKind); - ID.AddPointer(CL); - ID.AddPointer(superRegion); -} - -void CXXThisRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, - const PointerType *PT, - const MemRegion *sRegion) { - ID.AddInteger((unsigned) CXXThisRegionKind); - ID.AddPointer(PT); - ID.AddPointer(sRegion); -} - -void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const { - CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion); -} - -void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl* D, - const MemRegion* superRegion, Kind k) { - ID.AddInteger((unsigned) k); - ID.AddPointer(D); - ID.AddPointer(superRegion); -} - -void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const { - DeclRegion::ProfileRegion(ID, D, superRegion, getKind()); -} - -void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const { - VarRegion::ProfileRegion(ID, getDecl(), superRegion); -} - -void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym, - const MemRegion *sreg) { - ID.AddInteger((unsigned) MemRegion::SymbolicRegionKind); - ID.Add(sym); - ID.AddPointer(sreg); -} - -void SymbolicRegion::Profile(llvm::FoldingSetNodeID& ID) const { - SymbolicRegion::ProfileRegion(ID, sym, getSuperRegion()); -} - -void ElementRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - QualType ElementType, SVal Idx, - const MemRegion* superRegion) { - ID.AddInteger(MemRegion::ElementRegionKind); - ID.Add(ElementType); - ID.AddPointer(superRegion); - Idx.Profile(ID); -} - -void ElementRegion::Profile(llvm::FoldingSetNodeID& ID) const { - ElementRegion::ProfileRegion(ID, ElementType, Index, superRegion); -} - -void FunctionTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const FunctionDecl *FD, - const MemRegion*) { - ID.AddInteger(MemRegion::FunctionTextRegionKind); - ID.AddPointer(FD); -} - -void FunctionTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { - FunctionTextRegion::ProfileRegion(ID, FD, superRegion); -} - -void BlockTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const BlockDecl *BD, CanQualType, - const AnalysisContext *AC, - const MemRegion*) { - ID.AddInteger(MemRegion::BlockTextRegionKind); - ID.AddPointer(BD); -} - -void BlockTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { - BlockTextRegion::ProfileRegion(ID, BD, locTy, AC, superRegion); -} - -void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const BlockTextRegion *BC, - const LocationContext *LC, - const MemRegion *sReg) { - ID.AddInteger(MemRegion::BlockDataRegionKind); - ID.AddPointer(BC); - ID.AddPointer(LC); - ID.AddPointer(sReg); -} - -void BlockDataRegion::Profile(llvm::FoldingSetNodeID& ID) const { - BlockDataRegion::ProfileRegion(ID, BC, LC, getSuperRegion()); -} - -void CXXObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, - Expr const *Ex, - const MemRegion *sReg) { - ID.AddPointer(Ex); - ID.AddPointer(sReg); -} - -void CXXObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { - ProfileRegion(ID, Ex, getSuperRegion()); -} - -//===----------------------------------------------------------------------===// -// Region pretty-printing. -//===----------------------------------------------------------------------===// - -void MemRegion::dump() const { - dumpToStream(llvm::errs()); -} - -std::string MemRegion::getString() const { - std::string s; - llvm::raw_string_ostream os(s); - dumpToStream(os); - return os.str(); -} - -void MemRegion::dumpToStream(llvm::raw_ostream& os) const { - os << ""; -} - -void AllocaRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "alloca{" << (void*) Ex << ',' << Cnt << '}'; -} - -void FunctionTextRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "code{" << getDecl()->getDeclName().getAsString() << '}'; -} - -void BlockTextRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "block_code{" << (void*) this << '}'; -} - -void BlockDataRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "block_data{" << BC << '}'; -} - - -void CompoundLiteralRegion::dumpToStream(llvm::raw_ostream& os) const { - // FIXME: More elaborate pretty-printing. - os << "{ " << (void*) CL << " }"; -} - -void CXXThisRegion::dumpToStream(llvm::raw_ostream &os) const { - os << "this"; -} - -void ElementRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "element{" << superRegion << ',' - << Index << ',' << getElementType().getAsString() << '}'; -} - -void FieldRegion::dumpToStream(llvm::raw_ostream& os) const { - os << superRegion << "->" << getDecl()->getNameAsString(); -} - -void ObjCIvarRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "ivar{" << superRegion << ',' << getDecl()->getNameAsString() << '}'; -} - -void StringRegion::dumpToStream(llvm::raw_ostream& os) const { - Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOptions())); -} - -void SymbolicRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "SymRegion{" << sym << '}'; -} - -void VarRegion::dumpToStream(llvm::raw_ostream& os) const { - os << cast(D)->getNameAsString(); -} - -void RegionRawOffset::dump() const { - dumpToStream(llvm::errs()); -} - -void RegionRawOffset::dumpToStream(llvm::raw_ostream& os) const { - os << "raw_offset{" << getRegion() << ',' << getByteOffset() << '}'; -} - -//===----------------------------------------------------------------------===// -// MemRegionManager methods. -//===----------------------------------------------------------------------===// - -template -const REG *MemRegionManager::LazyAllocate(REG*& region) { - if (!region) { - region = (REG*) A.Allocate(); - new (region) REG(this); - } - - return region; -} - -template -const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) { - if (!region) { - region = (REG*) A.Allocate(); - new (region) REG(this, a); - } - - return region; -} - -const StackLocalsSpaceRegion* -MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) { - assert(STC); - if (STC == cachedStackLocalsFrame) - return cachedStackLocalsRegion; - cachedStackLocalsFrame = STC; - return LazyAllocate(cachedStackLocalsRegion, STC); -} - -const StackArgumentsSpaceRegion * -MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) { - assert(STC); - if (STC == cachedStackArgumentsFrame) - return cachedStackArgumentsRegion; - - cachedStackArgumentsFrame = STC; - return LazyAllocate(cachedStackArgumentsRegion, STC); -} - -const GlobalsSpaceRegion *MemRegionManager::getGlobalsRegion() { - return LazyAllocate(globals); -} - -const HeapSpaceRegion *MemRegionManager::getHeapRegion() { - return LazyAllocate(heap); -} - -const MemSpaceRegion *MemRegionManager::getUnknownRegion() { - return LazyAllocate(unknown); -} - -const MemSpaceRegion *MemRegionManager::getCodeRegion() { - return LazyAllocate(code); -} - -//===----------------------------------------------------------------------===// -// Constructing regions. -//===----------------------------------------------------------------------===// - -const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str) { - return getSubRegion(Str, getGlobalsRegion()); -} - -const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, - const LocationContext *LC) { - const MemRegion *sReg = 0; - - if (D->hasLocalStorage()) { - // FIXME: Once we implement scope handling, we will need to properly lookup - // 'D' to the proper LocationContext. - const DeclContext *DC = D->getDeclContext(); - const StackFrameContext *STC = LC->getStackFrameForDeclContext(DC); - - if (!STC) - sReg = getUnknownRegion(); - else { - sReg = isa(D) || isa(D) - ? static_cast(getStackArgumentsRegion(STC)) - : static_cast(getStackLocalsRegion(STC)); - } - } - else { - sReg = getGlobalsRegion(); - } - - return getSubRegion(D, sReg); -} - -const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, - const MemRegion *superR) { - return getSubRegion(D, superR); -} - -const BlockDataRegion * -MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC, - const LocationContext *LC) { - const MemRegion *sReg = 0; - - if (LC) { - // FIXME: Once we implement scope handling, we want the parent region - // to be the scope. - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); - sReg = getStackLocalsRegion(STC); - } - else { - // We allow 'LC' to be NULL for cases where want BlockDataRegions - // without context-sensitivity. - sReg = getUnknownRegion(); - } - - return getSubRegion(BC, LC, sReg); -} - -const CompoundLiteralRegion* -MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL, - const LocationContext *LC) { - - const MemRegion *sReg = 0; - - if (CL->isFileScope()) - sReg = getGlobalsRegion(); - else { - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); - sReg = getStackLocalsRegion(STC); - } - - return getSubRegion(CL, sReg); -} - -const ElementRegion* -MemRegionManager::getElementRegion(QualType elementType, SVal Idx, - const MemRegion* superRegion, - ASTContext& Ctx){ - - QualType T = Ctx.getCanonicalType(elementType); - - llvm::FoldingSetNodeID ID; - ElementRegion::ProfileRegion(ID, T, Idx, superRegion); - - void* InsertPos; - MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); - ElementRegion* R = cast_or_null(data); - - if (!R) { - R = (ElementRegion*) A.Allocate(); - new (R) ElementRegion(T, Idx, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -const FunctionTextRegion * -MemRegionManager::getFunctionTextRegion(const FunctionDecl *FD) { - return getSubRegion(FD, getCodeRegion()); -} - -const BlockTextRegion * -MemRegionManager::getBlockTextRegion(const BlockDecl *BD, CanQualType locTy, - AnalysisContext *AC) { - return getSubRegion(BD, locTy, AC, getCodeRegion()); -} - - -/// getSymbolicRegion - Retrieve or create a "symbolic" memory region. -const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { - return getSubRegion(sym, getUnknownRegion()); -} - -const FieldRegion* -MemRegionManager::getFieldRegion(const FieldDecl* d, - const MemRegion* superRegion){ - return getSubRegion(d, superRegion); -} - -const ObjCIvarRegion* -MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl* d, - const MemRegion* superRegion) { - return getSubRegion(d, superRegion); -} - -const CXXObjectRegion* -MemRegionManager::getCXXObjectRegion(Expr const *E, - LocationContext const *LC) { - const StackFrameContext *SFC = LC->getCurrentStackFrame(); - assert(SFC); - return getSubRegion(E, getStackLocalsRegion(SFC)); -} - -const CXXThisRegion* -MemRegionManager::getCXXThisRegion(QualType thisPointerTy, - const LocationContext *LC) { - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); - const PointerType *PT = thisPointerTy->getAs(); - assert(PT); - return getSubRegion(PT, getStackArgumentsRegion(STC)); -} - -const AllocaRegion* -MemRegionManager::getAllocaRegion(const Expr* E, unsigned cnt, - const LocationContext *LC) { - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); - return getSubRegion(E, cnt, getStackLocalsRegion(STC)); -} - -const MemSpaceRegion *MemRegion::getMemorySpace() const { - const MemRegion *R = this; - const SubRegion* SR = dyn_cast(this); - - while (SR) { - R = SR->getSuperRegion(); - SR = dyn_cast(R); - } - - return dyn_cast(R); -} - -bool MemRegion::hasStackStorage() const { - return isa(getMemorySpace()); -} - -bool MemRegion::hasStackNonParametersStorage() const { - return isa(getMemorySpace()); -} - -bool MemRegion::hasStackParametersStorage() const { - return isa(getMemorySpace()); -} - -bool MemRegion::hasGlobalsOrParametersStorage() const { - const MemSpaceRegion *MS = getMemorySpace(); - return isa(MS) || - isa(MS); -} - -// getBaseRegion strips away all elements and fields, and get the base region -// of them. -const MemRegion *MemRegion::getBaseRegion() const { - const MemRegion *R = this; - while (true) { - if (const ElementRegion *ER = dyn_cast(R)) { - R = ER->getSuperRegion(); - continue; - } - if (const FieldRegion *FR = dyn_cast(R)) { - R = FR->getSuperRegion(); - continue; - } - break; - } - return R; -} - -//===----------------------------------------------------------------------===// -// View handling. -//===----------------------------------------------------------------------===// - -const MemRegion *MemRegion::StripCasts() const { - const MemRegion *R = this; - while (true) { - if (const ElementRegion *ER = dyn_cast(R)) { - // FIXME: generalize. Essentially we want to strip away ElementRegions - // that were layered on a symbolic region because of casts. We only - // want to strip away ElementRegions, however, where the index is 0. - SVal index = ER->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast(&index)) { - if (CI->getValue().getSExtValue() == 0) { - R = ER->getSuperRegion(); - continue; - } - } - } - break; - } - return R; -} - -// FIXME: Merge with the implementation of the same method in Store.cpp -static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { - if (const RecordType *RT = Ty->getAs()) { - const RecordDecl *D = RT->getDecl(); - if (!D->getDefinition(Ctx)) - return false; - } - - return true; -} - -RegionRawOffset ElementRegion::getAsRawOffset() const { - CharUnits offset = CharUnits::Zero(); - const ElementRegion *ER = this; - const MemRegion *superR = NULL; - ASTContext &C = getContext(); - - // FIXME: Handle multi-dimensional arrays. - - while (ER) { - superR = ER->getSuperRegion(); - - // FIXME: generalize to symbolic offsets. - SVal index = ER->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast(&index)) { - // Update the offset. - int64_t i = CI->getValue().getSExtValue(); - - if (i != 0) { - QualType elemType = ER->getElementType(); - - // If we are pointing to an incomplete type, go no further. - if (!IsCompleteType(C, elemType)) { - superR = ER; - break; - } - - CharUnits size = C.getTypeSizeInChars(elemType); - offset += (i * size); - } - - // Go to the next ElementRegion (if any). - ER = dyn_cast(superR); - continue; - } - - return NULL; - } - - assert(superR && "super region cannot be NULL"); - return RegionRawOffset(superR, offset.getQuantity()); -} - -//===----------------------------------------------------------------------===// -// BlockDataRegion -//===----------------------------------------------------------------------===// - -void BlockDataRegion::LazyInitializeReferencedVars() { - if (ReferencedVars) - return; - - AnalysisContext *AC = getCodeRegion()->getAnalysisContext(); - AnalysisContext::referenced_decls_iterator I, E; - llvm::tie(I, E) = AC->getReferencedBlockVars(BC->getDecl()); - - if (I == E) { - ReferencedVars = (void*) 0x1; - return; - } - - MemRegionManager &MemMgr = *getMemRegionManager(); - llvm::BumpPtrAllocator &A = MemMgr.getAllocator(); - BumpVectorContext BC(A); - - typedef BumpVector VarVec; - VarVec *BV = (VarVec*) A.Allocate(); - new (BV) VarVec(BC, E - I); - - for ( ; I != E; ++I) { - const VarDecl *VD = *I; - const VarRegion *VR = 0; - - if (!VD->getAttr()) - VR = MemMgr.getVarRegion(VD, this); - else { - if (LC) - VR = MemMgr.getVarRegion(VD, LC); - else { - VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); - } - } - - assert(VR); - BV->push_back(VR, BC); - } - - ReferencedVars = BV; -} - -BlockDataRegion::referenced_vars_iterator -BlockDataRegion::referenced_vars_begin() const { - const_cast(this)->LazyInitializeReferencedVars(); - - BumpVector *Vec = - static_cast*>(ReferencedVars); - - return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? - NULL : Vec->begin()); -} - -BlockDataRegion::referenced_vars_iterator -BlockDataRegion::referenced_vars_end() const { - const_cast(this)->LazyInitializeReferencedVars(); - - BumpVector *Vec = - static_cast*>(ReferencedVars); - - return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? - NULL : Vec->end()); -} diff --git a/lib/Analysis/NSAutoreleasePoolChecker.cpp b/lib/Analysis/NSAutoreleasePoolChecker.cpp deleted file mode 100644 index 2ff04878f7ab..000000000000 --- a/lib/Analysis/NSAutoreleasePoolChecker.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//=- NSAutoreleasePoolChecker.cpp --------------------------------*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a NSAutoreleasePoolChecker, a small checker that warns -// about subpar uses of NSAutoreleasePool. Note that while the check itself -// (in it's current form) could be written as a flow-insensitive check, in -// can be potentially enhanced in the future with flow-sensitive information. -// It is also a good example of the CheckerVisitor interface. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "BasicObjCFoundationChecks.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Decl.h" - -using namespace clang; - -namespace { -class NSAutoreleasePoolChecker - : public CheckerVisitor { - - Selector releaseS; - -public: - NSAutoreleasePoolChecker(Selector release_s) : releaseS(release_s) {} - - static void *getTag() { - static int x = 0; - return &x; - } - - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); -}; - -} // end anonymous namespace - - -void clang::RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng) { - ASTContext &Ctx = Eng.getContext(); - if (Ctx.getLangOptions().getGCMode() != LangOptions::NonGC) { - Eng.registerCheck(new NSAutoreleasePoolChecker(GetNullarySelector("release", - Ctx))); - } -} - -void -NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) { - - const Expr *receiver = ME->getReceiver(); - if (!receiver) - return; - - // FIXME: Enhance with value-tracking information instead of consulting - // the type of the expression. - const ObjCObjectPointerType* PT = - receiver->getType()->getAs(); - - if (!PT) - return; - const ObjCInterfaceDecl* OD = PT->getInterfaceDecl(); - if (!OD) - return; - if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) - return; - - // Sending 'release' message? - if (ME->getSelector() != releaseS) - return; - - SourceRange R = ME->getSourceRange(); - - C.getBugReporter().EmitBasicReport("Use -drain instead of -release", - "API Upgrade (Apple)", - "Use -drain instead of -release when using NSAutoreleasePool " - "and garbage collection", ME->getLocStart(), &R, 1); -} diff --git a/lib/Analysis/NSErrorChecker.cpp b/lib/Analysis/NSErrorChecker.cpp deleted file mode 100644 index e3cf57fd0c1b..000000000000 --- a/lib/Analysis/NSErrorChecker.cpp +++ /dev/null @@ -1,237 +0,0 @@ -//=- NSErrorCheckerer.cpp - Coding conventions for uses of NSError -*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a CheckNSError, a flow-insenstive check -// that determines if an Objective-C class interface correctly returns -// a non-void return type. -// -// File under feature request PR 2600. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/Checkers/DereferenceChecker.h" -#include "BasicObjCFoundationChecks.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Decl.h" -#include "llvm/ADT/SmallVector.h" - -using namespace clang; - -namespace { -class NSErrorChecker : public BugType { - const Decl &CodeDecl; - const bool isNSErrorWarning; - IdentifierInfo * const II; - GRExprEngine &Eng; - - void CheckSignature(const ObjCMethodDecl& MD, QualType& ResultTy, - llvm::SmallVectorImpl& ErrorParams); - - void CheckSignature(const FunctionDecl& MD, QualType& ResultTy, - llvm::SmallVectorImpl& ErrorParams); - - bool CheckNSErrorArgument(QualType ArgTy); - bool CheckCFErrorArgument(QualType ArgTy); - - void CheckParamDeref(const VarDecl *V, const LocationContext *LC, - const GRState *state, BugReporter& BR); - - void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl); - -public: - NSErrorChecker(const Decl &D, bool isNSError, GRExprEngine& eng) - : BugType(isNSError ? "NSError** null dereference" - : "CFErrorRef* null dereference", - "Coding conventions (Apple)"), - CodeDecl(D), - isNSErrorWarning(isNSError), - II(&eng.getContext().Idents.get(isNSErrorWarning ? "NSError":"CFErrorRef")), - Eng(eng) {} - - void FlushReports(BugReporter& BR); -}; - -} // end anonymous namespace - -void clang::RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng, - const Decl &D) { - BR.Register(new NSErrorChecker(D, true, Eng)); - BR.Register(new NSErrorChecker(D, false, Eng)); -} - -void NSErrorChecker::FlushReports(BugReporter& BR) { - // Get the analysis engine and the exploded analysis graph. - ExplodedGraph& G = Eng.getGraph(); - - // Get the ASTContext, which is useful for querying type information. - ASTContext &Ctx = BR.getContext(); - - QualType ResultTy; - llvm::SmallVector ErrorParams; - - if (const ObjCMethodDecl* MD = dyn_cast(&CodeDecl)) - CheckSignature(*MD, ResultTy, ErrorParams); - else if (const FunctionDecl* FD = dyn_cast(&CodeDecl)) - CheckSignature(*FD, ResultTy, ErrorParams); - else - return; - - if (ErrorParams.empty()) - return; - - if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl); - - for (ExplodedGraph::roots_iterator RI=G.roots_begin(), RE=G.roots_end(); - RI!=RE; ++RI) { - // Scan the parameters for an implicit null dereference. - for (llvm::SmallVectorImpl::iterator I=ErrorParams.begin(), - E=ErrorParams.end(); I!=E; ++I) - CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR); - } -} - -void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - if (isa(CodeDecl)) - os << "Method"; - else - os << "Function"; - - os << " accepting "; - os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*"); - os << " should have a non-void return value to indicate whether or not an " - "error occurred"; - - BR.EmitBasicReport(isNSErrorWarning - ? "Bad return type when passing NSError**" - : "Bad return type when passing CFError*", - getCategory(), os.str(), - CodeDecl.getLocation()); -} - -void -NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, - llvm::SmallVectorImpl& ErrorParams) { - - ResultTy = M.getResultType(); - - for (ObjCMethodDecl::param_iterator I=M.param_begin(), - E=M.param_end(); I!=E; ++I) { - - QualType T = (*I)->getType(); - - if (isNSErrorWarning) { - if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); - } - else if (CheckCFErrorArgument(T)) - ErrorParams.push_back(*I); - } -} - -void -NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy, - llvm::SmallVectorImpl& ErrorParams) { - - ResultTy = F.getResultType(); - - for (FunctionDecl::param_const_iterator I = F.param_begin(), - E = F.param_end(); I != E; ++I) { - - QualType T = (*I)->getType(); - - if (isNSErrorWarning) { - if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); - } - else if (CheckCFErrorArgument(T)) - ErrorParams.push_back(*I); - } -} - - -bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) { - - const PointerType* PPT = ArgTy->getAs(); - if (!PPT) - return false; - - const ObjCObjectPointerType* PT = - PPT->getPointeeType()->getAs(); - - if (!PT) - return false; - - const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); - - // FIXME: Can ID ever be NULL? - if (ID) - return II == ID->getIdentifier(); - - return false; -} - -bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) { - - const PointerType* PPT = ArgTy->getAs(); - if (!PPT) return false; - - const TypedefType* TT = PPT->getPointeeType()->getAs(); - if (!TT) return false; - - return TT->getDecl()->getIdentifier() == II; -} - -void NSErrorChecker::CheckParamDeref(const VarDecl *Param, - const LocationContext *LC, - const GRState *rootState, - BugReporter& BR) { - - SVal ParamL = rootState->getLValue(Param, LC); - const MemRegion* ParamR = cast(ParamL).getRegionAs(); - assert (ParamR && "Parameters always have VarRegions."); - SVal ParamSVal = rootState->getSVal(ParamR); - - // FIXME: For now assume that ParamSVal is symbolic. We need to generalize - // this later. - SymbolRef ParamSym = ParamSVal.getAsLocSymbol(); - if (!ParamSym) - return; - - // Iterate over the implicit-null dereferences. - ExplodedNode *const* I, *const* E; - llvm::tie(I, E) = GetImplicitNullDereferences(Eng); - for ( ; I != E; ++I) { - const GRState *state = (*I)->getState(); - SVal location = state->getSVal((*I)->getLocationAs()->getStmt()); - if (location.getAsSymbol() != ParamSym) - continue; - - // Emit an error. - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Potential null dereference. According to coding standards "; - - if (isNSErrorWarning) - os << "in 'Creating and Returning NSError Objects' the parameter '"; - else - os << "documented in CoreFoundation/CFError.h the parameter '"; - - os << Param->getNameAsString() << "' may be null."; - - BugReport *report = new BugReport(*this, os.str(), *I); - // FIXME: Notable symbols are now part of the report. We should - // add support for notable symbols in BugReport. - // BR.addNotableSymbol(SV->getSymbol()); - BR.EmitReport(report); - } -} diff --git a/lib/Analysis/NoReturnFunctionChecker.cpp b/lib/Analysis/NoReturnFunctionChecker.cpp deleted file mode 100644 index 5cfd9acd5f56..000000000000 --- a/lib/Analysis/NoReturnFunctionChecker.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines NoReturnFunctionChecker, which evaluates functions that do not -// return to the caller. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/Checker.h" -#include "llvm/ADT/StringSwitch.h" - -using namespace clang; - -namespace { - -class NoReturnFunctionChecker : public Checker { -public: - static void *getTag() { static int tag = 0; return &tag; } - virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); -}; - -} - -void clang::RegisterNoReturnFunctionChecker(GRExprEngine &Eng) { - Eng.registerCheck(new NoReturnFunctionChecker()); -} - -bool NoReturnFunctionChecker::EvalCallExpr(CheckerContext &C, - const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - const FunctionDecl *FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - bool BuildSinks = false; - - if (FD->getAttr() || FD->getAttr()) - BuildSinks = true; - else if (const IdentifierInfo *II = FD->getIdentifier()) { - // HACK: Some functions are not marked noreturn, and don't return. - // Here are a few hardwired ones. If this takes too long, we can - // potentially cache these results. - BuildSinks - = llvm::StringSwitch(llvm::StringRef(II->getName())) - .Case("exit", true) - .Case("panic", true) - .Case("error", true) - .Case("Assert", true) - // FIXME: This is just a wrapper around throwing an exception. - // Eventually inter-procedural analysis should handle this easily. - .Case("ziperr", true) - .Case("assfail", true) - .Case("db_error", true) - .Case("__assert", true) - .Case("__assert_rtn", true) - .Case("__assert_fail", true) - .Case("dtrace_assfail", true) - .Case("yy_fatal_error", true) - .Case("_XCAssertionFailureHandler", true) - .Case("_DTAssertionFailureHandler", true) - .Case("_TSAssertionFailureHandler", true) - .Default(false); - } - - if (!BuildSinks) - return false; - - C.GenerateSink(CE); - return true; -} diff --git a/lib/Analysis/OSAtomicChecker.cpp b/lib/Analysis/OSAtomicChecker.cpp deleted file mode 100644 index 9d34e9ec5c8b..000000000000 --- a/lib/Analysis/OSAtomicChecker.cpp +++ /dev/null @@ -1,198 +0,0 @@ -//=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This checker evaluates OSAtomic functions. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/Checker.h" -#include "clang/Basic/Builtins.h" -#include "llvm/ADT/StringSwitch.h" - -using namespace clang; - -namespace { - -class OSAtomicChecker : public Checker { -public: - static void *getTag() { static int tag = 0; return &tag; } - virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); - -private: - bool EvalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE); -}; - -} - -void clang::RegisterOSAtomicChecker(GRExprEngine &Eng) { - Eng.registerCheck(new OSAtomicChecker()); -} - -bool OSAtomicChecker::EvalCallExpr(CheckerContext &C,const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - - const FunctionDecl* FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - const IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return false; - - llvm::StringRef FName(II->getName()); - - // Check for compare and swap. - if (FName.startswith("OSAtomicCompareAndSwap") || - FName.startswith("objc_atomicCompareAndSwap")) - return EvalOSAtomicCompareAndSwap(C, CE); - - // FIXME: Other atomics. - return false; -} - -bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C, - const CallExpr *CE) { - // Not enough arguments to match OSAtomicCompareAndSwap? - if (CE->getNumArgs() != 3) - return false; - - ASTContext &Ctx = C.getASTContext(); - const Expr *oldValueExpr = CE->getArg(0); - QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); - - const Expr *newValueExpr = CE->getArg(1); - QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType()); - - // Do the types of 'oldValue' and 'newValue' match? - if (oldValueType != newValueType) - return false; - - const Expr *theValueExpr = CE->getArg(2); - const PointerType *theValueType=theValueExpr->getType()->getAs(); - - // theValueType not a pointer? - if (!theValueType) - return false; - - QualType theValueTypePointee = - Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType(); - - // The pointee must match newValueType and oldValueType. - if (theValueTypePointee != newValueType) - return false; - - static unsigned magic_load = 0; - static unsigned magic_store = 0; - - const void *OSAtomicLoadTag = &magic_load; - const void *OSAtomicStoreTag = &magic_store; - - // Load 'theValue'. - GRExprEngine &Engine = C.getEngine(); - const GRState *state = C.getState(); - ExplodedNodeSet Tmp; - SVal location = state->getSVal(theValueExpr); - // Here we should use the value type of the region as the load type. - QualType LoadTy; - if (const TypedRegion *TR = - dyn_cast_or_null(location.getAsRegion())) { - LoadTy = TR->getValueType(Ctx); - } - Engine.EvalLoad(Tmp, const_cast(theValueExpr), C.getPredecessor(), - state, location, OSAtomicLoadTag, LoadTy); - - if (Tmp.empty()) { - // If no nodes were generated, other checkers must generated sinks. But - // since the builder state was restored, we set it manually to prevent - // auto transition. - // FIXME: there should be a better approach. - C.getNodeBuilder().BuildSinks = true; - return true; - } - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); - I != E; ++I) { - - ExplodedNode *N = *I; - const GRState *stateLoad = N->getState(); - SVal theValueVal_untested = stateLoad->getSVal(theValueExpr); - SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr); - - // FIXME: Issue an error. - if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { - return false; - } - - DefinedOrUnknownSVal theValueVal = - cast(theValueVal_untested); - DefinedOrUnknownSVal oldValueVal = - cast(oldValueVal_untested); - - SValuator &SVator = Engine.getSValuator(); - - // Perform the comparison. - DefinedOrUnknownSVal Cmp = SVator.EvalEQ(stateLoad,theValueVal,oldValueVal); - - const GRState *stateEqual = stateLoad->Assume(Cmp, true); - - // Were they equal? - if (stateEqual) { - // Perform the store. - ExplodedNodeSet TmpStore; - SVal val = stateEqual->getSVal(newValueExpr); - - // Handle implicit value casts. - if (const TypedRegion *R = - dyn_cast_or_null(location.getAsRegion())) { - llvm::tie(state, val) = SVator.EvalCast(val, state,R->getValueType(Ctx), - newValueExpr->getType()); - } - - Engine.EvalStore(TmpStore, NULL, const_cast(theValueExpr), N, - stateEqual, location, val, OSAtomicStoreTag); - - if (TmpStore.empty()) { - // If no nodes were generated, other checkers must generated sinks. But - // since the builder state was restored, we set it manually to prevent - // auto transition. - // FIXME: there should be a better approach. - C.getNodeBuilder().BuildSinks = true; - return true; - } - - // Now bind the result of the comparison. - for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), - E2 = TmpStore.end(); I2 != E2; ++I2) { - ExplodedNode *predNew = *I2; - const GRState *stateNew = predNew->getState(); - // Check for 'void' return type if we have a bogus function prototype. - SVal Res = UnknownVal(); - QualType T = CE->getType(); - if (!T->isVoidType()) - Res = Engine.getValueManager().makeTruthVal(true, T); - C.GenerateNode(stateNew->BindExpr(CE, Res), predNew); - } - } - - // Were they not equal? - if (const GRState *stateNotEqual = stateLoad->Assume(Cmp, false)) { - // Check for 'void' return type if we have a bogus function prototype. - SVal Res = UnknownVal(); - QualType T = CE->getType(); - if (!T->isVoidType()) - Res = Engine.getValueManager().makeTruthVal(false, CE->getType()); - C.GenerateNode(stateNotEqual->BindExpr(CE, Res), N); - } - } - - return true; -} diff --git a/lib/Analysis/PathDiagnostic.cpp b/lib/Analysis/PathDiagnostic.cpp deleted file mode 100644 index 734570a21e64..000000000000 --- a/lib/Analysis/PathDiagnostic.cpp +++ /dev/null @@ -1,281 +0,0 @@ -//===--- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the PathDiagnostic-related interfaces. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/AST/Expr.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/StmtCXX.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/Casting.h" - -using namespace clang; -using llvm::dyn_cast; -using llvm::isa; - -bool PathDiagnosticMacroPiece::containsEvent() const { - for (const_iterator I = begin(), E = end(); I!=E; ++I) { - if (isa(*I)) - return true; - - if (PathDiagnosticMacroPiece *MP = dyn_cast(*I)) - if (MP->containsEvent()) - return true; - } - - return false; -} - -static llvm::StringRef StripTrailingDots(llvm::StringRef s) { - for (llvm::StringRef::size_type i = s.size(); i != 0; --i) - if (s[i - 1] != '.') - return s.substr(0, i); - return ""; -} - -PathDiagnosticPiece::PathDiagnosticPiece(llvm::StringRef s, - Kind k, DisplayHint hint) - : str(StripTrailingDots(s)), kind(k), Hint(hint) {} - -PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) - : kind(k), Hint(hint) {} - -PathDiagnosticPiece::~PathDiagnosticPiece() {} -PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {} -PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} - -PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() { - for (iterator I = begin(), E = end(); I != E; ++I) delete *I; -} - -PathDiagnostic::PathDiagnostic() : Size(0) {} - -PathDiagnostic::~PathDiagnostic() { - for (iterator I = begin(), E = end(); I != E; ++I) delete &*I; -} - -void PathDiagnostic::resetPath(bool deletePieces) { - Size = 0; - - if (deletePieces) - for (iterator I=begin(), E=end(); I!=E; ++I) - delete &*I; - - path.clear(); -} - - -PathDiagnostic::PathDiagnostic(llvm::StringRef bugtype, llvm::StringRef desc, - llvm::StringRef category) - : Size(0), - BugType(StripTrailingDots(bugtype)), - Desc(StripTrailingDots(desc)), - Category(StripTrailingDots(category)) {} - -void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, - const DiagnosticInfo &Info) { - - // Create a PathDiagnostic with a single piece. - - PathDiagnostic* D = new PathDiagnostic(); - - const char *LevelStr; - switch (DiagLevel) { - default: - case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); - case Diagnostic::Note: LevelStr = "note: "; break; - case Diagnostic::Warning: LevelStr = "warning: "; break; - case Diagnostic::Error: LevelStr = "error: "; break; - case Diagnostic::Fatal: LevelStr = "fatal error: "; break; - } - - llvm::SmallString<100> StrC; - StrC += LevelStr; - Info.FormatDiagnostic(StrC); - - PathDiagnosticPiece *P = - new PathDiagnosticEventPiece(Info.getLocation(), StrC.str()); - - for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) - P->addRange(Info.getRange(i)); - for (unsigned i = 0, e = Info.getNumCodeModificationHints(); i != e; ++i) - P->addCodeModificationHint(Info.getCodeModificationHint(i)); - D->push_front(P); - - HandlePathDiagnostic(D); -} - -//===----------------------------------------------------------------------===// -// PathDiagnosticLocation methods. -//===----------------------------------------------------------------------===// - -FullSourceLoc PathDiagnosticLocation::asLocation() const { - assert(isValid()); - // Note that we want a 'switch' here so that the compiler can warn us in - // case we add more cases. - switch (K) { - case SingleLocK: - case RangeK: - break; - case StmtK: - return FullSourceLoc(S->getLocStart(), const_cast(*SM)); - case DeclK: - return FullSourceLoc(D->getLocation(), const_cast(*SM)); - } - - return FullSourceLoc(R.getBegin(), const_cast(*SM)); -} - -PathDiagnosticRange PathDiagnosticLocation::asRange() const { - assert(isValid()); - // Note that we want a 'switch' here so that the compiler can warn us in - // case we add more cases. - switch (K) { - case SingleLocK: - return PathDiagnosticRange(R, true); - case RangeK: - break; - case StmtK: { - const Stmt *S = asStmt(); - switch (S->getStmtClass()) { - default: - break; - case Stmt::DeclStmtClass: { - const DeclStmt *DS = cast(S); - if (DS->isSingleDecl()) { - // Should always be the case, but we'll be defensive. - return SourceRange(DS->getLocStart(), - DS->getSingleDecl()->getLocation()); - } - break; - } - // FIXME: Provide better range information for different - // terminators. - case Stmt::IfStmtClass: - case Stmt::WhileStmtClass: - case Stmt::DoStmtClass: - case Stmt::ForStmtClass: - case Stmt::ChooseExprClass: - case Stmt::IndirectGotoStmtClass: - case Stmt::SwitchStmtClass: - case Stmt::ConditionalOperatorClass: - case Stmt::ObjCForCollectionStmtClass: { - SourceLocation L = S->getLocStart(); - return SourceRange(L, L); - } - } - - return S->getSourceRange(); - } - case DeclK: - if (const ObjCMethodDecl *MD = dyn_cast(D)) - return MD->getSourceRange(); - if (const FunctionDecl *FD = dyn_cast(D)) { - // FIXME: We would like to always get the function body, even - // when it needs to be de-serialized, but getting the - // ASTContext here requires significant changes. - if (Stmt *Body = FD->getBody()) { - if (CompoundStmt *CS = dyn_cast(Body)) - return CS->getSourceRange(); - else - return cast(Body)->getSourceRange(); - } - } - else { - SourceLocation L = D->getLocation(); - return PathDiagnosticRange(SourceRange(L, L), true); - } - } - - return R; -} - -void PathDiagnosticLocation::flatten() { - if (K == StmtK) { - R = asRange(); - K = RangeK; - S = 0; - D = 0; - } - else if (K == DeclK) { - SourceLocation L = D->getLocation(); - R = SourceRange(L, L); - K = SingleLocK; - S = 0; - D = 0; - } -} - -//===----------------------------------------------------------------------===// -// FoldingSet profiling methods. -//===----------------------------------------------------------------------===// - -void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned) K); - switch (K) { - case RangeK: - ID.AddInteger(R.getBegin().getRawEncoding()); - ID.AddInteger(R.getEnd().getRawEncoding()); - break; - case SingleLocK: - ID.AddInteger(R.getBegin().getRawEncoding()); - break; - case StmtK: - ID.Add(S); - break; - case DeclK: - ID.Add(D); - break; - } - return; -} - -void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned) getKind()); - ID.AddString(str); - // FIXME: Add profiling support for code hints. - ID.AddInteger((unsigned) getDisplayHint()); - for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) { - ID.AddInteger(I->getBegin().getRawEncoding()); - ID.AddInteger(I->getEnd().getRawEncoding()); - } -} - -void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticPiece::Profile(ID); - ID.Add(Pos); -} - -void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticPiece::Profile(ID); - for (const_iterator I = begin(), E = end(); I != E; ++I) - ID.Add(*I); -} - -void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticSpotPiece::Profile(ID); - for (const_iterator I = begin(), E = end(); I != E; ++I) - ID.Add(**I); -} - -void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(Size); - ID.AddString(BugType); - ID.AddString(Desc); - ID.AddString(Category); - for (const_iterator I = begin(), E = end(); I != E; ++I) - ID.Add(*I); - - for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) - ID.AddString(*I); -} diff --git a/lib/Analysis/PointerArithChecker.cpp b/lib/Analysis/PointerArithChecker.cpp deleted file mode 100644 index 370233ce38bd..000000000000 --- a/lib/Analysis/PointerArithChecker.cpp +++ /dev/null @@ -1,71 +0,0 @@ -//=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This files defines PointerArithChecker, a builtin checker that checks for -// pointer arithmetic on locations other than array elements. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class PointerArithChecker - : public CheckerVisitor { - BuiltinBug *BT; -public: - PointerArithChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; -} - -void *PointerArithChecker::getTag() { - static int x; - return &x; -} - -void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - if (B->getOpcode() != BinaryOperator::Sub && - B->getOpcode() != BinaryOperator::Add) - return; - - const GRState *state = C.getState(); - SVal LV = state->getSVal(B->getLHS()); - SVal RV = state->getSVal(B->getRHS()); - - const MemRegion *LR = LV.getAsRegion(); - - if (!LR || !RV.isConstant()) - return; - - // If pointer arithmetic is done on variables of non-array type, this often - // means behavior rely on memory organization, which is dangerous. - if (isa(LR) || isa(LR) || - isa(LR)) { - - if (ExplodedNode *N = C.GenerateNode()) { - if (!BT) - BT = new BuiltinBug("Dangerous pointer arithmetic", - "Pointer arithmetic done on non-array variables " - "means reliance on memory layout, which is " - "dangerous."); - RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); - R->addRange(B->getSourceRange()); - C.EmitReport(R); - } - } -} - -void clang::RegisterPointerArithChecker(GRExprEngine &Eng) { - Eng.registerCheck(new PointerArithChecker()); -} diff --git a/lib/Analysis/PointerSubChecker.cpp b/lib/Analysis/PointerSubChecker.cpp deleted file mode 100644 index c597a2580751..000000000000 --- a/lib/Analysis/PointerSubChecker.cpp +++ /dev/null @@ -1,77 +0,0 @@ -//=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This files defines PointerSubChecker, a builtin checker that checks for -// pointer subtractions on two pointers pointing to different memory chunks. -// This check corresponds to CWE-469. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class PointerSubChecker - : public CheckerVisitor { - BuiltinBug *BT; -public: - PointerSubChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; -} - -void *PointerSubChecker::getTag() { - static int x; - return &x; -} - -void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - // When doing pointer subtraction, if the two pointers do not point to the - // same memory chunk, emit a warning. - if (B->getOpcode() != BinaryOperator::Sub) - return; - - const GRState *state = C.getState(); - SVal LV = state->getSVal(B->getLHS()); - SVal RV = state->getSVal(B->getRHS()); - - const MemRegion *LR = LV.getAsRegion(); - const MemRegion *RR = RV.getAsRegion(); - - if (!(LR && RR)) - return; - - const MemRegion *BaseLR = LR->getBaseRegion(); - const MemRegion *BaseRR = RR->getBaseRegion(); - - if (BaseLR == BaseRR) - return; - - // Allow arithmetic on different symbolic regions. - if (isa(BaseLR) || isa(BaseRR)) - return; - - if (ExplodedNode *N = C.GenerateNode()) { - if (!BT) - BT = new BuiltinBug("Pointer subtraction", - "Subtraction of two pointers that do not point to " - "the same memory chunk may cause incorrect result."); - RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); - R->addRange(B->getSourceRange()); - C.EmitReport(R); - } -} - -void clang::RegisterPointerSubChecker(GRExprEngine &Eng) { - Eng.registerCheck(new PointerSubChecker()); -} diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp new file mode 100644 index 000000000000..55abd1077150 --- /dev/null +++ b/lib/Analysis/PrintfFormatString.cpp @@ -0,0 +1,436 @@ +//= PrintfFormatStrings.cpp - Analysis of printf format strings --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Handling of format string in printf and friends. The structure of format +// strings for fprintf() are described in C99 7.19.6.1. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/PrintfFormatString.h" +#include "clang/AST/ASTContext.h" + +using clang::analyze_printf::FormatSpecifier; +using clang::analyze_printf::OptionalAmount; +using clang::analyze_printf::ArgTypeResult; +using clang::analyze_printf::FormatStringHandler; +using namespace clang; + +namespace { +class FormatSpecifierResult { + FormatSpecifier FS; + const char *Start; + bool Stop; +public: + FormatSpecifierResult(bool stop = false) + : Start(0), Stop(stop) {} + FormatSpecifierResult(const char *start, + const FormatSpecifier &fs) + : FS(fs), Start(start), Stop(false) {} + + + const char *getStart() const { return Start; } + bool shouldStop() const { return Stop; } + bool hasValue() const { return Start != 0; } + const FormatSpecifier &getValue() const { + assert(hasValue()); + return FS; + } + const FormatSpecifier &getValue() { return FS; } +}; +} // end anonymous namespace + +template +class UpdateOnReturn { + T &ValueToUpdate; + const T &ValueToCopy; +public: + UpdateOnReturn(T &valueToUpdate, const T &valueToCopy) + : ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {} + + ~UpdateOnReturn() { + ValueToUpdate = ValueToCopy; + } +}; + +//===----------------------------------------------------------------------===// +// Methods for parsing format strings. +//===----------------------------------------------------------------------===// + +static OptionalAmount ParseAmount(const char *&Beg, const char *E) { + const char *I = Beg; + UpdateOnReturn UpdateBeg(Beg, I); + + bool foundDigits = false; + unsigned accumulator = 0; + + for ( ; I != E; ++I) { + char c = *I; + if (c >= '0' && c <= '9') { + foundDigits = true; + accumulator += (accumulator * 10) + (c - '0'); + continue; + } + + if (foundDigits) + return OptionalAmount(accumulator, Beg); + + if (c == '*') { + ++I; + return OptionalAmount(OptionalAmount::Arg, Beg); + } + + break; + } + + return OptionalAmount(); +} + +static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, + const char *&Beg, + const char *E) { + + using namespace clang::analyze_printf; + + const char *I = Beg; + const char *Start = 0; + UpdateOnReturn UpdateBeg(Beg, I); + + // Look for a '%' character that indicates the start of a format specifier. + for ( ; I != E ; ++I) { + char c = *I; + if (c == '\0') { + // Detect spurious null characters, which are likely errors. + H.HandleNullChar(I); + return true; + } + if (c == '%') { + Start = I++; // Record the start of the format specifier. + break; + } + } + + // No format specifier found? + if (!Start) + return false; + + if (I == E) { + // No more characters left? + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + + FormatSpecifier FS; + + // Look for flags (if any). + bool hasMore = true; + for ( ; I != E; ++I) { + switch (*I) { + default: hasMore = false; break; + case '-': FS.setIsLeftJustified(); break; + case '+': FS.setHasPlusPrefix(); break; + case ' ': FS.setHasSpacePrefix(); break; + case '#': FS.setHasAlternativeForm(); break; + case '0': FS.setHasLeadingZeros(); break; + } + if (!hasMore) + break; + } + + if (I == E) { + // No more characters left? + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + + // Look for the field width (if any). + FS.setFieldWidth(ParseAmount(I, E)); + + if (I == E) { + // No more characters left? + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + + // Look for the precision (if any). + if (*I == '.') { + ++I; + if (I == E) { + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + + FS.setPrecision(ParseAmount(I, E)); + + if (I == E) { + // No more characters left? + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + } + + // Look for the length modifier. + LengthModifier lm = None; + switch (*I) { + default: + break; + case 'h': + ++I; + lm = (I != E && *I == 'h') ? ++I, AsChar : AsShort; + break; + case 'l': + ++I; + lm = (I != E && *I == 'l') ? ++I, AsLongLong : AsLong; + break; + case 'j': lm = AsIntMax; ++I; break; + case 'z': lm = AsSizeT; ++I; break; + case 't': lm = AsPtrDiff; ++I; break; + case 'L': lm = AsLongDouble; ++I; break; + case 'q': lm = AsLongLong; ++I; break; + } + FS.setLengthModifier(lm); + + if (I == E) { + // No more characters left? + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + + if (*I == '\0') { + // Detect spurious null characters, which are likely errors. + H.HandleNullChar(I); + return true; + } + + // Finally, look for the conversion specifier. + const char *conversionPosition = I++; + ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier; + switch (*conversionPosition) { + default: + break; + // C99: 7.19.6.1 (section 8). + case 'd': k = ConversionSpecifier::dArg; break; + case 'i': k = ConversionSpecifier::iArg; break; + case 'o': k = ConversionSpecifier::oArg; break; + case 'u': k = ConversionSpecifier::uArg; break; + case 'x': k = ConversionSpecifier::xArg; break; + case 'X': k = ConversionSpecifier::XArg; break; + case 'f': k = ConversionSpecifier::fArg; break; + case 'F': k = ConversionSpecifier::FArg; break; + case 'e': k = ConversionSpecifier::eArg; break; + case 'E': k = ConversionSpecifier::EArg; break; + case 'g': k = ConversionSpecifier::gArg; break; + case 'G': k = ConversionSpecifier::GArg; break; + case 'a': k = ConversionSpecifier::aArg; break; + case 'A': k = ConversionSpecifier::AArg; break; + case 'c': k = ConversionSpecifier::IntAsCharArg; break; + case 's': k = ConversionSpecifier::CStrArg; break; + case 'p': k = ConversionSpecifier::VoidPtrArg; break; + case 'n': k = ConversionSpecifier::OutIntPtrArg; break; + case '%': k = ConversionSpecifier::PercentArg; break; + // Objective-C. + case '@': k = ConversionSpecifier::ObjCObjArg; break; + // Glibc specific. + case 'm': k = ConversionSpecifier::PrintErrno; break; + } + FS.setConversionSpecifier(ConversionSpecifier(conversionPosition, k)); + + if (k == ConversionSpecifier::InvalidSpecifier) { + H.HandleInvalidConversionSpecifier(FS, Beg, I - Beg); + return false; // Keep processing format specifiers. + } + return FormatSpecifierResult(Start, FS); +} + +bool clang::analyze_printf::ParseFormatString(FormatStringHandler &H, + const char *I, const char *E) { + // Keep looking for a format specifier until we have exhausted the string. + while (I != E) { + const FormatSpecifierResult &FSR = ParseFormatSpecifier(H, I, E); + // Did a fail-stop error of any kind occur when parsing the specifier? + // If so, don't do any more processing. + if (FSR.shouldStop()) + return true;; + // Did we exhaust the string or encounter an error that + // we can recover from? + if (!FSR.hasValue()) + continue; + // We have a format specifier. Pass it to the callback. + if (!H.HandleFormatSpecifier(FSR.getValue(), FSR.getStart(), + I - FSR.getStart())) + return true; + } + assert(I == E && "Format string not exhausted"); + return false; +} + +FormatStringHandler::~FormatStringHandler() {} + +//===----------------------------------------------------------------------===// +// Methods on ArgTypeResult. +//===----------------------------------------------------------------------===// + +bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { + assert(isValid()); + + if (K == UnknownTy) + return true; + + if (K == SpecificTy) { + argTy = C.getCanonicalType(argTy).getUnqualifiedType(); + + if (T == argTy) + return true; + + if (const BuiltinType *BT = argTy->getAs()) + switch (BT->getKind()) { + default: + break; + case BuiltinType::Char_S: + case BuiltinType::SChar: + return T == C.UnsignedCharTy; + case BuiltinType::Char_U: + case BuiltinType::UChar: + return T == C.SignedCharTy; + case BuiltinType::Short: + return T == C.UnsignedShortTy; + case BuiltinType::UShort: + return T == C.ShortTy; + case BuiltinType::Int: + return T == C.UnsignedIntTy; + case BuiltinType::UInt: + return T == C.IntTy; + case BuiltinType::Long: + return T == C.UnsignedLongTy; + case BuiltinType::ULong: + return T == C.LongTy; + case BuiltinType::LongLong: + return T == C.UnsignedLongLongTy; + case BuiltinType::ULongLong: + return T == C.LongLongTy; + } + + return false; + } + + if (K == CStrTy) { + const PointerType *PT = argTy->getAs(); + if (!PT) + return false; + + QualType pointeeTy = PT->getPointeeType(); + + if (const BuiltinType *BT = pointeeTy->getAs()) + switch (BT->getKind()) { + case BuiltinType::Void: + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::Char_S: + case BuiltinType::SChar: + return true; + default: + break; + } + + return false; + } + + if (K == WCStrTy) { + const PointerType *PT = argTy->getAs(); + if (!PT) + return false; + + QualType pointeeTy = PT->getPointeeType(); + return pointeeTy == C.WCharTy; + } + + return false; +} + +QualType ArgTypeResult::getRepresentativeType(ASTContext &C) const { + assert(isValid()); + if (K == SpecificTy) + return T; + if (K == CStrTy) + return C.getPointerType(C.CharTy); + if (K == WCStrTy) + return C.getPointerType(C.WCharTy); + if (K == ObjCPointerTy) + return C.ObjCBuiltinIdTy; + + return QualType(); +} + +//===----------------------------------------------------------------------===// +// Methods on OptionalAmount. +//===----------------------------------------------------------------------===// + +ArgTypeResult OptionalAmount::getArgType(ASTContext &Ctx) const { + return Ctx.IntTy; +} + +//===----------------------------------------------------------------------===// +// Methods on FormatSpecifier. +//===----------------------------------------------------------------------===// + +ArgTypeResult FormatSpecifier::getArgType(ASTContext &Ctx) const { + if (!CS.consumesDataArgument()) + return ArgTypeResult::Invalid(); + + if (CS.isIntArg()) + switch (LM) { + case AsLongDouble: + return ArgTypeResult::Invalid(); + case None: return Ctx.IntTy; + case AsChar: return Ctx.SignedCharTy; + case AsShort: return Ctx.ShortTy; + case AsLong: return Ctx.LongTy; + case AsLongLong: return Ctx.LongLongTy; + case AsIntMax: + // FIXME: Return unknown for now. + return ArgTypeResult(); + case AsSizeT: return Ctx.getSizeType(); + case AsPtrDiff: return Ctx.getPointerDiffType(); + } + + if (CS.isUIntArg()) + switch (LM) { + case AsLongDouble: + return ArgTypeResult::Invalid(); + case None: return Ctx.UnsignedIntTy; + case AsChar: return Ctx.UnsignedCharTy; + case AsShort: return Ctx.UnsignedShortTy; + case AsLong: return Ctx.UnsignedLongTy; + case AsLongLong: return Ctx.UnsignedLongLongTy; + case AsIntMax: + // FIXME: Return unknown for now. + return ArgTypeResult(); + case AsSizeT: + // FIXME: How to get the corresponding unsigned + // version of size_t? + return ArgTypeResult(); + case AsPtrDiff: + // FIXME: How to get the corresponding unsigned + // version of ptrdiff_t? + return ArgTypeResult(); + } + + if (CS.isDoubleArg()) { + if (LM == AsLongDouble) + return Ctx.LongDoubleTy; + return Ctx.DoubleTy; + } + + if (CS.getKind() == ConversionSpecifier::CStrArg) + return ArgTypeResult(LM == AsWideChar ? ArgTypeResult::WCStrTy + : ArgTypeResult::CStrTy); + + // FIXME: Handle other cases. + return ArgTypeResult(); +} + diff --git a/lib/Analysis/PthreadLockChecker.cpp b/lib/Analysis/PthreadLockChecker.cpp deleted file mode 100644 index e95095c7975e..000000000000 --- a/lib/Analysis/PthreadLockChecker.cpp +++ /dev/null @@ -1,141 +0,0 @@ -//===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually -// this shouldn't be registered with GRExprEngineInternalChecks. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "GRExprEngineExperimentalChecks.h" -#include "llvm/ADT/ImmutableSet.h" - -using namespace clang; - -namespace { -class PthreadLockChecker - : public CheckerVisitor { - BugType *BT; -public: - PthreadLockChecker() : BT(0) {} - static void *getTag() { - static int x = 0; - return &x; - } - void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); - - void AcquireLock(CheckerContext &C, const CallExpr *CE, - SVal lock, bool isTryLock); - - void ReleaseLock(CheckerContext &C, const CallExpr *CE, - SVal lock); - -}; -} // end anonymous namespace - -// GDM Entry for tracking lock state. -namespace { class LockSet {}; } -namespace clang { -template <> struct GRStateTrait : - public GRStatePartialTrait > { - static void* GDMIndex() { return PthreadLockChecker::getTag(); } -}; -} // end clang namespace - -void clang::RegisterPthreadLockChecker(GRExprEngine &Eng) { - Eng.registerCheck(new PthreadLockChecker()); -} - - -void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, - const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - const FunctionTextRegion *R = - dyn_cast_or_null(state->getSVal(Callee).getAsRegion()); - - if (!R) - return; - - llvm::StringRef FName = R->getDecl()->getName(); - - if (FName == "pthread_mutex_lock") { - if (CE->getNumArgs() != 1) - return; - AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false); - } - else if (FName == "pthread_mutex_trylock") { - if (CE->getNumArgs() != 1) - return; - AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true); - } - else if (FName == "pthread_mutex_unlock") { - if (CE->getNumArgs() != 1) - return; - ReleaseLock(C, CE, state->getSVal(CE->getArg(0))); - } -} - -void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, - SVal lock, bool isTryLock) { - - const MemRegion *lockR = lock.getAsRegion(); - if (!lockR) - return; - - const GRState *state = C.getState(); - - SVal X = state->getSVal(CE); - if (X.isUnknownOrUndef()) - return; - - DefinedSVal retVal = cast(X); - const GRState *lockSucc = state; - - if (isTryLock) { - // Bifurcate the state, and allow a mode where the lock acquisition fails. - const GRState *lockFail; - llvm::tie(lockFail, lockSucc) = state->Assume(retVal); - assert(lockFail && lockSucc); - C.addTransition(C.GenerateNode(CE, lockFail)); - } - else { - // Assume that the return value was 0. - lockSucc = state->Assume(retVal, false); - assert(lockSucc); - } - - // Record that the lock was acquired. - lockSucc = lockSucc->add(lockR); - - C.addTransition(lockSucc != state ? C.GenerateNode(CE, lockSucc) : - C.getPredecessor()); -} - -void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, - SVal lock) { - - const MemRegion *lockR = lock.getAsRegion(); - if (!lockR) - return; - - const GRState *state = C.getState(); - - // Record that the lock was released. - // FIXME: Handle unlocking locks that were never acquired. This may - // require IPA for wrappers. - const GRState *unlockState = state->remove(lockR); - - if (state == unlockState) - return; - - C.addTransition(C.GenerateNode(CE, unlockState)); -} diff --git a/lib/Analysis/RangeConstraintManager.cpp b/lib/Analysis/RangeConstraintManager.cpp deleted file mode 100644 index 2cf3dfb6d0db..000000000000 --- a/lib/Analysis/RangeConstraintManager.cpp +++ /dev/null @@ -1,359 +0,0 @@ -//== RangeConstraintManager.cpp - Manage range constraints.------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines RangeConstraintManager, a class that tracks simple -// equality and inequality constraints on symbolic values of GRState. -// -//===----------------------------------------------------------------------===// - -#include "SimpleConstraintManager.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" -#include "clang/Analysis/ManagerRegistry.h" -#include "llvm/Support/Debug.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/ImmutableSet.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -namespace { class ConstraintRange {}; } -static int ConstraintRangeIndex = 0; - -/// A Range represents the closed range [from, to]. The caller must -/// guarantee that from <= to. Note that Range is immutable, so as not -/// to subvert RangeSet's immutability. -namespace { -class Range : public std::pair { -public: - Range(const llvm::APSInt &from, const llvm::APSInt &to) - : std::pair(&from, &to) { - assert(from <= to); - } - bool Includes(const llvm::APSInt &v) const { - return *first <= v && v <= *second; - } - const llvm::APSInt &From() const { - return *first; - } - const llvm::APSInt &To() const { - return *second; - } - const llvm::APSInt *getConcreteValue() const { - return &From() == &To() ? &From() : NULL; - } - - void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddPointer(&From()); - ID.AddPointer(&To()); - } -}; - - -class RangeTrait : public llvm::ImutContainerInfo { -public: - // When comparing if one Range is less than another, we should compare - // the actual APSInt values instead of their pointers. This keeps the order - // consistent (instead of comparing by pointer values) and can potentially - // be used to speed up some of the operations in RangeSet. - static inline bool isLess(key_type_ref lhs, key_type_ref rhs) { - return *lhs.first < *rhs.first || (!(*rhs.first < *lhs.first) && - *lhs.second < *rhs.second); - } -}; - -/// RangeSet contains a set of ranges. If the set is empty, then -/// there the value of a symbol is overly constrained and there are no -/// possible values for that symbol. -class RangeSet { - typedef llvm::ImmutableSet PrimRangeSet; - PrimRangeSet ranges; // no need to make const, since it is an - // ImmutableSet - this allows default operator= - // to work. -public: - typedef PrimRangeSet::Factory Factory; - typedef PrimRangeSet::iterator iterator; - - RangeSet(PrimRangeSet RS) : ranges(RS) {} - RangeSet(Factory& F) : ranges(F.GetEmptySet()) {} - - iterator begin() const { return ranges.begin(); } - iterator end() const { return ranges.end(); } - - bool isEmpty() const { return ranges.isEmpty(); } - - /// Construct a new RangeSet representing '{ [from, to] }'. - RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to) - : ranges(F.Add(F.GetEmptySet(), Range(from, to))) {} - - /// Profile - Generates a hash profile of this RangeSet for use - /// by FoldingSet. - void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); } - - /// getConcreteValue - If a symbol is contrained to equal a specific integer - /// constant then this method returns that value. Otherwise, it returns - /// NULL. - const llvm::APSInt* getConcreteValue() const { - return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : 0; - } - - /// AddEQ - Create a new RangeSet with the additional constraint that the - /// value be equal to V. - RangeSet AddEQ(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - // Search for a range that includes 'V'. If so, return a new RangeSet - // representing { [V, V] }. - for (PrimRangeSet::iterator i = begin(), e = end(); i!=e; ++i) - if (i->Includes(V)) - return RangeSet(F, V, V); - - return RangeSet(F); - } - - /// AddNE - Create a new RangeSet with the additional constraint that the - /// value be not be equal to V. - RangeSet AddNE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - PrimRangeSet newRanges = ranges; - - // FIXME: We can perhaps enhance ImmutableSet to do this search for us - // in log(N) time using the sorted property of the internal AVL tree. - for (iterator i = begin(), e = end(); i != e; ++i) { - if (i->Includes(V)) { - // Remove the old range. - newRanges = F.Remove(newRanges, *i); - // Split the old range into possibly one or two ranges. - if (V != i->From()) - newRanges = F.Add(newRanges, Range(i->From(), BV.Sub1(V))); - if (V != i->To()) - newRanges = F.Add(newRanges, Range(BV.Add1(V), i->To())); - // All of the ranges are non-overlapping, so we can stop. - break; - } - } - - return newRanges; - } - - /// AddNE - Create a new RangeSet with the additional constraint that the - /// value be less than V. - RangeSet AddLT(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - PrimRangeSet newRanges = F.GetEmptySet(); - - for (iterator i = begin(), e = end() ; i != e ; ++i) { - if (i->Includes(V) && i->From() < V) - newRanges = F.Add(newRanges, Range(i->From(), BV.Sub1(V))); - else if (i->To() < V) - newRanges = F.Add(newRanges, *i); - } - - return newRanges; - } - - RangeSet AddLE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - PrimRangeSet newRanges = F.GetEmptySet(); - - for (iterator i = begin(), e = end(); i != e; ++i) { - // Strictly we should test for includes *V + 1, but no harm is - // done by this formulation - if (i->Includes(V)) - newRanges = F.Add(newRanges, Range(i->From(), V)); - else if (i->To() <= V) - newRanges = F.Add(newRanges, *i); - } - - return newRanges; - } - - RangeSet AddGT(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - PrimRangeSet newRanges = F.GetEmptySet(); - - for (PrimRangeSet::iterator i = begin(), e = end(); i != e; ++i) { - if (i->Includes(V) && i->To() > V) - newRanges = F.Add(newRanges, Range(BV.Add1(V), i->To())); - else if (i->From() > V) - newRanges = F.Add(newRanges, *i); - } - - return newRanges; - } - - RangeSet AddGE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - PrimRangeSet newRanges = F.GetEmptySet(); - - for (PrimRangeSet::iterator i = begin(), e = end(); i != e; ++i) { - // Strictly we should test for includes *V - 1, but no harm is - // done by this formulation - if (i->Includes(V)) - newRanges = F.Add(newRanges, Range(V, i->To())); - else if (i->From() >= V) - newRanges = F.Add(newRanges, *i); - } - - return newRanges; - } - - void print(llvm::raw_ostream &os) const { - bool isFirst = true; - os << "{ "; - for (iterator i = begin(), e = end(); i != e; ++i) { - if (isFirst) - isFirst = false; - else - os << ", "; - - os << '[' << i->From().toString(10) << ", " << i->To().toString(10) - << ']'; - } - os << " }"; - } - - bool operator==(const RangeSet &other) const { - return ranges == other.ranges; - } -}; -} // end anonymous namespace - -typedef llvm::ImmutableMap ConstraintRangeTy; - -namespace clang { -template<> -struct GRStateTrait - : public GRStatePartialTrait { - static inline void* GDMIndex() { return &ConstraintRangeIndex; } -}; -} - -namespace { -class RangeConstraintManager : public SimpleConstraintManager{ - RangeSet GetRange(const GRState *state, SymbolRef sym); -public: - RangeConstraintManager(GRSubEngine &subengine) - : SimpleConstraintManager(subengine) {} - - const GRState* AssumeSymNE(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymEQ(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymLT(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymGT(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymGE(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymLE(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const llvm::APSInt* getSymVal(const GRState* St, SymbolRef sym) const; - - // FIXME: Refactor into SimpleConstraintManager? - bool isEqual(const GRState* St, SymbolRef sym, const llvm::APSInt& V) const { - const llvm::APSInt *i = getSymVal(St, sym); - return i ? *i == V : false; - } - - const GRState* RemoveDeadBindings(const GRState* St, SymbolReaper& SymReaper); - - void print(const GRState* St, llvm::raw_ostream& Out, - const char* nl, const char *sep); - -private: - RangeSet::Factory F; -}; - -} // end anonymous namespace - -ConstraintManager* clang::CreateRangeConstraintManager(GRStateManager&, - GRSubEngine &subeng) { - return new RangeConstraintManager(subeng); -} - -const llvm::APSInt* RangeConstraintManager::getSymVal(const GRState* St, - SymbolRef sym) const { - const ConstraintRangeTy::data_type *T = St->get(sym); - return T ? T->getConcreteValue() : NULL; -} - -/// Scan all symbols referenced by the constraints. If the symbol is not alive -/// as marked in LSymbols, mark it as dead in DSymbols. -const GRState* -RangeConstraintManager::RemoveDeadBindings(const GRState* state, - SymbolReaper& SymReaper) { - - ConstraintRangeTy CR = state->get(); - ConstraintRangeTy::Factory& CRFactory = state->get_context(); - - for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) { - SymbolRef sym = I.getKey(); - if (SymReaper.maybeDead(sym)) - CR = CRFactory.Remove(CR, sym); - } - - return state->set(CR); -} - -//===------------------------------------------------------------------------=== -// AssumeSymX methods: public interface for RangeConstraintManager. -//===------------------------------------------------------------------------===/ - -RangeSet -RangeConstraintManager::GetRange(const GRState *state, SymbolRef sym) { - if (ConstraintRangeTy::data_type* V = state->get(sym)) - return *V; - - // Lazily generate a new RangeSet representing all possible values for the - // given symbol type. - QualType T = state->getSymbolManager().getType(sym); - BasicValueFactory& BV = state->getBasicVals(); - return RangeSet(F, BV.getMinValue(T), BV.getMaxValue(T)); -} - -//===------------------------------------------------------------------------=== -// AssumeSymX methods: public interface for RangeConstraintManager. -//===------------------------------------------------------------------------===/ - -#define AssumeX(OP)\ -const GRState*\ -RangeConstraintManager::AssumeSym ## OP(const GRState* state, SymbolRef sym,\ - const llvm::APSInt& V){\ - const RangeSet& R = GetRange(state, sym).Add##OP(state->getBasicVals(), F, V);\ - return !R.isEmpty() ? state->set(sym, R) : NULL;\ -} - -AssumeX(EQ) -AssumeX(NE) -AssumeX(LT) -AssumeX(GT) -AssumeX(LE) -AssumeX(GE) - -//===------------------------------------------------------------------------=== -// Pretty-printing. -//===------------------------------------------------------------------------===/ - -void RangeConstraintManager::print(const GRState* St, llvm::raw_ostream& Out, - const char* nl, const char *sep) { - - ConstraintRangeTy Ranges = St->get(); - - if (Ranges.isEmpty()) - return; - - Out << nl << sep << "ranges of symbol values:"; - - for (ConstraintRangeTy::iterator I=Ranges.begin(), E=Ranges.end(); I!=E; ++I){ - Out << nl << ' ' << I.getKey() << " : "; - I.getData().print(Out); - } -} diff --git a/lib/Analysis/RegionStore.cpp b/lib/Analysis/RegionStore.cpp deleted file mode 100644 index a735ed94578e..000000000000 --- a/lib/Analysis/RegionStore.cpp +++ /dev/null @@ -1,2015 +0,0 @@ -//== RegionStore.cpp - Field-sensitive store model --------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a basic region store model. In this model, we do have field -// sensitivity. But we assume nothing about the heap shape. So recursive data -// structures are largely ignored. Basically we do 1-limiting analysis. -// Parameter pointers are assumed with no aliasing. Pointee objects of -// parameters are created lazily. -// -//===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/MemRegion.h" -#include "clang/Analysis/PathSensitive/AnalysisContext.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/Analysis/Support/Optional.h" -#include "clang/Basic/TargetInfo.h" -#include "clang/AST/CharUnits.h" - -#include "llvm/ADT/ImmutableMap.h" -#include "llvm/ADT/ImmutableList.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -#define USE_EXPLICIT_COMPOUND 0 - -//===----------------------------------------------------------------------===// -// Representation of value bindings. -//===----------------------------------------------------------------------===// - -namespace { -class BindingVal { -public: - enum BindingKind { Direct, Default }; -private: - SVal Value; - BindingKind Kind; - -public: - BindingVal(SVal V, BindingKind K) : Value(V), Kind(K) {} - - bool isDefault() const { return Kind == Default; } - - const SVal *getValue() const { return &Value; } - - const SVal *getDirectValue() const { return isDefault() ? 0 : &Value; } - - const SVal *getDefaultValue() const { return isDefault() ? &Value : 0; } - - void Profile(llvm::FoldingSetNodeID& ID) const { - Value.Profile(ID); - ID.AddInteger(Kind); - } - - inline bool operator==(const BindingVal& R) const { - return Value == R.Value && Kind == R.Kind; - } - - inline bool operator!=(const BindingVal& R) const { - return !(*this == R); - } -}; -} - -namespace llvm { -static inline -llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BindingVal V) { - if (V.isDefault()) - os << "(default) "; - else - os << "(direct) "; - os << *V.getValue(); - return os; -} -} // end llvm namespace - -//===----------------------------------------------------------------------===// -// Representation of binding keys. -//===----------------------------------------------------------------------===// - -namespace { - class BindingKey : public std::pair { -public: - explicit BindingKey(const MemRegion *r, uint64_t offset) - : std::pair(r, offset) { assert(r); } - - const MemRegion *getRegion() const { return first; } - uint64_t getOffset() const { return second; } - - void Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddPointer(getRegion()); - ID.AddInteger(getOffset()); - } - - static BindingKey Make(const MemRegion *R); -}; -} // end anonymous namespace - -namespace llvm { - static inline - llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BindingKey K) { - os << '(' << K.getRegion() << ',' << K.getOffset() << ')'; - return os; - } -} // end llvm namespace - -//===----------------------------------------------------------------------===// -// Actual Store type. -//===----------------------------------------------------------------------===// - -typedef llvm::ImmutableMap RegionBindings; - -//===----------------------------------------------------------------------===// -// Fine-grained control of RegionStoreManager. -//===----------------------------------------------------------------------===// - -namespace { -struct minimal_features_tag {}; -struct maximal_features_tag {}; - -class RegionStoreFeatures { - bool SupportsFields; - bool SupportsRemaining; - -public: - RegionStoreFeatures(minimal_features_tag) : - SupportsFields(false), SupportsRemaining(false) {} - - RegionStoreFeatures(maximal_features_tag) : - SupportsFields(true), SupportsRemaining(false) {} - - void enableFields(bool t) { SupportsFields = t; } - - bool supportsFields() const { return SupportsFields; } - bool supportsRemaining() const { return SupportsRemaining; } -}; -} - -//===----------------------------------------------------------------------===// -// Region "Extents" -//===----------------------------------------------------------------------===// -// -// MemRegions represent chunks of memory with a size (their "extent"). This -// GDM entry tracks the extents for regions. Extents are in bytes. -// -namespace { class RegionExtents {}; } -static int RegionExtentsIndex = 0; -namespace clang { - template<> struct GRStateTrait - : public GRStatePartialTrait > { - static void* GDMIndex() { return &RegionExtentsIndex; } - }; -} - -//===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -static bool IsAnyPointerOrIntptr(QualType ty, ASTContext &Ctx) { - if (ty->isAnyPointerType()) - return true; - - return ty->isIntegerType() && ty->isScalarType() && - Ctx.getTypeSize(ty) == Ctx.getTypeSize(Ctx.VoidPtrTy); -} - -//===----------------------------------------------------------------------===// -// Main RegionStore logic. -//===----------------------------------------------------------------------===// - -namespace { - -class RegionStoreSubRegionMap : public SubRegionMap { - typedef llvm::ImmutableSet SetTy; - typedef llvm::DenseMap Map; - SetTy::Factory F; - Map M; -public: - bool add(const MemRegion* Parent, const MemRegion* SubRegion) { - Map::iterator I = M.find(Parent); - - if (I == M.end()) { - M.insert(std::make_pair(Parent, F.Add(F.GetEmptySet(), SubRegion))); - return true; - } - - I->second = F.Add(I->second, SubRegion); - return false; - } - - void process(llvm::SmallVectorImpl &WL, const SubRegion *R); - - ~RegionStoreSubRegionMap() {} - - bool iterSubRegions(const MemRegion* Parent, Visitor& V) const { - Map::const_iterator I = M.find(Parent); - - if (I == M.end()) - return true; - - llvm::ImmutableSet S = I->second; - for (llvm::ImmutableSet::iterator SI=S.begin(),SE=S.end(); - SI != SE; ++SI) { - if (!V.Visit(Parent, *SI)) - return false; - } - - return true; - } - - typedef SetTy::iterator iterator; - - std::pair begin_end(const MemRegion *R) { - Map::iterator I = M.find(R); - SetTy S = I == M.end() ? F.GetEmptySet() : I->second; - return std::make_pair(S.begin(), S.end()); - } -}; - -class RegionStoreManager : public StoreManager { - const RegionStoreFeatures Features; - RegionBindings::Factory RBFactory; - - typedef llvm::DenseMap SMCache; - SMCache SC; - -public: - RegionStoreManager(GRStateManager& mgr, const RegionStoreFeatures &f) - : StoreManager(mgr), - Features(f), - RBFactory(mgr.getAllocator()) {} - - virtual ~RegionStoreManager() { - for (SMCache::iterator I = SC.begin(), E = SC.end(); I != E; ++I) - delete (*I).second; - } - - SubRegionMap *getSubRegionMap(const GRState *state); - - RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store); - - Optional getBinding(RegionBindings B, const MemRegion *R); - Optional getDirectBinding(RegionBindings B, const MemRegion *R); - /// getDefaultBinding - Returns an SVal* representing an optional default - /// binding associated with a region and its subregions. - Optional getDefaultBinding(RegionBindings B, const MemRegion *R); - - /// setImplicitDefaultValue - Set the default binding for the provided - /// MemRegion to the value implicitly defined for compound literals when - /// the value is not specified. - const GRState *setImplicitDefaultValue(const GRState *state, - const MemRegion *R, - QualType T); - - /// getLValueString - Returns an SVal representing the lvalue of a - /// StringLiteral. Within RegionStore a StringLiteral has an - /// associated StringRegion, and the lvalue of a StringLiteral is - /// the lvalue of that region. - SVal getLValueString(const StringLiteral* S); - - /// getLValueCompoundLiteral - Returns an SVal representing the - /// lvalue of a compound literal. Within RegionStore a compound - /// literal has an associated region, and the lvalue of the - /// compound literal is the lvalue of that region. - SVal getLValueCompoundLiteral(const CompoundLiteralExpr*); - - /// getLValueVar - Returns an SVal that represents the lvalue of a - /// variable. Within RegionStore a variable has an associated - /// VarRegion, and the lvalue of the variable is the lvalue of that region. - SVal getLValueVar(const VarDecl *VD, const LocationContext *LC); - - SVal getLValueIvar(const ObjCIvarDecl* D, SVal Base); - - SVal getLValueField(const FieldDecl* D, SVal Base); - - SVal getLValueFieldOrIvar(const Decl* D, SVal Base); - - SVal getLValueElement(QualType elementType, SVal Offset, SVal Base); - - - /// ArrayToPointer - Emulates the "decay" of an array to a pointer - /// type. 'Array' represents the lvalue of the array being decayed - /// to a pointer, and the returned SVal represents the decayed - /// version of that lvalue (i.e., a pointer to the first element of - /// the array). This is called by GRExprEngine when evaluating - /// casts from arrays to pointers. - SVal ArrayToPointer(Loc Array); - - SVal EvalBinOp(const GRState *state, BinaryOperator::Opcode Op,Loc L, - NonLoc R, QualType resultTy); - - Store getInitialStore(const LocationContext *InitLoc) { - return RBFactory.GetEmptyMap().getRoot(); - } - - //===-------------------------------------------------------------------===// - // Binding values to regions. - //===-------------------------------------------------------------------===// - - const GRState *InvalidateRegion(const GRState *state, const MemRegion *R, - const Expr *E, unsigned Count, - InvalidatedSymbols *IS) { - return RegionStoreManager::InvalidateRegions(state, &R, &R+1, E, Count, IS); - } - - const GRState *InvalidateRegions(const GRState *state, - const MemRegion * const *Begin, - const MemRegion * const *End, - const Expr *E, unsigned Count, - InvalidatedSymbols *IS); - -private: - void RemoveSubRegionBindings(RegionBindings &B, const MemRegion *R, - RegionStoreSubRegionMap &M); - - RegionBindings Add(RegionBindings B, BindingKey K, BindingVal V); - RegionBindings Add(RegionBindings B, const MemRegion *R, BindingVal V); - - const BindingVal *Lookup(RegionBindings B, BindingKey K); - const BindingVal *Lookup(RegionBindings B, const MemRegion *R); - - RegionBindings Remove(RegionBindings B, BindingKey K); - RegionBindings Remove(RegionBindings B, const MemRegion *R); - Store Remove(Store store, BindingKey K); - -public: - const GRState *Bind(const GRState *state, Loc LV, SVal V); - - const GRState *BindCompoundLiteral(const GRState *state, - const CompoundLiteralExpr* CL, - const LocationContext *LC, - SVal V); - - const GRState *BindDecl(const GRState *ST, const VarRegion *VR, - SVal InitVal); - - const GRState *BindDeclWithNoInit(const GRState *state, - const VarRegion *) { - return state; - } - - /// BindStruct - Bind a compound value to a structure. - const GRState *BindStruct(const GRState *, const TypedRegion* R, SVal V); - - const GRState *BindArray(const GRState *state, const TypedRegion* R, SVal V); - - /// KillStruct - Set the entire struct to unknown. - Store KillStruct(Store store, const TypedRegion* R); - - Store Remove(Store store, Loc LV); - - - //===------------------------------------------------------------------===// - // Loading values from regions. - //===------------------------------------------------------------------===// - - /// The high level logic for this method is this: - /// Retrieve (L) - /// if L has binding - /// return L's binding - /// else if L is in killset - /// return unknown - /// else - /// if L is on stack or heap - /// return undefined - /// else - /// return symbolic - SValuator::CastResult Retrieve(const GRState *state, Loc L, - QualType T = QualType()); - - SVal RetrieveElement(const GRState *state, const ElementRegion *R); - - SVal RetrieveField(const GRState *state, const FieldRegion *R); - - SVal RetrieveObjCIvar(const GRState *state, const ObjCIvarRegion *R); - - SVal RetrieveVar(const GRState *state, const VarRegion *R); - - SVal RetrieveLazySymbol(const GRState *state, const TypedRegion *R); - - SVal RetrieveFieldOrElementCommon(const GRState *state, const TypedRegion *R, - QualType Ty, const MemRegion *superR); - - /// Retrieve the values in a struct and return a CompoundVal, used when doing - /// struct copy: - /// struct s x, y; - /// x = y; - /// y's value is retrieved by this method. - SVal RetrieveStruct(const GRState *St, const TypedRegion* R); - - SVal RetrieveArray(const GRState *St, const TypedRegion* R); - - /// Get the state and region whose binding this region R corresponds to. - std::pair - GetLazyBinding(RegionBindings B, const MemRegion *R); - - const GRState* CopyLazyBindings(nonloc::LazyCompoundVal V, - const GRState *state, - const TypedRegion *R); - - const ElementRegion *GetElementZeroRegion(const SymbolicRegion *SR, - QualType T); - - //===------------------------------------------------------------------===// - // State pruning. - //===------------------------------------------------------------------===// - - /// RemoveDeadBindings - Scans the RegionStore of 'state' for dead values. - /// It returns a new Store with these values removed. - void RemoveDeadBindings(GRState &state, Stmt* Loc, SymbolReaper& SymReaper, - llvm::SmallVectorImpl& RegionRoots); - - const GRState *EnterStackFrame(const GRState *state, - const StackFrameContext *frame); - - //===------------------------------------------------------------------===// - // Region "extents". - //===------------------------------------------------------------------===// - - const GRState *setExtent(const GRState *state,const MemRegion* R,SVal Extent); - DefinedOrUnknownSVal getSizeInElements(const GRState *state, - const MemRegion* R, QualType EleTy); - - //===------------------------------------------------------------------===// - // Utility methods. - //===------------------------------------------------------------------===// - - static inline RegionBindings GetRegionBindings(Store store) { - return RegionBindings(static_cast(store)); - } - - void print(Store store, llvm::raw_ostream& Out, const char* nl, - const char *sep); - - void iterBindings(Store store, BindingsHandler& f) { - // FIXME: Implement. - } - - // FIXME: Remove. - BasicValueFactory& getBasicVals() { - return StateMgr.getBasicVals(); - } - - // FIXME: Remove. - ASTContext& getContext() { return StateMgr.getContext(); } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// RegionStore creation. -//===----------------------------------------------------------------------===// - -StoreManager *clang::CreateRegionStoreManager(GRStateManager& StMgr) { - RegionStoreFeatures F = maximal_features_tag(); - return new RegionStoreManager(StMgr, F); -} - -StoreManager *clang::CreateFieldsOnlyRegionStoreManager(GRStateManager &StMgr) { - RegionStoreFeatures F = minimal_features_tag(); - F.enableFields(true); - return new RegionStoreManager(StMgr, F); -} - -void -RegionStoreSubRegionMap::process(llvm::SmallVectorImpl &WL, - const SubRegion *R) { - const MemRegion *superR = R->getSuperRegion(); - if (add(superR, R)) - if (const SubRegion *sr = dyn_cast(superR)) - WL.push_back(sr); -} - -RegionStoreSubRegionMap* -RegionStoreManager::getRegionStoreSubRegionMap(Store store) { - RegionBindings B = GetRegionBindings(store); - RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap(); - - llvm::SmallVector WL; - - for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) - if (const SubRegion *R = dyn_cast(I.getKey().getRegion())) - M->process(WL, R); - - // We also need to record in the subregion map "intermediate" regions that - // don't have direct bindings but are super regions of those that do. - while (!WL.empty()) { - const SubRegion *R = WL.back(); - WL.pop_back(); - M->process(WL, R); - } - - return M; -} - -SubRegionMap *RegionStoreManager::getSubRegionMap(const GRState *state) { - return getRegionStoreSubRegionMap(state->getStore()); -} - -//===----------------------------------------------------------------------===// -// Binding invalidation. -//===----------------------------------------------------------------------===// - -void RegionStoreManager::RemoveSubRegionBindings(RegionBindings &B, - const MemRegion *R, - RegionStoreSubRegionMap &M) { - RegionStoreSubRegionMap::iterator I, E; - - for (llvm::tie(I, E) = M.begin_end(R); I != E; ++I) - RemoveSubRegionBindings(B, *I, M); - - B = Remove(B, R); -} - -const GRState *RegionStoreManager::InvalidateRegions(const GRState *state, - const MemRegion * const *I, - const MemRegion * const *E, - const Expr *Ex, - unsigned Count, - InvalidatedSymbols *IS) { - ASTContext& Ctx = StateMgr.getContext(); - - // Get the mapping of regions -> subregions. - llvm::OwningPtr - SubRegions(getRegionStoreSubRegionMap(state->getStore())); - - RegionBindings B = GetRegionBindings(state->getStore()); - - llvm::DenseMap Visited; - llvm::SmallVector WorkList; - - for ( ; I != E; ++I) { - // Strip away casts. - WorkList.push_back((*I)->StripCasts()); - } - - while (!WorkList.empty()) { - const MemRegion *R = WorkList.back(); - WorkList.pop_back(); - - // Have we visited this region before? - unsigned &visited = Visited[R]; - if (visited) - continue; - visited = 1; - - // Add subregions to work list. - RegionStoreSubRegionMap::iterator I, E; - for (llvm::tie(I, E) = SubRegions->begin_end(R); I!=E; ++I) - WorkList.push_back(*I); - - // Get the old binding. Is it a region? If so, add it to the worklist. - if (Optional V = getDirectBinding(B, R)) { - if (const MemRegion *RV = V->getAsRegion()) - WorkList.push_back(RV); - - // A symbol? Mark it touched by the invalidation. - if (IS) { - if (SymbolRef Sym = V->getAsSymbol()) - IS->insert(Sym); - } - } - - // Symbolic region? Mark that symbol touched by the invalidation. - if (IS) { - if (const SymbolicRegion *SR = dyn_cast(R)) - IS->insert(SR->getSymbol()); - } - - // BlockDataRegion? If so, invalidate captured variables that are passed - // by reference. - if (const BlockDataRegion *BR = dyn_cast(R)) { - for (BlockDataRegion::referenced_vars_iterator - I = BR->referenced_vars_begin(), E = BR->referenced_vars_end() ; - I != E; ++I) { - const VarRegion *VR = *I; - if (VR->getDecl()->getAttr()) - WorkList.push_back(VR); - } - continue; - } - - // Handle the region itself. - if (isa(R) || isa(R)) { - // Invalidate the region by setting its default value to - // conjured symbol. The type of the symbol is irrelavant. - DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(R, Ex, Ctx.IntTy, - Count); - B = Add(B, R, BindingVal(V, BindingVal::Default)); - continue; - } - - if (!R->isBoundable()) - continue; - - const TypedRegion *TR = cast(R); - QualType T = TR->getValueType(Ctx); - - if (const RecordType *RT = T->getAsStructureType()) { - const RecordDecl *RD = RT->getDecl()->getDefinition(Ctx); - - // No record definition. There is nothing we can do. - if (!RD) - continue; - - // Invalidate the region by setting its default value to - // conjured symbol. The type of the symbol is irrelavant. - DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(R, Ex, Ctx.IntTy, - Count); - B = Add(B, R, BindingVal(V, BindingVal::Default)); - continue; - } - - if (const ArrayType *AT = Ctx.getAsArrayType(T)) { - // Set the default value of the array to conjured symbol. - DefinedOrUnknownSVal V = - ValMgr.getConjuredSymbolVal(R, Ex, AT->getElementType(), Count); - B = Add(B, R, BindingVal(V, BindingVal::Default)); - continue; - } - - if ((isa(R)||isa(R)||isa(R)) - && Visited[cast(R)->getSuperRegion()]) { - // For fields and elements whose super region has also been invalidated, - // only remove the old binding. The super region will get set with a - // default value from which we can lazily derive a new symbolic value. - B = Remove(B, R); - continue; - } - - // Invalidate the binding. - DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(R, Ex, T, Count); - assert(SymbolManager::canSymbolicate(T) || V.isUnknown()); - B = Add(B, R, BindingVal(V, BindingVal::Direct)); - } - - // Create a new state with the updated bindings. - return state->makeWithStore(B.getRoot()); -} - -//===----------------------------------------------------------------------===// -// getLValueXXX methods. -//===----------------------------------------------------------------------===// - -/// getLValueString - Returns an SVal representing the lvalue of a -/// StringLiteral. Within RegionStore a StringLiteral has an -/// associated StringRegion, and the lvalue of a StringLiteral is the -/// lvalue of that region. -SVal RegionStoreManager::getLValueString(const StringLiteral* S) { - return loc::MemRegionVal(MRMgr.getStringRegion(S)); -} - -/// getLValueVar - Returns an SVal that represents the lvalue of a -/// variable. Within RegionStore a variable has an associated -/// VarRegion, and the lvalue of the variable is the lvalue of that region. -SVal RegionStoreManager::getLValueVar(const VarDecl *VD, - const LocationContext *LC) { - return loc::MemRegionVal(MRMgr.getVarRegion(VD, LC)); -} - -SVal RegionStoreManager::getLValueIvar(const ObjCIvarDecl* D, SVal Base) { - return getLValueFieldOrIvar(D, Base); -} - -SVal RegionStoreManager::getLValueField(const FieldDecl* D, SVal Base) { - return getLValueFieldOrIvar(D, Base); -} - -SVal RegionStoreManager::getLValueFieldOrIvar(const Decl* D, SVal Base) { - if (Base.isUnknownOrUndef()) - return Base; - - Loc BaseL = cast(Base); - const MemRegion* BaseR = 0; - - switch (BaseL.getSubKind()) { - case loc::MemRegionKind: - BaseR = cast(BaseL).getRegion(); - break; - - case loc::GotoLabelKind: - // These are anormal cases. Flag an undefined value. - return UndefinedVal(); - - case loc::ConcreteIntKind: - // While these seem funny, this can happen through casts. - // FIXME: What we should return is the field offset. For example, - // add the field offset to the integer value. That way funny things - // like this work properly: &(((struct foo *) 0xa)->f) - return Base; - - default: - assert(0 && "Unhandled Base."); - return Base; - } - - // NOTE: We must have this check first because ObjCIvarDecl is a subclass - // of FieldDecl. - if (const ObjCIvarDecl *ID = dyn_cast(D)) - return loc::MemRegionVal(MRMgr.getObjCIvarRegion(ID, BaseR)); - - return loc::MemRegionVal(MRMgr.getFieldRegion(cast(D), BaseR)); -} - -SVal RegionStoreManager::getLValueElement(QualType elementType, SVal Offset, - SVal Base) { - - // If the base is an unknown or undefined value, just return it back. - // FIXME: For absolute pointer addresses, we just return that value back as - // well, although in reality we should return the offset added to that - // value. - if (Base.isUnknownOrUndef() || isa(Base)) - return Base; - - // Only handle integer offsets... for now. - if (!isa(Offset)) - return UnknownVal(); - - const MemRegion* BaseRegion = cast(Base).getRegion(); - - // Pointer of any type can be cast and used as array base. - const ElementRegion *ElemR = dyn_cast(BaseRegion); - - // Convert the offset to the appropriate size and signedness. - Offset = ValMgr.convertToArrayIndex(Offset); - - if (!ElemR) { - // - // If the base region is not an ElementRegion, create one. - // This can happen in the following example: - // - // char *p = __builtin_alloc(10); - // p[1] = 8; - // - // Observe that 'p' binds to an AllocaRegion. - // - return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset, - BaseRegion, getContext())); - } - - SVal BaseIdx = ElemR->getIndex(); - - if (!isa(BaseIdx)) - return UnknownVal(); - - const llvm::APSInt& BaseIdxI = cast(BaseIdx).getValue(); - const llvm::APSInt& OffI = cast(Offset).getValue(); - assert(BaseIdxI.isSigned()); - - // Compute the new index. - SVal NewIdx = nonloc::ConcreteInt(getBasicVals().getValue(BaseIdxI + OffI)); - - // Construct the new ElementRegion. - const MemRegion *ArrayR = ElemR->getSuperRegion(); - return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR, - getContext())); -} - -//===----------------------------------------------------------------------===// -// Extents for regions. -//===----------------------------------------------------------------------===// - -DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state, - const MemRegion *R, - QualType EleTy) { - - switch (R->getKind()) { - case MemRegion::CXXThisRegionKind: - assert(0 && "Cannot get size of 'this' region"); - case MemRegion::GenericMemSpaceRegionKind: - case MemRegion::StackLocalsSpaceRegionKind: - case MemRegion::StackArgumentsSpaceRegionKind: - case MemRegion::HeapSpaceRegionKind: - case MemRegion::GlobalsSpaceRegionKind: - case MemRegion::UnknownSpaceRegionKind: - assert(0 && "Cannot index into a MemSpace"); - return UnknownVal(); - - case MemRegion::FunctionTextRegionKind: - case MemRegion::BlockTextRegionKind: - case MemRegion::BlockDataRegionKind: - // Technically this can happen if people do funny things with casts. - return UnknownVal(); - - // Not yet handled. - case MemRegion::AllocaRegionKind: - case MemRegion::CompoundLiteralRegionKind: - case MemRegion::ElementRegionKind: - case MemRegion::FieldRegionKind: - case MemRegion::ObjCIvarRegionKind: - case MemRegion::CXXObjectRegionKind: - return UnknownVal(); - - case MemRegion::SymbolicRegionKind: { - const SVal *Size = state->get(R); - if (!Size) - return UnknownVal(); - const nonloc::ConcreteInt *CI = dyn_cast(Size); - if (!CI) - return UnknownVal(); - - CharUnits RegionSize = - CharUnits::fromQuantity(CI->getValue().getSExtValue()); - CharUnits EleSize = getContext().getTypeSizeInChars(EleTy); - assert(RegionSize % EleSize == 0); - - return ValMgr.makeIntVal(RegionSize / EleSize, false); - } - - case MemRegion::StringRegionKind: { - const StringLiteral* Str = cast(R)->getStringLiteral(); - // We intentionally made the size value signed because it participates in - // operations with signed indices. - return ValMgr.makeIntVal(Str->getByteLength()+1, false); - } - - case MemRegion::VarRegionKind: { - const VarRegion* VR = cast(R); - // Get the type of the variable. - QualType T = VR->getDesugaredValueType(getContext()); - - // FIXME: Handle variable-length arrays. - if (isa(T)) - return UnknownVal(); - - if (const ConstantArrayType* CAT = dyn_cast(T)) { - // return the size as signed integer. - return ValMgr.makeIntVal(CAT->getSize(), false); - } - - // Clients can use ordinary variables as if they were arrays. These - // essentially are arrays of size 1. - return ValMgr.makeIntVal(1, false); - } - } - - assert(0 && "Unreachable"); - return UnknownVal(); -} - -const GRState *RegionStoreManager::setExtent(const GRState *state, - const MemRegion *region, - SVal extent) { - return state->set(region, extent); -} - -//===----------------------------------------------------------------------===// -// Location and region casting. -//===----------------------------------------------------------------------===// - -/// ArrayToPointer - Emulates the "decay" of an array to a pointer -/// type. 'Array' represents the lvalue of the array being decayed -/// to a pointer, and the returned SVal represents the decayed -/// version of that lvalue (i.e., a pointer to the first element of -/// the array). This is called by GRExprEngine when evaluating casts -/// from arrays to pointers. -SVal RegionStoreManager::ArrayToPointer(Loc Array) { - if (!isa(Array)) - return UnknownVal(); - - const MemRegion* R = cast(&Array)->getRegion(); - const TypedRegion* ArrayR = dyn_cast(R); - - if (!ArrayR) - return UnknownVal(); - - // Strip off typedefs from the ArrayRegion's ValueType. - QualType T = ArrayR->getValueType(getContext()).getDesugaredType(); - ArrayType *AT = cast(T); - T = AT->getElementType(); - - SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, - getContext())); -} - -//===----------------------------------------------------------------------===// -// Pointer arithmetic. -//===----------------------------------------------------------------------===// - -SVal RegionStoreManager::EvalBinOp(const GRState *state, - BinaryOperator::Opcode Op, Loc L, NonLoc R, - QualType resultTy) { - // Assume the base location is MemRegionVal. - if (!isa(L)) - return UnknownVal(); - - const MemRegion* MR = cast(L).getRegion(); - const ElementRegion *ER = 0; - - switch (MR->getKind()) { - case MemRegion::SymbolicRegionKind: { - const SymbolicRegion *SR = cast(MR); - SymbolRef Sym = SR->getSymbol(); - QualType T = Sym->getType(getContext()); - QualType EleTy; - - if (const PointerType *PT = T->getAs()) - EleTy = PT->getPointeeType(); - else - EleTy = T->getAs()->getPointeeType(); - - SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - ER = MRMgr.getElementRegion(EleTy, ZeroIdx, SR, getContext()); - break; - } - case MemRegion::AllocaRegionKind: { - const AllocaRegion *AR = cast(MR); - QualType T = getContext().CharTy; // Create an ElementRegion of bytes. - QualType EleTy = T->getAs()->getPointeeType(); - SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - ER = MRMgr.getElementRegion(EleTy, ZeroIdx, AR, getContext()); - break; - } - - case MemRegion::ElementRegionKind: { - ER = cast(MR); - break; - } - - // Not yet handled. - case MemRegion::VarRegionKind: - case MemRegion::StringRegionKind: { - - } - // Fall-through. - case MemRegion::CompoundLiteralRegionKind: - case MemRegion::FieldRegionKind: - case MemRegion::ObjCIvarRegionKind: - case MemRegion::CXXObjectRegionKind: - return UnknownVal(); - - case MemRegion::FunctionTextRegionKind: - case MemRegion::BlockTextRegionKind: - case MemRegion::BlockDataRegionKind: - // Technically this can happen if people do funny things with casts. - return UnknownVal(); - - case MemRegion::CXXThisRegionKind: - assert(0 && - "Cannot perform pointer arithmetic on implicit argument 'this'"); - case MemRegion::GenericMemSpaceRegionKind: - case MemRegion::StackLocalsSpaceRegionKind: - case MemRegion::StackArgumentsSpaceRegionKind: - case MemRegion::HeapSpaceRegionKind: - case MemRegion::GlobalsSpaceRegionKind: - case MemRegion::UnknownSpaceRegionKind: - assert(0 && "Cannot perform pointer arithmetic on a MemSpace"); - return UnknownVal(); - } - - SVal Idx = ER->getIndex(); - nonloc::ConcreteInt* Base = dyn_cast(&Idx); - - // For now, only support: - // (a) concrete integer indices that can easily be resolved - // (b) 0 + symbolic index - if (Base) { - if (nonloc::ConcreteInt *Offset = dyn_cast(&R)) { - // FIXME: Should use SValuator here. - SVal NewIdx = - Base->evalBinOp(ValMgr, Op, - cast(ValMgr.convertToArrayIndex(*Offset))); - const MemRegion* NewER = - MRMgr.getElementRegion(ER->getElementType(), NewIdx, - ER->getSuperRegion(), getContext()); - return ValMgr.makeLoc(NewER); - } - if (0 == Base->getValue()) { - const MemRegion* NewER = - MRMgr.getElementRegion(ER->getElementType(), R, - ER->getSuperRegion(), getContext()); - return ValMgr.makeLoc(NewER); - } - } - - return UnknownVal(); -} - -//===----------------------------------------------------------------------===// -// Loading values from regions. -//===----------------------------------------------------------------------===// - -Optional RegionStoreManager::getDirectBinding(RegionBindings B, - const MemRegion *R) { - if (const BindingVal *BV = Lookup(B, R)) - return Optional::create(BV->getDirectValue()); - - return Optional(); -} - -Optional RegionStoreManager::getDefaultBinding(RegionBindings B, - const MemRegion *R) { - - if (R->isBoundable()) - if (const TypedRegion *TR = dyn_cast(R)) - if (TR->getValueType(getContext())->isUnionType()) - return UnknownVal(); - - if (const BindingVal *V = Lookup(B, R)) - return Optional::create(V->getDefaultValue()); - - return Optional(); -} - -Optional RegionStoreManager::getBinding(RegionBindings B, - const MemRegion *R) { - if (const BindingVal *BV = Lookup(B, R)) - return Optional::create(BV->getValue()); - - return Optional(); -} - -static bool IsReinterpreted(QualType RTy, QualType UsedTy, ASTContext &Ctx) { - RTy = Ctx.getCanonicalType(RTy); - UsedTy = Ctx.getCanonicalType(UsedTy); - - if (RTy == UsedTy) - return false; - - - // Recursively check the types. We basically want to see if a pointer value - // is ever reinterpreted as a non-pointer, e.g. void** and intptr_t* - // represents a reinterpretation. - if (Loc::IsLocType(RTy) && Loc::IsLocType(UsedTy)) { - const PointerType *PRTy = RTy->getAs(); - const PointerType *PUsedTy = UsedTy->getAs(); - - return PUsedTy && PRTy && - IsReinterpreted(PRTy->getPointeeType(), - PUsedTy->getPointeeType(), Ctx); - } - - return true; -} - -const ElementRegion * -RegionStoreManager::GetElementZeroRegion(const SymbolicRegion *SR, QualType T) { - ASTContext &Ctx = getContext(); - SVal idx = ValMgr.makeZeroArrayIndex(); - assert(!T.isNull()); - return MRMgr.getElementRegion(T, idx, SR, Ctx); -} - - - -SValuator::CastResult -RegionStoreManager::Retrieve(const GRState *state, Loc L, QualType T) { - - assert(!isa(L) && "location unknown"); - assert(!isa(L) && "location undefined"); - - // FIXME: Is this even possible? Shouldn't this be treated as a null - // dereference at a higher level? - if (isa(L)) - return SValuator::CastResult(state, UndefinedVal()); - - const MemRegion *MR = cast(L).getRegion(); - - // FIXME: return symbolic value for these cases. - // Example: - // void f(int* p) { int x = *p; } - // char* p = alloca(); - // read(p); - // c = *p; - if (isa(MR)) - return SValuator::CastResult(state, UnknownVal()); - - if (const SymbolicRegion *SR = dyn_cast(MR)) - MR = GetElementZeroRegion(SR, T); - - if (isa(MR)) - return SValuator::CastResult(state, UnknownVal()); - - // FIXME: Perhaps this method should just take a 'const MemRegion*' argument - // instead of 'Loc', and have the other Loc cases handled at a higher level. - const TypedRegion *R = cast(MR); - QualType RTy = R->getValueType(getContext()); - - // FIXME: We should eventually handle funny addressing. e.g.: - // - // int x = ...; - // int *p = &x; - // char *q = (char*) p; - // char c = *q; // returns the first byte of 'x'. - // - // Such funny addressing will occur due to layering of regions. - -#if 0 - ASTContext &Ctx = getContext(); - if (!T.isNull() && IsReinterpreted(RTy, T, Ctx)) { - SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - R = MRMgr.getElementRegion(T, ZeroIdx, R, Ctx); - RTy = T; - assert(Ctx.getCanonicalType(RTy) == - Ctx.getCanonicalType(R->getValueType(Ctx))); - } -#endif - - if (RTy->isStructureType()) - return SValuator::CastResult(state, RetrieveStruct(state, R)); - - // FIXME: Handle unions. - if (RTy->isUnionType()) - return SValuator::CastResult(state, UnknownVal()); - - if (RTy->isArrayType()) - return SValuator::CastResult(state, RetrieveArray(state, R)); - - // FIXME: handle Vector types. - if (RTy->isVectorType()) - return SValuator::CastResult(state, UnknownVal()); - - if (const FieldRegion* FR = dyn_cast(R)) - return SValuator::CastResult(state, - CastRetrievedVal(RetrieveField(state, FR), FR, - T, false)); - - if (const ElementRegion* ER = dyn_cast(R)) { - // FIXME: Here we actually perform an implicit conversion from the loaded - // value to the element type. Eventually we want to compose these values - // more intelligently. For example, an 'element' can encompass multiple - // bound regions (e.g., several bound bytes), or could be a subset of - // a larger value. - return SValuator::CastResult(state, - CastRetrievedVal(RetrieveElement(state, ER), - ER, T, false)); - } - - if (const ObjCIvarRegion *IVR = dyn_cast(R)) { - // FIXME: Here we actually perform an implicit conversion from the loaded - // value to the ivar type. What we should model is stores to ivars - // that blow past the extent of the ivar. If the address of the ivar is - // reinterpretted, it is possible we stored a different value that could - // fit within the ivar. Either we need to cast these when storing them - // or reinterpret them lazily (as we do here). - return SValuator::CastResult(state, - CastRetrievedVal(RetrieveObjCIvar(state, IVR), - IVR, T, false)); - } - - if (const VarRegion *VR = dyn_cast(R)) { - // FIXME: Here we actually perform an implicit conversion from the loaded - // value to the variable type. What we should model is stores to variables - // that blow past the extent of the variable. If the address of the - // variable is reinterpretted, it is possible we stored a different value - // that could fit within the variable. Either we need to cast these when - // storing them or reinterpret them lazily (as we do here). - return SValuator::CastResult(state, - CastRetrievedVal(RetrieveVar(state, VR), VR, T, - false)); - } - - RegionBindings B = GetRegionBindings(state->getStore()); - const BindingVal *V = Lookup(B, R); - - // Check if the region has a binding. - if (V) - if (SVal const *SV = V->getValue()) - return SValuator::CastResult(state, *SV); - - // The location does not have a bound value. This means that it has - // the value it had upon its creation and/or entry to the analyzed - // function/method. These are either symbolic values or 'undefined'. - if (R->hasStackNonParametersStorage()) { - // All stack variables are considered to have undefined values - // upon creation. All heap allocated blocks are considered to - // have undefined values as well unless they are explicitly bound - // to specific values. - return SValuator::CastResult(state, UndefinedVal()); - } - - // All other values are symbolic. - return SValuator::CastResult(state, ValMgr.getRegionValueSymbolVal(R, RTy)); -} - -std::pair -RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R) { - if (Optional OV = getDirectBinding(B, R)) - if (const nonloc::LazyCompoundVal *V = - dyn_cast(OV.getPointer())) - return std::make_pair(V->getState(), V->getRegion()); - - if (const ElementRegion *ER = dyn_cast(R)) { - const std::pair &X = - GetLazyBinding(B, ER->getSuperRegion()); - - if (X.first) - return std::make_pair(X.first, - MRMgr.getElementRegionWithSuper(ER, X.second)); - } - else if (const FieldRegion *FR = dyn_cast(R)) { - const std::pair &X = - GetLazyBinding(B, FR->getSuperRegion()); - - if (X.first) - return std::make_pair(X.first, - MRMgr.getFieldRegionWithSuper(FR, X.second)); - } - - return std::make_pair((const GRState*) 0, (const MemRegion *) 0); -} - -SVal RegionStoreManager::RetrieveElement(const GRState* state, - const ElementRegion* R) { - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(state->getStore()); - if (Optional V = getDirectBinding(B, R)) - return *V; - - const MemRegion* superR = R->getSuperRegion(); - - // Check if the region is an element region of a string literal. - if (const StringRegion *StrR=dyn_cast(superR)) { - // FIXME: Handle loads from strings where the literal is treated as - // an integer, e.g., *((unsigned int*)"hello") - ASTContext &Ctx = getContext(); - QualType T = Ctx.getAsArrayType(StrR->getValueType(Ctx))->getElementType(); - if (T != Ctx.getCanonicalType(R->getElementType())) - return UnknownVal(); - - const StringLiteral *Str = StrR->getStringLiteral(); - SVal Idx = R->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast(&Idx)) { - int64_t i = CI->getValue().getSExtValue(); - int64_t byteLength = Str->getByteLength(); - if (i > byteLength) { - // Buffer overflow checking in GRExprEngine should handle this case, - // but we shouldn't rely on it to not overflow here if that checking - // is disabled. - return UnknownVal(); - } - char c = (i == byteLength) ? '\0' : Str->getStrData()[i]; - return ValMgr.makeIntVal(c, T); - } - } - - // Check if the immediate super region has a direct binding. - if (Optional V = getDirectBinding(B, superR)) { - if (SymbolRef parentSym = V->getAsSymbol()) - return ValMgr.getDerivedRegionValueSymbolVal(parentSym, R); - - if (V->isUnknownOrUndef()) - return *V; - - // Handle LazyCompoundVals for the immediate super region. Other cases - // are handled in 'RetrieveFieldOrElementCommon'. - if (const nonloc::LazyCompoundVal *LCV = - dyn_cast(V)) { - - R = MRMgr.getElementRegionWithSuper(R, LCV->getRegion()); - return RetrieveElement(LCV->getState(), R); - } - - // Other cases: give up. - return UnknownVal(); - } - - return RetrieveFieldOrElementCommon(state, R, R->getElementType(), superR); -} - -SVal RegionStoreManager::RetrieveField(const GRState* state, - const FieldRegion* R) { - - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(state->getStore()); - if (Optional V = getDirectBinding(B, R)) - return *V; - - QualType Ty = R->getValueType(getContext()); - return RetrieveFieldOrElementCommon(state, R, Ty, R->getSuperRegion()); -} - -SVal RegionStoreManager::RetrieveFieldOrElementCommon(const GRState *state, - const TypedRegion *R, - QualType Ty, - const MemRegion *superR) { - - // At this point we have already checked in either RetrieveElement or - // RetrieveField if 'R' has a direct binding. - - RegionBindings B = GetRegionBindings(state->getStore()); - - while (superR) { - if (const Optional &D = getDefaultBinding(B, superR)) { - if (SymbolRef parentSym = D->getAsSymbol()) - return ValMgr.getDerivedRegionValueSymbolVal(parentSym, R); - - if (D->isZeroConstant()) - return ValMgr.makeZeroVal(Ty); - - if (D->isUnknown()) - return *D; - - assert(0 && "Unknown default value"); - } - - // If our super region is a field or element itself, walk up the region - // hierarchy to see if there is a default value installed in an ancestor. - if (isa(superR) || isa(superR)) { - superR = cast(superR)->getSuperRegion(); - continue; - } - - break; - } - - // Lazy binding? - const GRState *lazyBindingState = NULL; - const MemRegion *lazyBindingRegion = NULL; - llvm::tie(lazyBindingState, lazyBindingRegion) = GetLazyBinding(B, R); - - if (lazyBindingState) { - assert(lazyBindingRegion && "Lazy-binding region not set"); - - if (isa(R)) - return RetrieveElement(lazyBindingState, - cast(lazyBindingRegion)); - - return RetrieveField(lazyBindingState, - cast(lazyBindingRegion)); - } - - if (R->hasStackNonParametersStorage()) { - if (isa(R)) { - // Currently we don't reason specially about Clang-style vectors. Check - // if superR is a vector and if so return Unknown. - if (const TypedRegion *typedSuperR = dyn_cast(superR)) { - if (typedSuperR->getValueType(getContext())->isVectorType()) - return UnknownVal(); - } - } - - return UndefinedVal(); - } - - // All other values are symbolic. - return ValMgr.getRegionValueSymbolVal(R, Ty); -} - -SVal RegionStoreManager::RetrieveObjCIvar(const GRState* state, - const ObjCIvarRegion* R) { - - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(state->getStore()); - - if (Optional V = getDirectBinding(B, R)) - return *V; - - const MemRegion *superR = R->getSuperRegion(); - - // Check if the super region has a default binding. - if (Optional V = getDefaultBinding(B, superR)) { - if (SymbolRef parentSym = V->getAsSymbol()) - return ValMgr.getDerivedRegionValueSymbolVal(parentSym, R); - - // Other cases: give up. - return UnknownVal(); - } - - return RetrieveLazySymbol(state, R); -} - -SVal RegionStoreManager::RetrieveVar(const GRState *state, - const VarRegion *R) { - - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(state->getStore()); - - if (Optional V = getDirectBinding(B, R)) - return *V; - - // Lazily derive a value for the VarRegion. - const VarDecl *VD = R->getDecl(); - - if (R->hasGlobalsOrParametersStorage() || - isa(R->getMemorySpace())) - return ValMgr.getRegionValueSymbolVal(R, VD->getType()); - - return UndefinedVal(); -} - -SVal RegionStoreManager::RetrieveLazySymbol(const GRState *state, - const TypedRegion *R) { - - QualType valTy = R->getValueType(getContext()); - - // All other values are symbolic. - return ValMgr.getRegionValueSymbolVal(R, valTy); -} - -SVal RegionStoreManager::RetrieveStruct(const GRState *state, - const TypedRegion* R) { - QualType T = R->getValueType(getContext()); - assert(T->isStructureType()); - - const RecordType* RT = T->getAsStructureType(); - RecordDecl* RD = RT->getDecl(); - assert(RD->isDefinition()); - (void)RD; -#if USE_EXPLICIT_COMPOUND - llvm::ImmutableList StructVal = getBasicVals().getEmptySValList(); - - // FIXME: We shouldn't use a std::vector. If RecordDecl doesn't have a - // reverse iterator, we should implement one. - std::vector Fields(RD->field_begin(), RD->field_end()); - - for (std::vector::reverse_iterator Field = Fields.rbegin(), - FieldEnd = Fields.rend(); - Field != FieldEnd; ++Field) { - FieldRegion* FR = MRMgr.getFieldRegion(*Field, R); - QualType FTy = (*Field)->getType(); - SVal FieldValue = Retrieve(state, loc::MemRegionVal(FR), FTy).getSVal(); - StructVal = getBasicVals().consVals(FieldValue, StructVal); - } - - return ValMgr.makeCompoundVal(T, StructVal); -#else - return ValMgr.makeLazyCompoundVal(state, R); -#endif -} - -SVal RegionStoreManager::RetrieveArray(const GRState *state, - const TypedRegion * R) { -#if USE_EXPLICIT_COMPOUND - QualType T = R->getValueType(getContext()); - ConstantArrayType* CAT = cast(T.getTypePtr()); - - llvm::ImmutableList ArrayVal = getBasicVals().getEmptySValList(); - uint64_t size = CAT->getSize().getZExtValue(); - for (uint64_t i = 0; i < size; ++i) { - SVal Idx = ValMgr.makeArrayIndex(i); - ElementRegion* ER = MRMgr.getElementRegion(CAT->getElementType(), Idx, R, - getContext()); - QualType ETy = ER->getElementType(); - SVal ElementVal = Retrieve(state, loc::MemRegionVal(ER), ETy).getSVal(); - ArrayVal = getBasicVals().consVals(ElementVal, ArrayVal); - } - - return ValMgr.makeCompoundVal(T, ArrayVal); -#else - assert(isa(R->getValueType(getContext()))); - return ValMgr.makeLazyCompoundVal(state, R); -#endif -} - -//===----------------------------------------------------------------------===// -// Binding values to regions. -//===----------------------------------------------------------------------===// - -Store RegionStoreManager::Remove(Store store, Loc L) { - if (isa(L)) - if (const MemRegion* R = cast(L).getRegion()) - return Remove(store, BindingKey::Make(R)); - - return store; -} - -const GRState *RegionStoreManager::Bind(const GRState *state, Loc L, SVal V) { - if (isa(L)) - return state; - - // If we get here, the location should be a region. - const MemRegion *R = cast(L).getRegion(); - - // Check if the region is a struct region. - if (const TypedRegion* TR = dyn_cast(R)) - if (TR->getValueType(getContext())->isStructureType()) - return BindStruct(state, TR, V); - - // Special case: the current region represents a cast and it and the super - // region both have pointer types or intptr_t types. If so, perform the - // bind to the super region. - // This is needed to support OSAtomicCompareAndSwap and friends or other - // loads that treat integers as pointers and vis versa. - if (const ElementRegion *ER = dyn_cast(R)) { - if (ER->getIndex().isZeroConstant()) { - if (const TypedRegion *superR = - dyn_cast(ER->getSuperRegion())) { - ASTContext &Ctx = getContext(); - QualType superTy = superR->getValueType(Ctx); - QualType erTy = ER->getValueType(Ctx); - - if (IsAnyPointerOrIntptr(superTy, Ctx) && - IsAnyPointerOrIntptr(erTy, Ctx)) { - SValuator::CastResult cr = - ValMgr.getSValuator().EvalCast(V, state, superTy, erTy); - return Bind(cr.getState(), loc::MemRegionVal(superR), cr.getSVal()); - } - // For now, just invalidate the fields of the struct/union/class. - // FIXME: Precisely handle the fields of the record. - if (superTy->isRecordType()) - return InvalidateRegion(state, superR, NULL, 0, NULL); - } - } - } - else if (const SymbolicRegion *SR = dyn_cast(R)) { - // Binding directly to a symbolic region should be treated as binding - // to element 0. - QualType T = SR->getSymbol()->getType(getContext()); - - // FIXME: Is this the right way to handle symbols that are references? - if (const PointerType *PT = T->getAs()) - T = PT->getPointeeType(); - else - T = T->getAs()->getPointeeType(); - - R = GetElementZeroRegion(SR, T); - } - - // Perform the binding. - RegionBindings B = GetRegionBindings(state->getStore()); - return state->makeWithStore(Add(B, R, - BindingVal(V, BindingVal::Direct)).getRoot()); -} - -const GRState *RegionStoreManager::BindDecl(const GRState *ST, - const VarRegion *VR, - SVal InitVal) { - - QualType T = VR->getDecl()->getType(); - - if (T->isArrayType()) - return BindArray(ST, VR, InitVal); - if (T->isStructureType()) - return BindStruct(ST, VR, InitVal); - - return Bind(ST, ValMgr.makeLoc(VR), InitVal); -} - -// FIXME: this method should be merged into Bind(). -const GRState * -RegionStoreManager::BindCompoundLiteral(const GRState *state, - const CompoundLiteralExpr *CL, - const LocationContext *LC, - SVal V) { - return Bind(state, loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)), - V); -} - -const GRState *RegionStoreManager::setImplicitDefaultValue(const GRState *state, - const MemRegion *R, - QualType T) { - Store store = state->getStore(); - RegionBindings B = GetRegionBindings(store); - SVal V; - - if (Loc::IsLocType(T)) - V = ValMgr.makeNull(); - else if (T->isIntegerType()) - V = ValMgr.makeZeroVal(T); - else if (T->isStructureType() || T->isArrayType()) { - // Set the default value to a zero constant when it is a structure - // or array. The type doesn't really matter. - V = ValMgr.makeZeroVal(ValMgr.getContext().IntTy); - } - else { - return state; - } - - return state->makeWithStore(Add(B, R, - BindingVal(V, BindingVal::Default)).getRoot()); -} - -const GRState *RegionStoreManager::BindArray(const GRState *state, - const TypedRegion* R, - SVal Init) { - - QualType T = R->getValueType(getContext()); - ConstantArrayType* CAT = cast(T.getTypePtr()); - QualType ElementTy = CAT->getElementType(); - - uint64_t size = CAT->getSize().getZExtValue(); - - // Check if the init expr is a StringLiteral. - if (isa(Init)) { - const MemRegion* InitR = cast(Init).getRegion(); - const StringLiteral* S = cast(InitR)->getStringLiteral(); - const char* str = S->getStrData(); - unsigned len = S->getByteLength(); - unsigned j = 0; - - // Copy bytes from the string literal into the target array. Trailing bytes - // in the array that are not covered by the string literal are initialized - // to zero. - for (uint64_t i = 0; i < size; ++i, ++j) { - if (j >= len) - break; - - SVal Idx = ValMgr.makeArrayIndex(i); - const ElementRegion* ER = MRMgr.getElementRegion(ElementTy, Idx, R, - getContext()); - - SVal V = ValMgr.makeIntVal(str[j], sizeof(char)*8, true); - state = Bind(state, loc::MemRegionVal(ER), V); - } - - return state; - } - - // Handle lazy compound values. - if (nonloc::LazyCompoundVal *LCV = dyn_cast(&Init)) - return CopyLazyBindings(*LCV, state, R); - - // Remaining case: explicit compound values. - - if (Init.isUnknown()) - return setImplicitDefaultValue(state, R, ElementTy); - - nonloc::CompoundVal& CV = cast(Init); - nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); - uint64_t i = 0; - - for (; i < size; ++i, ++VI) { - // The init list might be shorter than the array length. - if (VI == VE) - break; - - SVal Idx = ValMgr.makeArrayIndex(i); - const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, getContext()); - - if (CAT->getElementType()->isStructureType()) - state = BindStruct(state, ER, *VI); - else - // FIXME: Do we need special handling of nested arrays? - state = Bind(state, ValMgr.makeLoc(ER), *VI); - } - - // If the init list is shorter than the array length, set the - // array default value. - if (i < size) - state = setImplicitDefaultValue(state, R, ElementTy); - - return state; -} - -const GRState * -RegionStoreManager::BindStruct(const GRState *state, const TypedRegion* R, - SVal V) { - - if (!Features.supportsFields()) - return state; - - QualType T = R->getValueType(getContext()); - assert(T->isStructureType()); - - const RecordType* RT = T->getAs(); - RecordDecl* RD = RT->getDecl(); - - if (!RD->isDefinition()) - return state; - - // Handle lazy compound values. - if (const nonloc::LazyCompoundVal *LCV=dyn_cast(&V)) - return CopyLazyBindings(*LCV, state, R); - - // We may get non-CompoundVal accidentally due to imprecise cast logic. - // Ignore them and kill the field values. - if (V.isUnknown() || !isa(V)) - return state->makeWithStore(KillStruct(state->getStore(), R)); - - nonloc::CompoundVal& CV = cast(V); - nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); - - RecordDecl::field_iterator FI, FE; - - for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI, ++VI) { - - if (VI == VE) - break; - - QualType FTy = (*FI)->getType(); - const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); - - if (FTy->isArrayType()) - state = BindArray(state, FR, *VI); - else if (FTy->isStructureType()) - state = BindStruct(state, FR, *VI); - else - state = Bind(state, ValMgr.makeLoc(FR), *VI); - } - - // There may be fewer values in the initialize list than the fields of struct. - if (FI != FE) { - Store store = state->getStore(); - RegionBindings B = GetRegionBindings(store); - B = Add(B, R, BindingVal(ValMgr.makeIntVal(0, false), BindingVal::Default)); - state = state->makeWithStore(B.getRoot()); - } - - return state; -} - -Store RegionStoreManager::KillStruct(Store store, const TypedRegion* R) { - RegionBindings B = GetRegionBindings(store); - llvm::OwningPtr - SubRegions(getRegionStoreSubRegionMap(store)); - RemoveSubRegionBindings(B, R, *SubRegions); - - // Set the default value of the struct region to "unknown". - B = Add(B, R, BindingVal(UnknownVal(), BindingVal::Default)); - - return B.getRoot(); -} - -const GRState* -RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, - const GRState *state, - const TypedRegion *R) { - - // Nuke the old bindings stemming from R. - RegionBindings B = GetRegionBindings(state->getStore()); - - llvm::OwningPtr - SubRegions(getRegionStoreSubRegionMap(state->getStore())); - - // B and DVM are updated after the call to RemoveSubRegionBindings. - RemoveSubRegionBindings(B, R, *SubRegions.get()); - - // Now copy the bindings. This amounts to just binding 'V' to 'R'. This - // results in a zero-copy algorithm. - return state->makeWithStore(Add(B, R, - BindingVal(V, BindingVal::Direct)).getRoot()); -} - -//===----------------------------------------------------------------------===// -// "Raw" retrievals and bindings. -//===----------------------------------------------------------------------===// - -BindingKey BindingKey::Make(const MemRegion *R) { - if (const ElementRegion *ER = dyn_cast(R)) { - const RegionRawOffset &O = ER->getAsRawOffset(); - - if (O.getRegion()) - return BindingKey(O.getRegion(), O.getByteOffset()); - - // FIXME: There are some ElementRegions for which we cannot compute - // raw offsets yet, including regions with symbolic offsets. - } - - return BindingKey(R, 0); -} - -RegionBindings RegionStoreManager::Add(RegionBindings B, BindingKey K, - BindingVal V) { - return RBFactory.Add(B, K, V); -} - -RegionBindings RegionStoreManager::Add(RegionBindings B, const MemRegion *R, - BindingVal V) { - return Add(B, BindingKey::Make(R), V); -} - -const BindingVal *RegionStoreManager::Lookup(RegionBindings B, BindingKey K) { - return B.lookup(K); -} - -const BindingVal *RegionStoreManager::Lookup(RegionBindings B, - const MemRegion *R) { - return Lookup(B, BindingKey::Make(R)); -} - -RegionBindings RegionStoreManager::Remove(RegionBindings B, BindingKey K) { - return RBFactory.Remove(B, K); -} - -RegionBindings RegionStoreManager::Remove(RegionBindings B, const MemRegion *R){ - return Remove(B, BindingKey::Make(R)); -} - -Store RegionStoreManager::Remove(Store store, BindingKey K) { - RegionBindings B = GetRegionBindings(store); - return Remove(B, K).getRoot(); -} - -//===----------------------------------------------------------------------===// -// State pruning. -//===----------------------------------------------------------------------===// - -void RegionStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl& RegionRoots) -{ - typedef std::pair RBDNode; - - Store store = state.getStore(); - RegionBindings B = GetRegionBindings(store); - - // The backmap from regions to subregions. - llvm::OwningPtr - SubRegions(getRegionStoreSubRegionMap(store)); - - // Do a pass over the regions in the store. For VarRegions we check if - // the variable is still live and if so add it to the list of live roots. - // For other regions we populate our region backmap. - llvm::SmallVector IntermediateRoots; - - // Scan the direct bindings for "intermediate" roots. - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - const MemRegion *R = I.getKey().getRegion(); - IntermediateRoots.push_back(R); - } - - // Process the "intermediate" roots to find if they are referenced by - // real roots. - llvm::SmallVector WorkList; - llvm::SmallVector Postponed; - - llvm::DenseSet IntermediateVisited; - - while (!IntermediateRoots.empty()) { - const MemRegion* R = IntermediateRoots.back(); - IntermediateRoots.pop_back(); - - if (IntermediateVisited.count(R)) - continue; - IntermediateVisited.insert(R); - - if (const VarRegion* VR = dyn_cast(R)) { - if (SymReaper.isLive(Loc, VR)) - WorkList.push_back(std::make_pair(&state, VR)); - continue; - } - - if (const SymbolicRegion* SR = dyn_cast(R)) { - llvm::SmallVectorImpl &Q = - SymReaper.isLive(SR->getSymbol()) ? WorkList : Postponed; - - Q.push_back(std::make_pair(&state, SR)); - - continue; - } - - // Add the super region for R to the worklist if it is a subregion. - if (const SubRegion* superR = - dyn_cast(cast(R)->getSuperRegion())) - IntermediateRoots.push_back(superR); - } - - // Enqueue the RegionRoots onto WorkList. - for (llvm::SmallVectorImpl::iterator I=RegionRoots.begin(), - E=RegionRoots.end(); I!=E; ++I) { - WorkList.push_back(std::make_pair(&state, *I)); - } - RegionRoots.clear(); - - llvm::DenseSet Visited; - -tryAgain: - while (!WorkList.empty()) { - RBDNode N = WorkList.back(); - WorkList.pop_back(); - - // Have we visited this node before? - if (Visited.count(N)) - continue; - Visited.insert(N); - - const MemRegion *R = N.second; - const GRState *state_N = N.first; - - // Enqueue subregions. - RegionStoreSubRegionMap *M; - - if (&state == state_N) - M = SubRegions.get(); - else { - RegionStoreSubRegionMap *& SM = SC[state_N]; - if (!SM) - SM = getRegionStoreSubRegionMap(state_N->getStore()); - M = SM; - } - - RegionStoreSubRegionMap::iterator I, E; - for (llvm::tie(I, E) = M->begin_end(R); I != E; ++I) - WorkList.push_back(std::make_pair(state_N, *I)); - - // Enqueue the super region. - if (const SubRegion *SR = dyn_cast(R)) { - const MemRegion *superR = SR->getSuperRegion(); - if (!isa(superR)) { - // If 'R' is a field or an element, we want to keep the bindings - // for the other fields and elements around. The reason is that - // pointer arithmetic can get us to the other fields or elements. - assert(isa(R) || isa(R) - || isa(R)); - WorkList.push_back(std::make_pair(state_N, superR)); - } - } - - // Mark the symbol for any live SymbolicRegion as "live". This means we - // should continue to track that symbol. - if (const SymbolicRegion *SymR = dyn_cast(R)) - SymReaper.markLive(SymR->getSymbol()); - - // For BlockDataRegions, enqueue the VarRegions for variables marked - // with __block (passed-by-reference). - // via BlockDeclRefExprs. - if (const BlockDataRegion *BD = dyn_cast(R)) { - for (BlockDataRegion::referenced_vars_iterator - RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end(); - RI != RE; ++RI) { - if ((*RI)->getDecl()->getAttr()) - WorkList.push_back(std::make_pair(state_N, *RI)); - } - // No possible data bindings on a BlockDataRegion. Continue to the - // next region in the worklist. - continue; - } - - Store store_N = state_N->getStore(); - RegionBindings B_N = GetRegionBindings(store_N); - - // Get the data binding for R (if any). - Optional V = getBinding(B_N, R); - - if (V) { - // Check for lazy bindings. - if (const nonloc::LazyCompoundVal *LCV = - dyn_cast(V.getPointer())) { - - const LazyCompoundValData *D = LCV->getCVData(); - WorkList.push_back(std::make_pair(D->getState(), D->getRegion())); - } - else { - // Update the set of live symbols. - for (SVal::symbol_iterator SI=V->symbol_begin(), SE=V->symbol_end(); - SI!=SE;++SI) - SymReaper.markLive(*SI); - - // If V is a region, then add it to the worklist. - if (const MemRegion *RX = V->getAsRegion()) - WorkList.push_back(std::make_pair(state_N, RX)); - } - } - } - - // See if any postponed SymbolicRegions are actually live now, after - // having done a scan. - for (llvm::SmallVectorImpl::iterator I = Postponed.begin(), - E = Postponed.end() ; I != E ; ++I) { - if (const SymbolicRegion *SR = cast_or_null(I->second)) { - if (SymReaper.isLive(SR->getSymbol())) { - WorkList.push_back(*I); - I->second = NULL; - } - } - } - - if (!WorkList.empty()) - goto tryAgain; - - // We have now scanned the store, marking reachable regions and symbols - // as live. We now remove all the regions that are dead from the store - // as well as update DSymbols with the set symbols that are now dead. - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - const MemRegion* R = I.getKey().getRegion(); - // If this region live? Is so, none of its symbols are dead. - if (Visited.count(std::make_pair(&state, R))) - continue; - - // Remove this dead region from the store. - store = Remove(store, I.getKey()); - - // Mark all non-live symbols that this region references as dead. - if (const SymbolicRegion* SymR = dyn_cast(R)) - SymReaper.maybeDead(SymR->getSymbol()); - - SVal X = *I.getData().getValue(); - SVal::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); - for (; SI != SE; ++SI) - SymReaper.maybeDead(*SI); - } - - // Write the store back. - state.setStore(store); -} - -GRState const *RegionStoreManager::EnterStackFrame(GRState const *state, - StackFrameContext const *frame) { - FunctionDecl const *FD = cast(frame->getDecl()); - CallExpr const *CE = cast(frame->getCallSite()); - - FunctionDecl::param_const_iterator PI = FD->param_begin(); - - CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); - - // Copy the arg expression value to the arg variables. - for (; AI != AE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI); - state = Bind(state, ValMgr.makeLoc(MRMgr.getVarRegion(*PI, frame)), ArgVal); - } - - return state; -} - -//===----------------------------------------------------------------------===// -// Utility methods. -//===----------------------------------------------------------------------===// - -void RegionStoreManager::print(Store store, llvm::raw_ostream& OS, - const char* nl, const char *sep) { - RegionBindings B = GetRegionBindings(store); - OS << "Store (direct and default bindings):" << nl; - - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) - OS << ' ' << I.getKey() << " : " << I.getData() << nl; -} diff --git a/lib/Analysis/ReturnPointerRangeChecker.cpp b/lib/Analysis/ReturnPointerRangeChecker.cpp deleted file mode 100644 index b0350cb576ff..000000000000 --- a/lib/Analysis/ReturnPointerRangeChecker.cpp +++ /dev/null @@ -1,97 +0,0 @@ -//== ReturnPointerRangeChecker.cpp ------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ReturnPointerRangeChecker, which is a path-sensitive check -// which looks for an out-of-bound pointer being returned to callers. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" - -using namespace clang; - -namespace { -class ReturnPointerRangeChecker : - public CheckerVisitor { - BuiltinBug *BT; -public: - ReturnPointerRangeChecker() : BT(0) {} - static void *getTag(); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); -}; -} - -void clang::RegisterReturnPointerRangeChecker(GRExprEngine &Eng) { - Eng.registerCheck(new ReturnPointerRangeChecker()); -} - -void *ReturnPointerRangeChecker::getTag() { - static int x = 0; return &x; -} - -void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, - const ReturnStmt *RS) { - const GRState *state = C.getState(); - - const Expr *RetE = RS->getRetValue(); - if (!RetE) - return; - - SVal V = state->getSVal(RetE); - const MemRegion *R = V.getAsRegion(); - if (!R) - return; - - R = R->StripCasts(); - if (!R) - return; - - const ElementRegion *ER = dyn_cast_or_null(R); - if (!ER) - return; - - DefinedOrUnknownSVal &Idx = cast(ER->getIndex()); - - // FIXME: All of this out-of-bounds checking should eventually be refactored - // into a common place. - - DefinedOrUnknownSVal NumElements - = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), - ER->getValueType(C.getASTContext())); - - const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); - const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); - if (StOutBound && !StInBound) { - ExplodedNode *N = C.GenerateSink(StOutBound); - - if (!N) - return; - - // FIXME: This bug correspond to CWE-466. Eventually we should have bug - // types explicitly reference such exploit categories (when applicable). - if (!BT) - BT = new BuiltinBug("Return of pointer value outside of expected range", - "Returned pointer value points outside the original object " - "(potential buffer overflow)"); - - // FIXME: It would be nice to eventually make this diagnostic more clear, - // e.g., by referencing the original declaration or by saying *why* this - // reference is outside the range. - - // Generate a report for this bug. - RangedBugReport *report = - new RangedBugReport(*BT, BT->getDescription(), N); - - report->addRange(RetE->getSourceRange()); - C.EmitReport(report); - } -} diff --git a/lib/Analysis/ReturnStackAddressChecker.cpp b/lib/Analysis/ReturnStackAddressChecker.cpp deleted file mode 100644 index 4d7e8ade98f7..000000000000 --- a/lib/Analysis/ReturnStackAddressChecker.cpp +++ /dev/null @@ -1,114 +0,0 @@ -//== ReturnStackAddressChecker.cpp ------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ReturnStackAddressChecker, which is a path-sensitive -// check which looks for the addresses of stack variables being returned to -// callers. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Basic/SourceManager.h" -#include "llvm/ADT/SmallString.h" - -using namespace clang; - -namespace { -class ReturnStackAddressChecker : - public CheckerVisitor { - BuiltinBug *BT; -public: - ReturnStackAddressChecker() : BT(0) {} - static void *getTag(); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); -}; -} - -void clang::RegisterReturnStackAddressChecker(GRExprEngine &Eng) { - Eng.registerCheck(new ReturnStackAddressChecker()); -} - -void *ReturnStackAddressChecker::getTag() { - static int x = 0; return &x; -} - -void ReturnStackAddressChecker::PreVisitReturnStmt(CheckerContext &C, - const ReturnStmt *RS) { - - const Expr *RetE = RS->getRetValue(); - if (!RetE) - return; - - SVal V = C.getState()->getSVal(RetE); - const MemRegion *R = V.getAsRegion(); - - if (!R || !R->hasStackStorage()) - return; - - ExplodedNode *N = C.GenerateSink(); - - if (!N) - return; - - if (!BT) - BT = new BuiltinBug("Return of address to stack-allocated memory"); - - // Generate a report for this bug. - llvm::SmallString<100> buf; - llvm::raw_svector_ostream os(buf); - SourceRange range; - - // Get the base region, stripping away fields and elements. - R = R->getBaseRegion(); - - // Check if the region is a compound literal. - if (const CompoundLiteralRegion* CR = dyn_cast(R)) { - const CompoundLiteralExpr* CL = CR->getLiteralExpr(); - os << "Address of stack memory associated with a compound literal " - "declared on line " - << C.getSourceManager().getInstantiationLineNumber(CL->getLocStart()) - << " returned to caller"; - range = CL->getSourceRange(); - } - else if (const AllocaRegion* AR = dyn_cast(R)) { - const Expr* ARE = AR->getExpr(); - SourceLocation L = ARE->getLocStart(); - range = ARE->getSourceRange(); - os << "Address of stack memory allocated by call to alloca() on line " - << C.getSourceManager().getInstantiationLineNumber(L) - << " returned to caller"; - } - else if (const BlockDataRegion *BR = dyn_cast(R)) { - const BlockDecl *BD = BR->getCodeRegion()->getDecl(); - SourceLocation L = BD->getLocStart(); - range = BD->getSourceRange(); - os << "Address of stack-allocated block declared on line " - << C.getSourceManager().getInstantiationLineNumber(L) - << " returned to caller"; - } - else if (const VarRegion *VR = dyn_cast(R)) { - os << "Address of stack memory associated with local variable '" - << VR->getString() << "' returned"; - range = VR->getDecl()->getSourceRange(); - } - else { - assert(false && "Invalid region in ReturnStackAddressChecker."); - return; - } - - RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); - report->addRange(RetE->getSourceRange()); - if (range.isValid()) - report->addRange(range); - - C.EmitReport(report); -} diff --git a/lib/Analysis/ReturnUndefChecker.cpp b/lib/Analysis/ReturnUndefChecker.cpp deleted file mode 100644 index 7cd71265805b..000000000000 --- a/lib/Analysis/ReturnUndefChecker.cpp +++ /dev/null @@ -1,68 +0,0 @@ -//== ReturnUndefChecker.cpp -------------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ReturnUndefChecker, which is a path-sensitive -// check which looks for undefined or garbage values being returned to the -// caller. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "llvm/ADT/SmallString.h" - -using namespace clang; - -namespace { -class ReturnUndefChecker : - public CheckerVisitor { - BuiltinBug *BT; -public: - ReturnUndefChecker() : BT(0) {} - static void *getTag(); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); -}; -} - -void clang::RegisterReturnUndefChecker(GRExprEngine &Eng) { - Eng.registerCheck(new ReturnUndefChecker()); -} - -void *ReturnUndefChecker::getTag() { - static int x = 0; return &x; -} - -void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C, - const ReturnStmt *RS) { - - const Expr *RetE = RS->getRetValue(); - if (!RetE) - return; - - if (!C.getState()->getSVal(RetE).isUndef()) - return; - - ExplodedNode *N = C.GenerateSink(); - - if (!N) - return; - - if (!BT) - BT = new BuiltinBug("Garbage return value", - "Undefined or garbage value returned to caller"); - - EnhancedBugReport *report = - new EnhancedBugReport(*BT, BT->getDescription(), N); - - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE); - - C.EmitReport(report); -} diff --git a/lib/Analysis/SVals.cpp b/lib/Analysis/SVals.cpp deleted file mode 100644 index fbdb73b0ef2e..000000000000 --- a/lib/Analysis/SVals.cpp +++ /dev/null @@ -1,327 +0,0 @@ -//= RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines SVal, Loc, and NonLoc, classes that represent -// abstract r-values for use with path-sensitive value tracking. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Basic/IdentifierTable.h" - -using namespace clang; -using llvm::dyn_cast; -using llvm::cast; -using llvm::APSInt; - -//===----------------------------------------------------------------------===// -// Symbol iteration within an SVal. -//===----------------------------------------------------------------------===// - - -//===----------------------------------------------------------------------===// -// Utility methods. -//===----------------------------------------------------------------------===// - -bool SVal::hasConjuredSymbol() const { - if (const nonloc::SymbolVal* SV = dyn_cast(this)) { - SymbolRef sym = SV->getSymbol(); - if (isa(sym)) - return true; - } - - if (const loc::MemRegionVal *RV = dyn_cast(this)) { - const MemRegion *R = RV->getRegion(); - if (const SymbolicRegion *SR = dyn_cast(R)) { - SymbolRef sym = SR->getSymbol(); - if (isa(sym)) - return true; - } - } - - return false; -} - -const FunctionDecl *SVal::getAsFunctionDecl() const { - if (const loc::MemRegionVal* X = dyn_cast(this)) { - const MemRegion* R = X->getRegion(); - if (const FunctionTextRegion *CTR = R->getAs()) - return CTR->getDecl(); - } - - return NULL; -} - -/// getAsLocSymbol - If this SVal is a location (subclasses Loc) and -/// wraps a symbol, return that SymbolRef. Otherwise return 0. -// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? -SymbolRef SVal::getAsLocSymbol() const { - if (const loc::MemRegionVal *X = dyn_cast(this)) { - const MemRegion *R = X->StripCasts(); - if (const SymbolicRegion *SymR = dyn_cast(R)) - return SymR->getSymbol(); - } - return NULL; -} - -/// getAsSymbol - If this Sval wraps a symbol return that SymbolRef. -/// Otherwise return 0. -// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? -SymbolRef SVal::getAsSymbol() const { - if (const nonloc::SymbolVal *X = dyn_cast(this)) - return X->getSymbol(); - - if (const nonloc::SymExprVal *X = dyn_cast(this)) - if (SymbolRef Y = dyn_cast(X->getSymbolicExpression())) - return Y; - - return getAsLocSymbol(); -} - -/// getAsSymbolicExpression - If this Sval wraps a symbolic expression then -/// return that expression. Otherwise return NULL. -const SymExpr *SVal::getAsSymbolicExpression() const { - if (const nonloc::SymExprVal *X = dyn_cast(this)) - return X->getSymbolicExpression(); - - return getAsSymbol(); -} - -const MemRegion *SVal::getAsRegion() const { - if (const loc::MemRegionVal *X = dyn_cast(this)) - return X->getRegion(); - - if (const nonloc::LocAsInteger *X = dyn_cast(this)) { - return X->getLoc().getAsRegion(); - } - - return 0; -} - -const MemRegion *loc::MemRegionVal::StripCasts() const { - const MemRegion *R = getRegion(); - return R ? R->StripCasts() : NULL; -} - -bool SVal::symbol_iterator::operator==(const symbol_iterator &X) const { - return itr == X.itr; -} - -bool SVal::symbol_iterator::operator!=(const symbol_iterator &X) const { - return itr != X.itr; -} - -SVal::symbol_iterator::symbol_iterator(const SymExpr *SE) { - itr.push_back(SE); - while (!isa(itr.back())) expand(); -} - -SVal::symbol_iterator& SVal::symbol_iterator::operator++() { - assert(!itr.empty() && "attempting to iterate on an 'end' iterator"); - assert(isa(itr.back())); - itr.pop_back(); - if (!itr.empty()) - while (!isa(itr.back())) expand(); - return *this; -} - -SymbolRef SVal::symbol_iterator::operator*() { - assert(!itr.empty() && "attempting to dereference an 'end' iterator"); - return cast(itr.back()); -} - -void SVal::symbol_iterator::expand() { - const SymExpr *SE = itr.back(); - itr.pop_back(); - - if (const SymIntExpr *SIE = dyn_cast(SE)) { - itr.push_back(SIE->getLHS()); - return; - } - else if (const SymSymExpr *SSE = dyn_cast(SE)) { - itr.push_back(SSE->getLHS()); - itr.push_back(SSE->getRHS()); - return; - } - - assert(false && "unhandled expansion case"); -} - -const GRState *nonloc::LazyCompoundVal::getState() const { - return static_cast(Data)->getState(); -} - -const TypedRegion *nonloc::LazyCompoundVal::getRegion() const { - return static_cast(Data)->getRegion(); -} - -//===----------------------------------------------------------------------===// -// Other Iterators. -//===----------------------------------------------------------------------===// - -nonloc::CompoundVal::iterator nonloc::CompoundVal::begin() const { - return getValue()->begin(); -} - -nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { - return getValue()->end(); -} - -//===----------------------------------------------------------------------===// -// Useful predicates. -//===----------------------------------------------------------------------===// - -bool SVal::isConstant() const { - return isa(this) || isa(this); -} - -bool SVal::isZeroConstant() const { - if (isa(*this)) - return cast(*this).getValue() == 0; - else if (isa(*this)) - return cast(*this).getValue() == 0; - else - return false; -} - - -//===----------------------------------------------------------------------===// -// Transfer function dispatch for Non-Locs. -//===----------------------------------------------------------------------===// - -SVal nonloc::ConcreteInt::evalBinOp(ValueManager &ValMgr, - BinaryOperator::Opcode Op, - const nonloc::ConcreteInt& R) const { - const llvm::APSInt* X = - ValMgr.getBasicValueFactory().EvaluateAPSInt(Op, getValue(), R.getValue()); - - if (X) - return nonloc::ConcreteInt(*X); - else - return UndefinedVal(); -} - -nonloc::ConcreteInt -nonloc::ConcreteInt::evalComplement(ValueManager &ValMgr) const { - return ValMgr.makeIntVal(~getValue()); -} - -nonloc::ConcreteInt nonloc::ConcreteInt::evalMinus(ValueManager &ValMgr) const { - return ValMgr.makeIntVal(-getValue()); -} - -//===----------------------------------------------------------------------===// -// Transfer function dispatch for Locs. -//===----------------------------------------------------------------------===// - -SVal loc::ConcreteInt::EvalBinOp(BasicValueFactory& BasicVals, - BinaryOperator::Opcode Op, - const loc::ConcreteInt& R) const { - - assert (Op == BinaryOperator::Add || Op == BinaryOperator::Sub || - (Op >= BinaryOperator::LT && Op <= BinaryOperator::NE)); - - const llvm::APSInt* X = BasicVals.EvaluateAPSInt(Op, getValue(), R.getValue()); - - if (X) - return loc::ConcreteInt(*X); - else - return UndefinedVal(); -} - -//===----------------------------------------------------------------------===// -// Pretty-Printing. -//===----------------------------------------------------------------------===// - -void SVal::dump() const { dumpToStream(llvm::errs()); } - -void SVal::dumpToStream(llvm::raw_ostream& os) const { - switch (getBaseKind()) { - case UnknownKind: - os << "Invalid"; - break; - case NonLocKind: - cast(this)->dumpToStream(os); - break; - case LocKind: - cast(this)->dumpToStream(os); - break; - case UndefinedKind: - os << "Undefined"; - break; - default: - assert (false && "Invalid SVal."); - } -} - -void NonLoc::dumpToStream(llvm::raw_ostream& os) const { - switch (getSubKind()) { - case nonloc::ConcreteIntKind: - os << cast(this)->getValue().getZExtValue(); - if (cast(this)->getValue().isUnsigned()) - os << 'U'; - break; - case nonloc::SymbolValKind: - os << '$' << cast(this)->getSymbol(); - break; - case nonloc::SymExprValKind: { - const nonloc::SymExprVal& C = *cast(this); - const SymExpr *SE = C.getSymbolicExpression(); - os << SE; - break; - } - case nonloc::LocAsIntegerKind: { - const nonloc::LocAsInteger& C = *cast(this); - os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]"; - break; - } - case nonloc::CompoundValKind: { - const nonloc::CompoundVal& C = *cast(this); - os << "compoundVal{"; - bool first = true; - for (nonloc::CompoundVal::iterator I=C.begin(), E=C.end(); I!=E; ++I) { - if (first) { - os << ' '; first = false; - } - else - os << ", "; - - (*I).dumpToStream(os); - } - os << "}"; - break; - } - case nonloc::LazyCompoundValKind: { - const nonloc::LazyCompoundVal &C = *cast(this); - os << "lazyCompoundVal{" << (void*) C.getState() << ',' << C.getRegion() - << '}'; - break; - } - default: - assert (false && "Pretty-printed not implemented for this NonLoc."); - break; - } -} - -void Loc::dumpToStream(llvm::raw_ostream& os) const { - switch (getSubKind()) { - case loc::ConcreteIntKind: - os << cast(this)->getValue().getZExtValue() << " (Loc)"; - break; - case loc::GotoLabelKind: - os << "&&" << cast(this)->getLabel()->getID()->getName(); - break; - case loc::MemRegionKind: - os << '&' << cast(this)->getRegion()->getString(); - break; - default: - assert(false && "Pretty-printing not implemented for this Loc."); - break; - } -} diff --git a/lib/Analysis/SValuator.cpp b/lib/Analysis/SValuator.cpp deleted file mode 100644 index 8392fcf65a2c..000000000000 --- a/lib/Analysis/SValuator.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// SValuator.cpp - Basic class for all SValuator implementations --*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines SValuator, the base class for all (complete) SValuator -// implementations. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/SValuator.h" -#include "clang/Analysis/PathSensitive/GRState.h" - -using namespace clang; - - -SVal SValuator::EvalBinOp(const GRState *ST, BinaryOperator::Opcode Op, - SVal L, SVal R, QualType T) { - - if (L.isUndef() || R.isUndef()) - return UndefinedVal(); - - if (L.isUnknown() || R.isUnknown()) - return UnknownVal(); - - if (isa(L)) { - if (isa(R)) - return EvalBinOpLL(Op, cast(L), cast(R), T); - - return EvalBinOpLN(ST, Op, cast(L), cast(R), T); - } - - if (isa(R)) { - // Support pointer arithmetic where the increment/decrement operand - // is on the left and the pointer on the right. - assert(Op == BinaryOperator::Add || Op == BinaryOperator::Sub); - - // Commute the operands. - return EvalBinOpLN(ST, Op, cast(R), cast(L), T); - } - - return EvalBinOpNN(ST, Op, cast(L), cast(R), T); -} - -DefinedOrUnknownSVal SValuator::EvalEQ(const GRState *ST, - DefinedOrUnknownSVal L, - DefinedOrUnknownSVal R) { - return cast(EvalBinOp(ST, BinaryOperator::EQ, L, R, - ValMgr.getContext().IntTy)); -} - -SValuator::CastResult SValuator::EvalCast(SVal val, const GRState *state, - QualType castTy, QualType originalTy){ - - if (val.isUnknownOrUndef() || castTy == originalTy) - return CastResult(state, val); - - ASTContext &C = ValMgr.getContext(); - - // For const casts, just propagate the value. - if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) - if (C.hasSameUnqualifiedType(castTy, originalTy)) - return CastResult(state, val); - - if (castTy->isIntegerType() && originalTy->isIntegerType()) - return CastResult(state, EvalCastNL(cast(val), castTy)); - - // Check for casts from pointers to integers. - if (castTy->isIntegerType() && Loc::IsLocType(originalTy)) - return CastResult(state, EvalCastL(cast(val), castTy)); - - // Check for casts from integers to pointers. - if (Loc::IsLocType(castTy) && originalTy->isIntegerType()) { - if (nonloc::LocAsInteger *LV = dyn_cast(&val)) { - if (const MemRegion *R = LV->getLoc().getAsRegion()) { - StoreManager &storeMgr = ValMgr.getStateManager().getStoreManager(); - R = storeMgr.CastRegion(R, castTy); - return R ? CastResult(state, loc::MemRegionVal(R)) - : CastResult(state, UnknownVal()); - } - return CastResult(state, LV->getLoc()); - } - goto DispatchCast; - } - - // Just pass through function and block pointers. - if (originalTy->isBlockPointerType() || originalTy->isFunctionPointerType()) { - assert(Loc::IsLocType(castTy)); - return CastResult(state, val); - } - - // Check for casts from array type to another type. - if (originalTy->isArrayType()) { - // We will always decay to a pointer. - val = ValMgr.getStateManager().ArrayToPointer(cast(val)); - - // Are we casting from an array to a pointer? If so just pass on - // the decayed value. - if (castTy->isPointerType()) - return CastResult(state, val); - - // Are we casting from an array to an integer? If so, cast the decayed - // pointer value to an integer. - assert(castTy->isIntegerType()); - - // FIXME: Keep these here for now in case we decide soon that we - // need the original decayed type. - // QualType elemTy = cast(originalTy)->getElementType(); - // QualType pointerTy = C.getPointerType(elemTy); - return CastResult(state, EvalCastL(cast(val), castTy)); - } - - // Check for casts from a region to a specific type. - if (const MemRegion *R = val.getAsRegion()) { - // FIXME: We should handle the case where we strip off view layers to get - // to a desugared type. - - assert(Loc::IsLocType(castTy)); - // We get a symbolic function pointer for a dereference of a function - // pointer, but it is of function type. Example: - - // struct FPRec { - // void (*my_func)(int * x); - // }; - // - // int bar(int x); - // - // int f1_a(struct FPRec* foo) { - // int x; - // (*foo->my_func)(&x); - // return bar(x)+1; // no-warning - // } - - assert(Loc::IsLocType(originalTy) || originalTy->isFunctionType() || - originalTy->isBlockPointerType()); - - StoreManager &storeMgr = ValMgr.getStateManager().getStoreManager(); - - // Delegate to store manager to get the result of casting a region to a - // different type. If the MemRegion* returned is NULL, this expression - // evaluates to UnknownVal. - R = storeMgr.CastRegion(R, castTy); - return R ? CastResult(state, loc::MemRegionVal(R)) - : CastResult(state, UnknownVal()); - } - -DispatchCast: - // All other cases. - return CastResult(state, - isa(val) ? EvalCastL(cast(val), castTy) - : EvalCastNL(cast(val), castTy)); -} - -SValuator::DefinedOrUnknownCastResult -SValuator::EvalCast(DefinedOrUnknownSVal V, const GRState *ST, - QualType castTy, QualType originalType) { - SValuator::CastResult X = EvalCast((SVal) V, ST, castTy, originalType); - return DefinedOrUnknownCastResult(X.getState(), - cast(X.getSVal())); -} diff --git a/lib/Analysis/SimpleConstraintManager.cpp b/lib/Analysis/SimpleConstraintManager.cpp deleted file mode 100644 index eca20d574db3..000000000000 --- a/lib/Analysis/SimpleConstraintManager.cpp +++ /dev/null @@ -1,249 +0,0 @@ -//== SimpleConstraintManager.cpp --------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines SimpleConstraintManager, a class that holds code shared -// between BasicConstraintManager and RangeConstraintManager. -// -//===----------------------------------------------------------------------===// - -#include "SimpleConstraintManager.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/Checker.h" - -namespace clang { - -SimpleConstraintManager::~SimpleConstraintManager() {} - -bool SimpleConstraintManager::canReasonAbout(SVal X) const { - if (nonloc::SymExprVal *SymVal = dyn_cast(&X)) { - const SymExpr *SE = SymVal->getSymbolicExpression(); - - if (isa(SE)) - return true; - - if (const SymIntExpr *SIE = dyn_cast(SE)) { - switch (SIE->getOpcode()) { - // We don't reason yet about bitwise-constraints on symbolic values. - case BinaryOperator::And: - case BinaryOperator::Or: - case BinaryOperator::Xor: - return false; - // We don't reason yet about arithmetic constraints on symbolic values. - case BinaryOperator::Mul: - case BinaryOperator::Div: - case BinaryOperator::Rem: - case BinaryOperator::Add: - case BinaryOperator::Sub: - case BinaryOperator::Shl: - case BinaryOperator::Shr: - return false; - // All other cases. - default: - return true; - } - } - - return false; - } - - return true; -} - -const GRState *SimpleConstraintManager::Assume(const GRState *state, - DefinedSVal Cond, - bool Assumption) { - if (isa(Cond)) - return Assume(state, cast(Cond), Assumption); - else - return Assume(state, cast(Cond), Assumption); -} - -const GRState *SimpleConstraintManager::Assume(const GRState *state, Loc cond, - bool assumption) { - state = AssumeAux(state, cond, assumption); - return SU.ProcessAssume(state, cond, assumption); -} - -const GRState *SimpleConstraintManager::AssumeAux(const GRState *state, - Loc Cond, bool Assumption) { - - BasicValueFactory &BasicVals = state->getBasicVals(); - - switch (Cond.getSubKind()) { - default: - assert (false && "'Assume' not implemented for this Loc."); - return state; - - case loc::MemRegionKind: { - // FIXME: Should this go into the storemanager? - - const MemRegion *R = cast(Cond).getRegion(); - const SubRegion *SubR = dyn_cast(R); - - while (SubR) { - // FIXME: now we only find the first symbolic region. - if (const SymbolicRegion *SymR = dyn_cast(SubR)) { - if (Assumption) - return AssumeSymNE(state, SymR->getSymbol(), - BasicVals.getZeroWithPtrWidth()); - else - return AssumeSymEQ(state, SymR->getSymbol(), - BasicVals.getZeroWithPtrWidth()); - } - SubR = dyn_cast(SubR->getSuperRegion()); - } - - // FALL-THROUGH. - } - - case loc::GotoLabelKind: - return Assumption ? state : NULL; - - case loc::ConcreteIntKind: { - bool b = cast(Cond).getValue() != 0; - bool isFeasible = b ? Assumption : !Assumption; - return isFeasible ? state : NULL; - } - } // end switch -} - -const GRState *SimpleConstraintManager::Assume(const GRState *state, - NonLoc cond, - bool assumption) { - state = AssumeAux(state, cond, assumption); - return SU.ProcessAssume(state, cond, assumption); -} - -const GRState *SimpleConstraintManager::AssumeAux(const GRState *state, - NonLoc Cond, - bool Assumption) { - - // We cannot reason about SymIntExpr and SymSymExpr. - if (!canReasonAbout(Cond)) { - // Just return the current state indicating that the path is feasible. - // This may be an over-approximation of what is possible. - return state; - } - - BasicValueFactory &BasicVals = state->getBasicVals(); - SymbolManager &SymMgr = state->getSymbolManager(); - - switch (Cond.getSubKind()) { - default: - assert(false && "'Assume' not implemented for this NonLoc"); - - case nonloc::SymbolValKind: { - nonloc::SymbolVal& SV = cast(Cond); - SymbolRef sym = SV.getSymbol(); - QualType T = SymMgr.getType(sym); - const llvm::APSInt &zero = BasicVals.getValue(0, T); - - return Assumption ? AssumeSymNE(state, sym, zero) - : AssumeSymEQ(state, sym, zero); - } - - case nonloc::SymExprValKind: { - nonloc::SymExprVal V = cast(Cond); - if (const SymIntExpr *SE = dyn_cast(V.getSymbolicExpression())){ - // FIXME: This is a hack. It silently converts the RHS integer to be - // of the same type as on the left side. This should be removed once - // we support truncation/extension of symbolic values. - GRStateManager &StateMgr = state->getStateManager(); - ASTContext &Ctx = StateMgr.getContext(); - QualType LHSType = SE->getLHS()->getType(Ctx); - BasicValueFactory &BasicVals = StateMgr.getBasicVals(); - const llvm::APSInt &RHS = BasicVals.Convert(LHSType, SE->getRHS()); - SymIntExpr SENew(SE->getLHS(), SE->getOpcode(), RHS, SE->getType(Ctx)); - - return AssumeSymInt(state, Assumption, &SENew); - } - - // For all other symbolic expressions, over-approximate and consider - // the constraint feasible. - return state; - } - - case nonloc::ConcreteIntKind: { - bool b = cast(Cond).getValue() != 0; - bool isFeasible = b ? Assumption : !Assumption; - return isFeasible ? state : NULL; - } - - case nonloc::LocAsIntegerKind: - return AssumeAux(state, cast(Cond).getLoc(), - Assumption); - } // end switch -} - -const GRState *SimpleConstraintManager::AssumeSymInt(const GRState *state, - bool Assumption, - const SymIntExpr *SE) { - - - // Here we assume that LHS is a symbol. This is consistent with the - // rest of the constraint manager logic. - SymbolRef Sym = cast(SE->getLHS()); - const llvm::APSInt &Int = SE->getRHS(); - - switch (SE->getOpcode()) { - default: - // No logic yet for other operators. Assume the constraint is feasible. - return state; - - case BinaryOperator::EQ: - return Assumption ? AssumeSymEQ(state, Sym, Int) - : AssumeSymNE(state, Sym, Int); - - case BinaryOperator::NE: - return Assumption ? AssumeSymNE(state, Sym, Int) - : AssumeSymEQ(state, Sym, Int); - case BinaryOperator::GT: - return Assumption ? AssumeSymGT(state, Sym, Int) - : AssumeSymLE(state, Sym, Int); - - case BinaryOperator::GE: - return Assumption ? AssumeSymGE(state, Sym, Int) - : AssumeSymLT(state, Sym, Int); - - case BinaryOperator::LT: - return Assumption ? AssumeSymLT(state, Sym, Int) - : AssumeSymGE(state, Sym, Int); - - case BinaryOperator::LE: - return Assumption ? AssumeSymLE(state, Sym, Int) - : AssumeSymGT(state, Sym, Int); - } // end switch -} - -const GRState *SimpleConstraintManager::AssumeInBound(const GRState *state, - DefinedSVal Idx, - DefinedSVal UpperBound, - bool Assumption) { - - // Only support ConcreteInt for now. - if (!(isa(Idx) && isa(UpperBound))) - return state; - - const llvm::APSInt& Zero = state->getBasicVals().getZeroWithPtrWidth(false); - llvm::APSInt IdxV = cast(Idx).getValue(); - // IdxV might be too narrow. - if (IdxV.getBitWidth() < Zero.getBitWidth()) - IdxV.extend(Zero.getBitWidth()); - // UBV might be too narrow, too. - llvm::APSInt UBV = cast(UpperBound).getValue(); - if (UBV.getBitWidth() < Zero.getBitWidth()) - UBV.extend(Zero.getBitWidth()); - - bool InBound = (Zero <= IdxV) && (IdxV < UBV); - bool isFeasible = Assumption ? InBound : !InBound; - return isFeasible ? state : NULL; -} - -} // end of namespace clang diff --git a/lib/Analysis/SimpleConstraintManager.h b/lib/Analysis/SimpleConstraintManager.h deleted file mode 100644 index 818239831948..000000000000 --- a/lib/Analysis/SimpleConstraintManager.h +++ /dev/null @@ -1,83 +0,0 @@ -//== SimpleConstraintManager.h ----------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Code shared between BasicConstraintManager and RangeConstraintManager. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_ANALYSIS_SIMPLE_CONSTRAINT_MANAGER_H -#define LLVM_CLANG_ANALYSIS_SIMPLE_CONSTRAINT_MANAGER_H - -#include "clang/Analysis/PathSensitive/ConstraintManager.h" -#include "clang/Analysis/PathSensitive/GRState.h" - -namespace clang { - -class SimpleConstraintManager : public ConstraintManager { - GRSubEngine &SU; -public: - SimpleConstraintManager(GRSubEngine &subengine) : SU(subengine) {} - virtual ~SimpleConstraintManager(); - - //===------------------------------------------------------------------===// - // Common implementation for the interface provided by ConstraintManager. - //===------------------------------------------------------------------===// - - bool canReasonAbout(SVal X) const; - - const GRState *Assume(const GRState *state, DefinedSVal Cond, - bool Assumption); - - const GRState *Assume(const GRState *state, Loc Cond, bool Assumption); - - const GRState *Assume(const GRState *state, NonLoc Cond, bool Assumption); - - const GRState *AssumeSymInt(const GRState *state, bool Assumption, - const SymIntExpr *SE); - - const GRState *AssumeInBound(const GRState *state, DefinedSVal Idx, - DefinedSVal UpperBound, - bool Assumption); - -protected: - - //===------------------------------------------------------------------===// - // Interface that subclasses must implement. - //===------------------------------------------------------------------===// - - virtual const GRState *AssumeSymNE(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - virtual const GRState *AssumeSymEQ(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - virtual const GRState *AssumeSymLT(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - virtual const GRState *AssumeSymGT(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - virtual const GRState *AssumeSymLE(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - virtual const GRState *AssumeSymGE(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - //===------------------------------------------------------------------===// - // Internal implementation. - //===------------------------------------------------------------------===// - - const GRState *AssumeAux(const GRState *state, Loc Cond,bool Assumption); - - const GRState *AssumeAux(const GRState *state, NonLoc Cond, bool Assumption); -}; - -} // end clang namespace - -#endif diff --git a/lib/Analysis/SimpleSValuator.cpp b/lib/Analysis/SimpleSValuator.cpp deleted file mode 100644 index 8f2f5a1b134c..000000000000 --- a/lib/Analysis/SimpleSValuator.cpp +++ /dev/null @@ -1,428 +0,0 @@ -// SimpleSValuator.cpp - A basic SValuator ------------------------*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines SimpleSValuator, a basic implementation of SValuator. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/SValuator.h" -#include "clang/Analysis/PathSensitive/GRState.h" - -using namespace clang; - -namespace { -class SimpleSValuator : public SValuator { -protected: - virtual SVal EvalCastNL(NonLoc val, QualType castTy); - virtual SVal EvalCastL(Loc val, QualType castTy); - -public: - SimpleSValuator(ValueManager &valMgr) : SValuator(valMgr) {} - virtual ~SimpleSValuator() {} - - virtual SVal EvalMinus(NonLoc val); - virtual SVal EvalComplement(NonLoc val); - virtual SVal EvalBinOpNN(const GRState *state, BinaryOperator::Opcode op, - NonLoc lhs, NonLoc rhs, QualType resultTy); - virtual SVal EvalBinOpLL(BinaryOperator::Opcode op, Loc lhs, Loc rhs, - QualType resultTy); - virtual SVal EvalBinOpLN(const GRState *state, BinaryOperator::Opcode op, - Loc lhs, NonLoc rhs, QualType resultTy); -}; -} // end anonymous namespace - -SValuator *clang::CreateSimpleSValuator(ValueManager &valMgr) { - return new SimpleSValuator(valMgr); -} - -//===----------------------------------------------------------------------===// -// Transfer function for Casts. -//===----------------------------------------------------------------------===// - -SVal SimpleSValuator::EvalCastNL(NonLoc val, QualType castTy) { - - bool isLocType = Loc::IsLocType(castTy); - - if (nonloc::LocAsInteger *LI = dyn_cast(&val)) { - if (isLocType) - return LI->getLoc(); - - // FIXME: Correctly support promotions/truncations. - ASTContext &Ctx = ValMgr.getContext(); - unsigned castSize = Ctx.getTypeSize(castTy); - if (castSize == LI->getNumBits()) - return val; - - return ValMgr.makeLocAsInteger(LI->getLoc(), castSize); - } - - if (const SymExpr *se = val.getAsSymbolicExpression()) { - ASTContext &Ctx = ValMgr.getContext(); - QualType T = Ctx.getCanonicalType(se->getType(Ctx)); - if (T == Ctx.getCanonicalType(castTy)) - return val; - - // FIXME: Remove this hack when we support symbolic truncation/extension. - // HACK: If both castTy and T are integers, ignore the cast. This is - // not a permanent solution. Eventually we want to precisely handle - // extension/truncation of symbolic integers. This prevents us from losing - // precision when we assign 'x = y' and 'y' is symbolic and x and y are - // different integer types. - if (T->isIntegerType() && castTy->isIntegerType()) - return val; - - return UnknownVal(); - } - - if (!isa(val)) - return UnknownVal(); - - // Only handle casts from integers to integers. - if (!isLocType && !castTy->isIntegerType()) - return UnknownVal(); - - llvm::APSInt i = cast(val).getValue(); - i.setIsUnsigned(castTy->isUnsignedIntegerType() || Loc::IsLocType(castTy)); - i.extOrTrunc(ValMgr.getContext().getTypeSize(castTy)); - - if (isLocType) - return ValMgr.makeIntLocVal(i); - else - return ValMgr.makeIntVal(i); -} - -SVal SimpleSValuator::EvalCastL(Loc val, QualType castTy) { - - // Casts from pointers -> pointers, just return the lval. - // - // Casts from pointers -> references, just return the lval. These - // can be introduced by the frontend for corner cases, e.g - // casting from va_list* to __builtin_va_list&. - // - if (Loc::IsLocType(castTy) || castTy->isReferenceType()) - return val; - - // FIXME: Handle transparent unions where a value can be "transparently" - // lifted into a union type. - if (castTy->isUnionType()) - return UnknownVal(); - - assert(castTy->isIntegerType()); - unsigned BitWidth = ValMgr.getContext().getTypeSize(castTy); - - if (!isa(val)) - return ValMgr.makeLocAsInteger(val, BitWidth); - - llvm::APSInt i = cast(val).getValue(); - i.setIsUnsigned(castTy->isUnsignedIntegerType() || Loc::IsLocType(castTy)); - i.extOrTrunc(BitWidth); - return ValMgr.makeIntVal(i); -} - -//===----------------------------------------------------------------------===// -// Transfer function for unary operators. -//===----------------------------------------------------------------------===// - -SVal SimpleSValuator::EvalMinus(NonLoc val) { - switch (val.getSubKind()) { - case nonloc::ConcreteIntKind: - return cast(val).evalMinus(ValMgr); - default: - return UnknownVal(); - } -} - -SVal SimpleSValuator::EvalComplement(NonLoc X) { - switch (X.getSubKind()) { - case nonloc::ConcreteIntKind: - return cast(X).evalComplement(ValMgr); - default: - return UnknownVal(); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function for binary operators. -//===----------------------------------------------------------------------===// - -static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { - switch (op) { - default: - assert(false && "Invalid opcode."); - case BinaryOperator::LT: return BinaryOperator::GE; - case BinaryOperator::GT: return BinaryOperator::LE; - case BinaryOperator::LE: return BinaryOperator::GT; - case BinaryOperator::GE: return BinaryOperator::LT; - case BinaryOperator::EQ: return BinaryOperator::NE; - case BinaryOperator::NE: return BinaryOperator::EQ; - } -} - -// Equality operators for Locs. -// FIXME: All this logic will be revamped when we have MemRegion::getLocation() -// implemented. - -static SVal EvalEquality(ValueManager &ValMgr, Loc lhs, Loc rhs, bool isEqual, - QualType resultTy) { - - switch (lhs.getSubKind()) { - default: - assert(false && "EQ/NE not implemented for this Loc."); - return UnknownVal(); - - case loc::ConcreteIntKind: { - if (SymbolRef rSym = rhs.getAsSymbol()) - return ValMgr.makeNonLoc(rSym, - isEqual ? BinaryOperator::EQ - : BinaryOperator::NE, - cast(lhs).getValue(), - resultTy); - break; - } - case loc::MemRegionKind: { - if (SymbolRef lSym = lhs.getAsLocSymbol()) { - if (isa(rhs)) { - return ValMgr.makeNonLoc(lSym, - isEqual ? BinaryOperator::EQ - : BinaryOperator::NE, - cast(rhs).getValue(), - resultTy); - } - } - break; - } - - case loc::GotoLabelKind: - break; - } - - return ValMgr.makeTruthVal(isEqual ? lhs == rhs : lhs != rhs, resultTy); -} - -SVal SimpleSValuator::EvalBinOpNN(const GRState *state, - BinaryOperator::Opcode op, - NonLoc lhs, NonLoc rhs, - QualType resultTy) { - // Handle trivial case where left-side and right-side are the same. - if (lhs == rhs) - switch (op) { - default: - break; - case BinaryOperator::EQ: - case BinaryOperator::LE: - case BinaryOperator::GE: - return ValMgr.makeTruthVal(true, resultTy); - case BinaryOperator::LT: - case BinaryOperator::GT: - case BinaryOperator::NE: - return ValMgr.makeTruthVal(false, resultTy); - } - - while (1) { - switch (lhs.getSubKind()) { - default: - return UnknownVal(); - case nonloc::LocAsIntegerKind: { - Loc lhsL = cast(lhs).getLoc(); - switch (rhs.getSubKind()) { - case nonloc::LocAsIntegerKind: - return EvalBinOpLL(op, lhsL, cast(rhs).getLoc(), - resultTy); - case nonloc::ConcreteIntKind: { - // Transform the integer into a location and compare. - ASTContext& Ctx = ValMgr.getContext(); - llvm::APSInt i = cast(rhs).getValue(); - i.setIsUnsigned(true); - i.extOrTrunc(Ctx.getTypeSize(Ctx.VoidPtrTy)); - return EvalBinOpLL(op, lhsL, ValMgr.makeLoc(i), resultTy); - } - default: - switch (op) { - case BinaryOperator::EQ: - return ValMgr.makeTruthVal(false, resultTy); - case BinaryOperator::NE: - return ValMgr.makeTruthVal(true, resultTy); - default: - // This case also handles pointer arithmetic. - return UnknownVal(); - } - } - } - case nonloc::SymExprValKind: { - // Logical not? - if (!(op == BinaryOperator::EQ && rhs.isZeroConstant())) - return UnknownVal(); - - const SymExpr *symExpr = - cast(lhs).getSymbolicExpression(); - - // Only handle ($sym op constant) for now. - if (const SymIntExpr *symIntExpr = dyn_cast(symExpr)) { - BinaryOperator::Opcode opc = symIntExpr->getOpcode(); - switch (opc) { - case BinaryOperator::LAnd: - case BinaryOperator::LOr: - assert(false && "Logical operators handled by branching logic."); - return UnknownVal(); - case BinaryOperator::Assign: - case BinaryOperator::MulAssign: - case BinaryOperator::DivAssign: - case BinaryOperator::RemAssign: - case BinaryOperator::AddAssign: - case BinaryOperator::SubAssign: - case BinaryOperator::ShlAssign: - case BinaryOperator::ShrAssign: - case BinaryOperator::AndAssign: - case BinaryOperator::XorAssign: - case BinaryOperator::OrAssign: - case BinaryOperator::Comma: - assert(false && "'=' and ',' operators handled by GRExprEngine."); - return UnknownVal(); - case BinaryOperator::PtrMemD: - case BinaryOperator::PtrMemI: - assert(false && "Pointer arithmetic not handled here."); - return UnknownVal(); - case BinaryOperator::Mul: - case BinaryOperator::Div: - case BinaryOperator::Rem: - case BinaryOperator::Add: - case BinaryOperator::Sub: - case BinaryOperator::Shl: - case BinaryOperator::Shr: - case BinaryOperator::And: - case BinaryOperator::Xor: - case BinaryOperator::Or: - // Not handled yet. - return UnknownVal(); - case BinaryOperator::LT: - case BinaryOperator::GT: - case BinaryOperator::LE: - case BinaryOperator::GE: - case BinaryOperator::EQ: - case BinaryOperator::NE: - opc = NegateComparison(opc); - assert(symIntExpr->getType(ValMgr.getContext()) == resultTy); - return ValMgr.makeNonLoc(symIntExpr->getLHS(), opc, - symIntExpr->getRHS(), resultTy); - } - } - } - case nonloc::ConcreteIntKind: { - if (isa(rhs)) { - const nonloc::ConcreteInt& lhsInt = cast(lhs); - return lhsInt.evalBinOp(ValMgr, op, cast(rhs)); - } - else { - // Swap the left and right sides and flip the operator if doing so - // allows us to better reason about the expression (this is a form - // of expression canonicalization). - NonLoc tmp = rhs; - rhs = lhs; - lhs = tmp; - - switch (op) { - case BinaryOperator::LT: op = BinaryOperator::GT; continue; - case BinaryOperator::GT: op = BinaryOperator::LT; continue; - case BinaryOperator::LE: op = BinaryOperator::GE; continue; - case BinaryOperator::GE: op = BinaryOperator::LE; continue; - case BinaryOperator::EQ: - case BinaryOperator::NE: - case BinaryOperator::Add: - case BinaryOperator::Mul: - continue; - default: - return UnknownVal(); - } - } - } - case nonloc::SymbolValKind: { - nonloc::SymbolVal *slhs = cast(&lhs); - SymbolRef Sym = slhs->getSymbol(); - - // Does the symbol simplify to a constant? If so, "fold" the constant - // by setting 'lhs' to a ConcreteInt and try again. - if (Sym->getType(ValMgr.getContext())->isIntegerType()) - if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { - // The symbol evaluates to a constant. If necessary, promote the - // folded constant (LHS) to the result type. - BasicValueFactory &BVF = ValMgr.getBasicValueFactory(); - const llvm::APSInt &lhs_I = BVF.Convert(resultTy, *Constant); - lhs = nonloc::ConcreteInt(lhs_I); - - // Also promote the RHS (if necessary). - - // For shifts, it necessary promote the RHS to the result type. - if (BinaryOperator::isShiftOp(op)) - continue; - - // Other operators: do an implicit conversion. This shouldn't be - // necessary once we support truncation/extension of symbolic values. - if (nonloc::ConcreteInt *rhs_I = dyn_cast(&rhs)){ - rhs = nonloc::ConcreteInt(BVF.Convert(resultTy, rhs_I->getValue())); - } - - continue; - } - - if (isa(rhs)) { - return ValMgr.makeNonLoc(slhs->getSymbol(), op, - cast(rhs).getValue(), - resultTy); - } - - return UnknownVal(); - } - } - } -} - -SVal SimpleSValuator::EvalBinOpLL(BinaryOperator::Opcode op, Loc lhs, Loc rhs, - QualType resultTy) { - switch (op) { - default: - return UnknownVal(); - case BinaryOperator::EQ: - case BinaryOperator::NE: - return EvalEquality(ValMgr, lhs, rhs, op == BinaryOperator::EQ, resultTy); - case BinaryOperator::LT: - case BinaryOperator::GT: - // FIXME: Generalize. For now, just handle the trivial case where - // the two locations are identical. - if (lhs == rhs) - return ValMgr.makeTruthVal(false, resultTy); - return UnknownVal(); - } -} - -SVal SimpleSValuator::EvalBinOpLN(const GRState *state, - BinaryOperator::Opcode op, - Loc lhs, NonLoc rhs, QualType resultTy) { - // Special case: 'rhs' is an integer that has the same width as a pointer and - // we are using the integer location in a comparison. Normally this cannot be - // triggered, but transfer functions like those for OSCommpareAndSwapBarrier32 - // can generate comparisons that trigger this code. - // FIXME: Are all locations guaranteed to have pointer width? - if (BinaryOperator::isEqualityOp(op)) { - if (nonloc::ConcreteInt *rhsInt = dyn_cast(&rhs)) { - const llvm::APSInt *x = &rhsInt->getValue(); - ASTContext &ctx = ValMgr.getContext(); - if (ctx.getTypeSize(ctx.VoidPtrTy) == x->getBitWidth()) { - // Convert the signedness of the integer (if necessary). - if (x->isSigned()) - x = &ValMgr.getBasicValueFactory().getValue(*x, true); - - return EvalBinOpLL(op, lhs, loc::ConcreteInt(*x), resultTy); - } - } - } - - // Delegate pointer arithmetic to the StoreManager. - return state->getStateManager().getStoreManager().EvalBinOp(state, op, lhs, - rhs, resultTy); -} diff --git a/lib/Analysis/Store.cpp b/lib/Analysis/Store.cpp deleted file mode 100644 index 1724a9250c25..000000000000 --- a/lib/Analysis/Store.cpp +++ /dev/null @@ -1,250 +0,0 @@ -//== Store.cpp - Interface for maps from Locations to Values ----*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defined the types Store and StoreManager. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/Store.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/AST/CharUnits.h" - -using namespace clang; - -StoreManager::StoreManager(GRStateManager &stateMgr) - : ValMgr(stateMgr.getValueManager()), StateMgr(stateMgr), - MRMgr(ValMgr.getRegionManager()) {} - -const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, - QualType EleTy, uint64_t index) { - SVal idx = ValMgr.makeArrayIndex(index); - return MRMgr.getElementRegion(EleTy, idx, Base, ValMgr.getContext()); -} - -// FIXME: Merge with the implementation of the same method in MemRegion.cpp -static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { - if (const RecordType *RT = Ty->getAs()) { - const RecordDecl *D = RT->getDecl(); - if (!D->getDefinition(Ctx)) - return false; - } - - return true; -} - -const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) { - - ASTContext& Ctx = StateMgr.getContext(); - - // Handle casts to Objective-C objects. - if (CastToTy->isObjCObjectPointerType()) - return R->StripCasts(); - - if (CastToTy->isBlockPointerType()) { - // FIXME: We may need different solutions, depending on the symbol - // involved. Blocks can be casted to/from 'id', as they can be treated - // as Objective-C objects. This could possibly be handled by enhancing - // our reasoning of downcasts of symbolic objects. - if (isa(R) || isa(R)) - return R; - - // We don't know what to make of it. Return a NULL region, which - // will be interpretted as UnknownVal. - return NULL; - } - - // Now assume we are casting from pointer to pointer. Other cases should - // already be handled. - QualType PointeeTy = CastToTy->getAs()->getPointeeType(); - QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); - - // Handle casts to void*. We just pass the region through. - if (CanonPointeeTy.getLocalUnqualifiedType() == Ctx.VoidTy) - return R; - - // Handle casts from compatible types. - if (R->isBoundable()) - if (const TypedRegion *TR = dyn_cast(R)) { - QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx)); - if (CanonPointeeTy == ObjTy) - return R; - } - - // Process region cast according to the kind of the region being cast. - switch (R->getKind()) { - case MemRegion::CXXThisRegionKind: - case MemRegion::GenericMemSpaceRegionKind: - case MemRegion::StackLocalsSpaceRegionKind: - case MemRegion::StackArgumentsSpaceRegionKind: - case MemRegion::HeapSpaceRegionKind: - case MemRegion::UnknownSpaceRegionKind: - case MemRegion::GlobalsSpaceRegionKind: { - assert(0 && "Invalid region cast"); - break; - } - - case MemRegion::FunctionTextRegionKind: - case MemRegion::BlockTextRegionKind: - case MemRegion::BlockDataRegionKind: { - // CodeTextRegion should be cast to only a function or block pointer type, - // although they can in practice be casted to anything, e.g, void*, char*, - // etc. - // Just return the region. - return R; - } - - case MemRegion::StringRegionKind: - // FIXME: Need to handle arbitrary downcasts. - case MemRegion::SymbolicRegionKind: - case MemRegion::AllocaRegionKind: - case MemRegion::CompoundLiteralRegionKind: - case MemRegion::FieldRegionKind: - case MemRegion::ObjCIvarRegionKind: - case MemRegion::VarRegionKind: - case MemRegion::CXXObjectRegionKind: - return MakeElementRegion(R, PointeeTy); - - case MemRegion::ElementRegionKind: { - // If we are casting from an ElementRegion to another type, the - // algorithm is as follows: - // - // (1) Compute the "raw offset" of the ElementRegion from the - // base region. This is done by calling 'getAsRawOffset()'. - // - // (2a) If we get a 'RegionRawOffset' after calling - // 'getAsRawOffset()', determine if the absolute offset - // can be exactly divided into chunks of the size of the - // casted-pointee type. If so, create a new ElementRegion with - // the pointee-cast type as the new ElementType and the index - // being the offset divded by the chunk size. If not, create - // a new ElementRegion at offset 0 off the raw offset region. - // - // (2b) If we don't a get a 'RegionRawOffset' after calling - // 'getAsRawOffset()', it means that we are at offset 0. - // - // FIXME: Handle symbolic raw offsets. - - const ElementRegion *elementR = cast(R); - const RegionRawOffset &rawOff = elementR->getAsRawOffset(); - const MemRegion *baseR = rawOff.getRegion(); - - // If we cannot compute a raw offset, throw up our hands and return - // a NULL MemRegion*. - if (!baseR) - return NULL; - - CharUnits off = CharUnits::fromQuantity(rawOff.getByteOffset()); - - if (off.isZero()) { - // Edge case: we are at 0 bytes off the beginning of baseR. We - // check to see if type we are casting to is the same as the base - // region. If so, just return the base region. - if (const TypedRegion *TR = dyn_cast(baseR)) { - QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx)); - QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); - if (CanonPointeeTy == ObjTy) - return baseR; - } - - // Otherwise, create a new ElementRegion at offset 0. - return MakeElementRegion(baseR, PointeeTy); - } - - // We have a non-zero offset from the base region. We want to determine - // if the offset can be evenly divided by sizeof(PointeeTy). If so, - // we create an ElementRegion whose index is that value. Otherwise, we - // create two ElementRegions, one that reflects a raw offset and the other - // that reflects the cast. - - // Compute the index for the new ElementRegion. - int64_t newIndex = 0; - const MemRegion *newSuperR = 0; - - // We can only compute sizeof(PointeeTy) if it is a complete type. - if (IsCompleteType(Ctx, PointeeTy)) { - // Compute the size in **bytes**. - CharUnits pointeeTySize = Ctx.getTypeSizeInChars(PointeeTy); - - // Is the offset a multiple of the size? If so, we can layer the - // ElementRegion (with elementType == PointeeTy) directly on top of - // the base region. - if (off % pointeeTySize == 0) { - newIndex = off / pointeeTySize; - newSuperR = baseR; - } - } - - if (!newSuperR) { - // Create an intermediate ElementRegion to represent the raw byte. - // This will be the super region of the final ElementRegion. - newSuperR = MakeElementRegion(baseR, Ctx.CharTy, off.getQuantity()); - } - - return MakeElementRegion(newSuperR, PointeeTy, newIndex); - } - } - - assert(0 && "unreachable"); - return 0; -} - - -/// CastRetrievedVal - Used by subclasses of StoreManager to implement -/// implicit casts that arise from loads from regions that are reinterpreted -/// as another region. -SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R, - QualType castTy, bool performTestOnly) { - - if (castTy.isNull()) - return V; - - ASTContext &Ctx = ValMgr.getContext(); - - if (performTestOnly) { - // Automatically translate references to pointers. - QualType T = R->getValueType(Ctx); - if (const ReferenceType *RT = T->getAs()) - T = Ctx.getPointerType(RT->getPointeeType()); - - assert(ValMgr.getContext().hasSameUnqualifiedType(castTy, T)); - return V; - } - - if (const Loc *L = dyn_cast(&V)) - return ValMgr.getSValuator().EvalCastL(*L, castTy); - else if (const NonLoc *NL = dyn_cast(&V)) - return ValMgr.getSValuator().EvalCastNL(*NL, castTy); - - return V; -} - -const GRState *StoreManager::InvalidateRegions(const GRState *state, - const MemRegion * const *I, - const MemRegion * const *End, - const Expr *E, - unsigned Count, - InvalidatedSymbols *IS) { - for ( ; I != End ; ++I) - state = InvalidateRegion(state, *I, E, Count, IS); - - return state; -} - -//===----------------------------------------------------------------------===// -// Common getLValueXXX methods. -//===----------------------------------------------------------------------===// - -/// getLValueCompoundLiteral - Returns an SVal representing the lvalue -/// of a compound literal. Within RegionStore a compound literal -/// has an associated region, and the lvalue of the compound literal -/// is the lvalue of that region. -SVal StoreManager::getLValueCompoundLiteral(const CompoundLiteralExpr* CL, - const LocationContext *LC) { - return loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)); -} diff --git a/lib/Analysis/SymbolManager.cpp b/lib/Analysis/SymbolManager.cpp deleted file mode 100644 index 3fe36b064e3d..000000000000 --- a/lib/Analysis/SymbolManager.cpp +++ /dev/null @@ -1,228 +0,0 @@ -//== SymbolManager.h - Management of Symbolic Values ------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines SymbolManager, a class that manages symbolic values -// created for use by GRExprEngine and related classes. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/SymbolManager.h" -#include "clang/Analysis/PathSensitive/MemRegion.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -void SymExpr::dump() const { - dumpToStream(llvm::errs()); -} - -static void print(llvm::raw_ostream& os, BinaryOperator::Opcode Op) { - switch (Op) { - default: - assert(false && "operator printing not implemented"); - break; - case BinaryOperator::Mul: os << '*' ; break; - case BinaryOperator::Div: os << '/' ; break; - case BinaryOperator::Rem: os << '%' ; break; - case BinaryOperator::Add: os << '+' ; break; - case BinaryOperator::Sub: os << '-' ; break; - case BinaryOperator::Shl: os << "<<" ; break; - case BinaryOperator::Shr: os << ">>" ; break; - case BinaryOperator::LT: os << "<" ; break; - case BinaryOperator::GT: os << '>' ; break; - case BinaryOperator::LE: os << "<=" ; break; - case BinaryOperator::GE: os << ">=" ; break; - case BinaryOperator::EQ: os << "==" ; break; - case BinaryOperator::NE: os << "!=" ; break; - case BinaryOperator::And: os << '&' ; break; - case BinaryOperator::Xor: os << '^' ; break; - case BinaryOperator::Or: os << '|' ; break; - } -} - -void SymIntExpr::dumpToStream(llvm::raw_ostream& os) const { - os << '('; - getLHS()->dumpToStream(os); - os << ") "; - print(os, getOpcode()); - os << ' ' << getRHS().getZExtValue(); - if (getRHS().isUnsigned()) os << 'U'; -} - -void SymSymExpr::dumpToStream(llvm::raw_ostream& os) const { - os << '('; - getLHS()->dumpToStream(os); - os << ") "; - os << '('; - getRHS()->dumpToStream(os); - os << ')'; -} - -void SymbolConjured::dumpToStream(llvm::raw_ostream& os) const { - os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}'; -} - -void SymbolDerived::dumpToStream(llvm::raw_ostream& os) const { - os << "derived_$" << getSymbolID() << '{' - << getParentSymbol() << ',' << getRegion() << '}'; -} - -void SymbolRegionValue::dumpToStream(llvm::raw_ostream& os) const { - os << "reg_$" << getSymbolID() << "<" << R << ">"; -} - -const SymbolRegionValue* -SymbolManager::getRegionValueSymbol(const MemRegion* R, QualType T) { - llvm::FoldingSetNodeID profile; - SymbolRegionValue::Profile(profile, R, T); - void* InsertPos; - SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); - if (!SD) { - SD = (SymExpr*) BPAlloc.Allocate(); - new (SD) SymbolRegionValue(SymbolCounter, R, T); - DataSet.InsertNode(SD, InsertPos); - ++SymbolCounter; - } - - return cast(SD); -} - -const SymbolConjured* -SymbolManager::getConjuredSymbol(const Stmt* E, QualType T, unsigned Count, - const void* SymbolTag) { - - llvm::FoldingSetNodeID profile; - SymbolConjured::Profile(profile, E, T, Count, SymbolTag); - void* InsertPos; - SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); - if (!SD) { - SD = (SymExpr*) BPAlloc.Allocate(); - new (SD) SymbolConjured(SymbolCounter, E, T, Count, SymbolTag); - DataSet.InsertNode(SD, InsertPos); - ++SymbolCounter; - } - - return cast(SD); -} - -const SymbolDerived* -SymbolManager::getDerivedSymbol(SymbolRef parentSymbol, - const TypedRegion *R) { - - llvm::FoldingSetNodeID profile; - SymbolDerived::Profile(profile, parentSymbol, R); - void* InsertPos; - SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); - if (!SD) { - SD = (SymExpr*) BPAlloc.Allocate(); - new (SD) SymbolDerived(SymbolCounter, parentSymbol, R); - DataSet.InsertNode(SD, InsertPos); - ++SymbolCounter; - } - - return cast(SD); -} - -const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs, - BinaryOperator::Opcode op, - const llvm::APSInt& v, - QualType t) { - llvm::FoldingSetNodeID ID; - SymIntExpr::Profile(ID, lhs, op, v, t); - void *InsertPos; - SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); - - if (!data) { - data = (SymIntExpr*) BPAlloc.Allocate(); - new (data) SymIntExpr(lhs, op, v, t); - DataSet.InsertNode(data, InsertPos); - } - - return cast(data); -} - -const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs, - BinaryOperator::Opcode op, - const SymExpr *rhs, - QualType t) { - llvm::FoldingSetNodeID ID; - SymSymExpr::Profile(ID, lhs, op, rhs, t); - void *InsertPos; - SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); - - if (!data) { - data = (SymSymExpr*) BPAlloc.Allocate(); - new (data) SymSymExpr(lhs, op, rhs, t); - DataSet.InsertNode(data, InsertPos); - } - - return cast(data); -} - -QualType SymbolConjured::getType(ASTContext&) const { - return T; -} - - -QualType SymbolDerived::getType(ASTContext& Ctx) const { - return R->getValueType(Ctx); -} - -QualType SymbolRegionValue::getType(ASTContext& C) const { - if (!T.isNull()) - return T; - - if (const TypedRegion* TR = dyn_cast(R)) - return TR->getValueType(C); - - return QualType(); -} - -SymbolManager::~SymbolManager() {} - -bool SymbolManager::canSymbolicate(QualType T) { - return Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType()); -} - -void SymbolReaper::markLive(SymbolRef sym) { - TheLiving.insert(sym); - TheDead.erase(sym); -} - -bool SymbolReaper::maybeDead(SymbolRef sym) { - if (isLive(sym)) - return false; - - TheDead.insert(sym); - return true; -} - -bool SymbolReaper::isLive(SymbolRef sym) { - if (TheLiving.count(sym)) - return true; - - if (const SymbolDerived *derived = dyn_cast(sym)) { - if (isLive(derived->getParentSymbol())) { - markLive(sym); - return true; - } - return false; - } - - // Interogate the symbol. It may derive from an input value to - // the analyzed function/method. - return isa(sym); -} - -bool SymbolReaper::isLive(const Stmt *Loc, const VarRegion *VR) const { - const StackFrameContext *SFC = VR->getStackFrame(); - return SFC == CurrentStackFrame ? Liveness.isLive(Loc, VR->getDecl()) : true; -} - -SymbolVisitor::~SymbolVisitor() {} diff --git a/lib/Analysis/UndefBranchChecker.cpp b/lib/Analysis/UndefBranchChecker.cpp deleted file mode 100644 index c739d1ac4b22..000000000000 --- a/lib/Analysis/UndefBranchChecker.cpp +++ /dev/null @@ -1,117 +0,0 @@ -//=== UndefBranchChecker.cpp -----------------------------------*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines UndefBranchChecker, which checks for undefined branch -// condition. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/Checker.h" - -using namespace clang; - -namespace { - -class UndefBranchChecker : public Checker { - BuiltinBug *BT; - - struct FindUndefExpr { - GRStateManager& VM; - const GRState* St; - - FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} - - Expr* FindExpr(Expr* Ex) { - if (!MatchesCriteria(Ex)) - return 0; - - for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end();I!=E;++I) - if (Expr* ExI = dyn_cast_or_null(*I)) { - Expr* E2 = FindExpr(ExI); - if (E2) return E2; - } - - return Ex; - } - - bool MatchesCriteria(Expr* Ex) { return St->getSVal(Ex).isUndef(); } - }; - -public: - UndefBranchChecker() : BT(0) {} - static void *getTag(); - void VisitBranchCondition(GRBranchNodeBuilder &Builder, GRExprEngine &Eng, - Stmt *Condition, void *tag); -}; - -} - -void clang::RegisterUndefBranchChecker(GRExprEngine &Eng) { - Eng.registerCheck(new UndefBranchChecker()); -} - -void *UndefBranchChecker::getTag() { - static int x; - return &x; -} - -void UndefBranchChecker::VisitBranchCondition(GRBranchNodeBuilder &Builder, - GRExprEngine &Eng, - Stmt *Condition, void *tag) { - const GRState *state = Builder.getState(); - SVal X = state->getSVal(Condition); - if (X.isUndef()) { - ExplodedNode *N = Builder.generateNode(state, true); - if (N) { - N->markAsSink(); - if (!BT) - BT = new BuiltinBug("Branch condition evaluates to a garbage value"); - - // What's going on here: we want to highlight the subexpression of the - // condition that is the most likely source of the "uninitialized - // branch condition." We do a recursive walk of the condition's - // subexpressions and roughly look for the most nested subexpression - // that binds to Undefined. We then highlight that expression's range. - BlockEdge B = cast(N->getLocation()); - Expr* Ex = cast(B.getSrc()->getTerminatorCondition()); - assert (Ex && "Block must have a terminator."); - - // Get the predecessor node and check if is a PostStmt with the Stmt - // being the terminator condition. We want to inspect the state - // of that node instead because it will contain main information about - // the subexpressions. - assert (!N->pred_empty()); - - // Note: any predecessor will do. They should have identical state, - // since all the BlockEdge did was act as an error sink since the value - // had to already be undefined. - ExplodedNode *PrevN = *N->pred_begin(); - ProgramPoint P = PrevN->getLocation(); - const GRState* St = N->getState(); - - if (PostStmt* PS = dyn_cast(&P)) - if (PS->getStmt() == Ex) - St = PrevN->getState(); - - FindUndefExpr FindIt(Eng.getStateManager(), St); - Ex = FindIt.FindExpr(Ex); - - // Emit the bug report. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(),N); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); - R->addRange(Ex->getSourceRange()); - - Eng.getBugReporter().EmitReport(R); - } - - Builder.markInfeasible(true); - Builder.markInfeasible(false); - } -} diff --git a/lib/Analysis/UndefResultChecker.cpp b/lib/Analysis/UndefResultChecker.cpp deleted file mode 100644 index acc86dda5a97..000000000000 --- a/lib/Analysis/UndefResultChecker.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines UndefResultChecker, a builtin check in GRExprEngine that -// performs checks for undefined results of non-assignment binary operators. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" - -using namespace clang; - -namespace { -class UndefResultChecker - : public CheckerVisitor { - - BugType *BT; - -public: - UndefResultChecker() : BT(0) {} - static void *getTag() { static int tag = 0; return &tag; } - void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; -} // end anonymous namespace - -void clang::RegisterUndefResultChecker(GRExprEngine &Eng) { - Eng.registerCheck(new UndefResultChecker()); -} - -void UndefResultChecker::PostVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - const GRState *state = C.getState(); - if (state->getSVal(B).isUndef()) { - // Generate an error node. - ExplodedNode *N = C.GenerateSink(); - if (!N) - return; - - if (!BT) - BT = new BuiltinBug("Result of operation is garbage or undefined"); - - llvm::SmallString<256> sbuf; - llvm::raw_svector_ostream OS(sbuf); - const Expr *Ex = NULL; - bool isLeft = true; - - if (state->getSVal(B->getLHS()).isUndef()) { - Ex = B->getLHS()->IgnoreParenCasts(); - isLeft = true; - } - else if (state->getSVal(B->getRHS()).isUndef()) { - Ex = B->getRHS()->IgnoreParenCasts(); - isLeft = false; - } - - if (Ex) { - OS << "The " << (isLeft ? "left" : "right") - << " operand of '" - << BinaryOperator::getOpcodeStr(B->getOpcode()) - << "' is a garbage value"; - } - else { - // Neither operand was undefined, but the result is undefined. - OS << "The result of the '" - << BinaryOperator::getOpcodeStr(B->getOpcode()) - << "' expression is undefined"; - } - EnhancedBugReport *report = new EnhancedBugReport(*BT, OS.str(), N); - if (Ex) { - report->addRange(Ex->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); - } - else - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, B); - C.EmitReport(report); - } -} diff --git a/lib/Analysis/UndefinedArraySubscriptChecker.cpp b/lib/Analysis/UndefinedArraySubscriptChecker.cpp deleted file mode 100644 index d6aacaf1f85e..000000000000 --- a/lib/Analysis/UndefinedArraySubscriptChecker.cpp +++ /dev/null @@ -1,56 +0,0 @@ -//===--- UndefinedArraySubscriptChecker.h ----------------------*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines UndefinedArraySubscriptChecker, a builtin check in GRExprEngine -// that performs checks for undefined array subscripts. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class UndefinedArraySubscriptChecker - : public CheckerVisitor { - BugType *BT; -public: - UndefinedArraySubscriptChecker() : BT(0) {} - static void *getTag() { - static int x = 0; - return &x; - } - void PreVisitArraySubscriptExpr(CheckerContext &C, - const ArraySubscriptExpr *A); -}; -} // end anonymous namespace - -void clang::RegisterUndefinedArraySubscriptChecker(GRExprEngine &Eng) { - Eng.registerCheck(new UndefinedArraySubscriptChecker()); -} - -void -UndefinedArraySubscriptChecker::PreVisitArraySubscriptExpr(CheckerContext &C, - const ArraySubscriptExpr *A) { - if (C.getState()->getSVal(A->getIdx()).isUndef()) { - if (ExplodedNode *N = C.GenerateSink()) { - if (!BT) - BT = new BuiltinBug("Array subscript is undefined"); - - // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); - R->addRange(A->getIdx()->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - A->getIdx()); - C.EmitReport(R); - } - } -} diff --git a/lib/Analysis/UndefinedAssignmentChecker.cpp b/lib/Analysis/UndefinedAssignmentChecker.cpp deleted file mode 100644 index 4630b823a91e..000000000000 --- a/lib/Analysis/UndefinedAssignmentChecker.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines UndefinedAssginmentChecker, a builtin check in GRExprEngine that -// checks for assigning undefined values. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" - -using namespace clang; - -namespace { -class UndefinedAssignmentChecker - : public CheckerVisitor { - BugType *BT; -public: - UndefinedAssignmentChecker() : BT(0) {} - static void *getTag(); - virtual void PreVisitBind(CheckerContext &C, const Stmt *AssignE, - const Stmt *StoreE, SVal location, - SVal val); -}; -} - -void clang::RegisterUndefinedAssignmentChecker(GRExprEngine &Eng){ - Eng.registerCheck(new UndefinedAssignmentChecker()); -} - -void *UndefinedAssignmentChecker::getTag() { - static int x = 0; - return &x; -} - -void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, - const Stmt *AssignE, - const Stmt *StoreE, - SVal location, - SVal val) { - if (!val.isUndef()) - return; - - ExplodedNode *N = C.GenerateSink(); - - if (!N) - return; - - if (!BT) - BT = new BuiltinBug("Assigned value is garbage or undefined"); - - // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); - - if (AssignE) { - const Expr *ex = 0; - - if (const BinaryOperator *B = dyn_cast(AssignE)) - ex = B->getRHS(); - else if (const DeclStmt *DS = dyn_cast(AssignE)) { - const VarDecl* VD = dyn_cast(DS->getSingleDecl()); - ex = VD->getInit(); - } - if (ex) { - R->addRange(ex->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, ex); - } - } - - C.EmitReport(R); -} - diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp index 6fa4b539dc40..bdc0e7c621f7 100644 --- a/lib/Analysis/UninitializedValues.cpp +++ b/lib/Analysis/UninitializedValues.cpp @@ -13,7 +13,6 @@ #include "clang/Analysis/Analyses/UninitializedValues.h" #include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" -#include "clang/Analysis/LocalCheckers.h" #include "clang/Analysis/AnalysisDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/Analysis/FlowSensitive/DataflowSolver.h" diff --git a/lib/Analysis/VLASizeChecker.cpp b/lib/Analysis/VLASizeChecker.cpp deleted file mode 100644 index 2690d6f0cffc..000000000000 --- a/lib/Analysis/VLASizeChecker.cpp +++ /dev/null @@ -1,96 +0,0 @@ -//=== VLASizeChecker.cpp - Undefined dereference checker --------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines VLASizeChecker, a builtin check in GRExprEngine that -// performs checks for declaration of VLA of undefined or zero size. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" - -using namespace clang; - -namespace { -class VLASizeChecker : public CheckerVisitor { - BugType *BT_zero; - BugType *BT_undef; - -public: - VLASizeChecker() : BT_zero(0), BT_undef(0) {} - static void *getTag() { static int tag = 0; return &tag; } - void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS); -}; -} // end anonymous namespace - -void clang::RegisterVLASizeChecker(GRExprEngine &Eng) { - Eng.registerCheck(new VLASizeChecker()); -} - -void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { - if (!DS->isSingleDecl()) - return; - - const VarDecl *VD = dyn_cast(DS->getSingleDecl()); - if (!VD) - return; - - const VariableArrayType *VLA - = C.getASTContext().getAsVariableArrayType(VD->getType()); - if (!VLA) - return; - - // FIXME: Handle multi-dimensional VLAs. - const Expr* SE = VLA->getSizeExpr(); - const GRState *state = C.getState(); - SVal sizeV = state->getSVal(SE); - - if (sizeV.isUndef()) { - // Generate an error node. - ExplodedNode *N = C.GenerateSink(); - if (!N) - return; - - if (!BT_undef) - BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " - "garbage value as its size"); - - EnhancedBugReport *report = - new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); - report->addRange(SE->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); - C.EmitReport(report); - return; - } - - // Check if the size is zero. - DefinedOrUnknownSVal sizeD = cast(sizeV); - - const GRState *stateNotZero, *stateZero; - llvm::tie(stateNotZero, stateZero) = state->Assume(sizeD); - - if (stateZero && !stateNotZero) { - ExplodedNode* N = C.GenerateSink(stateZero); - if (!BT_zero) - BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " - "size"); - - EnhancedBugReport *report = - new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); - report->addRange(SE->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); - C.EmitReport(report); - return; - } - - // From this point on, assume that the size is not zero. - C.addTransition(stateNotZero); -} diff --git a/lib/Analysis/ValueManager.cpp b/lib/Analysis/ValueManager.cpp deleted file mode 100644 index d09137330cb2..000000000000 --- a/lib/Analysis/ValueManager.cpp +++ /dev/null @@ -1,153 +0,0 @@ -//== ValueManager.cpp - Aggregate manager of symbols and SVals --*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ValueManager, a class that manages symbolic values -// and SVals created for use by GRExprEngine and related classes. It -// wraps and owns SymbolManager, MemRegionManager, and BasicValueFactory. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/ValueManager.h" -#include "clang/Analysis/PathSensitive/AnalysisContext.h" - -using namespace clang; -using namespace llvm; - -//===----------------------------------------------------------------------===// -// Utility methods for constructing SVals. -//===----------------------------------------------------------------------===// - -DefinedOrUnknownSVal ValueManager::makeZeroVal(QualType T) { - if (Loc::IsLocType(T)) - return makeNull(); - - if (T->isIntegerType()) - return makeIntVal(0, T); - - // FIXME: Handle floats. - // FIXME: Handle structs. - return UnknownVal(); -} - -//===----------------------------------------------------------------------===// -// Utility methods for constructing Non-Locs. -//===----------------------------------------------------------------------===// - -NonLoc ValueManager::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, - const APSInt& v, QualType T) { - // The Environment ensures we always get a persistent APSInt in - // BasicValueFactory, so we don't need to get the APSInt from - // BasicValueFactory again. - assert(!Loc::IsLocType(T)); - return nonloc::SymExprVal(SymMgr.getSymIntExpr(lhs, op, v, T)); -} - -NonLoc ValueManager::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, - const SymExpr *rhs, QualType T) { - assert(SymMgr.getType(lhs) == SymMgr.getType(rhs)); - assert(!Loc::IsLocType(T)); - return nonloc::SymExprVal(SymMgr.getSymSymExpr(lhs, op, rhs, T)); -} - - -SVal ValueManager::convertToArrayIndex(SVal V) { - if (V.isUnknownOrUndef()) - return V; - - // Common case: we have an appropriately sized integer. - if (nonloc::ConcreteInt* CI = dyn_cast(&V)) { - const llvm::APSInt& I = CI->getValue(); - if (I.getBitWidth() == ArrayIndexWidth && I.isSigned()) - return V; - } - - return SVator->EvalCastNL(cast(V), ArrayIndexTy); -} - -DefinedOrUnknownSVal ValueManager::getRegionValueSymbolVal(const MemRegion* R, - QualType T) { - - if (T.isNull()) { - const TypedRegion* TR = cast(R); - T = TR->getValueType(SymMgr.getContext()); - } - - if (!SymbolManager::canSymbolicate(T)) - return UnknownVal(); - - SymbolRef sym = SymMgr.getRegionValueSymbol(R, T); - - if (Loc::IsLocType(T)) - return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - - return nonloc::SymbolVal(sym); -} - -DefinedOrUnknownSVal ValueManager::getConjuredSymbolVal(const void *SymbolTag, - const Expr *E, - unsigned Count) { - QualType T = E->getType(); - - if (!SymbolManager::canSymbolicate(T)) - return UnknownVal(); - - SymbolRef sym = SymMgr.getConjuredSymbol(E, Count, SymbolTag); - - if (Loc::IsLocType(T)) - return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - - return nonloc::SymbolVal(sym); -} - -DefinedOrUnknownSVal ValueManager::getConjuredSymbolVal(const void *SymbolTag, - const Expr *E, - QualType T, - unsigned Count) { - - if (!SymbolManager::canSymbolicate(T)) - return UnknownVal(); - - SymbolRef sym = SymMgr.getConjuredSymbol(E, T, Count, SymbolTag); - - if (Loc::IsLocType(T)) - return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - - return nonloc::SymbolVal(sym); -} - - -DefinedOrUnknownSVal -ValueManager::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, - const TypedRegion *R) { - QualType T = R->getValueType(R->getContext()); - - if (!SymbolManager::canSymbolicate(T)) - return UnknownVal(); - - SymbolRef sym = SymMgr.getDerivedSymbol(parentSymbol, R); - - if (Loc::IsLocType(T)) - return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - - return nonloc::SymbolVal(sym); -} - -DefinedSVal ValueManager::getFunctionPointer(const FunctionDecl* FD) { - return loc::MemRegionVal(MemMgr.getFunctionTextRegion(FD)); -} - -DefinedSVal ValueManager::getBlockPointer(const BlockDecl *D, - CanQualType locTy, - const LocationContext *LC) { - const BlockTextRegion *BC = - MemMgr.getBlockTextRegion(D, locTy, LC->getAnalysisContext()); - const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, LC); - return loc::MemRegionVal(BD); -} - diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index abbf6f9b6e41..094f7760a8ec 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -21,8 +21,10 @@ #include "clang/Analysis/AnalysisDiagnostic.h" #include "clang/Driver/DriverDiagnostic.h" +#include "clang/Basic/FileManager.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" @@ -385,6 +387,123 @@ Diagnostic::getDiagnosticLevel(unsigned DiagID, unsigned DiagClass) const { return Result; } +static bool ReadUnsigned(const char *&Memory, const char *MemoryEnd, + unsigned &Value) { + if (Memory + sizeof(unsigned) > MemoryEnd) + return true; + + memmove(&Value, Memory, sizeof(unsigned)); + Memory += sizeof(unsigned); + return false; +} + +static bool ReadSourceLocation(FileManager &FM, SourceManager &SM, + const char *&Memory, const char *MemoryEnd, + SourceLocation &Location) { + // Read the filename. + unsigned FileNameLen = 0; + if (ReadUnsigned(Memory, MemoryEnd, FileNameLen) || + Memory + FileNameLen > MemoryEnd) + return true; + + llvm::StringRef FileName(Memory, FileNameLen); + Memory += FileNameLen; + + // Read the line, column. + unsigned Line = 0, Column = 0; + if (ReadUnsigned(Memory, MemoryEnd, Line) || + ReadUnsigned(Memory, MemoryEnd, Column)) + return true; + + if (FileName.empty()) { + Location = SourceLocation(); + return false; + } + + const FileEntry *File = FM.getFile(FileName); + if (!File) + return true; + + // Make sure that this file has an entry in the source manager. + if (!SM.hasFileInfo(File)) + SM.createFileID(File, SourceLocation(), SrcMgr::C_User); + + Location = SM.getLocation(File, Line, Column); + return false; +} + +DiagnosticBuilder Diagnostic::Deserialize(FileManager &FM, SourceManager &SM, + const char *&Memory, + const char *MemoryEnd) { + if (Memory == MemoryEnd) + return DiagnosticBuilder(0); + + // Read the severity level. + unsigned Level = 0; + if (ReadUnsigned(Memory, MemoryEnd, Level) || Level > Fatal) + return DiagnosticBuilder(0); + + // Read the source location. + SourceLocation Location; + if (ReadSourceLocation(FM, SM, Memory, MemoryEnd, Location)) + return DiagnosticBuilder(0); + + // Read the diagnostic text. + if (Memory == MemoryEnd) + return DiagnosticBuilder(0); + + unsigned MessageLen = 0; + if (ReadUnsigned(Memory, MemoryEnd, MessageLen) || + Memory + MessageLen > MemoryEnd) + return DiagnosticBuilder(0); + + llvm::StringRef Message(Memory, MessageLen); + Memory += MessageLen; + + // At this point, we have enough information to form a diagnostic. Do so. + unsigned DiagID = getCustomDiagID((enum Level)Level, Message); + DiagnosticBuilder DB = Report(FullSourceLoc(Location, SM), DiagID); + if (Memory == MemoryEnd) + return DB; + + // Read the source ranges. + unsigned NumSourceRanges = 0; + if (ReadUnsigned(Memory, MemoryEnd, NumSourceRanges)) + return DB; + for (unsigned I = 0; I != NumSourceRanges; ++I) { + SourceLocation Begin, End; + if (ReadSourceLocation(FM, SM, Memory, MemoryEnd, Begin) || + ReadSourceLocation(FM, SM, Memory, MemoryEnd, End)) + return DB; + + DB << SourceRange(Begin, End); + } + + // Read the fix-it hints. + unsigned NumFixIts = 0; + if (ReadUnsigned(Memory, MemoryEnd, NumFixIts)) + return DB; + for (unsigned I = 0; I != NumFixIts; ++I) { + SourceLocation RemoveBegin, RemoveEnd, InsertionLoc; + unsigned InsertLen = 0; + if (ReadSourceLocation(FM, SM, Memory, MemoryEnd, RemoveBegin) || + ReadSourceLocation(FM, SM, Memory, MemoryEnd, RemoveEnd) || + ReadSourceLocation(FM, SM, Memory, MemoryEnd, InsertionLoc) || + ReadUnsigned(Memory, MemoryEnd, InsertLen) || + Memory + InsertLen > MemoryEnd) + return DB; + + CodeModificationHint Hint; + Hint.RemoveRange = SourceRange(RemoveBegin, RemoveEnd); + Hint.InsertionLoc = InsertionLoc; + Hint.CodeToInsert.assign(Memory, Memory + InsertLen); + Memory += InsertLen; + DB << Hint; + } + + return DB; +} + struct WarningOption { const char *Name; const short *Members; @@ -510,7 +629,7 @@ bool Diagnostic::ProcessDiag() { // it. if (SuppressSystemWarnings && !ShouldEmitInSystemHeader && Info.getLocation().isValid() && - Info.getLocation().getSpellingLoc().isInSystemHeader() && + Info.getLocation().getInstantiationLoc().isInSystemHeader() && (DiagLevel != Diagnostic::Note || LastDiagLevel == Diagnostic::Ignored)) { LastDiagLevel = Diagnostic::Ignored; return false; @@ -917,6 +1036,104 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, } } +static void WriteUnsigned(llvm::raw_ostream &OS, unsigned Value) { + OS.write((const char *)&Value, sizeof(unsigned)); +} + +static void WriteString(llvm::raw_ostream &OS, llvm::StringRef String) { + WriteUnsigned(OS, String.size()); + OS.write(String.data(), String.size()); +} + +static void WriteSourceLocation(llvm::raw_ostream &OS, + SourceManager *SM, + SourceLocation Location) { + if (!SM || Location.isInvalid()) { + // If we don't have a source manager or this location is invalid, + // just write an invalid location. + WriteUnsigned(OS, 0); + WriteUnsigned(OS, 0); + WriteUnsigned(OS, 0); + return; + } + + Location = SM->getInstantiationLoc(Location); + std::pair Decomposed = SM->getDecomposedLoc(Location); + + WriteString(OS, SM->getFileEntryForID(Decomposed.first)->getName()); + WriteUnsigned(OS, SM->getLineNumber(Decomposed.first, Decomposed.second)); + WriteUnsigned(OS, SM->getColumnNumber(Decomposed.first, Decomposed.second)); +} + +void DiagnosticInfo::Serialize(Diagnostic::Level DiagLevel, + llvm::raw_ostream &OS) const { + SourceManager *SM = 0; + if (getLocation().isValid()) + SM = &const_cast(getLocation().getManager()); + + // Write the diagnostic level and location. + WriteUnsigned(OS, (unsigned)DiagLevel); + WriteSourceLocation(OS, SM, getLocation()); + + // Write the diagnostic message. + llvm::SmallString<64> Message; + FormatDiagnostic(Message); + WriteString(OS, Message); + + // Count the number of ranges that don't point into macros, since + // only simple file ranges serialize well. + unsigned NumNonMacroRanges = 0; + for (unsigned I = 0, N = getNumRanges(); I != N; ++I) { + SourceRange R = getRange(I); + if (R.getBegin().isMacroID() || R.getEnd().isMacroID()) + continue; + + ++NumNonMacroRanges; + } + + // Write the ranges. + WriteUnsigned(OS, NumNonMacroRanges); + if (NumNonMacroRanges) { + for (unsigned I = 0, N = getNumRanges(); I != N; ++I) { + SourceRange R = getRange(I); + if (R.getBegin().isMacroID() || R.getEnd().isMacroID()) + continue; + + WriteSourceLocation(OS, SM, R.getBegin()); + WriteSourceLocation(OS, SM, R.getEnd()); + } + } + + // Determine if all of the fix-its involve rewrites with simple file + // locations (not in macro instantiations). If so, we can write + // fix-it information. + unsigned NumFixIts = getNumCodeModificationHints(); + for (unsigned I = 0; I != NumFixIts; ++I) { + const CodeModificationHint &Hint = getCodeModificationHint(I); + if (Hint.RemoveRange.isValid() && + (Hint.RemoveRange.getBegin().isMacroID() || + Hint.RemoveRange.getEnd().isMacroID())) { + NumFixIts = 0; + break; + } + + if (Hint.InsertionLoc.isValid() && Hint.InsertionLoc.isMacroID()) { + NumFixIts = 0; + break; + } + } + + // Write the fix-its. + WriteUnsigned(OS, NumFixIts); + for (unsigned I = 0; I != NumFixIts; ++I) { + const CodeModificationHint &Hint = getCodeModificationHint(I); + WriteSourceLocation(OS, SM, Hint.RemoveRange.getBegin()); + WriteSourceLocation(OS, SM, Hint.RemoveRange.getEnd()); + WriteSourceLocation(OS, SM, Hint.InsertionLoc); + WriteString(OS, Hint.CodeToInsert); + } +} + /// IncludeInDiagnosticCounts - This method (whose default implementation /// returns true) indicates whether the diagnostics handled by this /// DiagnosticClient should be included in the number of diagnostics diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp index 401e6cba0692..16a61b7156fa 100644 --- a/lib/Basic/IdentifierTable.cpp +++ b/lib/Basic/IdentifierTable.cpp @@ -68,7 +68,8 @@ namespace { KEYCXX0X = 8, KEYGNU = 16, KEYMS = 32, - BOOLSUPPORT = 64 + BOOLSUPPORT = 64, + KEYALTIVEC = 128 }; } @@ -91,6 +92,7 @@ static void AddKeyword(const char *Keyword, unsigned KWLen, else if (LangOpts.GNUMode && (Flags & KEYGNU)) AddResult = 1; else if (LangOpts.Microsoft && (Flags & KEYMS)) AddResult = 1; else if (LangOpts.Bool && (Flags & BOOLSUPPORT)) AddResult = 2; + else if (LangOpts.AltiVec && (Flags & KEYALTIVEC)) AddResult = 2; // Don't add this keyword if disabled in this language. if (AddResult == 0) return; diff --git a/lib/Basic/Makefile b/lib/Basic/Makefile index f7335789384f..58ac7eb86e75 100644 --- a/lib/Basic/Makefile +++ b/lib/Basic/Makefile @@ -14,7 +14,6 @@ LEVEL = ../../../.. LIBRARYNAME := clangBasic BUILD_ARCHIVE = 1 -CXXFLAGS = -fno-rtti CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include ifdef CLANG_VENDOR diff --git a/lib/Basic/SourceManager.cpp b/lib/Basic/SourceManager.cpp index 354bf7befbb3..b91671ad17b1 100644 --- a/lib/Basic/SourceManager.cpp +++ b/lib/Basic/SourceManager.cpp @@ -560,10 +560,14 @@ FileID SourceManager::getFileIDSlow(unsigned SLocOffset) const { SourceLocation SourceManager:: getInstantiationLocSlowCase(SourceLocation Loc) const { do { - std::pair LocInfo = getDecomposedLoc(Loc); - Loc = getSLocEntry(LocInfo.first).getInstantiation() + // Note: If Loc indicates an offset into a token that came from a macro + // expansion (e.g. the 5th character of the token) we do not want to add + // this offset when going to the instantiation location. The instatiation + // location is the macro invocation, which the offset has nothing to do + // with. This is unlike when we get the spelling loc, because the offset + // directly correspond to the token whose spelling we're inspecting. + Loc = getSLocEntry(getFileID(Loc)).getInstantiation() .getInstantiationLocStart(); - Loc = Loc.getFileLocWithOffset(LocInfo.second); } while (!Loc.isFileID()); return Loc; diff --git a/lib/Basic/TargetInfo.cpp b/lib/Basic/TargetInfo.cpp index 493beeea6dc5..136089fe90c2 100644 --- a/lib/Basic/TargetInfo.cpp +++ b/lib/Basic/TargetInfo.cpp @@ -150,39 +150,41 @@ void TargetInfo::setForcedLangOptions(LangOptions &Opts) { //===----------------------------------------------------------------------===// -static void removeGCCRegisterPrefix(const char *&Name) { +static llvm::StringRef removeGCCRegisterPrefix(llvm::StringRef Name) { if (Name[0] == '%' || Name[0] == '#') - Name++; + Name = Name.substr(1); + + return Name; } /// isValidGCCRegisterName - Returns whether the passed in string /// is a valid register name according to GCC. This is used by Sema for /// inline asm statements. -bool TargetInfo::isValidGCCRegisterName(const char *Name) const { +bool TargetInfo::isValidGCCRegisterName(llvm::StringRef Name) const { + if (Name.empty()) + return false; + const char * const *Names; unsigned NumNames; // Get rid of any register prefix. - removeGCCRegisterPrefix(Name); + Name = removeGCCRegisterPrefix(Name); - - if (strcmp(Name, "memory") == 0 || - strcmp(Name, "cc") == 0) + if (Name == "memory" || Name == "cc") return true; getGCCRegNames(Names, NumNames); // If we have a number it maps to an entry in the register name array. if (isdigit(Name[0])) { - char *End; - int n = (int)strtol(Name, &End, 0); - if (*End == 0) + int n; + if (!Name.getAsInteger(0, n)) return n >= 0 && (unsigned)n < NumNames; } // Check register names. for (unsigned i = 0; i < NumNames; i++) { - if (strcmp(Name, Names[i]) == 0) + if (Name == Names[i]) return true; } @@ -195,7 +197,7 @@ bool TargetInfo::isValidGCCRegisterName(const char *Name) const { for (unsigned j = 0 ; j < llvm::array_lengthof(Aliases[i].Aliases); j++) { if (!Aliases[i].Aliases[j]) break; - if (strcmp(Aliases[i].Aliases[j], Name) == 0) + if (Aliases[i].Aliases[j] == Name) return true; } } @@ -203,10 +205,12 @@ bool TargetInfo::isValidGCCRegisterName(const char *Name) const { return false; } -const char *TargetInfo::getNormalizedGCCRegisterName(const char *Name) const { +llvm::StringRef +TargetInfo::getNormalizedGCCRegisterName(llvm::StringRef Name) const { assert(isValidGCCRegisterName(Name) && "Invalid register passed in"); - removeGCCRegisterPrefix(Name); + // Get rid of any register prefix. + Name = removeGCCRegisterPrefix(Name); const char * const *Names; unsigned NumNames; @@ -215,9 +219,8 @@ const char *TargetInfo::getNormalizedGCCRegisterName(const char *Name) const { // First, check if we have a number. if (isdigit(Name[0])) { - char *End; - int n = (int)strtol(Name, &End, 0); - if (*End == 0) { + int n; + if (!Name.getAsInteger(0, n)) { assert(n >= 0 && (unsigned)n < NumNames && "Out of bounds register number!"); return Names[n]; @@ -233,7 +236,7 @@ const char *TargetInfo::getNormalizedGCCRegisterName(const char *Name) const { for (unsigned j = 0 ; j < llvm::array_lengthof(Aliases[i].Aliases); j++) { if (!Aliases[i].Aliases[j]) break; - if (strcmp(Aliases[i].Aliases[j], Name) == 0) + if (Aliases[i].Aliases[j] == Name) return Aliases[i].Register; } } diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index ea076ae0bbfc..c1cd96e361ed 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -74,7 +74,8 @@ public: } // end anonymous namespace -static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts) { +static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, + const llvm::Triple &Triple) { Builder.defineMacro("__APPLE_CC__", "5621"); Builder.defineMacro("__APPLE__"); Builder.defineMacro("__MACH__"); @@ -96,51 +97,45 @@ static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts) { if (Opts.POSIXThreads) Builder.defineMacro("_REENTRANT"); -} - -static void getDarwinOSXDefines(MacroBuilder &Builder, - const llvm::Triple &Triple) { - if (Triple.getOS() != llvm::Triple::Darwin) - return; - - // Figure out which "darwin number" the target triple is. "darwin9" -> 10.5. - unsigned Maj, Min, Rev; - Triple.getDarwinNumber(Maj, Min, Rev); - - char MacOSXStr[] = "1000"; - if (Maj >= 4 && Maj <= 13) { // 10.0-10.9 - // darwin7 -> 1030, darwin8 -> 1040, darwin9 -> 1050, etc. - MacOSXStr[2] = '0' + Maj-4; - } - - // Handle minor version: 10.4.9 -> darwin8.9 -> "1049" - // Cap 10.4.11 -> darwin8.11 -> "1049" - MacOSXStr[3] = std::min(Min, 9U)+'0'; - Builder.defineMacro("__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__", - MacOSXStr); -} - -static void getDarwinIPhoneOSDefines(MacroBuilder &Builder, - const llvm::Triple &Triple) { - if (Triple.getOS() != llvm::Triple::Darwin) - return; - // Figure out which "darwin number" the target triple is. "darwin9" -> 10.5. + // Get the OS version number from the triple. unsigned Maj, Min, Rev; - Triple.getDarwinNumber(Maj, Min, Rev); - // When targetting iPhone OS, interpret the minor version and - // revision as the iPhone OS version - char iPhoneOSStr[] = "10000"; - if (Min >= 2 && Min <= 9) { // iPhone OS 2.0-9.0 - // darwin9.2.0 -> 20000, darwin9.3.0 -> 30000, etc. - iPhoneOSStr[0] = '0' + Min; + // If no version was given, default to to 10.4.0, for simplifying tests. + if (Triple.getOSName() == "darwin") { + Min = Rev = 0; + Maj = 8; + } else + Triple.getDarwinNumber(Maj, Min, Rev); + + // Set the appropriate OS version define. + if (Triple.getEnvironmentName() == "iphoneos") { + assert(Maj < 10 && Min < 99 && Rev < 99 && "Invalid version!"); + char Str[6]; + Str[0] = '0' + Maj; + Str[1] = '0' + (Min / 10); + Str[2] = '0' + (Min % 10); + Str[3] = '0' + (Rev / 10); + Str[4] = '0' + (Rev % 10); + Str[5] = '\0'; + Builder.defineMacro("__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__", Str); + } else { + // For historical reasons that make little sense, the version passed here is + // the "darwin" version, which drops the 10 and offsets by 4. + Rev = Min; + Min = Maj - 4; + Maj = 10; + + assert(Triple.getEnvironmentName().empty() && "Invalid environment!"); + assert(Maj < 99 && Min < 10 && Rev < 10 && "Invalid version!"); + char Str[5]; + Str[0] = '0' + (Maj / 10); + Str[1] = '0' + (Maj % 10); + Str[2] = '0' + Min; + Str[3] = '0' + Rev; + Str[4] = '\0'; + Builder.defineMacro("__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__", Str); } - - // Handle minor version: 2.2 -> darwin9.2.2 -> 20200 - iPhoneOSStr[2] = std::min(Rev, 9U)+'0'; - Builder.defineMacro("__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__", - iPhoneOSStr); } namespace { @@ -149,8 +144,7 @@ class DarwinTargetInfo : public OSTargetInfo { protected: virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, MacroBuilder &Builder) const { - getDarwinDefines(Builder, Opts); - getDarwinOSXDefines(Builder, Triple); + getDarwinDefines(Builder, Opts, Triple); } public: @@ -159,11 +153,7 @@ public: this->TLSSupported = false; } - virtual const char *getUnicodeStringSection() const { - return "__TEXT,__ustring"; - } - - virtual std::string isValidSectionSpecifier(const llvm::StringRef &SR) const { + virtual std::string isValidSectionSpecifier(llvm::StringRef SR) const { // Let MCSectionMachO validate this. llvm::StringRef Segment, Section; unsigned TAA, StubSize; @@ -201,16 +191,10 @@ protected: // FreeBSD defines; list based off of gcc output // FIXME: Move version number handling to llvm::Triple. - const char *FreeBSD = strstr(Triple.getTriple().c_str(), - "-freebsd"); - FreeBSD += strlen("-freebsd"); - char release[] = "X"; - release[0] = FreeBSD[0]; - char version[] = "X00001"; - version[0] = FreeBSD[0]; - - Builder.defineMacro("__FreeBSD__", release); - Builder.defineMacro("__FreeBSD_cc_version", version); + llvm::StringRef Release = Triple.getOSName().substr(strlen("freebsd"), 1); + + Builder.defineMacro("__FreeBSD__", Release); + Builder.defineMacro("__FreeBSD_cc_version", Release + "00001"); Builder.defineMacro("__KPRINTF_ATTRIBUTE__"); DefineStd(Builder, "unix", Opts); Builder.defineMacro("__ELF__"); @@ -643,9 +627,13 @@ class X86TargetInfo : public TargetInfo { enum X86SSEEnum { NoMMXSSE, MMX, SSE1, SSE2, SSE3, SSSE3, SSE41, SSE42 } SSELevel; + enum AMD3DNowEnum { + NoAMD3DNow, AMD3DNow, AMD3DNowAthlon + } AMD3DNowLevel; + public: X86TargetInfo(const std::string& triple) - : TargetInfo(triple), SSELevel(NoMMXSSE) { + : TargetInfo(triple), SSELevel(NoMMXSSE), AMD3DNowLevel(NoAMD3DNow) { LongDoubleFormat = &llvm::APFloat::x87DoubleExtended; } virtual void getTargetBuiltins(const Builtin::Info *&Records, @@ -810,6 +798,14 @@ void X86TargetInfo::HandleTargetFeatures(std::vector &Features) { .Case("mmx", MMX) .Default(NoMMXSSE); SSELevel = std::max(SSELevel, Level); + + AMD3DNowEnum ThreeDNowLevel = + llvm::StringSwitch(Features[i].substr(1)) + .Case("3dnowa", AMD3DNowAthlon) + .Case("3dnow", AMD3DNow) + .Default(NoAMD3DNow); + + AMD3DNowLevel = std::max(AMD3DNowLevel, ThreeDNowLevel); } } @@ -864,6 +860,16 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts, case NoMMXSSE: break; } + + // Each case falls through to the previous one here. + switch (AMD3DNowLevel) { + case AMD3DNowAthlon: + Builder.defineMacro("__3dNOW_A__"); + case AMD3DNow: + Builder.defineMacro("__3dNOW__"); + case NoAMD3DNow: + break; + } } @@ -1224,7 +1230,7 @@ public: // FIXME: We need support for -meabi... we could just mangle it into the // name. if (Name == "apcs-gnu") { - DoubleAlign = LongLongAlign = 32; + DoubleAlign = LongLongAlign = LongDoubleAlign = 32; SizeType = UnsignedLong; if (IsThumb) { @@ -1379,9 +1385,6 @@ public: // when Neon instructions are actually available. if (FPU == NeonFPU && !SoftFloat && IsThumb2) Builder.defineMacro("__ARM_NEON__"); - - if (getTriple().getOS() == llvm::Triple::Darwin) - Builder.defineMacro("__USING_SJLJ_EXCEPTIONS__"); } virtual void getTargetBuiltins(const Builtin::Info *&Records, unsigned &NumRecords) const { @@ -1461,8 +1464,7 @@ class DarwinARMTargetInfo : protected: virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, MacroBuilder &Builder) const { - getDarwinDefines(Builder, Opts); - getDarwinIPhoneOSDefines(Builder, Triple); + getDarwinDefines(Builder, Opts, Triple); } public: @@ -1617,23 +1619,25 @@ namespace { virtual void getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const { Builder.defineMacro("__pic16"); + Builder.defineMacro("__PIC16"); Builder.defineMacro("rom", "__attribute__((address_space(1)))"); Builder.defineMacro("ram", "__attribute__((address_space(0)))"); - Builder.defineMacro("_section(SectName)", + Builder.defineMacro("__section(SectName)", "__attribute__((section(SectName)))"); Builder.defineMacro("near", "__attribute__((section(\"Address=NEAR\")))"); - Builder.defineMacro("_address(Addr)", + Builder.defineMacro("__address(Addr)", "__attribute__((section(\"Address=\"#Addr)))"); - Builder.defineMacro("_CONFIG(conf)", "asm(\"CONFIG \"#conf)"); - Builder.defineMacro("_interrupt", + Builder.defineMacro("__config(conf)", "asm(\"CONFIG \"#conf)"); + Builder.defineMacro("__idlocs(value)", "asm(\"__IDLOCS \"#value)"); + Builder.defineMacro("interrupt", "__attribute__((section(\"interrupt=0x4\"))) \ __attribute__((used))"); } virtual void getTargetBuiltins(const Builtin::Info *&Records, unsigned &NumRecords) const {} virtual const char *getVAListDeclaration() const { - return ""; + return "typedef char* __builtin_va_list;"; } virtual const char *getClobbers() const { return ""; @@ -1656,13 +1660,10 @@ namespace { public: MSP430TargetInfo(const std::string& triple) : TargetInfo(triple) { TLSSupported = false; - IntWidth = 16; - LongWidth = 32; - LongLongWidth = 64; - PointerWidth = 16; - IntAlign = 8; - LongAlign = LongLongAlign = 8; - PointerAlign = 8; + IntWidth = 16; IntAlign = 16; + LongWidth = 32; LongLongWidth = 64; + LongAlign = LongLongAlign = 16; + PointerWidth = 16; PointerAlign = 16; SizeType = UnsignedInt; IntMaxType = SignedLong; UIntMaxType = UnsignedLong; diff --git a/lib/Basic/Version.cpp b/lib/Basic/Version.cpp index b1b250f82933..98cf42b8c3d9 100644 --- a/lib/Basic/Version.cpp +++ b/lib/Basic/Version.cpp @@ -21,67 +21,55 @@ using namespace std; namespace clang { llvm::StringRef getClangRepositoryPath() { - static const char *Path = 0; - if (Path) - return Path; - - static char URL[] = "$URL: http://llvm.org/svn/llvm-project/cfe/trunk/lib/Basic/Version.cpp $"; - char *End = strstr(URL, "/lib/Basic"); + static const char URL[] = "$URL: http://llvm.org/svn/llvm-project/cfe/trunk/lib/Basic/Version.cpp $"; + const char *URLEnd = URL + strlen(URL); + + const char *End = strstr(URL, "/lib/Basic"); if (End) - *End = 0; - + URLEnd = End; + End = strstr(URL, "/clang/tools/clang"); if (End) - *End = 0; - - char *Begin = strstr(URL, "cfe/"); - if (Begin) { - Path = Begin + 4; - return Path; - } - - Path = URL; - return Path; -} + URLEnd = End; + + const char *Begin = strstr(URL, "cfe/"); + if (Begin) + return llvm::StringRef(Begin + 4, URLEnd - Begin - 4); + return llvm::StringRef(URL, URLEnd - URL); +} -llvm::StringRef getClangRevision() { +std::string getClangRevision() { #ifndef SVN_REVISION // Subversion was not available at build time? - return llvm::StringRef(); + return ""; #else - static std::string revision; - if (revision.empty()) { - llvm::raw_string_ostream OS(revision); - OS << strtol(SVN_REVISION, 0, 10); - } + std::string revision; + llvm::raw_string_ostream OS(revision); + OS << strtol(SVN_REVISION, 0, 10); return revision; #endif } -llvm::StringRef getClangFullRepositoryVersion() { - static std::string buf; - if (buf.empty()) { - llvm::raw_string_ostream OS(buf); - OS << getClangRepositoryPath(); - llvm::StringRef Revision = getClangRevision(); - if (!Revision.empty()) - OS << ' ' << Revision; - } +std::string getClangFullRepositoryVersion() { + std::string buf; + llvm::raw_string_ostream OS(buf); + OS << getClangRepositoryPath(); + const std::string &Revision = getClangRevision(); + if (!Revision.empty()) + OS << ' ' << Revision; return buf; } -const char *getClangFullVersion() { - static std::string buf; - if (buf.empty()) { - llvm::raw_string_ostream OS(buf); +std::string getClangFullVersion() { + std::string buf; + llvm::raw_string_ostream OS(buf); #ifdef CLANG_VENDOR - OS << CLANG_VENDOR; + OS << CLANG_VENDOR; #endif - OS << "clang version " CLANG_VERSION_STRING " (" - << getClangFullRepositoryVersion() << ')'; - } - return buf.c_str(); + OS << "clang version " CLANG_VERSION_STRING " (" + << getClangFullRepositoryVersion() << ')'; + return buf; } - + } // end namespace clang diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 2bfaa445e568..bc2cd460d92b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,3 +10,4 @@ add_subdirectory(Rewrite) add_subdirectory(Driver) add_subdirectory(Frontend) add_subdirectory(Index) +add_subdirectory(Checker) diff --git a/lib/Checker/AdjustedReturnValueChecker.cpp b/lib/Checker/AdjustedReturnValueChecker.cpp new file mode 100644 index 000000000000..e95a86b838b6 --- /dev/null +++ b/lib/Checker/AdjustedReturnValueChecker.cpp @@ -0,0 +1,98 @@ +//== AdjustedReturnValueChecker.cpp -----------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines AdjustedReturnValueChecker, a simple check to see if the +// return value of a function call is different than the one the caller thinks +// it is. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang; + +namespace { +class AdjustedReturnValueChecker : + public CheckerVisitor { +public: + AdjustedReturnValueChecker() {} + + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + + static void *getTag() { + static int x = 0; return &x; + } +}; +} + +void clang::RegisterAdjustedReturnValueChecker(GRExprEngine &Eng) { + Eng.registerCheck(new AdjustedReturnValueChecker()); +} + +void AdjustedReturnValueChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + + // Get the result type of the call. + QualType expectedResultTy = CE->getType(); + + // Fetch the signature of the called function. + const GRState *state = C.getState(); + + SVal V = state->getSVal(CE); + + if (V.isUnknown()) + return; + + // Casting to void? Discard the value. + if (expectedResultTy->isVoidType()) { + C.GenerateNode(state->BindExpr(CE, UnknownVal())); + return; + } + + const MemRegion *callee = state->getSVal(CE->getCallee()).getAsRegion(); + if (!callee) + return; + + QualType actualResultTy; + + if (const FunctionTextRegion *FT = dyn_cast(callee)) { + const FunctionDecl *FD = FT->getDecl(); + actualResultTy = FD->getResultType(); + } + else if (const BlockDataRegion *BD = dyn_cast(callee)) { + const BlockTextRegion *BR = BD->getCodeRegion(); + const BlockPointerType *BT = + BR->getLocationType(C.getASTContext())->getAs(); + const FunctionType *FT = BT->getPointeeType()->getAs(); + actualResultTy = FT->getResultType(); + } + + // Can this happen? + if (actualResultTy.isNull()) + return; + + // For now, ignore references. + if (actualResultTy->getAs()) + return; + + + // Are they the same? + if (expectedResultTy != actualResultTy) { + // FIXME: Do more checking and actual emit an error. At least performing + // the cast avoids some assertion failures elsewhere. + SValuator &SVator = C.getSValuator(); + V = SVator.EvalCast(V, expectedResultTy, actualResultTy); + C.GenerateNode(state->BindExpr(CE, V)); + } +} diff --git a/lib/Checker/ArrayBoundChecker.cpp b/lib/Checker/ArrayBoundChecker.cpp new file mode 100644 index 000000000000..74fb06f45564 --- /dev/null +++ b/lib/Checker/ArrayBoundChecker.cpp @@ -0,0 +1,91 @@ +//== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ArrayBoundChecker, which is a path-sensitive check +// which looks for an out-of-bound array element access. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" + +using namespace clang; + +namespace { +class ArrayBoundChecker : + public CheckerVisitor { + BuiltinBug *BT; +public: + ArrayBoundChecker() : BT(0) {} + static void *getTag(); + void VisitLocation(CheckerContext &C, const Stmt *S, SVal l); +}; +} + +void clang::RegisterArrayBoundChecker(GRExprEngine &Eng) { + Eng.registerCheck(new ArrayBoundChecker()); +} + +void *ArrayBoundChecker::getTag() { + static int x = 0; return &x; +} + +void ArrayBoundChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l){ + // Check for out of bound array element access. + const MemRegion *R = l.getAsRegion(); + if (!R) + return; + + R = R->StripCasts(); + + const ElementRegion *ER = dyn_cast(R); + if (!ER) + return; + + // Get the index of the accessed element. + DefinedOrUnknownSVal &Idx = cast(ER->getIndex()); + + const GRState *state = C.getState(); + + // Get the size of the array. + DefinedOrUnknownSVal NumElements + = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), + ER->getValueType(C.getASTContext())); + + const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); + const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.GenerateSink(StOutBound); + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Out-of-bound array access", + "Access out-of-bound array element (buffer overflow)"); + + // FIXME: It would be nice to eventually make this diagnostic more clear, + // e.g., by referencing the original declaration or by saying *why* this + // reference is outside the range. + + // Generate a report for this bug. + RangedBugReport *report = + new RangedBugReport(*BT, BT->getDescription(), N); + + report->addRange(S->getSourceRange()); + C.EmitReport(report); + return; + } + + // Array bound check succeeded. From this point forward the array bound + // should always succeed. + assert(StInBound); + C.addTransition(StInBound); +} diff --git a/lib/Checker/AttrNonNullChecker.cpp b/lib/Checker/AttrNonNullChecker.cpp new file mode 100644 index 000000000000..83dc13e92b63 --- /dev/null +++ b/lib/Checker/AttrNonNullChecker.cpp @@ -0,0 +1,112 @@ +//===--- AttrNonNullChecker.h - Undefined arguments checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines AttrNonNullChecker, a builtin check in GRExprEngine that +// performs checks for arguments declared to have nonnull attribute. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class AttrNonNullChecker + : public CheckerVisitor { + BugType *BT; +public: + AttrNonNullChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; +} // end anonymous namespace + +void clang::RegisterAttrNonNullChecker(GRExprEngine &Eng) { + Eng.registerCheck(new AttrNonNullChecker()); +} + +void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + + // Check if the callee has a 'nonnull' attribute. + SVal X = state->getSVal(CE->getCallee()); + + const FunctionDecl* FD = X.getAsFunctionDecl(); + if (!FD) + return; + + const NonNullAttr* Att = FD->getAttr(); + if (!Att) + return; + + // Iterate through the arguments of CE and check them for null. + unsigned idx = 0; + + for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; + ++I, ++idx) { + + if (!Att->isNonNull(idx)) + continue; + + const SVal &V = state->getSVal(*I); + const DefinedSVal *DV = dyn_cast(&V); + + if (!DV) + continue; + + ConstraintManager &CM = C.getConstraintManager(); + const GRState *stateNotNull, *stateNull; + llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, *DV); + + if (stateNull && !stateNotNull) { + // Generate an error node. Check for a null node in case + // we cache out. + if (ExplodedNode *errorNode = C.GenerateSink(stateNull)) { + + // Lazily allocate the BugType object if it hasn't already been + // created. Ownership is transferred to the BugReporter object once + // the BugReport is passed to 'EmitWarning'. + if (!BT) + BT = new BugType("Argument with 'nonnull' attribute passed null", + "API"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, + "Null pointer passed as an argument to a " + "'nonnull' parameter", errorNode); + + // Highlight the range of the argument that was null. + const Expr *arg = *I; + R->addRange(arg->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, arg); + + // Emit the bug report. + C.EmitReport(R); + } + + // Always return. Either we cached out or we just emitted an error. + return; + } + + // If a pointer value passed the check we should assume that it is + // indeed not null from this point forward. + assert(stateNotNull); + state = stateNotNull; + } + + // If we reach here all of the arguments passed the nonnull check. + // If 'state' has been updated generated a new node. + C.addTransition(state); +} diff --git a/lib/Checker/BasicConstraintManager.cpp b/lib/Checker/BasicConstraintManager.cpp new file mode 100644 index 000000000000..e89546ecb018 --- /dev/null +++ b/lib/Checker/BasicConstraintManager.cpp @@ -0,0 +1,317 @@ +//== BasicConstraintManager.cpp - Manage basic constraints.------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicConstraintManager, a class that tracks simple +// equality and inequality constraints on symbolic values of GRState. +// +//===----------------------------------------------------------------------===// + +#include "SimpleConstraintManager.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Checker/PathSensitive/GRStateTrait.h" +#include "clang/Checker/PathSensitive/GRTransferFuncs.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + + +namespace { class ConstNotEq {}; } +namespace { class ConstEq {}; } + +typedef llvm::ImmutableMap ConstNotEqTy; +typedef llvm::ImmutableMap ConstEqTy; + +static int ConstEqIndex = 0; +static int ConstNotEqIndex = 0; + +namespace clang { +template<> +struct GRStateTrait : public GRStatePartialTrait { + static inline void* GDMIndex() { return &ConstNotEqIndex; } +}; + +template<> +struct GRStateTrait : public GRStatePartialTrait { + static inline void* GDMIndex() { return &ConstEqIndex; } +}; +} + +namespace { +// BasicConstraintManager only tracks equality and inequality constraints of +// constants and integer variables. +class BasicConstraintManager + : public SimpleConstraintManager { + GRState::IntSetTy::Factory ISetFactory; +public: + BasicConstraintManager(GRStateManager &statemgr, GRSubEngine &subengine) + : SimpleConstraintManager(subengine), + ISetFactory(statemgr.getAllocator()) {} + + const GRState* AssumeSymNE(const GRState* state, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AssumeSymEQ(const GRState* state, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AssumeSymLT(const GRState* state, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AssumeSymGT(const GRState* state, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AssumeSymGE(const GRState* state, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AssumeSymLE(const GRState* state, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AddEQ(const GRState* state, SymbolRef sym, const llvm::APSInt& V); + + const GRState* AddNE(const GRState* state, SymbolRef sym, const llvm::APSInt& V); + + const llvm::APSInt* getSymVal(const GRState* state, SymbolRef sym) const; + bool isNotEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) + const; + bool isEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) + const; + + const GRState* RemoveDeadBindings(const GRState* state, SymbolReaper& SymReaper); + + void print(const GRState* state, llvm::raw_ostream& Out, + const char* nl, const char *sep); +}; + +} // end anonymous namespace + +ConstraintManager* clang::CreateBasicConstraintManager(GRStateManager& statemgr, + GRSubEngine &subengine) { + return new BasicConstraintManager(statemgr, subengine); +} + +const GRState* +BasicConstraintManager::AssumeSymNE(const GRState *state, SymbolRef sym, + const llvm::APSInt& V) { + // First, determine if sym == X, where X != V. + if (const llvm::APSInt* X = getSymVal(state, sym)) { + bool isFeasible = (*X != V); + return isFeasible ? state : NULL; + } + + // Second, determine if sym != V. + if (isNotEqual(state, sym, V)) + return state; + + // If we reach here, sym is not a constant and we don't know if it is != V. + // Make that assumption. + return AddNE(state, sym, V); +} + +const GRState *BasicConstraintManager::AssumeSymEQ(const GRState *state, + SymbolRef sym, + const llvm::APSInt &V) { + // First, determine if sym == X, where X != V. + if (const llvm::APSInt* X = getSymVal(state, sym)) { + bool isFeasible = *X == V; + return isFeasible ? state : NULL; + } + + // Second, determine if sym != V. + if (isNotEqual(state, sym, V)) + return NULL; + + // If we reach here, sym is not a constant and we don't know if it is == V. + // Make that assumption. + return AddEQ(state, sym, V); +} + +// These logic will be handled in another ConstraintManager. +const GRState *BasicConstraintManager::AssumeSymLT(const GRState *state, + SymbolRef sym, + const llvm::APSInt& V) { + // Is 'V' the smallest possible value? + if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { + // sym cannot be any value less than 'V'. This path is infeasible. + return NULL; + } + + // FIXME: For now have assuming x < y be the same as assuming sym != V; + return AssumeSymNE(state, sym, V); +} + +const GRState *BasicConstraintManager::AssumeSymGT(const GRState *state, + SymbolRef sym, + const llvm::APSInt& V) { + + // Is 'V' the largest possible value? + if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { + // sym cannot be any value greater than 'V'. This path is infeasible. + return NULL; + } + + // FIXME: For now have assuming x > y be the same as assuming sym != V; + return AssumeSymNE(state, sym, V); +} + +const GRState *BasicConstraintManager::AssumeSymGE(const GRState *state, + SymbolRef sym, + const llvm::APSInt &V) { + + // Reject a path if the value of sym is a constant X and !(X >= V). + if (const llvm::APSInt *X = getSymVal(state, sym)) { + bool isFeasible = *X >= V; + return isFeasible ? state : NULL; + } + + // Sym is not a constant, but it is worth looking to see if V is the + // maximum integer value. + if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { + // If we know that sym != V, then this condition is infeasible since + // there is no other value greater than V. + bool isFeasible = !isNotEqual(state, sym, V); + + // If the path is still feasible then as a consequence we know that + // 'sym == V' because we cannot have 'sym > V' (no larger values). + // Add this constraint. + return isFeasible ? AddEQ(state, sym, V) : NULL; + } + + return state; +} + +const GRState* +BasicConstraintManager::AssumeSymLE(const GRState* state, SymbolRef sym, + const llvm::APSInt& V) { + + // Reject a path if the value of sym is a constant X and !(X <= V). + if (const llvm::APSInt* X = getSymVal(state, sym)) { + bool isFeasible = *X <= V; + return isFeasible ? state : NULL; + } + + // Sym is not a constant, but it is worth looking to see if V is the + // minimum integer value. + if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { + // If we know that sym != V, then this condition is infeasible since + // there is no other value less than V. + bool isFeasible = !isNotEqual(state, sym, V); + + // If the path is still feasible then as a consequence we know that + // 'sym == V' because we cannot have 'sym < V' (no smaller values). + // Add this constraint. + return isFeasible ? AddEQ(state, sym, V) : NULL; + } + + return state; +} + +const GRState* BasicConstraintManager::AddEQ(const GRState* state, SymbolRef sym, + const llvm::APSInt& V) { + // Create a new state with the old binding replaced. + return state->set(sym, &V); +} + +const GRState* BasicConstraintManager::AddNE(const GRState* state, SymbolRef sym, + const llvm::APSInt& V) { + + // First, retrieve the NE-set associated with the given symbol. + ConstNotEqTy::data_type* T = state->get(sym); + GRState::IntSetTy S = T ? *T : ISetFactory.GetEmptySet(); + + // Now add V to the NE set. + S = ISetFactory.Add(S, &V); + + // Create a new state with the old binding replaced. + return state->set(sym, S); +} + +const llvm::APSInt* BasicConstraintManager::getSymVal(const GRState* state, + SymbolRef sym) const { + const ConstEqTy::data_type* T = state->get(sym); + return T ? *T : NULL; +} + +bool BasicConstraintManager::isNotEqual(const GRState* state, SymbolRef sym, + const llvm::APSInt& V) const { + + // Retrieve the NE-set associated with the given symbol. + const ConstNotEqTy::data_type* T = state->get(sym); + + // See if V is present in the NE-set. + return T ? T->contains(&V) : false; +} + +bool BasicConstraintManager::isEqual(const GRState* state, SymbolRef sym, + const llvm::APSInt& V) const { + // Retrieve the EQ-set associated with the given symbol. + const ConstEqTy::data_type* T = state->get(sym); + // See if V is present in the EQ-set. + return T ? **T == V : false; +} + +/// Scan all symbols referenced by the constraints. If the symbol is not alive +/// as marked in LSymbols, mark it as dead in DSymbols. +const GRState* +BasicConstraintManager::RemoveDeadBindings(const GRState* state, + SymbolReaper& SymReaper) { + + ConstEqTy CE = state->get(); + ConstEqTy::Factory& CEFactory = state->get_context(); + + for (ConstEqTy::iterator I = CE.begin(), E = CE.end(); I!=E; ++I) { + SymbolRef sym = I.getKey(); + if (SymReaper.maybeDead(sym)) CE = CEFactory.Remove(CE, sym); + } + state = state->set(CE); + + ConstNotEqTy CNE = state->get(); + ConstNotEqTy::Factory& CNEFactory = state->get_context(); + + for (ConstNotEqTy::iterator I = CNE.begin(), E = CNE.end(); I != E; ++I) { + SymbolRef sym = I.getKey(); + if (SymReaper.maybeDead(sym)) CNE = CNEFactory.Remove(CNE, sym); + } + + return state->set(CNE); +} + +void BasicConstraintManager::print(const GRState* state, llvm::raw_ostream& Out, + const char* nl, const char *sep) { + // Print equality constraints. + + ConstEqTy CE = state->get(); + + if (!CE.isEmpty()) { + Out << nl << sep << "'==' constraints:"; + for (ConstEqTy::iterator I = CE.begin(), E = CE.end(); I!=E; ++I) + Out << nl << " $" << I.getKey() << " : " << *I.getData(); + } + + // Print != constraints. + + ConstNotEqTy CNE = state->get(); + + if (!CNE.isEmpty()) { + Out << nl << sep << "'!=' constraints:"; + + for (ConstNotEqTy::iterator I = CNE.begin(), EI = CNE.end(); I!=EI; ++I) { + Out << nl << " $" << I.getKey() << " : "; + bool isFirst = true; + + GRState::IntSetTy::iterator J = I.getData().begin(), + EJ = I.getData().end(); + + for ( ; J != EJ; ++J) { + if (isFirst) isFirst = false; + else Out << ", "; + + Out << (*J)->getSExtValue(); // Hack: should print to raw_ostream. + } + } + } +} diff --git a/lib/Checker/BasicObjCFoundationChecks.cpp b/lib/Checker/BasicObjCFoundationChecks.cpp new file mode 100644 index 000000000000..d6c09a2e04a6 --- /dev/null +++ b/lib/Checker/BasicObjCFoundationChecks.cpp @@ -0,0 +1,559 @@ +//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicObjCFoundationChecks, a class that encapsulates +// a set of simple checks to run on Objective-C code using Apple's Foundation +// classes. +// +//===----------------------------------------------------------------------===// + +#include "BasicObjCFoundationChecks.h" + +#include "clang/Checker/PathSensitive/ExplodedGraph.h" +#include "clang/Checker/PathSensitive/GRSimpleAPICheck.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/MemRegion.h" +#include "clang/Checker/BugReporter/PathDiagnostic.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/Checkers/LocalCheckers.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ASTContext.h" + +using namespace clang; + +static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) { + const Expr* Receiver = ME->getReceiver(); + + if (!Receiver) + return NULL; + + if (const ObjCObjectPointerType *PT = + Receiver->getType()->getAs()) + return PT->getInterfaceType(); + + return NULL; +} + +static const char* GetReceiverNameType(const ObjCMessageExpr* ME) { + if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME)) + return ReceiverType->getDecl()->getIdentifier()->getNameStart(); + return NULL; +} + +namespace { + +class APIMisuse : public BugType { +public: + APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} +}; + +class BasicObjCFoundationChecks : public GRSimpleAPICheck { + APIMisuse *BT; + BugReporter& BR; + ASTContext &Ctx; + + bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix); + bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME); + + void Warn(ExplodedNode* N, const Expr* E, const std::string& s); + void WarnNilArg(ExplodedNode* N, const Expr* E); + + bool CheckNilArg(ExplodedNode* N, unsigned Arg); + +public: + BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br) + : BT(0), BR(br), Ctx(ctx) {} + + bool Audit(ExplodedNode* N, GRStateManager&); + +private: + void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Argument to '" << GetReceiverNameType(ME) << "' method '" + << ME->getSelector().getAsString() << "' cannot be nil."; + + // Lazily create the BugType object for NilArg. This will be owned + // by the BugReporter object 'BR' once we call BR.EmitWarning. + if (!BT) BT = new APIMisuse("nil argument"); + + RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); + R->addRange(ME->getArg(Arg)->getSourceRange()); + BR.EmitReport(R); + } +}; + +} // end anonymous namespace + + +GRSimpleAPICheck* +clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) { + return new BasicObjCFoundationChecks(Ctx, BR); +} + + + +bool BasicObjCFoundationChecks::Audit(ExplodedNode* N, + GRStateManager&) { + + const ObjCMessageExpr* ME = + cast(cast(N->getLocation()).getStmt()); + + const ObjCInterfaceType *ReceiverType = GetReceiverType(ME); + + if (!ReceiverType) + return false; + + if (isNSString(ReceiverType, + ReceiverType->getDecl()->getIdentifier()->getName())) + return AuditNSString(N, ME); + + return false; +} + +static inline bool isNil(SVal X) { + return isa(X); +} + +//===----------------------------------------------------------------------===// +// Error reporting. +//===----------------------------------------------------------------------===// + +bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) { + const ObjCMessageExpr* ME = + cast(cast(N->getLocation()).getStmt()); + + const Expr * E = ME->getArg(Arg); + + if (isNil(N->getState()->getSVal(E))) { + WarnNilArg(N, ME, Arg); + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// NSString checking. +//===----------------------------------------------------------------------===// + +bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T, + llvm::StringRef ClassName) { + return ClassName == "NSString" || ClassName == "NSMutableString"; +} + +bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N, + const ObjCMessageExpr* ME) { + + Selector S = ME->getSelector(); + + if (S.isUnarySelector()) + return false; + + // FIXME: This is going to be really slow doing these checks with + // lexical comparisons. + + std::string NameStr = S.getAsString(); + llvm::StringRef Name(NameStr); + assert(!Name.empty()); + + // FIXME: Checking for initWithFormat: will not work in most cases + // yet because [NSString alloc] returns id, not NSString*. We will + // need support for tracking expected-type information in the analyzer + // to find these errors. + if (Name == "caseInsensitiveCompare:" || + Name == "compare:" || + Name == "compare:options:" || + Name == "compare:options:range:" || + Name == "compare:options:range:locale:" || + Name == "componentsSeparatedByCharactersInSet:" || + Name == "initWithFormat:") + return CheckNilArg(N, 0); + + return false; +} + +//===----------------------------------------------------------------------===// +// Error reporting. +//===----------------------------------------------------------------------===// + +namespace { + +class AuditCFNumberCreate : public GRSimpleAPICheck { + APIMisuse* BT; + + // FIXME: Either this should be refactored into GRSimpleAPICheck, or + // it should always be passed with a call to Audit. The latter + // approach makes this class more stateless. + ASTContext& Ctx; + IdentifierInfo* II; + BugReporter& BR; + +public: + AuditCFNumberCreate(ASTContext& ctx, BugReporter& br) + : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){} + + ~AuditCFNumberCreate() {} + + bool Audit(ExplodedNode* N, GRStateManager&); + +private: + void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N, + uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); +}; +} // end anonymous namespace + +enum CFNumberType { + kCFNumberSInt8Type = 1, + kCFNumberSInt16Type = 2, + kCFNumberSInt32Type = 3, + kCFNumberSInt64Type = 4, + kCFNumberFloat32Type = 5, + kCFNumberFloat64Type = 6, + kCFNumberCharType = 7, + kCFNumberShortType = 8, + kCFNumberIntType = 9, + kCFNumberLongType = 10, + kCFNumberLongLongType = 11, + kCFNumberFloatType = 12, + kCFNumberDoubleType = 13, + kCFNumberCFIndexType = 14, + kCFNumberNSIntegerType = 15, + kCFNumberCGFloatType = 16 +}; + +namespace { + template + class Optional { + bool IsKnown; + T Val; + public: + Optional() : IsKnown(false), Val(0) {} + Optional(const T& val) : IsKnown(true), Val(val) {} + + bool isKnown() const { return IsKnown; } + + const T& getValue() const { + assert (isKnown()); + return Val; + } + + operator const T&() const { + return getValue(); + } + }; +} + +static Optional GetCFNumberSize(ASTContext& Ctx, uint64_t i) { + static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; + + if (i < kCFNumberCharType) + return FixedSize[i-1]; + + QualType T; + + switch (i) { + case kCFNumberCharType: T = Ctx.CharTy; break; + case kCFNumberShortType: T = Ctx.ShortTy; break; + case kCFNumberIntType: T = Ctx.IntTy; break; + case kCFNumberLongType: T = Ctx.LongTy; break; + case kCFNumberLongLongType: T = Ctx.LongLongTy; break; + case kCFNumberFloatType: T = Ctx.FloatTy; break; + case kCFNumberDoubleType: T = Ctx.DoubleTy; break; + case kCFNumberCFIndexType: + case kCFNumberNSIntegerType: + case kCFNumberCGFloatType: + // FIXME: We need a way to map from names to Type*. + default: + return Optional(); + } + + return Ctx.getTypeSize(T); +} + +#if 0 +static const char* GetCFNumberTypeStr(uint64_t i) { + static const char* Names[] = { + "kCFNumberSInt8Type", + "kCFNumberSInt16Type", + "kCFNumberSInt32Type", + "kCFNumberSInt64Type", + "kCFNumberFloat32Type", + "kCFNumberFloat64Type", + "kCFNumberCharType", + "kCFNumberShortType", + "kCFNumberIntType", + "kCFNumberLongType", + "kCFNumberLongLongType", + "kCFNumberFloatType", + "kCFNumberDoubleType", + "kCFNumberCFIndexType", + "kCFNumberNSIntegerType", + "kCFNumberCGFloatType" + }; + + return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; +} +#endif + +bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){ + const CallExpr* CE = + cast(cast(N->getLocation()).getStmt()); + const Expr* Callee = CE->getCallee(); + SVal CallV = N->getState()->getSVal(Callee); + const FunctionDecl* FD = CallV.getAsFunctionDecl(); + + if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3) + return false; + + // Get the value of the "theType" argument. + SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1)); + + // FIXME: We really should allow ranges of valid theType values, and + // bifurcate the state appropriately. + nonloc::ConcreteInt* V = dyn_cast(&TheTypeVal); + + if (!V) + return false; + + uint64_t NumberKind = V->getValue().getLimitedValue(); + Optional TargetSize = GetCFNumberSize(Ctx, NumberKind); + + // FIXME: In some cases we can emit an error. + if (!TargetSize.isKnown()) + return false; + + // Look at the value of the integer being passed by reference. Essentially + // we want to catch cases where the value passed in is not equal to the + // size of the type being created. + SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2)); + + // FIXME: Eventually we should handle arbitrary locations. We can do this + // by having an enhanced memory model that does low-level typing. + loc::MemRegionVal* LV = dyn_cast(&TheValueExpr); + + if (!LV) + return false; + + const TypedRegion* R = dyn_cast(LV->StripCasts()); + + if (!R) + return false; + + QualType T = Ctx.getCanonicalType(R->getValueType(Ctx)); + + // FIXME: If the pointee isn't an integer type, should we flag a warning? + // People can do weird stuff with pointers. + + if (!T->isIntegerType()) + return false; + + uint64_t SourceSize = Ctx.getTypeSize(T); + + // CHECK: is SourceSize == TargetSize + + if (SourceSize == TargetSize) + return false; + + AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind); + + // FIXME: We can actually create an abstract "CFNumber" object that has + // the bits initialized to the provided values. + return SourceSize < TargetSize; +} + +void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex, + ExplodedNode *N, + uint64_t SourceSize, uint64_t TargetSize, + uint64_t NumberKind) { + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << (SourceSize == 8 ? "An " : "A ") + << SourceSize << " bit integer is used to initialize a CFNumber " + "object that represents " + << (TargetSize == 8 ? "an " : "a ") + << TargetSize << " bit integer. "; + + if (SourceSize < TargetSize) + os << (TargetSize - SourceSize) + << " bits of the CFNumber value will be garbage." ; + else + os << (SourceSize - TargetSize) + << " bits of the input integer will be lost."; + + // Lazily create the BugType object. This will be owned + // by the BugReporter object 'BR' once we call BR.EmitWarning. + if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate"); + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + report->addRange(Ex->getSourceRange()); + BR.EmitReport(report); +} + +GRSimpleAPICheck* +clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) { + return new AuditCFNumberCreate(Ctx, BR); +} + +//===----------------------------------------------------------------------===// +// CFRetain/CFRelease auditing for null arguments. +//===----------------------------------------------------------------------===// + +namespace { +class AuditCFRetainRelease : public GRSimpleAPICheck { + APIMisuse *BT; + + // FIXME: Either this should be refactored into GRSimpleAPICheck, or + // it should always be passed with a call to Audit. The latter + // approach makes this class more stateless. + ASTContext& Ctx; + IdentifierInfo *Retain, *Release; + BugReporter& BR; + +public: + AuditCFRetainRelease(ASTContext& ctx, BugReporter& br) + : BT(0), Ctx(ctx), + Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")), + BR(br){} + + ~AuditCFRetainRelease() {} + + bool Audit(ExplodedNode* N, GRStateManager&); +}; +} // end anonymous namespace + + +bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) { + const CallExpr* CE = cast(cast(N->getLocation()).getStmt()); + + // If the CallExpr doesn't have exactly 1 argument just give up checking. + if (CE->getNumArgs() != 1) + return false; + + // Check if we called CFRetain/CFRelease. + const GRState* state = N->getState(); + SVal X = state->getSVal(CE->getCallee()); + const FunctionDecl* FD = X.getAsFunctionDecl(); + + if (!FD) + return false; + + const IdentifierInfo *FuncII = FD->getIdentifier(); + if (!(FuncII == Retain || FuncII == Release)) + return false; + + // Finally, check if the argument is NULL. + // FIXME: We should be able to bifurcate the state here, as a successful + // check will result in the value not being NULL afterwards. + // FIXME: Need a way to register vistors for the BugReporter. Would like + // to benefit from the same diagnostics that regular null dereference + // reporting has. + if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) { + if (!BT) + BT = new APIMisuse("null passed to CFRetain/CFRelease"); + + const char *description = (FuncII == Retain) + ? "Null pointer argument in call to CFRetain" + : "Null pointer argument in call to CFRelease"; + + RangedBugReport *report = new RangedBugReport(*BT, description, N); + report->addRange(CE->getArg(0)->getSourceRange()); + BR.EmitReport(report); + return true; + } + + return false; +} + + +GRSimpleAPICheck* +clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) { + return new AuditCFRetainRelease(Ctx, BR); +} + +//===----------------------------------------------------------------------===// +// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. +//===----------------------------------------------------------------------===// + +namespace { +class ClassReleaseChecker : + public CheckerVisitor { + Selector releaseS; + Selector retainS; + Selector autoreleaseS; + Selector drainS; + BugType *BT; +public: + ClassReleaseChecker(ASTContext &Ctx) + : releaseS(GetNullarySelector("release", Ctx)), + retainS(GetNullarySelector("retain", Ctx)), + autoreleaseS(GetNullarySelector("autorelease", Ctx)), + drainS(GetNullarySelector("drain", Ctx)), + BT(0) {} + + static void *getTag() { static int x = 0; return &x; } + + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); +}; +} + +void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + const IdentifierInfo *ClsName = ME->getClassName(); + if (!ClsName) + return; + + Selector S = ME->getSelector(); + if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) + return; + + if (!BT) + BT = new APIMisuse("message incorrectly sent to class instead of class " + "instance"); + + ExplodedNode *N = C.GenerateNode(); + + if (!N) + return; + + llvm::SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << "The '" << S.getAsString() << "' message should be sent to instances " + "of class '" << ClsName->getName() + << "' and not the class directly"; + + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + report->addRange(ME->getSourceRange()); + C.EmitReport(report); +} + +//===----------------------------------------------------------------------===// +// Check registration. +//===----------------------------------------------------------------------===// + +void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) { + ASTContext& Ctx = Eng.getContext(); + BugReporter &BR = Eng.getBugReporter(); + + Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR), + Stmt::ObjCMessageExprClass); + Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass); + Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass); + + RegisterNSErrorChecks(BR, Eng, D); + RegisterNSAutoreleasePoolChecks(Eng); + Eng.registerCheck(new ClassReleaseChecker(Ctx)); +} diff --git a/lib/Checker/BasicObjCFoundationChecks.h b/lib/Checker/BasicObjCFoundationChecks.h new file mode 100644 index 000000000000..679c6dc1df2d --- /dev/null +++ b/lib/Checker/BasicObjCFoundationChecks.h @@ -0,0 +1,41 @@ +//== BasicObjCFoundationChecks.h - Simple Apple-Foundation checks -*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicObjCFoundationChecks, a class that encapsulates +// a set of simple checks to run on Objective-C code using Apple's Foundation +// classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_BASICOBJCFOUNDATIONCHECKS +#define LLVM_CLANG_ANALYSIS_BASICOBJCFOUNDATIONCHECKS + +namespace clang { + +class ASTContext; +class BugReporter; +class Decl; +class GRExprEngine; +class GRSimpleAPICheck; + +GRSimpleAPICheck *CreateBasicObjCFoundationChecks(ASTContext& Ctx, + BugReporter& BR); + +GRSimpleAPICheck *CreateAuditCFNumberCreate(ASTContext& Ctx, + BugReporter& BR); + +GRSimpleAPICheck *CreateAuditCFRetainRelease(ASTContext& Ctx, + BugReporter& BR); + +void RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng, const Decl &D); +void RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng); + +} // end clang namespace + +#endif diff --git a/lib/Checker/BasicStore.cpp b/lib/Checker/BasicStore.cpp new file mode 100644 index 000000000000..6ef29429f681 --- /dev/null +++ b/lib/Checker/BasicStore.cpp @@ -0,0 +1,494 @@ +//== BasicStore.cpp - Basic map from Locations to Values --------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defined the BasicStore and BasicStoreManager classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ExprObjC.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "llvm/ADT/ImmutableMap.h" + +using namespace clang; + +typedef llvm::ImmutableMap BindingsTy; + +namespace { + +class BasicStoreSubRegionMap : public SubRegionMap { +public: + BasicStoreSubRegionMap() {} + + bool iterSubRegions(const MemRegion* R, Visitor& V) const { + return true; // Do nothing. No subregions. + } +}; + +class BasicStoreManager : public StoreManager { + BindingsTy::Factory VBFactory; +public: + BasicStoreManager(GRStateManager& mgr) + : StoreManager(mgr), VBFactory(mgr.getAllocator()) {} + + ~BasicStoreManager() {} + + SubRegionMap *getSubRegionMap(Store store) { + return new BasicStoreSubRegionMap(); + } + + SVal Retrieve(Store store, Loc loc, QualType T = QualType()); + + Store InvalidateRegion(Store store, const MemRegion *R, const Expr *E, + unsigned Count, InvalidatedSymbols *IS); + + Store scanForIvars(Stmt *B, const Decl* SelfDecl, + const MemRegion *SelfRegion, Store St); + + Store Bind(Store St, Loc loc, SVal V); + Store Remove(Store St, Loc loc); + Store getInitialStore(const LocationContext *InitLoc); + + // FIXME: Investigate what is using this. This method should be removed. + virtual Loc getLoc(const VarDecl* VD, const LocationContext *LC) { + return ValMgr.makeLoc(MRMgr.getVarRegion(VD, LC)); + } + + Store BindCompoundLiteral(Store store, const CompoundLiteralExpr*, + const LocationContext*, SVal val) { + return store; + } + + /// ArrayToPointer - Used by GRExprEngine::VistCast to handle implicit + /// conversions between arrays and pointers. + SVal ArrayToPointer(Loc Array) { return Array; } + + /// RemoveDeadBindings - Scans a BasicStore of 'state' for dead values. + /// It updatees the GRState object in place with the values removed. + Store RemoveDeadBindings(Store store, Stmt* Loc, SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots); + + void iterBindings(Store store, BindingsHandler& f); + + Store BindDecl(Store store, const VarRegion *VR, SVal InitVal) { + return BindDeclInternal(store, VR, &InitVal); + } + + Store BindDeclWithNoInit(Store store, const VarRegion *VR) { + return BindDeclInternal(store, VR, 0); + } + + Store BindDeclInternal(Store store, const VarRegion *VR, SVal *InitVal); + + static inline BindingsTy GetBindings(Store store) { + return BindingsTy(static_cast(store)); + } + + void print(Store store, llvm::raw_ostream& Out, const char* nl, + const char *sep); + +private: + ASTContext& getContext() { return StateMgr.getContext(); } +}; + +} // end anonymous namespace + + +StoreManager* clang::CreateBasicStoreManager(GRStateManager& StMgr) { + return new BasicStoreManager(StMgr); +} + +static bool isHigherOrderRawPtr(QualType T, ASTContext &C) { + bool foundPointer = false; + while (1) { + const PointerType *PT = T->getAs(); + if (!PT) { + if (!foundPointer) + return false; + + // intptr_t* or intptr_t**, etc? + if (T->isIntegerType() && C.getTypeSize(T) == C.getTypeSize(C.VoidPtrTy)) + return true; + + QualType X = C.getCanonicalType(T).getUnqualifiedType(); + return X == C.VoidTy; + } + + foundPointer = true; + T = PT->getPointeeType(); + } +} + +SVal BasicStoreManager::Retrieve(Store store, Loc loc, QualType T) { + if (isa(loc)) + return UnknownVal(); + + assert(!isa(loc)); + + switch (loc.getSubKind()) { + + case loc::MemRegionKind: { + const MemRegion* R = cast(loc).getRegion(); + + if (!(isa(R) || isa(R))) + return UnknownVal(); + + BindingsTy B = GetBindings(store); + BindingsTy::data_type *Val = B.lookup(R); + + if (!Val) + break; + + return CastRetrievedVal(*Val, cast(R), T); + } + + case loc::ConcreteIntKind: + // Some clients may call GetSVal with such an option simply because + // they are doing a quick scan through their Locs (potentially to + // invalidate their bindings). Just return Undefined. + return UndefinedVal(); + + default: + assert (false && "Invalid Loc."); + break; + } + + return UnknownVal(); +} + +Store BasicStoreManager::Bind(Store store, Loc loc, SVal V) { + if (isa(loc)) + return store; + + const MemRegion* R = cast(loc).getRegion(); + ASTContext &C = StateMgr.getContext(); + + // Special case: handle store of pointer values (Loc) to pointers via + // a cast to intXX_t*, void*, etc. This is needed to handle + // OSCompareAndSwap32Barrier/OSCompareAndSwap64Barrier. + if (isa(V) || isa(V)) + if (const ElementRegion *ER = dyn_cast(R)) { + // FIXME: Should check for index 0. + QualType T = ER->getLocationType(C); + + if (isHigherOrderRawPtr(T, C)) + R = ER->getSuperRegion(); + } + + if (!(isa(R) || isa(R))) + return store; + + const TypedRegion *TyR = cast(R); + + // Do not bind to arrays. We need to explicitly check for this so that + // we do not encounter any weirdness of trying to load/store from arrays. + if (TyR->isBoundable() && TyR->getValueType(C)->isArrayType()) + return store; + + if (nonloc::LocAsInteger *X = dyn_cast(&V)) { + // Only convert 'V' to a location iff the underlying region type + // is a location as well. + // FIXME: We are allowing a store of an arbitrary location to + // a pointer. We may wish to flag a type error here if the types + // are incompatible. This may also cause lots of breakage + // elsewhere. Food for thought. + if (TyR->isBoundable() && Loc::IsLocType(TyR->getValueType(C))) + V = X->getLoc(); + } + + BindingsTy B = GetBindings(store); + return V.isUnknown() + ? VBFactory.Remove(B, R).getRoot() + : VBFactory.Add(B, R, V).getRoot(); +} + +Store BasicStoreManager::Remove(Store store, Loc loc) { + switch (loc.getSubKind()) { + case loc::MemRegionKind: { + const MemRegion* R = cast(loc).getRegion(); + + if (!(isa(R) || isa(R))) + return store; + + return VBFactory.Remove(GetBindings(store), R).getRoot(); + } + default: + assert ("Remove for given Loc type not yet implemented."); + return store; + } +} + +Store BasicStoreManager::RemoveDeadBindings(Store store, Stmt* Loc, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots) +{ + BindingsTy B = GetBindings(store); + typedef SVal::symbol_iterator symbol_iterator; + + // Iterate over the variable bindings. + for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { + if (const VarRegion *VR = dyn_cast(I.getKey())) { + if (SymReaper.isLive(Loc, VR)) + RegionRoots.push_back(VR); + else + continue; + } + else if (isa(I.getKey())) { + RegionRoots.push_back(I.getKey()); + } + else + continue; + + // Mark the bindings in the data as live. + SVal X = I.getData(); + for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) + SymReaper.markLive(*SI); + } + + // Scan for live variables and live symbols. + llvm::SmallPtrSet Marked; + + while (!RegionRoots.empty()) { + const MemRegion* MR = RegionRoots.back(); + RegionRoots.pop_back(); + + while (MR) { + if (const SymbolicRegion* SymR = dyn_cast(MR)) { + SymReaper.markLive(SymR->getSymbol()); + break; + } + else if (isa(MR) || isa(MR)) { + if (Marked.count(MR)) + break; + + Marked.insert(MR); + SVal X = Retrieve(store, loc::MemRegionVal(MR)); + + // FIXME: We need to handle symbols nested in region definitions. + for (symbol_iterator SI=X.symbol_begin(),SE=X.symbol_end();SI!=SE;++SI) + SymReaper.markLive(*SI); + + if (!isa(X)) + break; + + const loc::MemRegionVal& LVD = cast(X); + RegionRoots.push_back(LVD.getRegion()); + break; + } + else if (const SubRegion* R = dyn_cast(MR)) + MR = R->getSuperRegion(); + else + break; + } + } + + // Remove dead variable bindings. + for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { + const MemRegion* R = I.getKey(); + + if (!Marked.count(R)) { + store = Remove(store, ValMgr.makeLoc(R)); + SVal X = I.getData(); + + for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) + SymReaper.maybeDead(*SI); + } + } + + return store; +} + +Store BasicStoreManager::scanForIvars(Stmt *B, const Decl* SelfDecl, + const MemRegion *SelfRegion, Store St) { + for (Stmt::child_iterator CI=B->child_begin(), CE=B->child_end(); + CI != CE; ++CI) { + + if (!*CI) + continue; + + // Check if the statement is an ivar reference. We only + // care about self.ivar. + if (ObjCIvarRefExpr *IV = dyn_cast(*CI)) { + const Expr *Base = IV->getBase()->IgnoreParenCasts(); + if (const DeclRefExpr *DR = dyn_cast(Base)) { + if (DR->getDecl() == SelfDecl) { + const MemRegion *IVR = MRMgr.getObjCIvarRegion(IV->getDecl(), + SelfRegion); + SVal X = ValMgr.getRegionValueSymbolVal(IVR); + St = Bind(St, ValMgr.makeLoc(IVR), X); + } + } + } + else + St = scanForIvars(*CI, SelfDecl, SelfRegion, St); + } + + return St; +} + +Store BasicStoreManager::getInitialStore(const LocationContext *InitLoc) { + // The LiveVariables information already has a compilation of all VarDecls + // used in the function. Iterate through this set, and "symbolicate" + // any VarDecl whose value originally comes from outside the function. + typedef LiveVariables::AnalysisDataTy LVDataTy; + LVDataTy& D = InitLoc->getLiveVariables()->getAnalysisData(); + Store St = VBFactory.GetEmptyMap().getRoot(); + + for (LVDataTy::decl_iterator I=D.begin_decl(), E=D.end_decl(); I != E; ++I) { + NamedDecl* ND = const_cast(I->first); + + // Handle implicit parameters. + if (ImplicitParamDecl* PD = dyn_cast(ND)) { + const Decl& CD = *InitLoc->getDecl(); + if (const ObjCMethodDecl* MD = dyn_cast(&CD)) { + if (MD->getSelfDecl() == PD) { + // FIXME: Add type constraints (when they become available) to + // SelfRegion? (i.e., it implements MD->getClassInterface()). + const MemRegion *VR = MRMgr.getVarRegion(PD, InitLoc); + const MemRegion *SelfRegion = + ValMgr.getRegionValueSymbolVal(VR).getAsRegion(); + assert(SelfRegion); + St = Bind(St, ValMgr.makeLoc(VR), loc::MemRegionVal(SelfRegion)); + // Scan the method for ivar references. While this requires an + // entire AST scan, the cost should not be high in practice. + St = scanForIvars(MD->getBody(), PD, SelfRegion, St); + } + } + } + else if (VarDecl* VD = dyn_cast(ND)) { + // Only handle simple types that we can symbolicate. + if (!SymbolManager::canSymbolicate(VD->getType())) + continue; + + // Initialize globals and parameters to symbolic values. + // Initialize local variables to undefined. + const MemRegion *R = ValMgr.getRegionManager().getVarRegion(VD, InitLoc); + SVal X = UndefinedVal(); + if (R->hasGlobalsOrParametersStorage()) + X = ValMgr.getRegionValueSymbolVal(R); + + St = Bind(St, ValMgr.makeLoc(R), X); + } + } + return St; +} + +Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR, + SVal* InitVal) { + + BasicValueFactory& BasicVals = StateMgr.getBasicVals(); + const VarDecl *VD = VR->getDecl(); + + // BasicStore does not model arrays and structs. + if (VD->getType()->isArrayType() || VD->getType()->isStructureType()) + return store; + + if (VD->hasGlobalStorage()) { + // Handle variables with global storage: extern, static, PrivateExtern. + + // FIXME:: static variables may have an initializer, but the second time a + // function is called those values may not be current. Currently, a function + // will not be called more than once. + + // Static global variables should not be visited here. + assert(!(VD->getStorageClass() == VarDecl::Static && + VD->isFileVarDecl())); + + // Process static variables. + if (VD->getStorageClass() == VarDecl::Static) { + // C99: 6.7.8 Initialization + // If an object that has static storage duration is not initialized + // explicitly, then: + // —if it has pointer type, it is initialized to a null pointer; + // —if it has arithmetic type, it is initialized to (positive or + // unsigned) zero; + if (!InitVal) { + QualType T = VD->getType(); + if (Loc::IsLocType(T)) + store = Bind(store, loc::MemRegionVal(VR), + loc::ConcreteInt(BasicVals.getValue(0, T))); + else if (T->isIntegerType()) + store = Bind(store, loc::MemRegionVal(VR), + nonloc::ConcreteInt(BasicVals.getValue(0, T))); + else { + // assert(0 && "ignore other types of variables"); + } + } else { + store = Bind(store, loc::MemRegionVal(VR), *InitVal); + } + } + } else { + // Process local scalar variables. + QualType T = VD->getType(); + if (ValMgr.getSymbolManager().canSymbolicate(T)) { + SVal V = InitVal ? *InitVal : UndefinedVal(); + store = Bind(store, loc::MemRegionVal(VR), V); + } + } + + return store; +} + +void BasicStoreManager::print(Store store, llvm::raw_ostream& Out, + const char* nl, const char *sep) { + + BindingsTy B = GetBindings(store); + Out << "Variables:" << nl; + + bool isFirst = true; + + for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) { + if (isFirst) + isFirst = false; + else + Out << nl; + + Out << ' ' << I.getKey() << " : " << I.getData(); + } +} + + +void BasicStoreManager::iterBindings(Store store, BindingsHandler& f) { + BindingsTy B = GetBindings(store); + + for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) + f.HandleBinding(*this, store, I.getKey(), I.getData()); + +} + +StoreManager::BindingsHandler::~BindingsHandler() {} + +//===----------------------------------------------------------------------===// +// Binding invalidation. +//===----------------------------------------------------------------------===// + +Store BasicStoreManager::InvalidateRegion(Store store, + const MemRegion *R, + const Expr *E, + unsigned Count, + InvalidatedSymbols *IS) { + R = R->StripCasts(); + + if (!(isa(R) || isa(R))) + return store; + + if (IS) { + BindingsTy B = GetBindings(store); + if (BindingsTy::data_type *Val = B.lookup(R)) { + if (SymbolRef Sym = Val->getAsSymbol()) + IS->insert(Sym); + } + } + + QualType T = cast(R)->getValueType(R->getContext()); + SVal V = ValMgr.getConjuredSymbolVal(R, E, T, Count); + return Bind(store, loc::MemRegionVal(R), V); +} + diff --git a/lib/Checker/BasicValueFactory.cpp b/lib/Checker/BasicValueFactory.cpp new file mode 100644 index 000000000000..246beead1208 --- /dev/null +++ b/lib/Checker/BasicValueFactory.cpp @@ -0,0 +1,289 @@ +//=== BasicValueFactory.cpp - Basic values for Path Sens analysis --*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicValueFactory, a class that manages the lifetime +// of APSInt objects and symbolic constraints used by GRExprEngine +// and related classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/BasicValueFactory.h" + +using namespace clang; + +void CompoundValData::Profile(llvm::FoldingSetNodeID& ID, QualType T, + llvm::ImmutableList L) { + T.Profile(ID); + ID.AddPointer(L.getInternalPointer()); +} + +void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID, + const void *store,const TypedRegion *region) { + ID.AddPointer(store); + ID.AddPointer(region); +} + +typedef std::pair SValData; +typedef std::pair SValPair; + +namespace llvm { +template<> struct FoldingSetTrait { + static inline void Profile(const SValData& X, llvm::FoldingSetNodeID& ID) { + X.first.Profile(ID); + ID.AddPointer( (void*) X.second); + } +}; + +template<> struct FoldingSetTrait { + static inline void Profile(const SValPair& X, llvm::FoldingSetNodeID& ID) { + X.first.Profile(ID); + X.second.Profile(ID); + } +}; +} + +typedef llvm::FoldingSet > + PersistentSValsTy; + +typedef llvm::FoldingSet > + PersistentSValPairsTy; + +BasicValueFactory::~BasicValueFactory() { + // Note that the dstor for the contents of APSIntSet will never be called, + // so we iterate over the set and invoke the dstor for each APSInt. This + // frees an aux. memory allocated to represent very large constants. + for (APSIntSetTy::iterator I=APSIntSet.begin(), E=APSIntSet.end(); I!=E; ++I) + I->getValue().~APSInt(); + + delete (PersistentSValsTy*) PersistentSVals; + delete (PersistentSValPairsTy*) PersistentSValPairs; +} + +const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) { + llvm::FoldingSetNodeID ID; + void* InsertPos; + typedef llvm::FoldingSetNodeWrapper FoldNodeTy; + + X.Profile(ID); + FoldNodeTy* P = APSIntSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!P) { + P = (FoldNodeTy*) BPAlloc.Allocate(); + new (P) FoldNodeTy(X); + APSIntSet.InsertNode(P, InsertPos); + } + + return *P; +} + +const llvm::APSInt& BasicValueFactory::getValue(const llvm::APInt& X, + bool isUnsigned) { + llvm::APSInt V(X, isUnsigned); + return getValue(V); +} + +const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, unsigned BitWidth, + bool isUnsigned) { + llvm::APSInt V(BitWidth, isUnsigned); + V = X; + return getValue(V); +} + +const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, QualType T) { + + unsigned bits = Ctx.getTypeSize(T); + llvm::APSInt V(bits, T->isUnsignedIntegerType() || Loc::IsLocType(T)); + V = X; + return getValue(V); +} + +const CompoundValData* +BasicValueFactory::getCompoundValData(QualType T, + llvm::ImmutableList Vals) { + + llvm::FoldingSetNodeID ID; + CompoundValData::Profile(ID, T, Vals); + void* InsertPos; + + CompoundValData* D = CompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!D) { + D = (CompoundValData*) BPAlloc.Allocate(); + new (D) CompoundValData(T, Vals); + CompoundValDataSet.InsertNode(D, InsertPos); + } + + return D; +} + +const LazyCompoundValData* +BasicValueFactory::getLazyCompoundValData(const void *store, + const TypedRegion *region) { + llvm::FoldingSetNodeID ID; + LazyCompoundValData::Profile(ID, store, region); + void* InsertPos; + + LazyCompoundValData *D = + LazyCompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!D) { + D = (LazyCompoundValData*) BPAlloc.Allocate(); + new (D) LazyCompoundValData(store, region); + LazyCompoundValDataSet.InsertNode(D, InsertPos); + } + + return D; +} + +const llvm::APSInt* +BasicValueFactory::EvaluateAPSInt(BinaryOperator::Opcode Op, + const llvm::APSInt& V1, const llvm::APSInt& V2) { + + switch (Op) { + default: + assert (false && "Invalid Opcode."); + + case BinaryOperator::Mul: + return &getValue( V1 * V2 ); + + case BinaryOperator::Div: + return &getValue( V1 / V2 ); + + case BinaryOperator::Rem: + return &getValue( V1 % V2 ); + + case BinaryOperator::Add: + return &getValue( V1 + V2 ); + + case BinaryOperator::Sub: + return &getValue( V1 - V2 ); + + case BinaryOperator::Shl: { + + // FIXME: This logic should probably go higher up, where we can + // test these conditions symbolically. + + // FIXME: Expand these checks to include all undefined behavior. + + if (V2.isSigned() && V2.isNegative()) + return NULL; + + uint64_t Amt = V2.getZExtValue(); + + if (Amt > V1.getBitWidth()) + return NULL; + + return &getValue( V1.operator<<( (unsigned) Amt )); + } + + case BinaryOperator::Shr: { + + // FIXME: This logic should probably go higher up, where we can + // test these conditions symbolically. + + // FIXME: Expand these checks to include all undefined behavior. + + if (V2.isSigned() && V2.isNegative()) + return NULL; + + uint64_t Amt = V2.getZExtValue(); + + if (Amt > V1.getBitWidth()) + return NULL; + + return &getValue( V1.operator>>( (unsigned) Amt )); + } + + case BinaryOperator::LT: + return &getTruthValue( V1 < V2 ); + + case BinaryOperator::GT: + return &getTruthValue( V1 > V2 ); + + case BinaryOperator::LE: + return &getTruthValue( V1 <= V2 ); + + case BinaryOperator::GE: + return &getTruthValue( V1 >= V2 ); + + case BinaryOperator::EQ: + return &getTruthValue( V1 == V2 ); + + case BinaryOperator::NE: + return &getTruthValue( V1 != V2 ); + + // Note: LAnd, LOr, Comma are handled specially by higher-level logic. + + case BinaryOperator::And: + return &getValue( V1 & V2 ); + + case BinaryOperator::Or: + return &getValue( V1 | V2 ); + + case BinaryOperator::Xor: + return &getValue( V1 ^ V2 ); + } +} + + +const std::pair& +BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) { + + // Lazily create the folding set. + if (!PersistentSVals) PersistentSVals = new PersistentSValsTy(); + + llvm::FoldingSetNodeID ID; + void* InsertPos; + V.Profile(ID); + ID.AddPointer((void*) Data); + + PersistentSValsTy& Map = *((PersistentSValsTy*) PersistentSVals); + + typedef llvm::FoldingSetNodeWrapper FoldNodeTy; + FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); + + if (!P) { + P = (FoldNodeTy*) BPAlloc.Allocate(); + new (P) FoldNodeTy(std::make_pair(V, Data)); + Map.InsertNode(P, InsertPos); + } + + return P->getValue(); +} + +const std::pair& +BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) { + + // Lazily create the folding set. + if (!PersistentSValPairs) PersistentSValPairs = new PersistentSValPairsTy(); + + llvm::FoldingSetNodeID ID; + void* InsertPos; + V1.Profile(ID); + V2.Profile(ID); + + PersistentSValPairsTy& Map = *((PersistentSValPairsTy*) PersistentSValPairs); + + typedef llvm::FoldingSetNodeWrapper FoldNodeTy; + FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); + + if (!P) { + P = (FoldNodeTy*) BPAlloc.Allocate(); + new (P) FoldNodeTy(std::make_pair(V1, V2)); + Map.InsertNode(P, InsertPos); + } + + return P->getValue(); +} + +const SVal* BasicValueFactory::getPersistentSVal(SVal X) { + return &getPersistentSValWithData(X, 0).first; +} + + diff --git a/lib/Checker/BugReporter.cpp b/lib/Checker/BugReporter.cpp new file mode 100644 index 000000000000..0cf593b26009 --- /dev/null +++ b/lib/Checker/BugReporter.cpp @@ -0,0 +1,1879 @@ +// BugReporter.cpp - Generate PathDiagnostics for Bugs ------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BugReporter, a utility class for generating +// PathDiagnostics. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/AST/ASTContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtObjC.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Checker/BugReporter/PathDiagnostic.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/OwningPtr.h" +#include + +using namespace clang; + +BugReporterVisitor::~BugReporterVisitor() {} +BugReporterContext::~BugReporterContext() { + for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) + if ((*I)->isOwnedByReporterContext()) delete *I; +} + +//===----------------------------------------------------------------------===// +// Helper routines for walking the ExplodedGraph and fetching statements. +//===----------------------------------------------------------------------===// + +static inline const Stmt* GetStmt(ProgramPoint P) { + if (const StmtPoint* SP = dyn_cast(&P)) + return SP->getStmt(); + else if (const BlockEdge* BE = dyn_cast(&P)) + return BE->getSrc()->getTerminator(); + + return 0; +} + +static inline const ExplodedNode* +GetPredecessorNode(const ExplodedNode* N) { + return N->pred_empty() ? NULL : *(N->pred_begin()); +} + +static inline const ExplodedNode* +GetSuccessorNode(const ExplodedNode* N) { + return N->succ_empty() ? NULL : *(N->succ_begin()); +} + +static const Stmt* GetPreviousStmt(const ExplodedNode* N) { + for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N)) + if (const Stmt *S = GetStmt(N->getLocation())) + return S; + + return 0; +} + +static const Stmt* GetNextStmt(const ExplodedNode* N) { + for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N)) + if (const Stmt *S = GetStmt(N->getLocation())) { + // Check if the statement is '?' or '&&'/'||'. These are "merges", + // not actual statement points. + switch (S->getStmtClass()) { + case Stmt::ChooseExprClass: + case Stmt::ConditionalOperatorClass: continue; + case Stmt::BinaryOperatorClass: { + BinaryOperator::Opcode Op = cast(S)->getOpcode(); + if (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr) + continue; + break; + } + default: + break; + } + + // Some expressions don't have locations. + if (S->getLocStart().isInvalid()) + continue; + + return S; + } + + return 0; +} + +static inline const Stmt* +GetCurrentOrPreviousStmt(const ExplodedNode* N) { + if (const Stmt *S = GetStmt(N->getLocation())) + return S; + + return GetPreviousStmt(N); +} + +static inline const Stmt* +GetCurrentOrNextStmt(const ExplodedNode* N) { + if (const Stmt *S = GetStmt(N->getLocation())) + return S; + + return GetNextStmt(N); +} + +//===----------------------------------------------------------------------===// +// PathDiagnosticBuilder and its associated routines and helper objects. +//===----------------------------------------------------------------------===// + +typedef llvm::DenseMap NodeBackMap; + +namespace { +class NodeMapClosure : public BugReport::NodeResolver { + NodeBackMap& M; +public: + NodeMapClosure(NodeBackMap *m) : M(*m) {} + ~NodeMapClosure() {} + + const ExplodedNode* getOriginalNode(const ExplodedNode* N) { + NodeBackMap::iterator I = M.find(N); + return I == M.end() ? 0 : I->second; + } +}; + +class PathDiagnosticBuilder : public BugReporterContext { + BugReport *R; + PathDiagnosticClient *PDC; + llvm::OwningPtr PM; + NodeMapClosure NMC; +public: + PathDiagnosticBuilder(GRBugReporter &br, + BugReport *r, NodeBackMap *Backmap, + PathDiagnosticClient *pdc) + : BugReporterContext(br), + R(r), PDC(pdc), NMC(Backmap) { + addVisitor(R); + } + + PathDiagnosticLocation ExecutionContinues(const ExplodedNode* N); + + PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream& os, + const ExplodedNode* N); + + Decl const &getCodeDecl() { return R->getEndNode()->getCodeDecl(); } + + ParentMap& getParentMap() { return R->getEndNode()->getParentMap(); } + + const Stmt *getParent(const Stmt *S) { + return getParentMap().getParent(S); + } + + virtual NodeMapClosure& getNodeResolver() { return NMC; } + BugReport& getReport() { return *R; } + + PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); + + PathDiagnosticLocation + getEnclosingStmtLocation(const PathDiagnosticLocation &L) { + if (const Stmt *S = L.asStmt()) + return getEnclosingStmtLocation(S); + + return L; + } + + PathDiagnosticClient::PathGenerationScheme getGenerationScheme() const { + return PDC ? PDC->getGenerationScheme() : PathDiagnosticClient::Extensive; + } + + bool supportsLogicalOpControlFlow() const { + return PDC ? PDC->supportsLogicalOpControlFlow() : true; + } +}; +} // end anonymous namespace + +PathDiagnosticLocation +PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode* N) { + if (const Stmt *S = GetNextStmt(N)) + return PathDiagnosticLocation(S, getSourceManager()); + + return FullSourceLoc(N->getLocationContext()->getDecl()->getBodyRBrace(), + getSourceManager()); +} + +PathDiagnosticLocation +PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os, + const ExplodedNode* N) { + + // Slow, but probably doesn't matter. + if (os.str().empty()) + os << ' '; + + const PathDiagnosticLocation &Loc = ExecutionContinues(N); + + if (Loc.asStmt()) + os << "Execution continues on line " + << getSourceManager().getInstantiationLineNumber(Loc.asLocation()) + << '.'; + else { + os << "Execution jumps to the end of the "; + const Decl *D = N->getLocationContext()->getDecl(); + if (isa(D)) + os << "method"; + else if (isa(D)) + os << "function"; + else { + assert(isa(D)); + os << "anonymous block"; + } + os << '.'; + } + + return Loc; +} + +static bool IsNested(const Stmt *S, ParentMap &PM) { + if (isa(S) && PM.isConsumedExpr(cast(S))) + return true; + + const Stmt *Parent = PM.getParentIgnoreParens(S); + + if (Parent) + switch (Parent->getStmtClass()) { + case Stmt::ForStmtClass: + case Stmt::DoStmtClass: + case Stmt::WhileStmtClass: + return true; + default: + break; + } + + return false; +} + +PathDiagnosticLocation +PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { + assert(S && "Null Stmt* passed to getEnclosingStmtLocation"); + ParentMap &P = getParentMap(); + SourceManager &SMgr = getSourceManager(); + + while (IsNested(S, P)) { + const Stmt *Parent = P.getParentIgnoreParens(S); + + if (!Parent) + break; + + switch (Parent->getStmtClass()) { + case Stmt::BinaryOperatorClass: { + const BinaryOperator *B = cast(Parent); + if (B->isLogicalOp()) + return PathDiagnosticLocation(S, SMgr); + break; + } + case Stmt::CompoundStmtClass: + case Stmt::StmtExprClass: + return PathDiagnosticLocation(S, SMgr); + case Stmt::ChooseExprClass: + // Similar to '?' if we are referring to condition, just have the edge + // point to the entire choose expression. + if (cast(Parent)->getCond() == S) + return PathDiagnosticLocation(Parent, SMgr); + else + return PathDiagnosticLocation(S, SMgr); + case Stmt::ConditionalOperatorClass: + // For '?', if we are referring to condition, just have the edge point + // to the entire '?' expression. + if (cast(Parent)->getCond() == S) + return PathDiagnosticLocation(Parent, SMgr); + else + return PathDiagnosticLocation(S, SMgr); + case Stmt::DoStmtClass: + return PathDiagnosticLocation(S, SMgr); + case Stmt::ForStmtClass: + if (cast(Parent)->getBody() == S) + return PathDiagnosticLocation(S, SMgr); + break; + case Stmt::IfStmtClass: + if (cast(Parent)->getCond() != S) + return PathDiagnosticLocation(S, SMgr); + break; + case Stmt::ObjCForCollectionStmtClass: + if (cast(Parent)->getBody() == S) + return PathDiagnosticLocation(S, SMgr); + break; + case Stmt::WhileStmtClass: + if (cast(Parent)->getCond() != S) + return PathDiagnosticLocation(S, SMgr); + break; + default: + break; + } + + S = Parent; + } + + assert(S && "Cannot have null Stmt for PathDiagnosticLocation"); + + // Special case: DeclStmts can appear in for statement declarations, in which + // case the ForStmt is the context. + if (isa(S)) { + if (const Stmt *Parent = P.getParent(S)) { + switch (Parent->getStmtClass()) { + case Stmt::ForStmtClass: + case Stmt::ObjCForCollectionStmtClass: + return PathDiagnosticLocation(Parent, SMgr); + default: + break; + } + } + } + else if (isa(S)) { + // Special case: the binary operator represents the initialization + // code in a for statement (this can happen when the variable being + // initialized is an old variable. + if (const ForStmt *FS = + dyn_cast_or_null(P.getParentIgnoreParens(S))) { + if (FS->getInit() == S) + return PathDiagnosticLocation(FS, SMgr); + } + } + + return PathDiagnosticLocation(S, SMgr); +} + +//===----------------------------------------------------------------------===// +// ScanNotableSymbols: closure-like callback for scanning Store bindings. +//===----------------------------------------------------------------------===// + +static const VarDecl* +GetMostRecentVarDeclBinding(const ExplodedNode* N, + GRStateManager& VMgr, SVal X) { + + for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) { + + ProgramPoint P = N->getLocation(); + + if (!isa(P)) + continue; + + const DeclRefExpr* DR = dyn_cast(cast(P).getStmt()); + + if (!DR) + continue; + + SVal Y = N->getState()->getSVal(DR); + + if (X != Y) + continue; + + const VarDecl* VD = dyn_cast(DR->getDecl()); + + if (!VD) + continue; + + return VD; + } + + return 0; +} + +namespace { +class NotableSymbolHandler +: public StoreManager::BindingsHandler { + + SymbolRef Sym; + const GRState* PrevSt; + const Stmt* S; + GRStateManager& VMgr; + const ExplodedNode* Pred; + PathDiagnostic& PD; + BugReporter& BR; + +public: + + NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s, + GRStateManager& vmgr, const ExplodedNode* pred, + PathDiagnostic& pd, BugReporter& br) + : Sym(sym), PrevSt(prevst), S(s), VMgr(vmgr), Pred(pred), PD(pd), BR(br) {} + + bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, + SVal V) { + + SymbolRef ScanSym = V.getAsSymbol(); + + if (ScanSym != Sym) + return true; + + // Check if the previous state has this binding. + SVal X = PrevSt->getSVal(loc::MemRegionVal(R)); + + if (X == V) // Same binding? + return true; + + // Different binding. Only handle assignments for now. We don't pull + // this check out of the loop because we will eventually handle other + // cases. + + VarDecl *VD = 0; + + if (const BinaryOperator* B = dyn_cast(S)) { + if (!B->isAssignmentOp()) + return true; + + // What variable did we assign to? + DeclRefExpr* DR = dyn_cast(B->getLHS()->IgnoreParenCasts()); + + if (!DR) + return true; + + VD = dyn_cast(DR->getDecl()); + } + else if (const DeclStmt* DS = dyn_cast(S)) { + // FIXME: Eventually CFGs won't have DeclStmts. Right now we + // assume that each DeclStmt has a single Decl. This invariant + // holds by contruction in the CFG. + VD = dyn_cast(*DS->decl_begin()); + } + + if (!VD) + return true; + + // What is the most recently referenced variable with this binding? + const VarDecl* MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V); + + if (!MostRecent) + return true; + + // Create the diagnostic. + FullSourceLoc L(S->getLocStart(), BR.getSourceManager()); + + if (Loc::IsLocType(VD->getType())) { + std::string msg = "'" + std::string(VD->getNameAsString()) + + "' now aliases '" + MostRecent->getNameAsString() + "'"; + + PD.push_front(new PathDiagnosticEventPiece(L, msg)); + } + + return true; + } +}; +} + +static void HandleNotableSymbol(const ExplodedNode* N, + const Stmt* S, + SymbolRef Sym, BugReporter& BR, + PathDiagnostic& PD) { + + const ExplodedNode* Pred = N->pred_empty() ? 0 : *N->pred_begin(); + const GRState* PrevSt = Pred ? Pred->getState() : 0; + + if (!PrevSt) + return; + + // Look at the region bindings of the current state that map to the + // specified symbol. Are any of them not in the previous state? + GRStateManager& VMgr = cast(BR).getStateManager(); + NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR); + cast(BR).getStateManager().iterBindings(N->getState(), H); +} + +namespace { +class ScanNotableSymbols +: public StoreManager::BindingsHandler { + + llvm::SmallSet AlreadyProcessed; + const ExplodedNode* N; + const Stmt* S; + GRBugReporter& BR; + PathDiagnostic& PD; + +public: + ScanNotableSymbols(const ExplodedNode* n, const Stmt* s, + GRBugReporter& br, PathDiagnostic& pd) + : N(n), S(s), BR(br), PD(pd) {} + + bool HandleBinding(StoreManager& SMgr, Store store, + const MemRegion* R, SVal V) { + + SymbolRef ScanSym = V.getAsSymbol(); + + if (!ScanSym) + return true; + + if (!BR.isNotable(ScanSym)) + return true; + + if (AlreadyProcessed.count(ScanSym)) + return true; + + AlreadyProcessed.insert(ScanSym); + + HandleNotableSymbol(N, S, ScanSym, BR, PD); + return true; + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// "Minimal" path diagnostic generation algorithm. +//===----------------------------------------------------------------------===// + +static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM); + +static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, + PathDiagnosticBuilder &PDB, + const ExplodedNode *N) { + + SourceManager& SMgr = PDB.getSourceManager(); + const ExplodedNode* NextNode = N->pred_empty() + ? NULL : *(N->pred_begin()); + while (NextNode) { + N = NextNode; + NextNode = GetPredecessorNode(N); + + ProgramPoint P = N->getLocation(); + + if (const BlockEdge* BE = dyn_cast(&P)) { + CFGBlock* Src = BE->getSrc(); + CFGBlock* Dst = BE->getDst(); + Stmt* T = Src->getTerminator(); + + if (!T) + continue; + + FullSourceLoc Start(T->getLocStart(), SMgr); + + switch (T->getStmtClass()) { + default: + break; + + case Stmt::GotoStmtClass: + case Stmt::IndirectGotoStmtClass: { + const Stmt* S = GetNextStmt(N); + + if (!S) + continue; + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); + + os << "Control jumps to line " + << End.asLocation().getInstantiationLineNumber(); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + break; + } + + case Stmt::SwitchStmtClass: { + // Figure out what case arm we took. + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + if (Stmt* S = Dst->getLabel()) { + PathDiagnosticLocation End(S, SMgr); + + switch (S->getStmtClass()) { + default: + os << "No cases match in the switch statement. " + "Control jumps to line " + << End.asLocation().getInstantiationLineNumber(); + break; + case Stmt::DefaultStmtClass: + os << "Control jumps to the 'default' case at line " + << End.asLocation().getInstantiationLineNumber(); + break; + + case Stmt::CaseStmtClass: { + os << "Control jumps to 'case "; + CaseStmt* Case = cast(S); + Expr* LHS = Case->getLHS()->IgnoreParenCasts(); + + // Determine if it is an enum. + bool GetRawInt = true; + + if (DeclRefExpr* DR = dyn_cast(LHS)) { + // FIXME: Maybe this should be an assertion. Are there cases + // were it is not an EnumConstantDecl? + EnumConstantDecl* D = + dyn_cast(DR->getDecl()); + + if (D) { + GetRawInt = false; + os << D->getNameAsString(); + } + } + + if (GetRawInt) + os << LHS->EvaluateAsInt(PDB.getASTContext()); + + os << ":' at line " + << End.asLocation().getInstantiationLineNumber(); + break; + } + } + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + else { + os << "'Default' branch taken. "; + const PathDiagnosticLocation &End = PDB.ExecutionContinues(os, N); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + + break; + } + + case Stmt::BreakStmtClass: + case Stmt::ContinueStmtClass: { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + break; + } + + // Determine control-flow for ternary '?'. + case Stmt::ConditionalOperatorClass: { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "'?' condition is "; + + if (*(Src->succ_begin()+1) == Dst) + os << "false"; + else + os << "true"; + + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + break; + } + + // Determine control-flow for short-circuited '&&' and '||'. + case Stmt::BinaryOperatorClass: { + if (!PDB.supportsLogicalOpControlFlow()) + break; + + BinaryOperator *B = cast(T); + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Left side of '"; + + if (B->getOpcode() == BinaryOperator::LAnd) { + os << "&&" << "' is "; + + if (*(Src->succ_begin()+1) == Dst) { + os << "false"; + PathDiagnosticLocation End(B->getLHS(), SMgr); + PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + else { + os << "true"; + PathDiagnosticLocation Start(B->getLHS(), SMgr); + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + } + else { + assert(B->getOpcode() == BinaryOperator::LOr); + os << "||" << "' is "; + + if (*(Src->succ_begin()+1) == Dst) { + os << "false"; + PathDiagnosticLocation Start(B->getLHS(), SMgr); + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + else { + os << "true"; + PathDiagnosticLocation End(B->getLHS(), SMgr); + PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + } + + break; + } + + case Stmt::DoStmtClass: { + if (*(Src->succ_begin()) == Dst) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "Loop condition is true. "; + PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + else { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + "Loop condition is false. Exiting loop")); + } + + break; + } + + case Stmt::WhileStmtClass: + case Stmt::ForStmtClass: { + if (*(Src->succ_begin()+1) == Dst) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "Loop condition is false. "; + PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + else { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + "Loop condition is true. Entering loop body")); + } + + break; + } + + case Stmt::IfStmtClass: { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + if (*(Src->succ_begin()+1) == Dst) + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + "Taking false branch")); + else + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + "Taking true branch")); + + break; + } + } + } + + if (NextNode) { + for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), + E = PDB.visitor_end(); I!=E; ++I) { + if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) + PD.push_front(p); + } + } + + if (const PostStmt* PS = dyn_cast(&P)) { + // Scan the region bindings, and see if a "notable" symbol has a new + // lval binding. + ScanNotableSymbols SNS(N, PS->getStmt(), PDB.getBugReporter(), PD); + PDB.getStateManager().iterBindings(N->getState(), SNS); + } + } + + // After constructing the full PathDiagnostic, do a pass over it to compact + // PathDiagnosticPieces that occur within a macro. + CompactPathDiagnostic(PD, PDB.getSourceManager()); +} + +//===----------------------------------------------------------------------===// +// "Extensive" PathDiagnostic generation. +//===----------------------------------------------------------------------===// + +static bool IsControlFlowExpr(const Stmt *S) { + const Expr *E = dyn_cast(S); + + if (!E) + return false; + + E = E->IgnoreParenCasts(); + + if (isa(E)) + return true; + + if (const BinaryOperator *B = dyn_cast(E)) + if (B->isLogicalOp()) + return true; + + return false; +} + +namespace { +class ContextLocation : public PathDiagnosticLocation { + bool IsDead; +public: + ContextLocation(const PathDiagnosticLocation &L, bool isdead = false) + : PathDiagnosticLocation(L), IsDead(isdead) {} + + void markDead() { IsDead = true; } + bool isDead() const { return IsDead; } +}; + +class EdgeBuilder { + std::vector CLocs; + typedef std::vector::iterator iterator; + PathDiagnostic &PD; + PathDiagnosticBuilder &PDB; + PathDiagnosticLocation PrevLoc; + + bool IsConsumedExpr(const PathDiagnosticLocation &L); + + bool containsLocation(const PathDiagnosticLocation &Container, + const PathDiagnosticLocation &Containee); + + PathDiagnosticLocation getContextLocation(const PathDiagnosticLocation &L); + + PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L, + bool firstCharOnly = false) { + if (const Stmt *S = L.asStmt()) { + const Stmt *Original = S; + while (1) { + // Adjust the location for some expressions that are best referenced + // by one of their subexpressions. + switch (S->getStmtClass()) { + default: + break; + case Stmt::ParenExprClass: + S = cast(S)->IgnoreParens(); + firstCharOnly = true; + continue; + case Stmt::ConditionalOperatorClass: + S = cast(S)->getCond(); + firstCharOnly = true; + continue; + case Stmt::ChooseExprClass: + S = cast(S)->getCond(); + firstCharOnly = true; + continue; + case Stmt::BinaryOperatorClass: + S = cast(S)->getLHS(); + firstCharOnly = true; + continue; + } + + break; + } + + if (S != Original) + L = PathDiagnosticLocation(S, L.getManager()); + } + + if (firstCharOnly) + L = PathDiagnosticLocation(L.asLocation()); + + return L; + } + + void popLocation() { + if (!CLocs.back().isDead() && CLocs.back().asLocation().isFileID()) { + // For contexts, we only one the first character as the range. + rawAddEdge(cleanUpLocation(CLocs.back(), true)); + } + CLocs.pop_back(); + } + + PathDiagnosticLocation IgnoreParens(const PathDiagnosticLocation &L); + +public: + EdgeBuilder(PathDiagnostic &pd, PathDiagnosticBuilder &pdb) + : PD(pd), PDB(pdb) { + + // If the PathDiagnostic already has pieces, add the enclosing statement + // of the first piece as a context as well. + if (!PD.empty()) { + PrevLoc = PD.begin()->getLocation(); + + if (const Stmt *S = PrevLoc.asStmt()) + addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); + } + } + + ~EdgeBuilder() { + while (!CLocs.empty()) popLocation(); + + // Finally, add an initial edge from the start location of the first + // statement (if it doesn't already exist). + // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. + if (const CompoundStmt *CS = + PDB.getCodeDecl().getCompoundBody()) + if (!CS->body_empty()) { + SourceLocation Loc = (*CS->body_begin())->getLocStart(); + rawAddEdge(PathDiagnosticLocation(Loc, PDB.getSourceManager())); + } + + } + + void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false); + + void addEdge(const Stmt *S, bool alwaysAdd = false) { + addEdge(PathDiagnosticLocation(S, PDB.getSourceManager()), alwaysAdd); + } + + void rawAddEdge(PathDiagnosticLocation NewLoc); + + void addContext(const Stmt *S); + void addExtendedContext(const Stmt *S); +}; +} // end anonymous namespace + + +PathDiagnosticLocation +EdgeBuilder::getContextLocation(const PathDiagnosticLocation &L) { + if (const Stmt *S = L.asStmt()) { + if (IsControlFlowExpr(S)) + return L; + + return PDB.getEnclosingStmtLocation(S); + } + + return L; +} + +bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, + const PathDiagnosticLocation &Containee) { + + if (Container == Containee) + return true; + + if (Container.asDecl()) + return true; + + if (const Stmt *S = Containee.asStmt()) + if (const Stmt *ContainerS = Container.asStmt()) { + while (S) { + if (S == ContainerS) + return true; + S = PDB.getParent(S); + } + return false; + } + + // Less accurate: compare using source ranges. + SourceRange ContainerR = Container.asRange(); + SourceRange ContaineeR = Containee.asRange(); + + SourceManager &SM = PDB.getSourceManager(); + SourceLocation ContainerRBeg = SM.getInstantiationLoc(ContainerR.getBegin()); + SourceLocation ContainerREnd = SM.getInstantiationLoc(ContainerR.getEnd()); + SourceLocation ContaineeRBeg = SM.getInstantiationLoc(ContaineeR.getBegin()); + SourceLocation ContaineeREnd = SM.getInstantiationLoc(ContaineeR.getEnd()); + + unsigned ContainerBegLine = SM.getInstantiationLineNumber(ContainerRBeg); + unsigned ContainerEndLine = SM.getInstantiationLineNumber(ContainerREnd); + unsigned ContaineeBegLine = SM.getInstantiationLineNumber(ContaineeRBeg); + unsigned ContaineeEndLine = SM.getInstantiationLineNumber(ContaineeREnd); + + assert(ContainerBegLine <= ContainerEndLine); + assert(ContaineeBegLine <= ContaineeEndLine); + + return (ContainerBegLine <= ContaineeBegLine && + ContainerEndLine >= ContaineeEndLine && + (ContainerBegLine != ContaineeBegLine || + SM.getInstantiationColumnNumber(ContainerRBeg) <= + SM.getInstantiationColumnNumber(ContaineeRBeg)) && + (ContainerEndLine != ContaineeEndLine || + SM.getInstantiationColumnNumber(ContainerREnd) >= + SM.getInstantiationColumnNumber(ContainerREnd))); +} + +PathDiagnosticLocation +EdgeBuilder::IgnoreParens(const PathDiagnosticLocation &L) { + if (const Expr* E = dyn_cast_or_null(L.asStmt())) + return PathDiagnosticLocation(E->IgnoreParenCasts(), + PDB.getSourceManager()); + return L; +} + +void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { + if (!PrevLoc.isValid()) { + PrevLoc = NewLoc; + return; + } + + const PathDiagnosticLocation &NewLocClean = cleanUpLocation(NewLoc); + const PathDiagnosticLocation &PrevLocClean = cleanUpLocation(PrevLoc); + + if (NewLocClean.asLocation() == PrevLocClean.asLocation()) + return; + + // FIXME: Ignore intra-macro edges for now. + if (NewLocClean.asLocation().getInstantiationLoc() == + PrevLocClean.asLocation().getInstantiationLoc()) + return; + + PD.push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean)); + PrevLoc = NewLoc; +} + +void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd) { + + if (!alwaysAdd && NewLoc.asLocation().isMacroID()) + return; + + const PathDiagnosticLocation &CLoc = getContextLocation(NewLoc); + + while (!CLocs.empty()) { + ContextLocation &TopContextLoc = CLocs.back(); + + // Is the top location context the same as the one for the new location? + if (TopContextLoc == CLoc) { + if (alwaysAdd) { + if (IsConsumedExpr(TopContextLoc) && + !IsControlFlowExpr(TopContextLoc.asStmt())) + TopContextLoc.markDead(); + + rawAddEdge(NewLoc); + } + + return; + } + + if (containsLocation(TopContextLoc, CLoc)) { + if (alwaysAdd) { + rawAddEdge(NewLoc); + + if (IsConsumedExpr(CLoc) && !IsControlFlowExpr(CLoc.asStmt())) { + CLocs.push_back(ContextLocation(CLoc, true)); + return; + } + } + + CLocs.push_back(CLoc); + return; + } + + // Context does not contain the location. Flush it. + popLocation(); + } + + // If we reach here, there is no enclosing context. Just add the edge. + rawAddEdge(NewLoc); +} + +bool EdgeBuilder::IsConsumedExpr(const PathDiagnosticLocation &L) { + if (const Expr *X = dyn_cast_or_null(L.asStmt())) + return PDB.getParentMap().isConsumedExpr(X) && !IsControlFlowExpr(X); + + return false; +} + +void EdgeBuilder::addExtendedContext(const Stmt *S) { + if (!S) + return; + + const Stmt *Parent = PDB.getParent(S); + while (Parent) { + if (isa(Parent)) + Parent = PDB.getParent(Parent); + else + break; + } + + if (Parent) { + switch (Parent->getStmtClass()) { + case Stmt::DoStmtClass: + case Stmt::ObjCAtSynchronizedStmtClass: + addContext(Parent); + default: + break; + } + } + + addContext(S); +} + +void EdgeBuilder::addContext(const Stmt *S) { + if (!S) + return; + + PathDiagnosticLocation L(S, PDB.getSourceManager()); + + while (!CLocs.empty()) { + const PathDiagnosticLocation &TopContextLoc = CLocs.back(); + + // Is the top location context the same as the one for the new location? + if (TopContextLoc == L) + return; + + if (containsLocation(TopContextLoc, L)) { + CLocs.push_back(L); + return; + } + + // Context does not contain the location. Flush it. + popLocation(); + } + + CLocs.push_back(L); +} + +static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, + PathDiagnosticBuilder &PDB, + const ExplodedNode *N) { + + + EdgeBuilder EB(PD, PDB); + + const ExplodedNode* NextNode = N->pred_empty() + ? NULL : *(N->pred_begin()); + while (NextNode) { + N = NextNode; + NextNode = GetPredecessorNode(N); + ProgramPoint P = N->getLocation(); + + do { + // Block edges. + if (const BlockEdge *BE = dyn_cast(&P)) { + const CFGBlock &Blk = *BE->getSrc(); + const Stmt *Term = Blk.getTerminator(); + + // Are we jumping to the head of a loop? Add a special diagnostic. + if (const Stmt *Loop = BE->getDst()->getLoopTarget()) { + PathDiagnosticLocation L(Loop, PDB.getSourceManager()); + const CompoundStmt *CS = NULL; + + if (!Term) { + if (const ForStmt *FS = dyn_cast(Loop)) + CS = dyn_cast(FS->getBody()); + else if (const WhileStmt *WS = dyn_cast(Loop)) + CS = dyn_cast(WS->getBody()); + } + + PathDiagnosticEventPiece *p = + new PathDiagnosticEventPiece(L, + "Looping back to the head of the loop"); + + EB.addEdge(p->getLocation(), true); + PD.push_front(p); + + if (CS) { + PathDiagnosticLocation BL(CS->getRBracLoc(), + PDB.getSourceManager()); + BL = PathDiagnosticLocation(BL.asLocation()); + EB.addEdge(BL); + } + } + + if (Term) + EB.addContext(Term); + + break; + } + + if (const BlockEntrance *BE = dyn_cast(&P)) { + if (const Stmt* S = BE->getFirstStmt()) { + if (IsControlFlowExpr(S)) { + // Add the proper context for '&&', '||', and '?'. + EB.addContext(S); + } + else + EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); + } + + break; + } + } while (0); + + if (!NextNode) + continue; + + for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), + E = PDB.visitor_end(); I!=E; ++I) { + if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) { + const PathDiagnosticLocation &Loc = p->getLocation(); + EB.addEdge(Loc, true); + PD.push_front(p); + if (const Stmt *S = Loc.asStmt()) + EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); + } + } + } +} + +//===----------------------------------------------------------------------===// +// Methods for BugType and subclasses. +//===----------------------------------------------------------------------===// +BugType::~BugType() { + // Free up the equivalence class objects. Observe that we get a pointer to + // the object first before incrementing the iterator, as destroying the + // node before doing so means we will read from freed memory. + for (iterator I = begin(), E = end(); I !=E; ) { + BugReportEquivClass *EQ = &*I; + ++I; + delete EQ; + } +} +void BugType::FlushReports(BugReporter &BR) {} + +//===----------------------------------------------------------------------===// +// Methods for BugReport and subclasses. +//===----------------------------------------------------------------------===// +BugReport::~BugReport() {} +RangedBugReport::~RangedBugReport() {} + +const Stmt* BugReport::getStmt() const { + ProgramPoint ProgP = EndNode->getLocation(); + const Stmt *S = NULL; + + if (BlockEntrance* BE = dyn_cast(&ProgP)) { + CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); + if (BE->getBlock() == &Exit) + S = GetPreviousStmt(EndNode); + } + if (!S) + S = GetStmt(ProgP); + + return S; +} + +PathDiagnosticPiece* +BugReport::getEndPath(BugReporterContext& BRC, + const ExplodedNode* EndPathNode) { + + const Stmt* S = getStmt(); + + if (!S) + return NULL; + + const SourceRange *Beg, *End; + getRanges(Beg, End); + PathDiagnosticLocation L(S, BRC.getSourceManager()); + + // Only add the statement itself as a range if we didn't specify any + // special ranges for this report. + PathDiagnosticPiece* P = new PathDiagnosticEventPiece(L, getDescription(), + Beg == End); + + for (; Beg != End; ++Beg) + P->addRange(*Beg); + + return P; +} + +void BugReport::getRanges(const SourceRange*& beg, const SourceRange*& end) { + if (const Expr* E = dyn_cast_or_null(getStmt())) { + R = E->getSourceRange(); + assert(R.isValid()); + beg = &R; + end = beg+1; + } + else + beg = end = 0; +} + +SourceLocation BugReport::getLocation() const { + if (EndNode) + if (const Stmt* S = GetCurrentOrPreviousStmt(EndNode)) { + // For member expressions, return the location of the '.' or '->'. + if (const MemberExpr *ME = dyn_cast(S)) + return ME->getMemberLoc(); + // For binary operators, return the location of the operator. + if (const BinaryOperator *B = dyn_cast(S)) + return B->getOperatorLoc(); + + return S->getLocStart(); + } + + return FullSourceLoc(); +} + +PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode* N, + const ExplodedNode* PrevN, + BugReporterContext &BRC) { + return NULL; +} + +//===----------------------------------------------------------------------===// +// Methods for BugReporter and subclasses. +//===----------------------------------------------------------------------===// + +BugReportEquivClass::~BugReportEquivClass() { + for (iterator I=begin(), E=end(); I!=E; ++I) delete *I; +} + +GRBugReporter::~GRBugReporter() { } +BugReporterData::~BugReporterData() {} + +ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); } + +GRStateManager& +GRBugReporter::getStateManager() { return Eng.getStateManager(); } + +BugReporter::~BugReporter() { FlushReports(); } + +void BugReporter::FlushReports() { + if (BugTypes.isEmpty()) + return; + + // First flush the warnings for each BugType. This may end up creating new + // warnings and new BugTypes. Because ImmutableSet is a functional data + // structure, we do not need to worry about the iterators being invalidated. + for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) + const_cast(*I)->FlushReports(*this); + + // Iterate through BugTypes a second time. BugTypes may have been updated + // with new BugType objects and new warnings. + for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) { + BugType *BT = const_cast(*I); + + typedef llvm::FoldingSet SetTy; + SetTy& EQClasses = BT->EQClasses; + + for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){ + BugReportEquivClass& EQ = *EI; + FlushReport(EQ); + } + + // Delete the BugType object. + delete BT; + } + + // Remove all references to the BugType objects. + BugTypes = F.GetEmptySet(); +} + +//===----------------------------------------------------------------------===// +// PathDiagnostics generation. +//===----------------------------------------------------------------------===// + +static std::pair, + std::pair > +MakeReportGraph(const ExplodedGraph* G, + const ExplodedNode** NStart, + const ExplodedNode** NEnd) { + + // Create the trimmed graph. It will contain the shortest paths from the + // error nodes to the root. In the new graph we should only have one + // error node unless there are two or more error nodes with the same minimum + // path length. + ExplodedGraph* GTrim; + InterExplodedGraphMap* NMap; + + llvm::DenseMap InverseMap; + llvm::tie(GTrim, NMap) = G->Trim(NStart, NEnd, &InverseMap); + + // Create owning pointers for GTrim and NMap just to ensure that they are + // released when this function exists. + llvm::OwningPtr AutoReleaseGTrim(GTrim); + llvm::OwningPtr AutoReleaseNMap(NMap); + + // Find the (first) error node in the trimmed graph. We just need to consult + // the node map (NMap) which maps from nodes in the original graph to nodes + // in the new graph. + + std::queue WS; + typedef llvm::DenseMap IndexMapTy; + IndexMapTy IndexMap; + + for (const ExplodedNode** I = NStart; I != NEnd; ++I) + if (const ExplodedNode *N = NMap->getMappedNode(*I)) { + unsigned NodeIndex = (I - NStart) / sizeof(*I); + WS.push(N); + IndexMap[*I] = NodeIndex; + } + + assert(!WS.empty() && "No error node found in the trimmed graph."); + + // Create a new (third!) graph with a single path. This is the graph + // that will be returned to the caller. + ExplodedGraph *GNew = new ExplodedGraph(GTrim->getContext()); + + // Sometimes the trimmed graph can contain a cycle. Perform a reverse BFS + // to the root node, and then construct a new graph that contains only + // a single path. + llvm::DenseMap Visited; + + unsigned cnt = 0; + const ExplodedNode* Root = 0; + + while (!WS.empty()) { + const ExplodedNode* Node = WS.front(); + WS.pop(); + + if (Visited.find(Node) != Visited.end()) + continue; + + Visited[Node] = cnt++; + + if (Node->pred_empty()) { + Root = Node; + break; + } + + for (ExplodedNode::const_pred_iterator I=Node->pred_begin(), + E=Node->pred_end(); I!=E; ++I) + WS.push(*I); + } + + assert(Root); + + // Now walk from the root down the BFS path, always taking the successor + // with the lowest number. + ExplodedNode *Last = 0, *First = 0; + NodeBackMap *BM = new NodeBackMap(); + unsigned NodeIndex = 0; + + for ( const ExplodedNode *N = Root ;;) { + // Lookup the number associated with the current node. + llvm::DenseMap::iterator I = Visited.find(N); + assert(I != Visited.end()); + + // Create the equivalent node in the new graph with the same state + // and location. + ExplodedNode* NewN = GNew->getNode(N->getLocation(), N->getState()); + + // Store the mapping to the original node. + llvm::DenseMap::iterator IMitr=InverseMap.find(N); + assert(IMitr != InverseMap.end() && "No mapping to original node."); + (*BM)[NewN] = (const ExplodedNode*) IMitr->second; + + // Link up the new node with the previous node. + if (Last) + NewN->addPredecessor(Last, *GNew); + + Last = NewN; + + // Are we at the final node? + IndexMapTy::iterator IMI = + IndexMap.find((const ExplodedNode*)(IMitr->second)); + if (IMI != IndexMap.end()) { + First = NewN; + NodeIndex = IMI->second; + break; + } + + // Find the next successor node. We choose the node that is marked + // with the lowest DFS number. + ExplodedNode::const_succ_iterator SI = N->succ_begin(); + ExplodedNode::const_succ_iterator SE = N->succ_end(); + N = 0; + + for (unsigned MinVal = 0; SI != SE; ++SI) { + + I = Visited.find(*SI); + + if (I == Visited.end()) + continue; + + if (!N || I->second < MinVal) { + N = *SI; + MinVal = I->second; + } + } + + assert(N); + } + + assert(First); + + return std::make_pair(std::make_pair(GNew, BM), + std::make_pair(First, NodeIndex)); +} + +/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object +/// and collapses PathDiagosticPieces that are expanded by macros. +static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { + typedef std::vector > + MacroStackTy; + + typedef std::vector + PiecesTy; + + MacroStackTy MacroStack; + PiecesTy Pieces; + + for (PathDiagnostic::iterator I = PD.begin(), E = PD.end(); I!=E; ++I) { + // Get the location of the PathDiagnosticPiece. + const FullSourceLoc Loc = I->getLocation().asLocation(); + + // Determine the instantiation location, which is the location we group + // related PathDiagnosticPieces. + SourceLocation InstantiationLoc = Loc.isMacroID() ? + SM.getInstantiationLoc(Loc) : + SourceLocation(); + + if (Loc.isFileID()) { + MacroStack.clear(); + Pieces.push_back(&*I); + continue; + } + + assert(Loc.isMacroID()); + + // Is the PathDiagnosticPiece within the same macro group? + if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) { + MacroStack.back().first->push_back(&*I); + continue; + } + + // We aren't in the same group. Are we descending into a new macro + // or are part of an old one? + PathDiagnosticMacroPiece *MacroGroup = 0; + + SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ? + SM.getInstantiationLoc(Loc) : + SourceLocation(); + + // Walk the entire macro stack. + while (!MacroStack.empty()) { + if (InstantiationLoc == MacroStack.back().second) { + MacroGroup = MacroStack.back().first; + break; + } + + if (ParentInstantiationLoc == MacroStack.back().second) { + MacroGroup = MacroStack.back().first; + break; + } + + MacroStack.pop_back(); + } + + if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) { + // Create a new macro group and add it to the stack. + PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc); + + if (MacroGroup) + MacroGroup->push_back(NewGroup); + else { + assert(InstantiationLoc.isFileID()); + Pieces.push_back(NewGroup); + } + + MacroGroup = NewGroup; + MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc)); + } + + // Finally, add the PathDiagnosticPiece to the group. + MacroGroup->push_back(&*I); + } + + // Now take the pieces and construct a new PathDiagnostic. + PD.resetPath(false); + + for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) { + if (PathDiagnosticMacroPiece *MP=dyn_cast(*I)) + if (!MP->containsEvent()) { + delete MP; + continue; + } + + PD.push_back(*I); + } +} + +void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, + BugReportEquivClass& EQ) { + + std::vector Nodes; + + for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { + const ExplodedNode* N = I->getEndNode(); + if (N) Nodes.push_back(N); + } + + if (Nodes.empty()) + return; + + // Construct a new graph that contains only a single path from the error + // node to a root. + const std::pair, + std::pair >& + GPair = MakeReportGraph(&getGraph(), &Nodes[0], &Nodes[0] + Nodes.size()); + + // Find the BugReport with the original location. + BugReport *R = 0; + unsigned i = 0; + for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I, ++i) + if (i == GPair.second.second) { R = *I; break; } + + assert(R && "No original report found for sliced graph."); + + llvm::OwningPtr ReportGraph(GPair.first.first); + llvm::OwningPtr BackMap(GPair.first.second); + const ExplodedNode *N = GPair.second.first; + + // Start building the path diagnostic... + PathDiagnosticBuilder PDB(*this, R, BackMap.get(), getPathDiagnosticClient()); + + if (PathDiagnosticPiece* Piece = R->getEndPath(PDB, N)) + PD.push_back(Piece); + else + return; + + R->registerInitialVisitors(PDB, N); + + switch (PDB.getGenerationScheme()) { + case PathDiagnosticClient::Extensive: + GenerateExtensivePathDiagnostic(PD, PDB, N); + break; + case PathDiagnosticClient::Minimal: + GenerateMinimalPathDiagnostic(PD, PDB, N); + break; + } +} + +void BugReporter::Register(BugType *BT) { + BugTypes = F.Add(BugTypes, BT); +} + +void BugReporter::EmitReport(BugReport* R) { + // Compute the bug report's hash to determine its equivalence class. + llvm::FoldingSetNodeID ID; + R->Profile(ID); + + // Lookup the equivance class. If there isn't one, create it. + BugType& BT = R->getBugType(); + Register(&BT); + void *InsertPos; + BugReportEquivClass* EQ = BT.EQClasses.FindNodeOrInsertPos(ID, InsertPos); + + if (!EQ) { + EQ = new BugReportEquivClass(R); + BT.EQClasses.InsertNode(EQ, InsertPos); + } + else + EQ->AddReport(R); +} + + +//===----------------------------------------------------------------------===// +// Emitting reports in equivalence classes. +//===----------------------------------------------------------------------===// + +namespace { +struct FRIEC_WLItem { + const ExplodedNode *N; + ExplodedNode::const_succ_iterator I, E; + + FRIEC_WLItem(const ExplodedNode *n) + : N(n), I(N->succ_begin()), E(N->succ_end()) {} +}; +} + +static BugReport *FindReportInEquivalenceClass(BugReportEquivClass& EQ) { + BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); + assert(I != E); + BugReport *R = *I; + BugType& BT = R->getBugType(); + + if (!BT.isSuppressOnSink()) + return R; + + // For bug reports that should be suppressed when all paths are post-dominated + // by a sink node, iterate through the reports in the equivalence class + // until we find one that isn't post-dominated (if one exists). We use a + // DFS traversal of the ExplodedGraph to find a non-sink node. We could write + // this as a recursive function, but we don't want to risk blowing out the + // stack for very long paths. + for (; I != E; ++I) { + R = *I; + const ExplodedNode *N = R->getEndNode(); + + if (!N) + continue; + + if (N->isSink()) { + assert(false && + "BugType::isSuppressSink() should not be 'true' for sink end nodes"); + return R; + } + + if (N->succ_empty()) + return R; + + // At this point we know that 'N' is not a sink and it has at least one + // successor. Use a DFS worklist to find a non-sink end-of-path node. + typedef FRIEC_WLItem WLItem; + typedef llvm::SmallVector DFSWorkList; + llvm::DenseMap Visited; + + DFSWorkList WL; + WL.push_back(N); + Visited[N] = 1; + + while (!WL.empty()) { + WLItem &WI = WL.back(); + assert(!WI.N->succ_empty()); + + for (; WI.I != WI.E; ++WI.I) { + const ExplodedNode *Succ = *WI.I; + // End-of-path node? + if (Succ->succ_empty()) { + // If we found an end-of-path node that is not a sink, then return + // this report. + if (!Succ->isSink()) + return R; + + // Found a sink? Continue on to the next successor. + continue; + } + + // Mark the successor as visited. If it hasn't been explored, + // enqueue it to the DFS worklist. + unsigned &mark = Visited[Succ]; + if (!mark) { + mark = 1; + WL.push_back(Succ); + break; + } + } + + if (&WL.back() == &WI) + WL.pop_back(); + } + } + + // If we reach here, the end nodes for all reports in the equivalence + // class are post-dominated by a sink node. + return NULL; +} + + +//===----------------------------------------------------------------------===// +// DiagnosticCache. This is a hack to cache analyzer diagnostics. It +// uses global state, which eventually should go elsewhere. +//===----------------------------------------------------------------------===// +namespace { +class DiagCacheItem : public llvm::FoldingSetNode { + llvm::FoldingSetNodeID ID; +public: + DiagCacheItem(BugReport *R, PathDiagnostic *PD) { + ID.AddString(R->getBugType().getName()); + ID.AddString(R->getBugType().getCategory()); + ID.AddString(R->getDescription()); + ID.AddInteger(R->getLocation().getRawEncoding()); + PD->Profile(ID); + } + + void Profile(llvm::FoldingSetNodeID &id) { + id = ID; + } + + llvm::FoldingSetNodeID &getID() { return ID; } +}; +} + +static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) { + // FIXME: Eventually this diagnostic cache should reside in something + // like AnalysisManager instead of being a static variable. This is + // really unsafe in the long term. + typedef llvm::FoldingSet DiagnosticCache; + static DiagnosticCache DC; + + void *InsertPos; + DiagCacheItem *Item = new DiagCacheItem(R, PD); + + if (DC.FindNodeOrInsertPos(Item->getID(), InsertPos)) { + delete Item; + return true; + } + + DC.InsertNode(Item, InsertPos); + return false; +} + +void BugReporter::FlushReport(BugReportEquivClass& EQ) { + BugReport *R = FindReportInEquivalenceClass(EQ); + + if (!R) + return; + + PathDiagnosticClient* PD = getPathDiagnosticClient(); + + // FIXME: Make sure we use the 'R' for the path that was actually used. + // Probably doesn't make a difference in practice. + BugType& BT = R->getBugType(); + + llvm::OwningPtr + D(new PathDiagnostic(R->getBugType().getName(), + !PD || PD->useVerboseDescription() + ? R->getDescription() : R->getShortDescription(), + BT.getCategory())); + + GeneratePathDiagnostic(*D.get(), EQ); + + if (IsCachedDiagnostic(R, D.get())) + return; + + // Get the meta data. + std::pair Meta = R->getExtraDescriptiveText(); + for (const char** s = Meta.first; s != Meta.second; ++s) + D->addMeta(*s); + + // Emit a summary diagnostic to the regular Diagnostics engine. + const SourceRange *Beg = 0, *End = 0; + R->getRanges(Beg, End); + Diagnostic& Diag = getDiagnostic(); + FullSourceLoc L(R->getLocation(), getSourceManager()); + + // Search the description for '%', as that will be interpretted as a + // format character by FormatDiagnostics. + llvm::StringRef desc = R->getShortDescription(); + unsigned ErrorDiag; + { + llvm::SmallString<512> TmpStr; + llvm::raw_svector_ostream Out(TmpStr); + for (llvm::StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) + if (*I == '%') + Out << "%%"; + else + Out << *I; + + Out.flush(); + ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning, TmpStr); + } + + switch (End-Beg) { + default: assert(0 && "Don't handle this many ranges yet!"); + case 0: Diag.Report(L, ErrorDiag); break; + case 1: Diag.Report(L, ErrorDiag) << Beg[0]; break; + case 2: Diag.Report(L, ErrorDiag) << Beg[0] << Beg[1]; break; + case 3: Diag.Report(L, ErrorDiag) << Beg[0] << Beg[1] << Beg[2]; break; + } + + // Emit a full diagnostic for the path if we have a PathDiagnosticClient. + if (!PD) + return; + + if (D->empty()) { + PathDiagnosticPiece* piece = + new PathDiagnosticEventPiece(L, R->getDescription()); + + for ( ; Beg != End; ++Beg) piece->addRange(*Beg); + D->push_back(piece); + } + + PD->HandlePathDiagnostic(D.take()); +} + +void BugReporter::EmitBasicReport(llvm::StringRef name, llvm::StringRef str, + SourceLocation Loc, + SourceRange* RBeg, unsigned NumRanges) { + EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); +} + +void BugReporter::EmitBasicReport(llvm::StringRef name, + llvm::StringRef category, + llvm::StringRef str, SourceLocation Loc, + SourceRange* RBeg, unsigned NumRanges) { + + // 'BT' will be owned by BugReporter as soon as we call 'EmitReport'. + BugType *BT = new BugType(name, category); + FullSourceLoc L = getContext().getFullLoc(Loc); + RangedBugReport *R = new DiagBugReport(*BT, str, L); + for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg); + EmitReport(R); +} diff --git a/lib/Checker/BugReporterVisitors.cpp b/lib/Checker/BugReporterVisitors.cpp new file mode 100644 index 000000000000..6cf41b14dc5a --- /dev/null +++ b/lib/Checker/BugReporterVisitors.cpp @@ -0,0 +1,367 @@ +// BugReporterVisitors.cpp - Helpers for reporting bugs -----------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a set of BugReporter "visitors" which can be used to +// enhance the diagnostics reported for a bug. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/BugReporter/PathDiagnostic.h" +#include "clang/Checker/PathSensitive/GRState.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +const Stmt *clang::bugreporter::GetDerefExpr(const ExplodedNode *N) { + // Pattern match for a few useful cases (do something smarter later): + // a[0], p->f, *p + const Stmt *S = N->getLocationAs()->getStmt(); + + if (const UnaryOperator *U = dyn_cast(S)) { + if (U->getOpcode() == UnaryOperator::Deref) + return U->getSubExpr()->IgnoreParenCasts(); + } + else if (const MemberExpr *ME = dyn_cast(S)) { + return ME->getBase()->IgnoreParenCasts(); + } + else if (const ArraySubscriptExpr *AE = dyn_cast(S)) { + // Retrieve the base for arrays since BasicStoreManager doesn't know how + // to reason about them. + return AE->getBase(); + } + + return NULL; +} + +const Stmt* +clang::bugreporter::GetReceiverExpr(const ExplodedNode *N){ + const Stmt *S = N->getLocationAs()->getStmt(); + if (const ObjCMessageExpr *ME = dyn_cast(S)) + return ME->getReceiver(); + return NULL; +} + +const Stmt* +clang::bugreporter::GetDenomExpr(const ExplodedNode *N) { + const Stmt *S = N->getLocationAs()->getStmt(); + if (const BinaryOperator *BE = dyn_cast(S)) + return BE->getRHS(); + return NULL; +} + +const Stmt* +clang::bugreporter::GetCalleeExpr(const ExplodedNode *N) { + // Callee is checked as a PreVisit to the CallExpr. + const Stmt *S = N->getLocationAs()->getStmt(); + if (const CallExpr *CE = dyn_cast(S)) + return CE->getCallee(); + return NULL; +} + +const Stmt* +clang::bugreporter::GetRetValExpr(const ExplodedNode *N) { + const Stmt *S = N->getLocationAs()->getStmt(); + if (const ReturnStmt *RS = dyn_cast(S)) + return RS->getRetValue(); + return NULL; +} + +//===----------------------------------------------------------------------===// +// Definitions for bug reporter visitors. +//===----------------------------------------------------------------------===// + +namespace { +class FindLastStoreBRVisitor : public BugReporterVisitor { + const MemRegion *R; + SVal V; + bool satisfied; + const ExplodedNode *StoreSite; +public: + FindLastStoreBRVisitor(SVal v, const MemRegion *r) + : R(r), V(v), satisfied(false), StoreSite(0) {} + + PathDiagnosticPiece* VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext& BRC) { + + if (satisfied) + return NULL; + + if (!StoreSite) { + const ExplodedNode *Node = N, *Last = NULL; + + for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { + + if (const VarRegion *VR = dyn_cast(R)) { + if (const PostStmt *P = Node->getLocationAs()) + if (const DeclStmt *DS = P->getStmtAs()) + if (DS->getSingleDecl() == VR->getDecl()) { + Last = Node; + break; + } + } + + if (Node->getState()->getSVal(R) != V) + break; + } + + if (!Node || !Last) { + satisfied = true; + return NULL; + } + + StoreSite = Last; + } + + if (StoreSite != N) + return NULL; + + satisfied = true; + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + if (const PostStmt *PS = N->getLocationAs()) { + if (const DeclStmt *DS = PS->getStmtAs()) { + + if (const VarRegion *VR = dyn_cast(R)) { + os << "Variable '" << VR->getDecl()->getNameAsString() << "' "; + } + else + return NULL; + + if (isa(V)) { + bool b = false; + ASTContext &C = BRC.getASTContext(); + if (R->isBoundable()) { + if (const TypedRegion *TR = dyn_cast(R)) { + if (TR->getValueType(C)->isObjCObjectPointerType()) { + os << "initialized to nil"; + b = true; + } + } + } + + if (!b) + os << "initialized to a null pointer value"; + } + else if (isa(V)) { + os << "initialized to " << cast(V).getValue(); + } + else if (V.isUndef()) { + if (isa(R)) { + const VarDecl *VD = cast(DS->getSingleDecl()); + if (VD->getInit()) + os << "initialized to a garbage value"; + else + os << "declared without an initial value"; + } + } + } + } + + if (os.str().empty()) { + if (isa(V)) { + bool b = false; + ASTContext &C = BRC.getASTContext(); + if (R->isBoundable()) { + if (const TypedRegion *TR = dyn_cast(R)) { + if (TR->getValueType(C)->isObjCObjectPointerType()) { + os << "nil object reference stored to "; + b = true; + } + } + } + + if (!b) + os << "Null pointer value stored to "; + } + else if (V.isUndef()) { + os << "Uninitialized value stored to "; + } + else if (isa(V)) { + os << "The value " << cast(V).getValue() + << " is assigned to "; + } + else + return NULL; + + if (const VarRegion *VR = dyn_cast(R)) { + os << '\'' << VR->getDecl()->getNameAsString() << '\''; + } + else + return NULL; + } + + // FIXME: Refactor this into BugReporterContext. + const Stmt *S = 0; + ProgramPoint P = N->getLocation(); + + if (BlockEdge *BE = dyn_cast(&P)) { + CFGBlock *BSrc = BE->getSrc(); + S = BSrc->getTerminatorCondition(); + } + else if (PostStmt *PS = dyn_cast(&P)) { + S = PS->getStmt(); + } + + if (!S) + return NULL; + + // Construct a new PathDiagnosticPiece. + PathDiagnosticLocation L(S, BRC.getSourceManager()); + return new PathDiagnosticEventPiece(L, os.str()); + } +}; + + +static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R, + SVal V) { + BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); +} + +class TrackConstraintBRVisitor : public BugReporterVisitor { + DefinedSVal Constraint; + const bool Assumption; + bool isSatisfied; +public: + TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) + : Constraint(constraint), Assumption(assumption), isSatisfied(false) {} + + PathDiagnosticPiece* VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext& BRC) { + if (isSatisfied) + return NULL; + + // Check if in the previous state it was feasible for this constraint + // to *not* be true. + if (PrevN->getState()->Assume(Constraint, !Assumption)) { + + isSatisfied = true; + + // As a sanity check, make sure that the negation of the constraint + // was infeasible in the current state. If it is feasible, we somehow + // missed the transition point. + if (N->getState()->Assume(Constraint, !Assumption)) + return NULL; + + // We found the transition point for the constraint. We now need to + // pretty-print the constraint. (work-in-progress) + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + if (isa(Constraint)) { + os << "Assuming pointer value is "; + os << (Assumption ? "non-null" : "null"); + } + + if (os.str().empty()) + return NULL; + + // FIXME: Refactor this into BugReporterContext. + const Stmt *S = 0; + ProgramPoint P = N->getLocation(); + + if (BlockEdge *BE = dyn_cast(&P)) { + CFGBlock *BSrc = BE->getSrc(); + S = BSrc->getTerminatorCondition(); + } + else if (PostStmt *PS = dyn_cast(&P)) { + S = PS->getStmt(); + } + + if (!S) + return NULL; + + // Construct a new PathDiagnosticPiece. + PathDiagnosticLocation L(S, BRC.getSourceManager()); + return new PathDiagnosticEventPiece(L, os.str()); + } + + return NULL; + } +}; +} // end anonymous namespace + +static void registerTrackConstraint(BugReporterContext& BRC, + DefinedSVal Constraint, + bool Assumption) { + BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption)); +} + +void clang::bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, + const void *data, + const ExplodedNode* N) { + + const Stmt *S = static_cast(data); + + if (!S) + return; + + GRStateManager &StateMgr = BRC.getStateManager(); + const GRState *state = N->getState(); + + if (const DeclRefExpr *DR = dyn_cast(S)) { + if (const VarDecl *VD = dyn_cast(DR->getDecl())) { + const VarRegion *R = + StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); + + // What did we load? + SVal V = state->getSVal(S); + + if (isa(V) || isa(V) + || V.isUndef()) { + ::registerFindLastStore(BRC, R, V); + } + } + } + + SVal V = state->getSValAsScalarOrLoc(S); + + // Uncomment this to find cases where we aren't properly getting the + // base value that was dereferenced. + // assert(!V.isUnknownOrUndef()); + + // Is it a symbolic value? + if (loc::MemRegionVal *L = dyn_cast(&V)) { + const SubRegion *R = cast(L->getRegion()); + while (R && !isa(R)) { + R = dyn_cast(R->getSuperRegion()); + } + + if (R) { + assert(isa(R)); + registerTrackConstraint(BRC, loc::MemRegionVal(R), false); + } + } +} + +void clang::bugreporter::registerFindLastStore(BugReporterContext& BRC, + const void *data, + const ExplodedNode* N) { + + const MemRegion *R = static_cast(data); + + if (!R) + return; + + const GRState *state = N->getState(); + SVal V = state->getSVal(R); + + if (V.isUnknown()) + return; + + BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); +} diff --git a/lib/Checker/BuiltinFunctionChecker.cpp b/lib/Checker/BuiltinFunctionChecker.cpp new file mode 100644 index 000000000000..8711492049c5 --- /dev/null +++ b/lib/Checker/BuiltinFunctionChecker.cpp @@ -0,0 +1,76 @@ +//=== BuiltinFunctionChecker.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker evaluates clang builtin functions. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/Checker.h" +#include "clang/Basic/Builtins.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; + +namespace { + +class BuiltinFunctionChecker : public Checker { +public: + static void *getTag() { static int tag = 0; return &tag; } + virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); +}; + +} + +void clang::RegisterBuiltinFunctionChecker(GRExprEngine &Eng) { + Eng.registerCheck(new BuiltinFunctionChecker()); +} + +bool BuiltinFunctionChecker::EvalCallExpr(CheckerContext &C,const CallExpr *CE){ + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + + if (!FD) + return false; + + unsigned id = FD->getBuiltinID(); + + if (!id) + return false; + + switch (id) { + case Builtin::BI__builtin_expect: { + // For __builtin_expect, just return the value of the subexpression. + assert (CE->arg_begin() != CE->arg_end()); + SVal X = state->getSVal(*(CE->arg_begin())); + C.GenerateNode(state->BindExpr(CE, X)); + return true; + } + + case Builtin::BI__builtin_alloca: { + // FIXME: Refactor into StoreManager itself? + MemRegionManager& RM = C.getStoreManager().getRegionManager(); + const MemRegion* R = + RM.getAllocaRegion(CE, C.getNodeBuilder().getCurrentBlockCount(), + C.getPredecessor()->getLocationContext()); + + // Set the extent of the region in bytes. This enables us to use the + // SVal of the argument directly. If we save the extent in bits, we + // cannot represent values like symbol*8. + SVal Extent = state->getSVal(*(CE->arg_begin())); + state = C.getStoreManager().setExtent(state, R, Extent); + C.GenerateNode(state->BindExpr(CE, loc::MemRegionVal(R))); + return true; + } + } + + return false; +} diff --git a/lib/Checker/CFRefCount.cpp b/lib/Checker/CFRefCount.cpp new file mode 100644 index 000000000000..324916a6f6eb --- /dev/null +++ b/lib/Checker/CFRefCount.cpp @@ -0,0 +1,3521 @@ +// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the methods for CFRefCount, which implements +// a reference count checker for Core Foundation (Mac OS X). +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Checker/PathSensitive/GRExprEngineBuilders.h" +#include "clang/Checker/PathSensitive/GRStateTrait.h" +#include "clang/Checker/BugReporter/PathDiagnostic.h" +#include "clang/Checker/Checkers/LocalCheckers.h" +#include "clang/Checker/BugReporter/PathDiagnostic.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/SymbolManager.h" +#include "clang/Checker/PathSensitive/GRTransferFuncs.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/DomainSpecific/CocoaConventions.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/StmtVisitor.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/STLExtras.h" +#include + +using namespace clang; +using llvm::StringRef; +using llvm::StrInStrNoCase; + +static const ObjCMethodDecl* +ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) { + ObjCInterfaceDecl *ID = + const_cast(MD->getClassInterface()); + + return MD->isInstanceMethod() + ? ID->lookupInstanceMethod(MD->getSelector()) + : ID->lookupClassMethod(MD->getSelector()); +} + +namespace { +class GenericNodeBuilder { + GRStmtNodeBuilder *SNB; + Stmt *S; + const void *tag; + GREndPathNodeBuilder *ENB; +public: + GenericNodeBuilder(GRStmtNodeBuilder &snb, Stmt *s, + const void *t) + : SNB(&snb), S(s), tag(t), ENB(0) {} + + GenericNodeBuilder(GREndPathNodeBuilder &enb) + : SNB(0), S(0), tag(0), ENB(&enb) {} + + ExplodedNode *MakeNode(const GRState *state, ExplodedNode *Pred) { + if (SNB) + return SNB->generateNode(PostStmt(S, Pred->getLocationContext(), tag), + state, Pred); + + assert(ENB); + return ENB->generateNode(state, Pred); + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Primitives used for constructing summaries for function/method calls. +//===----------------------------------------------------------------------===// + +/// ArgEffect is used to summarize a function/method call's effect on a +/// particular argument. +enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing, + DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape, + NewAutoreleasePool, SelfOwn, StopTracking }; + +namespace llvm { +template <> struct FoldingSetTrait { +static inline void Profile(const ArgEffect X, FoldingSetNodeID& ID) { + ID.AddInteger((unsigned) X); +} +}; +} // end llvm namespace + +/// ArgEffects summarizes the effects of a function/method call on all of +/// its arguments. +typedef llvm::ImmutableMap ArgEffects; + +namespace { + +/// RetEffect is used to summarize a function/method call's behavior with +/// respect to its return value. +class RetEffect { +public: + enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, + NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias, + OwnedWhenTrackedReceiver }; + + enum ObjKind { CF, ObjC, AnyObj }; + +private: + Kind K; + ObjKind O; + unsigned index; + + RetEffect(Kind k, unsigned idx = 0) : K(k), O(AnyObj), index(idx) {} + RetEffect(Kind k, ObjKind o) : K(k), O(o), index(0) {} + +public: + Kind getKind() const { return K; } + + ObjKind getObjKind() const { return O; } + + unsigned getIndex() const { + assert(getKind() == Alias); + return index; + } + + bool isOwned() const { + return K == OwnedSymbol || K == OwnedAllocatedSymbol || + K == OwnedWhenTrackedReceiver; + } + + static RetEffect MakeOwnedWhenTrackedReceiver() { + return RetEffect(OwnedWhenTrackedReceiver, ObjC); + } + + static RetEffect MakeAlias(unsigned Idx) { + return RetEffect(Alias, Idx); + } + static RetEffect MakeReceiverAlias() { + return RetEffect(ReceiverAlias); + } + static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) { + return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o); + } + static RetEffect MakeNotOwned(ObjKind o) { + return RetEffect(NotOwnedSymbol, o); + } + static RetEffect MakeGCNotOwned() { + return RetEffect(GCNotOwnedSymbol, ObjC); + } + + static RetEffect MakeNoRet() { + return RetEffect(NoRet); + } + + void Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddInteger((unsigned)K); + ID.AddInteger((unsigned)O); + ID.AddInteger(index); + } +}; + +//===----------------------------------------------------------------------===// +// Reference-counting logic (typestate + counts). +//===----------------------------------------------------------------------===// + +class RefVal { +public: + enum Kind { + Owned = 0, // Owning reference. + NotOwned, // Reference is not owned by still valid (not freed). + Released, // Object has been released. + ReturnedOwned, // Returned object passes ownership to caller. + ReturnedNotOwned, // Return object does not pass ownership to caller. + ERROR_START, + ErrorDeallocNotOwned, // -dealloc called on non-owned object. + ErrorDeallocGC, // Calling -dealloc with GC enabled. + ErrorUseAfterRelease, // Object used after released. + ErrorReleaseNotOwned, // Release of an object that was not owned. + ERROR_LEAK_START, + ErrorLeak, // A memory leak due to excessive reference counts. + ErrorLeakReturned, // A memory leak due to the returning method not having + // the correct naming conventions. + ErrorGCLeakReturned, + ErrorOverAutorelease, + ErrorReturnedNotOwned + }; + +private: + Kind kind; + RetEffect::ObjKind okind; + unsigned Cnt; + unsigned ACnt; + QualType T; + + RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t) + : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {} + + RefVal(Kind k, unsigned cnt = 0) + : kind(k), okind(RetEffect::AnyObj), Cnt(cnt), ACnt(0) {} + +public: + Kind getKind() const { return kind; } + + RetEffect::ObjKind getObjKind() const { return okind; } + + unsigned getCount() const { return Cnt; } + unsigned getAutoreleaseCount() const { return ACnt; } + unsigned getCombinedCounts() const { return Cnt + ACnt; } + void clearCounts() { Cnt = 0; ACnt = 0; } + void setCount(unsigned i) { Cnt = i; } + void setAutoreleaseCount(unsigned i) { ACnt = i; } + + QualType getType() const { return T; } + + // Useful predicates. + + static bool isError(Kind k) { return k >= ERROR_START; } + + static bool isLeak(Kind k) { return k >= ERROR_LEAK_START; } + + bool isOwned() const { + return getKind() == Owned; + } + + bool isNotOwned() const { + return getKind() == NotOwned; + } + + bool isReturnedOwned() const { + return getKind() == ReturnedOwned; + } + + bool isReturnedNotOwned() const { + return getKind() == ReturnedNotOwned; + } + + bool isNonLeakError() const { + Kind k = getKind(); + return isError(k) && !isLeak(k); + } + + static RefVal makeOwned(RetEffect::ObjKind o, QualType t, + unsigned Count = 1) { + return RefVal(Owned, o, Count, 0, t); + } + + static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t, + unsigned Count = 0) { + return RefVal(NotOwned, o, Count, 0, t); + } + + // Comparison, profiling, and pretty-printing. + + bool operator==(const RefVal& X) const { + return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt; + } + + RefVal operator-(size_t i) const { + return RefVal(getKind(), getObjKind(), getCount() - i, + getAutoreleaseCount(), getType()); + } + + RefVal operator+(size_t i) const { + return RefVal(getKind(), getObjKind(), getCount() + i, + getAutoreleaseCount(), getType()); + } + + RefVal operator^(Kind k) const { + return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(), + getType()); + } + + RefVal autorelease() const { + return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1, + getType()); + } + + void Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddInteger((unsigned) kind); + ID.AddInteger(Cnt); + ID.AddInteger(ACnt); + ID.Add(T); + } + + void print(llvm::raw_ostream& Out) const; +}; + +void RefVal::print(llvm::raw_ostream& Out) const { + if (!T.isNull()) + Out << "Tracked Type:" << T.getAsString() << '\n'; + + switch (getKind()) { + default: assert(false); + case Owned: { + Out << "Owned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case NotOwned: { + Out << "NotOwned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case ReturnedOwned: { + Out << "ReturnedOwned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case ReturnedNotOwned: { + Out << "ReturnedNotOwned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case Released: + Out << "Released"; + break; + + case ErrorDeallocGC: + Out << "-dealloc (GC)"; + break; + + case ErrorDeallocNotOwned: + Out << "-dealloc (not-owned)"; + break; + + case ErrorLeak: + Out << "Leaked"; + break; + + case ErrorLeakReturned: + Out << "Leaked (Bad naming)"; + break; + + case ErrorGCLeakReturned: + Out << "Leaked (GC-ed at return)"; + break; + + case ErrorUseAfterRelease: + Out << "Use-After-Release [ERROR]"; + break; + + case ErrorReleaseNotOwned: + Out << "Release of Not-Owned [ERROR]"; + break; + + case RefVal::ErrorOverAutorelease: + Out << "Over autoreleased"; + break; + + case RefVal::ErrorReturnedNotOwned: + Out << "Non-owned object returned instead of owned"; + break; + } + + if (ACnt) { + Out << " [ARC +" << ACnt << ']'; + } +} +} //end anonymous namespace + +//===----------------------------------------------------------------------===// +// RefBindings - State used to track object reference counts. +//===----------------------------------------------------------------------===// + +typedef llvm::ImmutableMap RefBindings; + +namespace clang { + template<> + struct GRStateTrait : public GRStatePartialTrait { + static void* GDMIndex() { + static int RefBIndex = 0; + return &RefBIndex; + } + }; +} + +//===----------------------------------------------------------------------===// +// Summaries +//===----------------------------------------------------------------------===// + +namespace { +class RetainSummary { + /// Args - an ordered vector of (index, ArgEffect) pairs, where index + /// specifies the argument (starting from 0). This can be sparsely + /// populated; arguments with no entry in Args use 'DefaultArgEffect'. + ArgEffects Args; + + /// DefaultArgEffect - The default ArgEffect to apply to arguments that + /// do not have an entry in Args. + ArgEffect DefaultArgEffect; + + /// Receiver - If this summary applies to an Objective-C message expression, + /// this is the effect applied to the state of the receiver. + ArgEffect Receiver; + + /// Ret - The effect on the return value. Used to indicate if the + /// function/method call returns a new tracked symbol, returns an + /// alias of one of the arguments in the call, and so on. + RetEffect Ret; + + /// EndPath - Indicates that execution of this method/function should + /// terminate the simulation of a path. + bool EndPath; + +public: + RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff, + ArgEffect ReceiverEff, bool endpath = false) + : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R), + EndPath(endpath) {} + + /// getArg - Return the argument effect on the argument specified by + /// idx (starting from 0). + ArgEffect getArg(unsigned idx) const { + if (const ArgEffect *AE = Args.lookup(idx)) + return *AE; + + return DefaultArgEffect; + } + + /// setDefaultArgEffect - Set the default argument effect. + void setDefaultArgEffect(ArgEffect E) { + DefaultArgEffect = E; + } + + /// setArg - Set the argument effect on the argument specified by idx. + void setArgEffect(ArgEffects::Factory& AF, unsigned idx, ArgEffect E) { + Args = AF.Add(Args, idx, E); + } + + /// getRetEffect - Returns the effect on the return value of the call. + RetEffect getRetEffect() const { return Ret; } + + /// setRetEffect - Set the effect of the return value of the call. + void setRetEffect(RetEffect E) { Ret = E; } + + /// isEndPath - Returns true if executing the given method/function should + /// terminate the path. + bool isEndPath() const { return EndPath; } + + /// getReceiverEffect - Returns the effect on the receiver of the call. + /// This is only meaningful if the summary applies to an ObjCMessageExpr*. + ArgEffect getReceiverEffect() const { return Receiver; } + + /// setReceiverEffect - Set the effect on the receiver of the call. + void setReceiverEffect(ArgEffect E) { Receiver = E; } + + typedef ArgEffects::iterator ExprIterator; + + ExprIterator begin_args() const { return Args.begin(); } + ExprIterator end_args() const { return Args.end(); } + + static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects A, + RetEffect RetEff, ArgEffect DefaultEff, + ArgEffect ReceiverEff, bool EndPath) { + ID.Add(A); + ID.Add(RetEff); + ID.AddInteger((unsigned) DefaultEff); + ID.AddInteger((unsigned) ReceiverEff); + ID.AddInteger((unsigned) EndPath); + } + + void Profile(llvm::FoldingSetNodeID& ID) const { + Profile(ID, Args, Ret, DefaultArgEffect, Receiver, EndPath); + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Data structures for constructing summaries. +//===----------------------------------------------------------------------===// + +namespace { +class ObjCSummaryKey { + IdentifierInfo* II; + Selector S; +public: + ObjCSummaryKey(IdentifierInfo* ii, Selector s) + : II(ii), S(s) {} + + ObjCSummaryKey(const ObjCInterfaceDecl* d, Selector s) + : II(d ? d->getIdentifier() : 0), S(s) {} + + ObjCSummaryKey(const ObjCInterfaceDecl* d, IdentifierInfo *ii, Selector s) + : II(d ? d->getIdentifier() : ii), S(s) {} + + ObjCSummaryKey(Selector s) + : II(0), S(s) {} + + IdentifierInfo* getIdentifier() const { return II; } + Selector getSelector() const { return S; } +}; +} + +namespace llvm { +template <> struct DenseMapInfo { + static inline ObjCSummaryKey getEmptyKey() { + return ObjCSummaryKey(DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()); + } + + static inline ObjCSummaryKey getTombstoneKey() { + return ObjCSummaryKey(DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()); + } + + static unsigned getHashValue(const ObjCSummaryKey &V) { + return (DenseMapInfo::getHashValue(V.getIdentifier()) + & 0x88888888) + | (DenseMapInfo::getHashValue(V.getSelector()) + & 0x55555555); + } + + static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) { + return DenseMapInfo::isEqual(LHS.getIdentifier(), + RHS.getIdentifier()) && + DenseMapInfo::isEqual(LHS.getSelector(), + RHS.getSelector()); + } + +}; +template <> +struct isPodLike { static const bool value = true; }; +} // end llvm namespace + +namespace { +class ObjCSummaryCache { + typedef llvm::DenseMap MapTy; + MapTy M; +public: + ObjCSummaryCache() {} + + RetainSummary* find(const ObjCInterfaceDecl* D, IdentifierInfo *ClsName, + Selector S) { + // Lookup the method using the decl for the class @interface. If we + // have no decl, lookup using the class name. + return D ? find(D, S) : find(ClsName, S); + } + + RetainSummary* find(const ObjCInterfaceDecl* D, Selector S) { + // Do a lookup with the (D,S) pair. If we find a match return + // the iterator. + ObjCSummaryKey K(D, S); + MapTy::iterator I = M.find(K); + + if (I != M.end() || !D) + return I->second; + + // Walk the super chain. If we find a hit with a parent, we'll end + // up returning that summary. We actually allow that key (null,S), as + // we cache summaries for the null ObjCInterfaceDecl* to allow us to + // generate initial summaries without having to worry about NSObject + // being declared. + // FIXME: We may change this at some point. + for (ObjCInterfaceDecl* C=D->getSuperClass() ;; C=C->getSuperClass()) { + if ((I = M.find(ObjCSummaryKey(C, S))) != M.end()) + break; + + if (!C) + return NULL; + } + + // Cache the summary with original key to make the next lookup faster + // and return the iterator. + RetainSummary *Summ = I->second; + M[K] = Summ; + return Summ; + } + + + RetainSummary* find(Expr* Receiver, Selector S) { + return find(getReceiverDecl(Receiver), S); + } + + RetainSummary* find(IdentifierInfo* II, Selector S) { + // FIXME: Class method lookup. Right now we dont' have a good way + // of going between IdentifierInfo* and the class hierarchy. + MapTy::iterator I = M.find(ObjCSummaryKey(II, S)); + + if (I == M.end()) + I = M.find(ObjCSummaryKey(S)); + + return I == M.end() ? NULL : I->second; + } + + const ObjCInterfaceDecl* getReceiverDecl(Expr* E) { + if (const ObjCObjectPointerType* PT = + E->getType()->getAs()) + return PT->getInterfaceDecl(); + + return NULL; + } + + RetainSummary*& operator[](ObjCMessageExpr* ME) { + + Selector S = ME->getSelector(); + + if (Expr* Receiver = ME->getReceiver()) { + const ObjCInterfaceDecl* OD = getReceiverDecl(Receiver); + return OD ? M[ObjCSummaryKey(OD->getIdentifier(), S)] : M[S]; + } + + return M[ObjCSummaryKey(ME->getClassName(), S)]; + } + + RetainSummary*& operator[](ObjCSummaryKey K) { + return M[K]; + } + + RetainSummary*& operator[](Selector S) { + return M[ ObjCSummaryKey(S) ]; + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Data structures for managing collections of summaries. +//===----------------------------------------------------------------------===// + +namespace { +class RetainSummaryManager { + + //==-----------------------------------------------------------------==// + // Typedefs. + //==-----------------------------------------------------------------==// + + typedef llvm::DenseMap + FuncSummariesTy; + + typedef ObjCSummaryCache ObjCMethodSummariesTy; + + //==-----------------------------------------------------------------==// + // Data. + //==-----------------------------------------------------------------==// + + /// Ctx - The ASTContext object for the analyzed ASTs. + ASTContext& Ctx; + + /// CFDictionaryCreateII - An IdentifierInfo* representing the indentifier + /// "CFDictionaryCreate". + IdentifierInfo* CFDictionaryCreateII; + + /// GCEnabled - Records whether or not the analyzed code runs in GC mode. + const bool GCEnabled; + + /// FuncSummaries - A map from FunctionDecls to summaries. + FuncSummariesTy FuncSummaries; + + /// ObjCClassMethodSummaries - A map from selectors (for instance methods) + /// to summaries. + ObjCMethodSummariesTy ObjCClassMethodSummaries; + + /// ObjCMethodSummaries - A map from selectors to summaries. + ObjCMethodSummariesTy ObjCMethodSummaries; + + /// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects, + /// and all other data used by the checker. + llvm::BumpPtrAllocator BPAlloc; + + /// AF - A factory for ArgEffects objects. + ArgEffects::Factory AF; + + /// ScratchArgs - A holding buffer for construct ArgEffects. + ArgEffects ScratchArgs; + + /// ObjCAllocRetE - Default return effect for methods returning Objective-C + /// objects. + RetEffect ObjCAllocRetE; + + /// ObjCInitRetE - Default return effect for init methods returning + /// Objective-C objects. + RetEffect ObjCInitRetE; + + RetainSummary DefaultSummary; + RetainSummary* StopSummary; + + //==-----------------------------------------------------------------==// + // Methods. + //==-----------------------------------------------------------------==// + + /// getArgEffects - Returns a persistent ArgEffects object based on the + /// data in ScratchArgs. + ArgEffects getArgEffects(); + + enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable }; + +public: + RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } + + RetainSummary *getDefaultSummary() { + RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate(); + return new (Summ) RetainSummary(DefaultSummary); + } + + RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func); + + RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD); + RetainSummary* getCFSummaryGetRule(FunctionDecl* FD); + RetainSummary* getCFCreateGetRuleSummary(FunctionDecl* FD, StringRef FName); + + RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff, + ArgEffect ReceiverEff = DoNothing, + ArgEffect DefaultEff = MayEscape, + bool isEndPath = false); + + RetainSummary* getPersistentSummary(RetEffect RE, + ArgEffect ReceiverEff = DoNothing, + ArgEffect DefaultEff = MayEscape) { + return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff); + } + + RetainSummary *getPersistentStopSummary() { + if (StopSummary) + return StopSummary; + + StopSummary = getPersistentSummary(RetEffect::MakeNoRet(), + StopTracking, StopTracking); + + return StopSummary; + } + + RetainSummary *getInitMethodSummary(QualType RetTy); + + void InitializeClassMethodSummaries(); + void InitializeMethodSummaries(); +private: + + void addClsMethSummary(IdentifierInfo* ClsII, Selector S, + RetainSummary* Summ) { + ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; + } + + void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) { + ObjCClassMethodSummaries[S] = Summ; + } + + void addNSObjectMethSummary(Selector S, RetainSummary *Summ) { + ObjCMethodSummaries[S] = Summ; + } + + void addClassMethSummary(const char* Cls, const char* nullaryName, + RetainSummary *Summ) { + IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); + Selector S = GetNullarySelector(nullaryName, Ctx); + ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; + } + + void addInstMethSummary(const char* Cls, const char* nullaryName, + RetainSummary *Summ) { + IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); + Selector S = GetNullarySelector(nullaryName, Ctx); + ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; + } + + Selector generateSelector(va_list argp) { + llvm::SmallVector II; + + while (const char* s = va_arg(argp, const char*)) + II.push_back(&Ctx.Idents.get(s)); + + return Ctx.Selectors.getSelector(II.size(), &II[0]); + } + + void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries, + RetainSummary* Summ, va_list argp) { + Selector S = generateSelector(argp); + Summaries[ObjCSummaryKey(ClsII, S)] = Summ; + } + + void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) { + va_list argp; + va_start(argp, Summ); + addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); + va_end(argp); + } + + void addClsMethSummary(const char* Cls, RetainSummary* Summ, ...) { + va_list argp; + va_start(argp, Summ); + addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp); + va_end(argp); + } + + void addClsMethSummary(IdentifierInfo *II, RetainSummary* Summ, ...) { + va_list argp; + va_start(argp, Summ); + addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp); + va_end(argp); + } + + void addPanicSummary(const char* Cls, ...) { + RetainSummary* Summ = getPersistentSummary(AF.GetEmptyMap(), + RetEffect::MakeNoRet(), + DoNothing, DoNothing, true); + va_list argp; + va_start (argp, Cls); + addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); + va_end(argp); + } + +public: + + RetainSummaryManager(ASTContext& ctx, bool gcenabled) + : Ctx(ctx), + CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")), + GCEnabled(gcenabled), AF(BPAlloc), ScratchArgs(AF.GetEmptyMap()), + ObjCAllocRetE(gcenabled ? RetEffect::MakeGCNotOwned() + : RetEffect::MakeOwned(RetEffect::ObjC, true)), + ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned() + : RetEffect::MakeOwnedWhenTrackedReceiver()), + DefaultSummary(AF.GetEmptyMap() /* per-argument effects (none) */, + RetEffect::MakeNoRet() /* return effect */, + MayEscape, /* default argument effect */ + DoNothing /* receiver effect */), + StopSummary(0) { + + InitializeClassMethodSummaries(); + InitializeMethodSummaries(); + } + + ~RetainSummaryManager(); + + RetainSummary* getSummary(FunctionDecl* FD); + + RetainSummary *getInstanceMethodSummary(const ObjCMessageExpr *ME, + const GRState *state, + const LocationContext *LC); + + RetainSummary* getInstanceMethodSummary(const ObjCMessageExpr* ME, + const ObjCInterfaceDecl* ID) { + return getInstanceMethodSummary(ME->getSelector(), ME->getClassName(), + ID, ME->getMethodDecl(), ME->getType()); + } + + RetainSummary* getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName, + const ObjCInterfaceDecl* ID, + const ObjCMethodDecl *MD, + QualType RetTy); + + RetainSummary *getClassMethodSummary(Selector S, IdentifierInfo *ClsName, + const ObjCInterfaceDecl *ID, + const ObjCMethodDecl *MD, + QualType RetTy); + + RetainSummary *getClassMethodSummary(const ObjCMessageExpr *ME) { + return getClassMethodSummary(ME->getSelector(), ME->getClassName(), + ME->getClassInfo().first, + ME->getMethodDecl(), ME->getType()); + } + + /// getMethodSummary - This version of getMethodSummary is used to query + /// the summary for the current method being analyzed. + RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) { + // FIXME: Eventually this should be unneeded. + const ObjCInterfaceDecl *ID = MD->getClassInterface(); + Selector S = MD->getSelector(); + IdentifierInfo *ClsName = ID->getIdentifier(); + QualType ResultTy = MD->getResultType(); + + // Resolve the method decl last. + if (const ObjCMethodDecl *InterfaceMD = ResolveToInterfaceMethodDecl(MD)) + MD = InterfaceMD; + + if (MD->isInstanceMethod()) + return getInstanceMethodSummary(S, ClsName, ID, MD, ResultTy); + else + return getClassMethodSummary(S, ClsName, ID, MD, ResultTy); + } + + RetainSummary* getCommonMethodSummary(const ObjCMethodDecl* MD, + Selector S, QualType RetTy); + + void updateSummaryFromAnnotations(RetainSummary &Summ, + const ObjCMethodDecl *MD); + + void updateSummaryFromAnnotations(RetainSummary &Summ, + const FunctionDecl *FD); + + bool isGCEnabled() const { return GCEnabled; } + + RetainSummary *copySummary(RetainSummary *OldSumm) { + RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate(); + new (Summ) RetainSummary(*OldSumm); + return Summ; + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Implementation of checker data structures. +//===----------------------------------------------------------------------===// + +RetainSummaryManager::~RetainSummaryManager() {} + +ArgEffects RetainSummaryManager::getArgEffects() { + ArgEffects AE = ScratchArgs; + ScratchArgs = AF.GetEmptyMap(); + return AE; +} + +RetainSummary* +RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff, + ArgEffect ReceiverEff, + ArgEffect DefaultEff, + bool isEndPath) { + // Create the summary and return it. + RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate(); + new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff, isEndPath); + return Summ; +} + +//===----------------------------------------------------------------------===// +// Summary creation for functions (largely uses of Core Foundation). +//===----------------------------------------------------------------------===// + +static bool isRetain(FunctionDecl* FD, StringRef FName) { + return FName.endswith("Retain"); +} + +static bool isRelease(FunctionDecl* FD, StringRef FName) { + return FName.endswith("Release"); +} + +RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) { + // Look up a summary in our cache of FunctionDecls -> Summaries. + FuncSummariesTy::iterator I = FuncSummaries.find(FD); + if (I != FuncSummaries.end()) + return I->second; + + // No summary? Generate one. + RetainSummary *S = 0; + + do { + // We generate "stop" summaries for implicitly defined functions. + if (FD->isImplicit()) { + S = getPersistentStopSummary(); + break; + } + + // [PR 3337] Use 'getAs' to strip away any typedefs on the + // function's type. + const FunctionType* FT = FD->getType()->getAs(); + const IdentifierInfo *II = FD->getIdentifier(); + if (!II) + break; + + StringRef FName = II->getName(); + + // Strip away preceding '_'. Doing this here will effect all the checks + // down below. + FName = FName.substr(FName.find_first_not_of('_')); + + // Inspect the result type. + QualType RetTy = FT->getResultType(); + + // FIXME: This should all be refactored into a chain of "summary lookup" + // filters. + assert(ScratchArgs.isEmpty()); + + if (FName == "pthread_create") { + // Part of: . This will be addressed + // better with IPA. + S = getPersistentStopSummary(); + } else if (FName == "NSMakeCollectable") { + // Handle: id NSMakeCollectable(CFTypeRef) + S = (RetTy->isObjCIdType()) + ? getUnarySummary(FT, cfmakecollectable) + : getPersistentStopSummary(); + } else if (FName == "IOBSDNameMatching" || + FName == "IOServiceMatching" || + FName == "IOServiceNameMatching" || + FName == "IORegistryEntryIDMatching" || + FName == "IOOpenFirmwarePathMatching") { + // Part of . (IOKit) + // This should be addressed using a API table. + S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), + DoNothing, DoNothing); + } else if (FName == "IOServiceGetMatchingService" || + FName == "IOServiceGetMatchingServices") { + // FIXES: + // This should be addressed using a API table. This strcmp is also + // a little gross, but there is no need to super optimize here. + ScratchArgs = AF.Add(ScratchArgs, 1, DecRef); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName == "IOServiceAddNotification" || + FName == "IOServiceAddMatchingNotification") { + // Part of . (IOKit) + // This should be addressed using a API table. + ScratchArgs = AF.Add(ScratchArgs, 2, DecRef); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName == "CVPixelBufferCreateWithBytes") { + // FIXES: + // Eventually this can be improved by recognizing that the pixel + // buffer passed to CVPixelBufferCreateWithBytes is released via + // a callback and doing full IPA to make sure this is done correctly. + // FIXME: This function has an out parameter that returns an + // allocated object. + ScratchArgs = AF.Add(ScratchArgs, 7, StopTracking); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName == "CGBitmapContextCreateWithData") { + // FIXES: + // Eventually this can be improved by recognizing that 'releaseInfo' + // passed to CGBitmapContextCreateWithData is released via + // a callback and doing full IPA to make sure this is done correctly. + ScratchArgs = AF.Add(ScratchArgs, 8, StopTracking); + S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), + DoNothing, DoNothing); + } else if (FName == "CVPixelBufferCreateWithPlanarBytes") { + // FIXES: + // Eventually this can be improved by recognizing that the pixel + // buffer passed to CVPixelBufferCreateWithPlanarBytes is released + // via a callback and doing full IPA to make sure this is done + // correctly. + ScratchArgs = AF.Add(ScratchArgs, 12, StopTracking); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } + + // Did we get a summary? + if (S) + break; + + // Enable this code once the semantics of NSDeallocateObject are resolved + // for GC. +#if 0 + // Handle: NSDeallocateObject(id anObject); + // This method does allow 'nil' (although we don't check it now). + if (strcmp(FName, "NSDeallocateObject") == 0) { + return RetTy == Ctx.VoidTy + ? getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Dealloc) + : getPersistentStopSummary(); + } +#endif + + if (RetTy->isPointerType()) { + // For CoreFoundation ('CF') types. + if (cocoa::isRefType(RetTy, "CF", FName)) { + if (isRetain(FD, FName)) + S = getUnarySummary(FT, cfretain); + else if (FName.find("MakeCollectable") != StringRef::npos) + S = getUnarySummary(FT, cfmakecollectable); + else + S = getCFCreateGetRuleSummary(FD, FName); + + break; + } + + // For CoreGraphics ('CG') types. + if (cocoa::isRefType(RetTy, "CG", FName)) { + if (isRetain(FD, FName)) + S = getUnarySummary(FT, cfretain); + else + S = getCFCreateGetRuleSummary(FD, FName); + + break; + } + + // For the Disk Arbitration API (DiskArbitration/DADisk.h) + if (cocoa::isRefType(RetTy, "DADisk") || + cocoa::isRefType(RetTy, "DADissenter") || + cocoa::isRefType(RetTy, "DASessionRef")) { + S = getCFCreateGetRuleSummary(FD, FName); + break; + } + + break; + } + + // Check for release functions, the only kind of functions that we care + // about that don't return a pointer type. + if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G')) { + // Test for 'CGCF'. + FName = FName.substr(FName.startswith("CGCF") ? 4 : 2); + + if (isRelease(FD, FName)) + S = getUnarySummary(FT, cfrelease); + else { + assert (ScratchArgs.isEmpty()); + // Remaining CoreFoundation and CoreGraphics functions. + // We use to assume that they all strictly followed the ownership idiom + // and that ownership cannot be transferred. While this is technically + // correct, many methods allow a tracked object to escape. For example: + // + // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...); + // CFDictionaryAddValue(y, key, x); + // CFRelease(x); + // ... it is okay to use 'x' since 'y' has a reference to it + // + // We handle this and similar cases with the follow heuristic. If the + // function name contains "InsertValue", "SetValue", "AddValue", + // "AppendValue", or "SetAttribute", then we assume that arguments may + // "escape." This means that something else holds on to the object, + // allowing it be used even after its local retain count drops to 0. + ArgEffect E = (StrInStrNoCase(FName, "InsertValue") != StringRef::npos|| + StrInStrNoCase(FName, "AddValue") != StringRef::npos || + StrInStrNoCase(FName, "SetValue") != StringRef::npos || + StrInStrNoCase(FName, "AppendValue") != StringRef::npos|| + StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) + ? MayEscape : DoNothing; + + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, E); + } + } + } + while (0); + + if (!S) + S = getDefaultSummary(); + + // Annotations override defaults. + assert(S); + updateSummaryFromAnnotations(*S, FD); + + FuncSummaries[FD] = S; + return S; +} + +RetainSummary* +RetainSummaryManager::getCFCreateGetRuleSummary(FunctionDecl* FD, + StringRef FName) { + + if (FName.find("Create") != StringRef::npos || + FName.find("Copy") != StringRef::npos) + return getCFSummaryCreateRule(FD); + + if (FName.find("Get") != StringRef::npos) + return getCFSummaryGetRule(FD); + + return getDefaultSummary(); +} + +RetainSummary* +RetainSummaryManager::getUnarySummary(const FunctionType* FT, + UnaryFuncKind func) { + + // Sanity check that this is *really* a unary function. This can + // happen if people do weird things. + const FunctionProtoType* FTP = dyn_cast(FT); + if (!FTP || FTP->getNumArgs() != 1) + return getPersistentStopSummary(); + + assert (ScratchArgs.isEmpty()); + + switch (func) { + case cfretain: { + ScratchArgs = AF.Add(ScratchArgs, 0, IncRef); + return getPersistentSummary(RetEffect::MakeAlias(0), + DoNothing, DoNothing); + } + + case cfrelease: { + ScratchArgs = AF.Add(ScratchArgs, 0, DecRef); + return getPersistentSummary(RetEffect::MakeNoRet(), + DoNothing, DoNothing); + } + + case cfmakecollectable: { + ScratchArgs = AF.Add(ScratchArgs, 0, MakeCollectable); + return getPersistentSummary(RetEffect::MakeAlias(0),DoNothing, DoNothing); + } + + default: + assert (false && "Not a supported unary function."); + return getDefaultSummary(); + } +} + +RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) { + assert (ScratchArgs.isEmpty()); + + if (FD->getIdentifier() == CFDictionaryCreateII) { + ScratchArgs = AF.Add(ScratchArgs, 1, DoNothingByRef); + ScratchArgs = AF.Add(ScratchArgs, 2, DoNothingByRef); + } + + return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); +} + +RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) { + assert (ScratchArgs.isEmpty()); + return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF), + DoNothing, DoNothing); +} + +//===----------------------------------------------------------------------===// +// Summary creation for Selectors. +//===----------------------------------------------------------------------===// + +RetainSummary* +RetainSummaryManager::getInitMethodSummary(QualType RetTy) { + assert(ScratchArgs.isEmpty()); + // 'init' methods conceptually return a newly allocated object and claim + // the receiver. + if (cocoa::isCocoaObjectRef(RetTy) || cocoa::isCFObjectRef(RetTy)) + return getPersistentSummary(ObjCInitRetE, DecRefMsg); + + return getDefaultSummary(); +} + +void +RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, + const FunctionDecl *FD) { + if (!FD) + return; + + QualType RetTy = FD->getResultType(); + + // Determine if there is a special return effect for this method. + if (cocoa::isCocoaObjectRef(RetTy)) { + if (FD->getAttr()) { + Summ.setRetEffect(ObjCAllocRetE); + } + else if (FD->getAttr()) { + Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); + } + } + else if (RetTy->getAs()) { + if (FD->getAttr()) { + Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); + } + } +} + +void +RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, + const ObjCMethodDecl *MD) { + if (!MD) + return; + + bool isTrackedLoc = false; + + // Determine if there is a special return effect for this method. + if (cocoa::isCocoaObjectRef(MD->getResultType())) { + if (MD->getAttr()) { + Summ.setRetEffect(ObjCAllocRetE); + return; + } + + isTrackedLoc = true; + } + + if (!isTrackedLoc) + isTrackedLoc = MD->getResultType()->getAs() != NULL; + + if (isTrackedLoc && MD->getAttr()) + Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); +} + +RetainSummary* +RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, + Selector S, QualType RetTy) { + + if (MD) { + // Scan the method decl for 'void*' arguments. These should be treated + // as 'StopTracking' because they are often used with delegates. + // Delegates are a frequent form of false positives with the retain + // count checker. + unsigned i = 0; + for (ObjCMethodDecl::param_iterator I = MD->param_begin(), + E = MD->param_end(); I != E; ++I, ++i) + if (ParmVarDecl *PD = *I) { + QualType Ty = Ctx.getCanonicalType(PD->getType()); + if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy) + ScratchArgs = AF.Add(ScratchArgs, i, StopTracking); + } + } + + // Any special effect for the receiver? + ArgEffect ReceiverEff = DoNothing; + + // If one of the arguments in the selector has the keyword 'delegate' we + // should stop tracking the reference count for the receiver. This is + // because the reference count is quite possibly handled by a delegate + // method. + if (S.isKeywordSelector()) { + const std::string &str = S.getAsString(); + assert(!str.empty()); + if (StrInStrNoCase(str, "delegate:") != StringRef::npos) + ReceiverEff = StopTracking; + } + + // Look for methods that return an owned object. + if (cocoa::isCocoaObjectRef(RetTy)) { + // EXPERIMENTAL: Assume the Cocoa conventions for all objects returned + // by instance methods. + RetEffect E = cocoa::followsFundamentalRule(S) + ? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC); + + return getPersistentSummary(E, ReceiverEff, MayEscape); + } + + // Look for methods that return an owned core foundation object. + if (cocoa::isCFObjectRef(RetTy)) { + RetEffect E = cocoa::followsFundamentalRule(S) + ? RetEffect::MakeOwned(RetEffect::CF, true) + : RetEffect::MakeNotOwned(RetEffect::CF); + + return getPersistentSummary(E, ReceiverEff, MayEscape); + } + + if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing) + return getDefaultSummary(); + + return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, MayEscape); +} + +RetainSummary* +RetainSummaryManager::getInstanceMethodSummary(const ObjCMessageExpr *ME, + const GRState *state, + const LocationContext *LC) { + + // We need the type-information of the tracked receiver object + // Retrieve it from the state. + const Expr *Receiver = ME->getReceiver(); + const ObjCInterfaceDecl* ID = 0; + + // FIXME: Is this really working as expected? There are cases where + // we just use the 'ID' from the message expression. + SVal receiverV = state->getSValAsScalarOrLoc(Receiver); + + // FIXME: Eventually replace the use of state->get with + // a generic API for reasoning about the Objective-C types of symbolic + // objects. + if (SymbolRef Sym = receiverV.getAsLocSymbol()) + if (const RefVal *T = state->get(Sym)) + if (const ObjCObjectPointerType* PT = + T->getType()->getAs()) + ID = PT->getInterfaceDecl(); + + // FIXME: this is a hack. This may or may not be the actual method + // that is called. + if (!ID) { + if (const ObjCObjectPointerType *PT = + Receiver->getType()->getAs()) + ID = PT->getInterfaceDecl(); + } + + // FIXME: The receiver could be a reference to a class, meaning that + // we should use the class method. + RetainSummary *Summ = getInstanceMethodSummary(ME, ID); + + // Special-case: are we sending a mesage to "self"? + // This is a hack. When we have full-IP this should be removed. + if (isa(LC->getDecl())) { + if (const loc::MemRegionVal *L = dyn_cast(&receiverV)) { + // Get the region associated with 'self'. + if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl()) { + SVal SelfVal = state->getSVal(state->getRegion(SelfDecl, LC)); + if (L->StripCasts() == SelfVal.getAsRegion()) { + // Update the summary to make the default argument effect + // 'StopTracking'. + Summ = copySummary(Summ); + Summ->setDefaultArgEffect(StopTracking); + } + } + } + } + + return Summ ? Summ : getDefaultSummary(); +} + +RetainSummary* +RetainSummaryManager::getInstanceMethodSummary(Selector S, + IdentifierInfo *ClsName, + const ObjCInterfaceDecl* ID, + const ObjCMethodDecl *MD, + QualType RetTy) { + + // Look up a summary in our summary cache. + RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S); + + if (!Summ) { + assert(ScratchArgs.isEmpty()); + + // "initXXX": pass-through for receiver. + if (cocoa::deriveNamingConvention(S) == cocoa::InitRule) + Summ = getInitMethodSummary(RetTy); + else + Summ = getCommonMethodSummary(MD, S, RetTy); + + // Annotations override defaults. + updateSummaryFromAnnotations(*Summ, MD); + + // Memoize the summary. + ObjCMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; + } + + return Summ; +} + +RetainSummary* +RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName, + const ObjCInterfaceDecl *ID, + const ObjCMethodDecl *MD, + QualType RetTy) { + + assert(ClsName && "Class name must be specified."); + RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S); + + if (!Summ) { + Summ = getCommonMethodSummary(MD, S, RetTy); + // Annotations override defaults. + updateSummaryFromAnnotations(*Summ, MD); + // Memoize the summary. + ObjCClassMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; + } + + return Summ; +} + +void RetainSummaryManager::InitializeClassMethodSummaries() { + assert(ScratchArgs.isEmpty()); + RetainSummary* Summ = getPersistentSummary(ObjCAllocRetE); + + // Create the summaries for "alloc", "new", and "allocWithZone:" for + // NSObject and its derivatives. + addNSObjectClsMethSummary(GetNullarySelector("alloc", Ctx), Summ); + addNSObjectClsMethSummary(GetNullarySelector("new", Ctx), Summ); + addNSObjectClsMethSummary(GetUnarySelector("allocWithZone", Ctx), Summ); + + // Create the [NSAssertionHandler currentHander] summary. + addClassMethSummary("NSAssertionHandler", "currentHandler", + getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC))); + + // Create the [NSAutoreleasePool addObject:] summary. + ScratchArgs = AF.Add(ScratchArgs, 0, Autorelease); + addClassMethSummary("NSAutoreleasePool", "addObject", + getPersistentSummary(RetEffect::MakeNoRet(), + DoNothing, Autorelease)); + + // Create a summary for [NSCursor dragCopyCursor]. + addClassMethSummary("NSCursor", "dragCopyCursor", + getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, + DoNothing)); + + // Create the summaries for [NSObject performSelector...]. We treat + // these as 'stop tracking' for the arguments because they are often + // used for delegates that can release the object. When we have better + // inter-procedural analysis we can potentially do something better. This + // workaround is to remove false positives. + Summ = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking); + IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject"); + addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", + "afterDelay", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", + "afterDelay", "inModes", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", + "withObject", "waitUntilDone", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", + "withObject", "waitUntilDone", "modes", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", + "withObject", "waitUntilDone", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", + "withObject", "waitUntilDone", "modes", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground", + "withObject", NULL); + + // Specially handle NSData. + RetainSummary *dataWithBytesNoCopySumm = + getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC), DoNothing, + DoNothing); + addClsMethSummary("NSData", dataWithBytesNoCopySumm, + "dataWithBytesNoCopy", "length", NULL); + addClsMethSummary("NSData", dataWithBytesNoCopySumm, + "dataWithBytesNoCopy", "length", "freeWhenDone", NULL); +} + +void RetainSummaryManager::InitializeMethodSummaries() { + + assert (ScratchArgs.isEmpty()); + + // Create the "init" selector. It just acts as a pass-through for the + // receiver. + RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg); + addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm); + + // awakeAfterUsingCoder: behaves basically like an 'init' method. It + // claims the receiver and returns a retained object. + addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx), + InitSumm); + + // The next methods are allocators. + RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE); + RetainSummary *CFAllocSumm = + getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); + + // Create the "copy" selector. + addNSObjectMethSummary(GetNullarySelector("copy", Ctx), AllocSumm); + + // Create the "mutableCopy" selector. + addNSObjectMethSummary(GetNullarySelector("mutableCopy", Ctx), AllocSumm); + + // Create the "retain" selector. + RetEffect E = RetEffect::MakeReceiverAlias(); + RetainSummary *Summ = getPersistentSummary(E, IncRefMsg); + addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); + + // Create the "release" selector. + Summ = getPersistentSummary(E, DecRefMsg); + addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); + + // Create the "drain" selector. + Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef); + addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ); + + // Create the -dealloc summary. + Summ = getPersistentSummary(RetEffect::MakeNoRet(), Dealloc); + addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); + + // Create the "autorelease" selector. + Summ = getPersistentSummary(E, Autorelease); + addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); + + // Specially handle NSAutoreleasePool. + addInstMethSummary("NSAutoreleasePool", "init", + getPersistentSummary(RetEffect::MakeReceiverAlias(), + NewAutoreleasePool)); + + // For NSWindow, allocated objects are (initially) self-owned. + // FIXME: For now we opt for false negatives with NSWindow, as these objects + // self-own themselves. However, they only do this once they are displayed. + // Thus, we need to track an NSWindow's display status. + // This is tracked in . + // See also http://llvm.org/bugs/show_bug.cgi?id=3714. + RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(), + StopTracking, + StopTracking); + + addClassMethSummary("NSWindow", "alloc", NoTrackYet); + +#if 0 + addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", + "styleMask", "backing", "defer", NULL); + + addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", + "styleMask", "backing", "defer", "screen", NULL); +#endif + + // For NSPanel (which subclasses NSWindow), allocated objects are not + // self-owned. + // FIXME: For now we don't track NSPanels. object for the same reason + // as for NSWindow objects. + addClassMethSummary("NSPanel", "alloc", NoTrackYet); + +#if 0 + addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", + "styleMask", "backing", "defer", NULL); + + addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", + "styleMask", "backing", "defer", "screen", NULL); +#endif + + // Don't track allocated autorelease pools yet, as it is okay to prematurely + // exit a method. + addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); + + // Create NSAssertionHandler summaries. + addPanicSummary("NSAssertionHandler", "handleFailureInFunction", "file", + "lineNumber", "description", NULL); + + addPanicSummary("NSAssertionHandler", "handleFailureInMethod", "object", + "file", "lineNumber", "description", NULL); + + // Create summaries QCRenderer/QCView -createSnapShotImageOfType: + addInstMethSummary("QCRenderer", AllocSumm, + "createSnapshotImageOfType", NULL); + addInstMethSummary("QCView", AllocSumm, + "createSnapshotImageOfType", NULL); + + // Create summaries for CIContext, 'createCGImage' and + // 'createCGLayerWithSize'. These objects are CF objects, and are not + // automatically garbage collected. + addInstMethSummary("CIContext", CFAllocSumm, + "createCGImage", "fromRect", NULL); + addInstMethSummary("CIContext", CFAllocSumm, + "createCGImage", "fromRect", "format", "colorSpace", NULL); + addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", + "info", NULL); +} + +//===----------------------------------------------------------------------===// +// AutoreleaseBindings - State used to track objects in autorelease pools. +//===----------------------------------------------------------------------===// + +typedef llvm::ImmutableMap ARCounts; +typedef llvm::ImmutableMap ARPoolContents; +typedef llvm::ImmutableList ARStack; + +static int AutoRCIndex = 0; +static int AutoRBIndex = 0; + +namespace { class AutoreleasePoolContents {}; } +namespace { class AutoreleaseStack {}; } + +namespace clang { +template<> struct GRStateTrait + : public GRStatePartialTrait { + static inline void* GDMIndex() { return &AutoRBIndex; } +}; + +template<> struct GRStateTrait + : public GRStatePartialTrait { + static inline void* GDMIndex() { return &AutoRCIndex; } +}; +} // end clang namespace + +static SymbolRef GetCurrentAutoreleasePool(const GRState* state) { + ARStack stack = state->get(); + return stack.isEmpty() ? SymbolRef() : stack.getHead(); +} + +static const GRState * SendAutorelease(const GRState *state, + ARCounts::Factory &F, SymbolRef sym) { + + SymbolRef pool = GetCurrentAutoreleasePool(state); + const ARCounts *cnts = state->get(pool); + ARCounts newCnts(0); + + if (cnts) { + const unsigned *cnt = (*cnts).lookup(sym); + newCnts = F.Add(*cnts, sym, cnt ? *cnt + 1 : 1); + } + else + newCnts = F.Add(F.GetEmptyMap(), sym, 1); + + return state->set(pool, newCnts); +} + +//===----------------------------------------------------------------------===// +// Transfer functions. +//===----------------------------------------------------------------------===// + +namespace { + +class CFRefCount : public GRTransferFuncs { +public: + class BindingsPrinter : public GRState::Printer { + public: + virtual void Print(llvm::raw_ostream& Out, const GRState* state, + const char* nl, const char* sep); + }; + +private: + typedef llvm::DenseMap + SummaryLogTy; + + RetainSummaryManager Summaries; + SummaryLogTy SummaryLog; + const LangOptions& LOpts; + ARCounts::Factory ARCountFactory; + + BugType *useAfterRelease, *releaseNotOwned; + BugType *deallocGC, *deallocNotOwned; + BugType *leakWithinFunction, *leakAtReturn; + BugType *overAutorelease; + BugType *returnNotOwnedForOwned; + BugReporter *BR; + + const GRState * Update(const GRState * state, SymbolRef sym, RefVal V, ArgEffect E, + RefVal::Kind& hasErr); + + void ProcessNonLeakError(ExplodedNodeSet& Dst, + GRStmtNodeBuilder& Builder, + Expr* NodeExpr, Expr* ErrorExpr, + ExplodedNode* Pred, + const GRState* St, + RefVal::Kind hasErr, SymbolRef Sym); + + const GRState * HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, + llvm::SmallVectorImpl &Leaked); + + ExplodedNode* ProcessLeaks(const GRState * state, + llvm::SmallVectorImpl &Leaked, + GenericNodeBuilder &Builder, + GRExprEngine &Eng, + ExplodedNode *Pred = 0); + +public: + CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts) + : Summaries(Ctx, gcenabled), + LOpts(lopts), useAfterRelease(0), releaseNotOwned(0), + deallocGC(0), deallocNotOwned(0), + leakWithinFunction(0), leakAtReturn(0), overAutorelease(0), + returnNotOwnedForOwned(0), BR(0) {} + + virtual ~CFRefCount() {} + + void RegisterChecks(GRExprEngine &Eng); + + virtual void RegisterPrinters(std::vector& Printers) { + Printers.push_back(new BindingsPrinter()); + } + + bool isGCEnabled() const { return Summaries.isGCEnabled(); } + const LangOptions& getLangOptions() const { return LOpts; } + + const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const { + SummaryLogTy::const_iterator I = SummaryLog.find(N); + return I == SummaryLog.end() ? 0 : I->second; + } + + // Calls. + + void EvalSummary(ExplodedNodeSet& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder& Builder, + Expr* Ex, + Expr* Receiver, + const RetainSummary& Summ, + const MemRegion *Callee, + ExprIterator arg_beg, ExprIterator arg_end, + ExplodedNode* Pred, const GRState *state); + + virtual void EvalCall(ExplodedNodeSet& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder& Builder, + CallExpr* CE, SVal L, + ExplodedNode* Pred); + + + virtual void EvalObjCMessageExpr(ExplodedNodeSet& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder& Builder, + ObjCMessageExpr* ME, + ExplodedNode* Pred, + const GRState *state); + + bool EvalObjCMessageExprAux(ExplodedNodeSet& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder& Builder, + ObjCMessageExpr* ME, + ExplodedNode* Pred); + + // Stores. + virtual void EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val); + + // End-of-path. + + virtual void EvalEndPath(GRExprEngine& Engine, + GREndPathNodeBuilder& Builder); + + virtual void EvalDeadSymbols(ExplodedNodeSet& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder& Builder, + ExplodedNode* Pred, + Stmt* S, const GRState* state, + SymbolReaper& SymReaper); + + std::pair + HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd, + ExplodedNode* Pred, GRExprEngine &Eng, + SymbolRef Sym, RefVal V, bool &stop); + // Return statements. + + virtual void EvalReturn(ExplodedNodeSet& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder& Builder, + ReturnStmt* S, + ExplodedNode* Pred); + + // Assumptions. + + virtual const GRState *EvalAssume(const GRState* state, SVal condition, + bool assumption); +}; + +} // end anonymous namespace + +static void PrintPool(llvm::raw_ostream &Out, SymbolRef Sym, + const GRState *state) { + Out << ' '; + if (Sym) + Out << Sym->getSymbolID(); + else + Out << ""; + Out << ":{"; + + // Get the contents of the pool. + if (const ARCounts *cnts = state->get(Sym)) + for (ARCounts::iterator J=cnts->begin(), EJ=cnts->end(); J != EJ; ++J) + Out << '(' << J.getKey() << ',' << J.getData() << ')'; + + Out << '}'; +} + +void CFRefCount::BindingsPrinter::Print(llvm::raw_ostream& Out, + const GRState* state, + const char* nl, const char* sep) { + + RefBindings B = state->get(); + + if (!B.isEmpty()) + Out << sep << nl; + + for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { + Out << (*I).first << " : "; + (*I).second.print(Out); + Out << nl; + } + + // Print the autorelease stack. + Out << sep << nl << "AR pool stack:"; + ARStack stack = state->get(); + + PrintPool(Out, SymbolRef(), state); // Print the caller's pool. + for (ARStack::iterator I=stack.begin(), E=stack.end(); I!=E; ++I) + PrintPool(Out, *I, state); + + Out << nl; +} + +//===----------------------------------------------------------------------===// +// Error reporting. +//===----------------------------------------------------------------------===// + +namespace { + + //===-------------===// + // Bug Descriptions. // + //===-------------===// + + class CFRefBug : public BugType { + protected: + CFRefCount& TF; + + CFRefBug(CFRefCount* tf, llvm::StringRef name) + : BugType(name, "Memory (Core Foundation/Objective-C)"), TF(*tf) {} + public: + + CFRefCount& getTF() { return TF; } + const CFRefCount& getTF() const { return TF; } + + // FIXME: Eventually remove. + virtual const char* getDescription() const = 0; + + virtual bool isLeak() const { return false; } + }; + + class UseAfterRelease : public CFRefBug { + public: + UseAfterRelease(CFRefCount* tf) + : CFRefBug(tf, "Use-after-release") {} + + const char* getDescription() const { + return "Reference-counted object is used after it is released"; + } + }; + + class BadRelease : public CFRefBug { + public: + BadRelease(CFRefCount* tf) : CFRefBug(tf, "Bad release") {} + + const char* getDescription() const { + return "Incorrect decrement of the reference count of an object that is " + "not owned at this point by the caller"; + } + }; + + class DeallocGC : public CFRefBug { + public: + DeallocGC(CFRefCount *tf) + : CFRefBug(tf, "-dealloc called while using garbage collection") {} + + const char *getDescription() const { + return "-dealloc called while using garbage collection"; + } + }; + + class DeallocNotOwned : public CFRefBug { + public: + DeallocNotOwned(CFRefCount *tf) + : CFRefBug(tf, "-dealloc sent to non-exclusively owned object") {} + + const char *getDescription() const { + return "-dealloc sent to object that may be referenced elsewhere"; + } + }; + + class OverAutorelease : public CFRefBug { + public: + OverAutorelease(CFRefCount *tf) : + CFRefBug(tf, "Object sent -autorelease too many times") {} + + const char *getDescription() const { + return "Object sent -autorelease too many times"; + } + }; + + class ReturnedNotOwnedForOwned : public CFRefBug { + public: + ReturnedNotOwnedForOwned(CFRefCount *tf) : + CFRefBug(tf, "Method should return an owned object") {} + + const char *getDescription() const { + return "Object with +0 retain counts returned to caller where a +1 " + "(owning) retain count is expected"; + } + }; + + class Leak : public CFRefBug { + const bool isReturn; + protected: + Leak(CFRefCount* tf, llvm::StringRef name, bool isRet) + : CFRefBug(tf, name), isReturn(isRet) {} + public: + + const char* getDescription() const { return ""; } + + bool isLeak() const { return true; } + }; + + class LeakAtReturn : public Leak { + public: + LeakAtReturn(CFRefCount* tf, llvm::StringRef name) + : Leak(tf, name, true) {} + }; + + class LeakWithinFunction : public Leak { + public: + LeakWithinFunction(CFRefCount* tf, llvm::StringRef name) + : Leak(tf, name, false) {} + }; + + //===---------===// + // Bug Reports. // + //===---------===// + + class CFRefReport : public RangedBugReport { + protected: + SymbolRef Sym; + const CFRefCount &TF; + public: + CFRefReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode *n, SymbolRef sym) + : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {} + + CFRefReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode *n, SymbolRef sym, llvm::StringRef endText) + : RangedBugReport(D, D.getDescription(), endText, n), Sym(sym), TF(tf) {} + + virtual ~CFRefReport() {} + + CFRefBug& getBugType() { + return (CFRefBug&) RangedBugReport::getBugType(); + } + const CFRefBug& getBugType() const { + return (const CFRefBug&) RangedBugReport::getBugType(); + } + + virtual void getRanges(const SourceRange*& beg, const SourceRange*& end) { + if (!getBugType().isLeak()) + RangedBugReport::getRanges(beg, end); + else + beg = end = 0; + } + + SymbolRef getSymbol() const { return Sym; } + + PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, + const ExplodedNode* N); + + std::pair getExtraDescriptiveText(); + + PathDiagnosticPiece* VisitNode(const ExplodedNode* N, + const ExplodedNode* PrevN, + BugReporterContext& BRC); + }; + + class CFRefLeakReport : public CFRefReport { + SourceLocation AllocSite; + const MemRegion* AllocBinding; + public: + CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode *n, SymbolRef sym, + GRExprEngine& Eng); + + PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, + const ExplodedNode* N); + + SourceLocation getLocation() const { return AllocSite; } + }; +} // end anonymous namespace + + + +static const char* Msgs[] = { + // GC only + "Code is compiled to only use garbage collection", + // No GC. + "Code is compiled to use reference counts", + // Hybrid, with GC. + "Code is compiled to use either garbage collection (GC) or reference counts" + " (non-GC). The bug occurs with GC enabled", + // Hybrid, without GC + "Code is compiled to use either garbage collection (GC) or reference counts" + " (non-GC). The bug occurs in non-GC mode" +}; + +std::pair CFRefReport::getExtraDescriptiveText() { + CFRefCount& TF = static_cast(getBugType()).getTF(); + + switch (TF.getLangOptions().getGCMode()) { + default: + assert(false); + + case LangOptions::GCOnly: + assert (TF.isGCEnabled()); + return std::make_pair(&Msgs[0], &Msgs[0]+1); + + case LangOptions::NonGC: + assert (!TF.isGCEnabled()); + return std::make_pair(&Msgs[1], &Msgs[1]+1); + + case LangOptions::HybridGC: + if (TF.isGCEnabled()) + return std::make_pair(&Msgs[2], &Msgs[2]+1); + else + return std::make_pair(&Msgs[3], &Msgs[3]+1); + } +} + +static inline bool contains(const llvm::SmallVectorImpl& V, + ArgEffect X) { + for (llvm::SmallVectorImpl::const_iterator I=V.begin(), E=V.end(); + I!=E; ++I) + if (*I == X) return true; + + return false; +} + +PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, + const ExplodedNode* PrevN, + BugReporterContext& BRC) { + + if (!isa(N->getLocation())) + return NULL; + + // Check if the type state has changed. + const GRState *PrevSt = PrevN->getState(); + const GRState *CurrSt = N->getState(); + + const RefVal* CurrT = CurrSt->get(Sym); + if (!CurrT) return NULL; + + const RefVal &CurrV = *CurrT; + const RefVal *PrevT = PrevSt->get(Sym); + + // Create a string buffer to constain all the useful things we want + // to tell the user. + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + // This is the allocation site since the previous node had no bindings + // for this symbol. + if (!PrevT) { + const Stmt* S = cast(N->getLocation()).getStmt(); + + if (const CallExpr *CE = dyn_cast(S)) { + // Get the name of the callee (if it is available). + SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee()); + if (const FunctionDecl* FD = X.getAsFunctionDecl()) + os << "Call to function '" << FD->getNameAsString() <<'\''; + else + os << "function call"; + } + else { + assert (isa(S)); + os << "Method"; + } + + if (CurrV.getObjKind() == RetEffect::CF) { + os << " returns a Core Foundation object with a "; + } + else { + assert (CurrV.getObjKind() == RetEffect::ObjC); + os << " returns an Objective-C object with a "; + } + + if (CurrV.isOwned()) { + os << "+1 retain count (owning reference)."; + + if (static_cast(getBugType()).getTF().isGCEnabled()) { + assert(CurrV.getObjKind() == RetEffect::CF); + os << " " + "Core Foundation objects are not automatically garbage collected."; + } + } + else { + assert (CurrV.isNotOwned()); + os << "+0 retain count (non-owning reference)."; + } + + PathDiagnosticLocation Pos(S, BRC.getSourceManager()); + return new PathDiagnosticEventPiece(Pos, os.str()); + } + + // Gather up the effects that were performed on the object at this + // program point + llvm::SmallVector AEffects; + + if (const RetainSummary *Summ = + TF.getSummaryOfNode(BRC.getNodeResolver().getOriginalNode(N))) { + // We only have summaries attached to nodes after evaluating CallExpr and + // ObjCMessageExprs. + const Stmt* S = cast(N->getLocation()).getStmt(); + + if (const CallExpr *CE = dyn_cast(S)) { + // Iterate through the parameter expressions and see if the symbol + // was ever passed as an argument. + unsigned i = 0; + + for (CallExpr::const_arg_iterator AI=CE->arg_begin(), AE=CE->arg_end(); + AI!=AE; ++AI, ++i) { + + // Retrieve the value of the argument. Is it the symbol + // we are interested in? + if (CurrSt->getSValAsScalarOrLoc(*AI).getAsLocSymbol() != Sym) + continue; + + // We have an argument. Get the effect! + AEffects.push_back(Summ->getArg(i)); + } + } + else if (const ObjCMessageExpr *ME = dyn_cast(S)) { + if (const Expr *receiver = ME->getReceiver()) + if (CurrSt->getSValAsScalarOrLoc(receiver).getAsLocSymbol() == Sym) { + // The symbol we are tracking is the receiver. + AEffects.push_back(Summ->getReceiverEffect()); + } + } + } + + do { + // Get the previous type state. + RefVal PrevV = *PrevT; + + // Specially handle -dealloc. + if (!TF.isGCEnabled() && contains(AEffects, Dealloc)) { + // Determine if the object's reference count was pushed to zero. + assert(!(PrevV == CurrV) && "The typestate *must* have changed."); + // We may not have transitioned to 'release' if we hit an error. + // This case is handled elsewhere. + if (CurrV.getKind() == RefVal::Released) { + assert(CurrV.getCombinedCounts() == 0); + os << "Object released by directly sending the '-dealloc' message"; + break; + } + } + + // Specially handle CFMakeCollectable and friends. + if (contains(AEffects, MakeCollectable)) { + // Get the name of the function. + const Stmt* S = cast(N->getLocation()).getStmt(); + SVal X = CurrSt->getSValAsScalarOrLoc(cast(S)->getCallee()); + const FunctionDecl* FD = X.getAsFunctionDecl(); + const std::string& FName = FD->getNameAsString(); + + if (TF.isGCEnabled()) { + // Determine if the object's reference count was pushed to zero. + assert(!(PrevV == CurrV) && "The typestate *must* have changed."); + + os << "In GC mode a call to '" << FName + << "' decrements an object's retain count and registers the " + "object with the garbage collector. "; + + if (CurrV.getKind() == RefVal::Released) { + assert(CurrV.getCount() == 0); + os << "Since it now has a 0 retain count the object can be " + "automatically collected by the garbage collector."; + } + else + os << "An object must have a 0 retain count to be garbage collected. " + "After this call its retain count is +" << CurrV.getCount() + << '.'; + } + else + os << "When GC is not enabled a call to '" << FName + << "' has no effect on its argument."; + + // Nothing more to say. + break; + } + + // Determine if the typestate has changed. + if (!(PrevV == CurrV)) + switch (CurrV.getKind()) { + case RefVal::Owned: + case RefVal::NotOwned: + + if (PrevV.getCount() == CurrV.getCount()) { + // Did an autorelease message get sent? + if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount()) + return 0; + + assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); + os << "Object sent -autorelease message"; + break; + } + + if (PrevV.getCount() > CurrV.getCount()) + os << "Reference count decremented."; + else + os << "Reference count incremented."; + + if (unsigned Count = CurrV.getCount()) + os << " The object now has a +" << Count << " retain count."; + + if (PrevV.getKind() == RefVal::Released) { + assert(TF.isGCEnabled() && CurrV.getCount() > 0); + os << " The object is not eligible for garbage collection until the " + "retain count reaches 0 again."; + } + + break; + + case RefVal::Released: + os << "Object released."; + break; + + case RefVal::ReturnedOwned: + os << "Object returned to caller as an owning reference (single retain " + "count transferred to caller)."; + break; + + case RefVal::ReturnedNotOwned: + os << "Object returned to caller with a +0 (non-owning) retain count."; + break; + + default: + return NULL; + } + + // Emit any remaining diagnostics for the argument effects (if any). + for (llvm::SmallVectorImpl::iterator I=AEffects.begin(), + E=AEffects.end(); I != E; ++I) { + + // A bunch of things have alternate behavior under GC. + if (TF.isGCEnabled()) + switch (*I) { + default: break; + case Autorelease: + os << "In GC mode an 'autorelease' has no effect."; + continue; + case IncRefMsg: + os << "In GC mode the 'retain' message has no effect."; + continue; + case DecRefMsg: + os << "In GC mode the 'release' message has no effect."; + continue; + } + } + } while (0); + + if (os.str().empty()) + return 0; // We have nothing to say! + + const Stmt* S = cast(N->getLocation()).getStmt(); + PathDiagnosticLocation Pos(S, BRC.getSourceManager()); + PathDiagnosticPiece* P = new PathDiagnosticEventPiece(Pos, os.str()); + + // Add the range by scanning the children of the statement for any bindings + // to Sym. + for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); + I!=E; ++I) + if (const Expr* Exp = dyn_cast_or_null(*I)) + if (CurrSt->getSValAsScalarOrLoc(Exp).getAsLocSymbol() == Sym) { + P->addRange(Exp->getSourceRange()); + break; + } + + return P; +} + +namespace { + class FindUniqueBinding : + public StoreManager::BindingsHandler { + SymbolRef Sym; + const MemRegion* Binding; + bool First; + + public: + FindUniqueBinding(SymbolRef sym) : Sym(sym), Binding(0), First(true) {} + + bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, + SVal val) { + + SymbolRef SymV = val.getAsSymbol(); + if (!SymV || SymV != Sym) + return true; + + if (Binding) { + First = false; + return false; + } + else + Binding = R; + + return true; + } + + operator bool() { return First && Binding; } + const MemRegion* getRegion() { return Binding; } + }; +} + +static std::pair +GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode* N, + SymbolRef Sym) { + + // Find both first node that referred to the tracked symbol and the + // memory location that value was store to. + const ExplodedNode* Last = N; + const MemRegion* FirstBinding = 0; + + while (N) { + const GRState* St = N->getState(); + RefBindings B = St->get(); + + if (!B.lookup(Sym)) + break; + + FindUniqueBinding FB(Sym); + StateMgr.iterBindings(St, FB); + if (FB) FirstBinding = FB.getRegion(); + + Last = N; + N = N->pred_empty() ? NULL : *(N->pred_begin()); + } + + return std::make_pair(Last, FirstBinding); +} + +PathDiagnosticPiece* +CFRefReport::getEndPath(BugReporterContext& BRC, + const ExplodedNode* EndN) { + // Tell the BugReporterContext to report cases when the tracked symbol is + // assigned to different variables, etc. + BRC.addNotableSymbol(Sym); + return RangedBugReport::getEndPath(BRC, EndN); +} + +PathDiagnosticPiece* +CFRefLeakReport::getEndPath(BugReporterContext& BRC, + const ExplodedNode* EndN){ + + // Tell the BugReporterContext to report cases when the tracked symbol is + // assigned to different variables, etc. + BRC.addNotableSymbol(Sym); + + // We are reporting a leak. Walk up the graph to get to the first node where + // the symbol appeared, and also get the first VarDecl that tracked object + // is stored to. + const ExplodedNode* AllocNode = 0; + const MemRegion* FirstBinding = 0; + + llvm::tie(AllocNode, FirstBinding) = + GetAllocationSite(BRC.getStateManager(), EndN, Sym); + + // Get the allocate site. + assert(AllocNode); + const Stmt* FirstStmt = cast(AllocNode->getLocation()).getStmt(); + + SourceManager& SMgr = BRC.getSourceManager(); + unsigned AllocLine =SMgr.getInstantiationLineNumber(FirstStmt->getLocStart()); + + // Compute an actual location for the leak. Sometimes a leak doesn't + // occur at an actual statement (e.g., transition between blocks; end + // of function) so we need to walk the graph and compute a real location. + const ExplodedNode* LeakN = EndN; + PathDiagnosticLocation L; + + while (LeakN) { + ProgramPoint P = LeakN->getLocation(); + + if (const PostStmt *PS = dyn_cast(&P)) { + L = PathDiagnosticLocation(PS->getStmt()->getLocStart(), SMgr); + break; + } + else if (const BlockEdge *BE = dyn_cast(&P)) { + if (const Stmt* Term = BE->getSrc()->getTerminator()) { + L = PathDiagnosticLocation(Term->getLocStart(), SMgr); + break; + } + } + + LeakN = LeakN->succ_empty() ? 0 : *(LeakN->succ_begin()); + } + + if (!L.isValid()) { + const Decl &D = EndN->getCodeDecl(); + L = PathDiagnosticLocation(D.getBodyRBrace(), SMgr); + } + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "Object allocated on line " << AllocLine; + + if (FirstBinding) + os << " and stored into '" << FirstBinding->getString() << '\''; + + // Get the retain count. + const RefVal* RV = EndN->getState()->get(Sym); + + if (RV->getKind() == RefVal::ErrorLeakReturned) { + // FIXME: Per comments in rdar://6320065, "create" only applies to CF + // ojbects. Only "copy", "alloc", "retain" and "new" transfer ownership + // to the caller for NS objects. + ObjCMethodDecl& MD = cast(EndN->getCodeDecl()); + os << " is returned from a method whose name ('" + << MD.getSelector().getAsString() + << "') does not contain 'copy' or otherwise starts with" + " 'new' or 'alloc'. This violates the naming convention rules given" + " in the Memory Management Guide for Cocoa (object leaked)"; + } + else if (RV->getKind() == RefVal::ErrorGCLeakReturned) { + ObjCMethodDecl& MD = cast(EndN->getCodeDecl()); + os << " and returned from method '" << MD.getSelector().getAsString() + << "' is potentially leaked when using garbage collection. Callers " + "of this method do not expect a returned object with a +1 retain " + "count since they expect the object to be managed by the garbage " + "collector"; + } + else + os << " is no longer referenced after this point and has a retain count of" + " +" << RV->getCount() << " (object leaked)"; + + return new PathDiagnosticEventPiece(L, os.str()); +} + +CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode *n, + SymbolRef sym, GRExprEngine& Eng) +: CFRefReport(D, tf, n, sym) { + + // Most bug reports are cached at the location where they occured. + // With leaks, we want to unique them by the location where they were + // allocated, and only report a single path. To do this, we need to find + // the allocation site of a piece of tracked memory, which we do via a + // call to GetAllocationSite. This will walk the ExplodedGraph backwards. + // Note that this is *not* the trimmed graph; we are guaranteed, however, + // that all ancestor nodes that represent the allocation site have the + // same SourceLocation. + const ExplodedNode* AllocNode = 0; + + llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. + GetAllocationSite(Eng.getStateManager(), getEndNode(), getSymbol()); + + // Get the SourceLocation for the allocation site. + ProgramPoint P = AllocNode->getLocation(); + AllocSite = cast(P).getStmt()->getLocStart(); + + // Fill in the description of the bug. + Description.clear(); + llvm::raw_string_ostream os(Description); + SourceManager& SMgr = Eng.getContext().getSourceManager(); + unsigned AllocLine = SMgr.getInstantiationLineNumber(AllocSite); + os << "Potential leak "; + if (tf.isGCEnabled()) { + os << "(when using garbage collection) "; + } + os << "of an object allocated on line " << AllocLine; + + // FIXME: AllocBinding doesn't get populated for RegionStore yet. + if (AllocBinding) + os << " and stored into '" << AllocBinding->getString() << '\''; +} + +//===----------------------------------------------------------------------===// +// Main checker logic. +//===----------------------------------------------------------------------===// + +/// GetReturnType - Used to get the return type of a message expression or +/// function call with the intention of affixing that type to a tracked symbol. +/// While the the return type can be queried directly from RetEx, when +/// invoking class methods we augment to the return type to be that of +/// a pointer to the class (as opposed it just being id). +static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) { + QualType RetTy = RetE->getType(); + // If RetE is not a message expression just return its type. + // If RetE is a message expression, return its types if it is something + /// more specific than id. + if (const ObjCMessageExpr *ME = dyn_cast(RetE)) + if (const ObjCObjectPointerType *PT = RetTy->getAs()) + if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() || + PT->isObjCClassType()) { + // At this point we know the return type of the message expression is + // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this + // is a call to a class method whose type we can resolve. In such + // cases, promote the return type to XXX* (where XXX is the class). + const ObjCInterfaceDecl *D = ME->getClassInfo().first; + return !D ? RetTy : Ctx.getPointerType(Ctx.getObjCInterfaceType(D)); + } + + return RetTy; +} + +void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder& Builder, + Expr* Ex, + Expr* Receiver, + const RetainSummary& Summ, + const MemRegion *Callee, + ExprIterator arg_beg, ExprIterator arg_end, + ExplodedNode* Pred, const GRState *state) { + + // Evaluate the effect of the arguments. + RefVal::Kind hasErr = (RefVal::Kind) 0; + unsigned idx = 0; + Expr* ErrorExpr = NULL; + SymbolRef ErrorSym = 0; + + llvm::SmallVector RegionsToInvalidate; + + for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) { + SVal V = state->getSValAsScalarOrLoc(*I); + SymbolRef Sym = V.getAsLocSymbol(); + + if (Sym) + if (RefBindings::data_type* T = state->get(Sym)) { + state = Update(state, Sym, *T, Summ.getArg(idx), hasErr); + if (hasErr) { + ErrorExpr = *I; + ErrorSym = Sym; + break; + } + continue; + } + + tryAgain: + if (isa(V)) { + if (loc::MemRegionVal* MR = dyn_cast(&V)) { + if (Summ.getArg(idx) == DoNothingByRef) + continue; + + // Invalidate the value of the variable passed by reference. + const MemRegion *R = MR->getRegion(); + + // Are we dealing with an ElementRegion? If the element type is + // a basic integer type (e.g., char, int) and the underying region + // is a variable region then strip off the ElementRegion. + // FIXME: We really need to think about this for the general case + // as sometimes we are reasoning about arrays and other times + // about (char*), etc., is just a form of passing raw bytes. + // e.g., void *p = alloca(); foo((char*)p); + if (const ElementRegion *ER = dyn_cast(R)) { + // Checking for 'integral type' is probably too promiscuous, but + // we'll leave it in for now until we have a systematic way of + // handling all of these cases. Eventually we need to come up + // with an interface to StoreManager so that this logic can be + // approriately delegated to the respective StoreManagers while + // still allowing us to do checker-specific logic (e.g., + // invalidating reference counts), probably via callbacks. + if (ER->getElementType()->isIntegralType()) { + const MemRegion *superReg = ER->getSuperRegion(); + if (isa(superReg) || isa(superReg) || + isa(superReg)) + R = cast(superReg); + } + // FIXME: What about layers of ElementRegions? + } + + // Mark this region for invalidation. We batch invalidate regions + // below for efficiency. + RegionsToInvalidate.push_back(R); + continue; + } + else { + // Nuke all other arguments passed by reference. + // FIXME: is this necessary or correct? This handles the non-Region + // cases. Is it ever valid to store to these? + state = state->unbindLoc(cast(V)); + } + } + else if (isa(V)) { + // If we are passing a location wrapped as an integer, unwrap it and + // invalidate the values referred by the location. + V = cast(V).getLoc(); + goto tryAgain; + } + } + + // Block calls result in all captured values passed-via-reference to be + // invalidated. + if (const BlockDataRegion *BR = dyn_cast_or_null(Callee)) { + RegionsToInvalidate.push_back(BR); + } + + // Invalidate regions we designed for invalidation use the batch invalidation + // API. + if (!RegionsToInvalidate.empty()) { + // FIXME: We can have collisions on the conjured symbol if the + // expression *I also creates conjured symbols. We probably want + // to identify conjured symbols by an expression pair: the enclosing + // expression (the context) and the expression itself. This should + // disambiguate conjured symbols. + unsigned Count = Builder.getCurrentBlockCount(); + StoreManager& StoreMgr = Eng.getStateManager().getStoreManager(); + + + StoreManager::InvalidatedSymbols IS; + Store store = state->getStore(); + store = StoreMgr.InvalidateRegions(store, RegionsToInvalidate.data(), + RegionsToInvalidate.data() + + RegionsToInvalidate.size(), + Ex, Count, &IS); + state = state->makeWithStore(store); + for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(), + E = IS.end(); I!=E; ++I) { + // Remove any existing reference-count binding. + state = state->remove(*I); + } + } + + // Evaluate the effect on the message receiver. + if (!ErrorExpr && Receiver) { + SymbolRef Sym = state->getSValAsScalarOrLoc(Receiver).getAsLocSymbol(); + if (Sym) { + if (const RefVal* T = state->get(Sym)) { + state = Update(state, Sym, *T, Summ.getReceiverEffect(), hasErr); + if (hasErr) { + ErrorExpr = Receiver; + ErrorSym = Sym; + } + } + } + } + + // Process any errors. + if (hasErr) { + ProcessNonLeakError(Dst, Builder, Ex, ErrorExpr, Pred, state, + hasErr, ErrorSym); + return; + } + + // Consult the summary for the return value. + RetEffect RE = Summ.getRetEffect(); + + if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) { + assert(Receiver); + SVal V = state->getSValAsScalarOrLoc(Receiver); + bool found = false; + if (SymbolRef Sym = V.getAsLocSymbol()) + if (state->get(Sym)) { + found = true; + RE = Summaries.getObjAllocRetEffect(); + } + + if (!found) + RE = RetEffect::MakeNoRet(); + } + + switch (RE.getKind()) { + default: + assert (false && "Unhandled RetEffect."); break; + + case RetEffect::NoRet: { + // Make up a symbol for the return value (not reference counted). + // FIXME: Most of this logic is not specific to the retain/release + // checker. + + // FIXME: We eventually should handle structs and other compound types + // that are returned by value. + + QualType T = Ex->getType(); + + // For CallExpr, use the result type to know if it returns a reference. + if (const CallExpr *CE = dyn_cast(Ex)) { + const Expr *Callee = CE->getCallee(); + if (const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl()) + T = FD->getResultType(); + } + else if (const ObjCMessageExpr *ME = dyn_cast(Ex)) { + if (const ObjCMethodDecl *MD = ME->getMethodDecl()) + T = MD->getResultType(); + } + + if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) { + unsigned Count = Builder.getCurrentBlockCount(); + ValueManager &ValMgr = Eng.getValueManager(); + SVal X = ValMgr.getConjuredSymbolVal(NULL, Ex, T, Count); + state = state->BindExpr(Ex, X, false); + } + + break; + } + + case RetEffect::Alias: { + unsigned idx = RE.getIndex(); + assert (arg_end >= arg_beg); + assert (idx < (unsigned) (arg_end - arg_beg)); + SVal V = state->getSValAsScalarOrLoc(*(arg_beg+idx)); + state = state->BindExpr(Ex, V, false); + break; + } + + case RetEffect::ReceiverAlias: { + assert (Receiver); + SVal V = state->getSValAsScalarOrLoc(Receiver); + state = state->BindExpr(Ex, V, false); + break; + } + + case RetEffect::OwnedAllocatedSymbol: + case RetEffect::OwnedSymbol: { + unsigned Count = Builder.getCurrentBlockCount(); + ValueManager &ValMgr = Eng.getValueManager(); + SymbolRef Sym = ValMgr.getConjuredSymbol(Ex, Count); + QualType RetT = GetReturnType(Ex, ValMgr.getContext()); + state = state->set(Sym, RefVal::makeOwned(RE.getObjKind(), + RetT)); + state = state->BindExpr(Ex, ValMgr.makeLoc(Sym), false); + + // FIXME: Add a flag to the checker where allocations are assumed to + // *not fail. +#if 0 + if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) { + bool isFeasible; + state = state.Assume(loc::SymbolVal(Sym), true, isFeasible); + assert(isFeasible && "Cannot assume fresh symbol is non-null."); + } +#endif + + break; + } + + case RetEffect::GCNotOwnedSymbol: + case RetEffect::NotOwnedSymbol: { + unsigned Count = Builder.getCurrentBlockCount(); + ValueManager &ValMgr = Eng.getValueManager(); + SymbolRef Sym = ValMgr.getConjuredSymbol(Ex, Count); + QualType RetT = GetReturnType(Ex, ValMgr.getContext()); + state = state->set(Sym, RefVal::makeNotOwned(RE.getObjKind(), + RetT)); + state = state->BindExpr(Ex, ValMgr.makeLoc(Sym), false); + break; + } + } + + // Generate a sink node if we are at the end of a path. + ExplodedNode *NewNode = + Summ.isEndPath() ? Builder.MakeSinkNode(Dst, Ex, Pred, state) + : Builder.MakeNode(Dst, Ex, Pred, state); + + // Annotate the edge with summary we used. + if (NewNode) SummaryLog[NewNode] = &Summ; +} + + +void CFRefCount::EvalCall(ExplodedNodeSet& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder& Builder, + CallExpr* CE, SVal L, + ExplodedNode* Pred) { + + RetainSummary *Summ = 0; + + // FIXME: Better support for blocks. For now we stop tracking anything + // that is passed to blocks. + // FIXME: Need to handle variables that are "captured" by the block. + if (dyn_cast_or_null(L.getAsRegion())) { + Summ = Summaries.getPersistentStopSummary(); + } + else { + const FunctionDecl* FD = L.getAsFunctionDecl(); + Summ = !FD ? Summaries.getDefaultSummary() : + Summaries.getSummary(const_cast(FD)); + } + + assert(Summ); + EvalSummary(Dst, Eng, Builder, CE, 0, *Summ, L.getAsRegion(), + CE->arg_begin(), CE->arg_end(), Pred, Builder.GetState(Pred)); +} + +void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder& Builder, + ObjCMessageExpr* ME, + ExplodedNode* Pred, + const GRState *state) { + RetainSummary *Summ = + ME->getReceiver() + ? Summaries.getInstanceMethodSummary(ME, state,Pred->getLocationContext()) + : Summaries.getClassMethodSummary(ME); + + assert(Summ && "RetainSummary is null"); + EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), *Summ, NULL, + ME->arg_begin(), ME->arg_end(), Pred, state); +} + +namespace { +class StopTrackingCallback : public SymbolVisitor { + const GRState *state; +public: + StopTrackingCallback(const GRState *st) : state(st) {} + const GRState *getState() const { return state; } + + bool VisitSymbol(SymbolRef sym) { + state = state->remove(sym); + return true; + } +}; +} // end anonymous namespace + + +void CFRefCount::EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val) { + // Are we storing to something that causes the value to "escape"? + bool escapes = false; + + // A value escapes in three possible cases (this may change): + // + // (1) we are binding to something that is not a memory region. + // (2) we are binding to a memregion that does not have stack storage + // (3) we are binding to a memregion with stack storage that the store + // does not understand. + const GRState *state = B.getState(); + + if (!isa(location)) + escapes = true; + else { + const MemRegion* R = cast(location).getRegion(); + escapes = !R->hasStackStorage(); + + if (!escapes) { + // To test (3), generate a new state with the binding removed. If it is + // the same state, then it escapes (since the store cannot represent + // the binding). + escapes = (state == (state->bindLoc(cast(location), UnknownVal()))); + } + } + + // If our store can represent the binding and we aren't storing to something + // that doesn't have local storage then just return and have the simulation + // state continue as is. + if (!escapes) + return; + + // Otherwise, find all symbols referenced by 'val' that we are tracking + // and stop tracking them. + B.MakeNode(state->scanReachableSymbols(val).getState()); +} + + // Return statements. + +void CFRefCount::EvalReturn(ExplodedNodeSet& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder& Builder, + ReturnStmt* S, + ExplodedNode* Pred) { + + Expr* RetE = S->getRetValue(); + if (!RetE) + return; + + const GRState *state = Builder.GetState(Pred); + SymbolRef Sym = state->getSValAsScalarOrLoc(RetE).getAsLocSymbol(); + + if (!Sym) + return; + + // Get the reference count binding (if any). + const RefVal* T = state->get(Sym); + + if (!T) + return; + + // Change the reference count. + RefVal X = *T; + + switch (X.getKind()) { + case RefVal::Owned: { + unsigned cnt = X.getCount(); + assert (cnt > 0); + X.setCount(cnt - 1); + X = X ^ RefVal::ReturnedOwned; + break; + } + + case RefVal::NotOwned: { + unsigned cnt = X.getCount(); + if (cnt) { + X.setCount(cnt - 1); + X = X ^ RefVal::ReturnedOwned; + } + else { + X = X ^ RefVal::ReturnedNotOwned; + } + break; + } + + default: + return; + } + + // Update the binding. + state = state->set(Sym, X); + Pred = Builder.MakeNode(Dst, S, Pred, state); + + // Did we cache out? + if (!Pred) + return; + + // Update the autorelease counts. + static unsigned autoreleasetag = 0; + GenericNodeBuilder Bd(Builder, S, &autoreleasetag); + bool stop = false; + llvm::tie(Pred, state) = HandleAutoreleaseCounts(state , Bd, Pred, Eng, Sym, + X, stop); + + // Did we cache out? + if (!Pred || stop) + return; + + // Get the updated binding. + T = state->get(Sym); + assert(T); + X = *T; + + // Any leaks or other errors? + if (X.isReturnedOwned() && X.getCount() == 0) { + Decl const *CD = &Pred->getCodeDecl(); + if (const ObjCMethodDecl* MD = dyn_cast(CD)) { + const RetainSummary &Summ = *Summaries.getMethodSummary(MD); + RetEffect RE = Summ.getRetEffect(); + bool hasError = false; + + if (RE.getKind() != RetEffect::NoRet) { + if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { + // Things are more complicated with garbage collection. If the + // returned object is suppose to be an Objective-C object, we have + // a leak (as the caller expects a GC'ed object) because no + // method should return ownership unless it returns a CF object. + hasError = true; + X = X ^ RefVal::ErrorGCLeakReturned; + } + else if (!RE.isOwned()) { + // Either we are using GC and the returned object is a CF type + // or we aren't using GC. In either case, we expect that the + // enclosing method is expected to return ownership. + hasError = true; + X = X ^ RefVal::ErrorLeakReturned; + } + } + + if (hasError) { + // Generate an error node. + static int ReturnOwnLeakTag = 0; + state = state->set(Sym, X); + ExplodedNode *N = + Builder.generateNode(PostStmt(S, Pred->getLocationContext(), + &ReturnOwnLeakTag), state, Pred); + if (N) { + CFRefReport *report = + new CFRefLeakReport(*static_cast(leakAtReturn), *this, + N, Sym, Eng); + BR->EmitReport(report); + } + } + } + } + else if (X.isReturnedNotOwned()) { + Decl const *CD = &Pred->getCodeDecl(); + if (const ObjCMethodDecl* MD = dyn_cast(CD)) { + const RetainSummary &Summ = *Summaries.getMethodSummary(MD); + if (Summ.getRetEffect().isOwned()) { + // Trying to return a not owned object to a caller expecting an + // owned object. + + static int ReturnNotOwnedForOwnedTag = 0; + state = state->set(Sym, X ^ RefVal::ErrorReturnedNotOwned); + if (ExplodedNode *N = + Builder.generateNode(PostStmt(S, Pred->getLocationContext(), + &ReturnNotOwnedForOwnedTag), + state, Pred)) { + CFRefReport *report = + new CFRefReport(*static_cast(returnNotOwnedForOwned), + *this, N, Sym); + BR->EmitReport(report); + } + } + } + } +} + +// Assumptions. + +const GRState* CFRefCount::EvalAssume(const GRState *state, + SVal Cond, bool Assumption) { + + // FIXME: We may add to the interface of EvalAssume the list of symbols + // whose assumptions have changed. For now we just iterate through the + // bindings and check if any of the tracked symbols are NULL. This isn't + // too bad since the number of symbols we will track in practice are + // probably small and EvalAssume is only called at branches and a few + // other places. + RefBindings B = state->get(); + + if (B.isEmpty()) + return state; + + bool changed = false; + RefBindings::Factory& RefBFactory = state->get_context(); + + for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { + // Check if the symbol is null (or equal to any constant). + // If this is the case, stop tracking the symbol. + if (state->getSymVal(I.getKey())) { + changed = true; + B = RefBFactory.Remove(B, I.getKey()); + } + } + + if (changed) + state = state->set(B); + + return state; +} + +const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, + RefVal V, ArgEffect E, + RefVal::Kind& hasErr) { + + // In GC mode [... release] and [... retain] do nothing. + switch (E) { + default: break; + case IncRefMsg: E = isGCEnabled() ? DoNothing : IncRef; break; + case DecRefMsg: E = isGCEnabled() ? DoNothing : DecRef; break; + case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break; + case NewAutoreleasePool: E = isGCEnabled() ? DoNothing : + NewAutoreleasePool; break; + } + + // Handle all use-after-releases. + if (!isGCEnabled() && V.getKind() == RefVal::Released) { + V = V ^ RefVal::ErrorUseAfterRelease; + hasErr = V.getKind(); + return state->set(sym, V); + } + + switch (E) { + default: + assert (false && "Unhandled CFRef transition."); + + case Dealloc: + // Any use of -dealloc in GC is *bad*. + if (isGCEnabled()) { + V = V ^ RefVal::ErrorDeallocGC; + hasErr = V.getKind(); + break; + } + + switch (V.getKind()) { + default: + assert(false && "Invalid case."); + case RefVal::Owned: + // The object immediately transitions to the released state. + V = V ^ RefVal::Released; + V.clearCounts(); + return state->set(sym, V); + case RefVal::NotOwned: + V = V ^ RefVal::ErrorDeallocNotOwned; + hasErr = V.getKind(); + break; + } + break; + + case NewAutoreleasePool: + assert(!isGCEnabled()); + return state->add(sym); + + case MayEscape: + if (V.getKind() == RefVal::Owned) { + V = V ^ RefVal::NotOwned; + break; + } + + // Fall-through. + + case DoNothingByRef: + case DoNothing: + return state; + + case Autorelease: + if (isGCEnabled()) + return state; + + // Update the autorelease counts. + state = SendAutorelease(state, ARCountFactory, sym); + V = V.autorelease(); + break; + + case StopTracking: + return state->remove(sym); + + case IncRef: + switch (V.getKind()) { + default: + assert(false); + + case RefVal::Owned: + case RefVal::NotOwned: + V = V + 1; + break; + case RefVal::Released: + // Non-GC cases are handled above. + assert(isGCEnabled()); + V = (V ^ RefVal::Owned) + 1; + break; + } + break; + + case SelfOwn: + V = V ^ RefVal::NotOwned; + // Fall-through. + case DecRef: + switch (V.getKind()) { + default: + // case 'RefVal::Released' handled above. + assert (false); + + case RefVal::Owned: + assert(V.getCount() > 0); + if (V.getCount() == 1) V = V ^ RefVal::Released; + V = V - 1; + break; + + case RefVal::NotOwned: + if (V.getCount() > 0) + V = V - 1; + else { + V = V ^ RefVal::ErrorReleaseNotOwned; + hasErr = V.getKind(); + } + break; + + case RefVal::Released: + // Non-GC cases are handled above. + assert(isGCEnabled()); + V = V ^ RefVal::ErrorUseAfterRelease; + hasErr = V.getKind(); + break; + } + break; + } + return state->set(sym, V); +} + +//===----------------------------------------------------------------------===// +// Handle dead symbols and end-of-path. +//===----------------------------------------------------------------------===// + +std::pair +CFRefCount::HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd, + ExplodedNode* Pred, + GRExprEngine &Eng, + SymbolRef Sym, RefVal V, bool &stop) { + + unsigned ACnt = V.getAutoreleaseCount(); + stop = false; + + // No autorelease counts? Nothing to be done. + if (!ACnt) + return std::make_pair(Pred, state); + + assert(!isGCEnabled() && "Autorelease counts in GC mode?"); + unsigned Cnt = V.getCount(); + + // FIXME: Handle sending 'autorelease' to already released object. + + if (V.getKind() == RefVal::ReturnedOwned) + ++Cnt; + + if (ACnt <= Cnt) { + if (ACnt == Cnt) { + V.clearCounts(); + if (V.getKind() == RefVal::ReturnedOwned) + V = V ^ RefVal::ReturnedNotOwned; + else + V = V ^ RefVal::NotOwned; + } + else { + V.setCount(Cnt - ACnt); + V.setAutoreleaseCount(0); + } + state = state->set(Sym, V); + ExplodedNode *N = Bd.MakeNode(state, Pred); + stop = (N == 0); + return std::make_pair(N, state); + } + + // Woah! More autorelease counts then retain counts left. + // Emit hard error. + stop = true; + V = V ^ RefVal::ErrorOverAutorelease; + state = state->set(Sym, V); + + if (ExplodedNode *N = Bd.MakeNode(state, Pred)) { + N->markAsSink(); + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Object over-autoreleased: object was sent -autorelease"; + if (V.getAutoreleaseCount() > 1) + os << V.getAutoreleaseCount() << " times"; + os << " but the object has "; + if (V.getCount() == 0) + os << "zero (locally visible)"; + else + os << "+" << V.getCount(); + os << " retain counts"; + + CFRefReport *report = + new CFRefReport(*static_cast(overAutorelease), + *this, N, Sym, os.str()); + BR->EmitReport(report); + } + + return std::make_pair((ExplodedNode*)0, state); +} + +const GRState * +CFRefCount::HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, + llvm::SmallVectorImpl &Leaked) { + + bool hasLeak = V.isOwned() || + ((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0); + + if (!hasLeak) + return state->remove(sid); + + Leaked.push_back(sid); + return state->set(sid, V ^ RefVal::ErrorLeak); +} + +ExplodedNode* +CFRefCount::ProcessLeaks(const GRState * state, + llvm::SmallVectorImpl &Leaked, + GenericNodeBuilder &Builder, + GRExprEngine& Eng, + ExplodedNode *Pred) { + + if (Leaked.empty()) + return Pred; + + // Generate an intermediate node representing the leak point. + ExplodedNode *N = Builder.MakeNode(state, Pred); + + if (N) { + for (llvm::SmallVectorImpl::iterator + I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { + + CFRefBug *BT = static_cast(Pred ? leakWithinFunction + : leakAtReturn); + assert(BT && "BugType not initialized."); + CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, *I, Eng); + BR->EmitReport(report); + } + } + + return N; +} + +void CFRefCount::EvalEndPath(GRExprEngine& Eng, + GREndPathNodeBuilder& Builder) { + + const GRState *state = Builder.getState(); + GenericNodeBuilder Bd(Builder); + RefBindings B = state->get(); + ExplodedNode *Pred = 0; + + for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + bool stop = false; + llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng, + (*I).first, + (*I).second, stop); + + if (stop) + return; + } + + B = state->get(); + llvm::SmallVector Leaked; + + for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) + state = HandleSymbolDeath(state, (*I).first, (*I).second, Leaked); + + ProcessLeaks(state, Leaked, Bd, Eng, Pred); +} + +void CFRefCount::EvalDeadSymbols(ExplodedNodeSet& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder& Builder, + ExplodedNode* Pred, + Stmt* S, + const GRState* state, + SymbolReaper& SymReaper) { + + RefBindings B = state->get(); + + // Update counts from autorelease pools + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I != E; ++I) { + SymbolRef Sym = *I; + if (const RefVal* T = B.lookup(Sym)){ + // Use the symbol as the tag. + // FIXME: This might not be as unique as we would like. + GenericNodeBuilder Bd(Builder, S, Sym); + bool stop = false; + llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng, + Sym, *T, stop); + if (stop) + return; + } + } + + B = state->get(); + llvm::SmallVector Leaked; + + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I != E; ++I) { + if (const RefVal* T = B.lookup(*I)) + state = HandleSymbolDeath(state, *I, *T, Leaked); + } + + static unsigned LeakPPTag = 0; + { + GenericNodeBuilder Bd(Builder, S, &LeakPPTag); + Pred = ProcessLeaks(state, Leaked, Bd, Eng, Pred); + } + + // Did we cache out? + if (!Pred) + return; + + // Now generate a new node that nukes the old bindings. + RefBindings::Factory& F = state->get_context(); + + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I!=E; ++I) B = F.Remove(B, *I); + + state = state->set(B); + Builder.MakeNode(Dst, S, Pred, state); +} + +void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, + GRStmtNodeBuilder& Builder, + Expr* NodeExpr, Expr* ErrorExpr, + ExplodedNode* Pred, + const GRState* St, + RefVal::Kind hasErr, SymbolRef Sym) { + Builder.BuildSinks = true; + ExplodedNode *N = Builder.MakeNode(Dst, NodeExpr, Pred, St); + + if (!N) + return; + + CFRefBug *BT = 0; + + switch (hasErr) { + default: + assert(false && "Unhandled error."); + return; + case RefVal::ErrorUseAfterRelease: + BT = static_cast(useAfterRelease); + break; + case RefVal::ErrorReleaseNotOwned: + BT = static_cast(releaseNotOwned); + break; + case RefVal::ErrorDeallocGC: + BT = static_cast(deallocGC); + break; + case RefVal::ErrorDeallocNotOwned: + BT = static_cast(deallocNotOwned); + break; + } + + CFRefReport *report = new CFRefReport(*BT, *this, N, Sym); + report->addRange(ErrorExpr->getSourceRange()); + BR->EmitReport(report); +} + +//===----------------------------------------------------------------------===// +// Pieces of the retain/release checker implemented using a CheckerVisitor. +// More pieces of the retain/release checker will be migrated to this interface +// (ideally, all of it some day). +//===----------------------------------------------------------------------===// + +namespace { +class RetainReleaseChecker + : public CheckerVisitor { + CFRefCount *TF; +public: + RetainReleaseChecker(CFRefCount *tf) : TF(tf) {} + static void* getTag() { static int x = 0; return &x; } + + void PostVisitBlockExpr(CheckerContext &C, const BlockExpr *BE); +}; +} // end anonymous namespace + + +void RetainReleaseChecker::PostVisitBlockExpr(CheckerContext &C, + const BlockExpr *BE) { + + // Scan the BlockDecRefExprs for any object the retain/release checker + // may be tracking. + if (!BE->hasBlockDeclRefExprs()) + return; + + const GRState *state = C.getState(); + const BlockDataRegion *R = + cast(state->getSVal(BE).getAsRegion()); + + BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), + E = R->referenced_vars_end(); + + if (I == E) + return; + + // FIXME: For now we invalidate the tracking of all symbols passed to blocks + // via captured variables, even though captured variables result in a copy + // and in implicit increment/decrement of a retain count. + llvm::SmallVector Regions; + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + MemRegionManager &MemMgr = C.getValueManager().getRegionManager(); + + for ( ; I != E; ++I) { + const VarRegion *VR = *I; + if (VR->getSuperRegion() == R) { + VR = MemMgr.getVarRegion(VR->getDecl(), LC); + } + Regions.push_back(VR); + } + + state = + state->scanReachableSymbols(Regions.data(), + Regions.data() + Regions.size()).getState(); + C.addTransition(state); +} + +//===----------------------------------------------------------------------===// +// Transfer function creation for external clients. +//===----------------------------------------------------------------------===// + +void CFRefCount::RegisterChecks(GRExprEngine& Eng) { + BugReporter &BR = Eng.getBugReporter(); + + useAfterRelease = new UseAfterRelease(this); + BR.Register(useAfterRelease); + + releaseNotOwned = new BadRelease(this); + BR.Register(releaseNotOwned); + + deallocGC = new DeallocGC(this); + BR.Register(deallocGC); + + deallocNotOwned = new DeallocNotOwned(this); + BR.Register(deallocNotOwned); + + overAutorelease = new OverAutorelease(this); + BR.Register(overAutorelease); + + returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this); + BR.Register(returnNotOwnedForOwned); + + // First register "return" leaks. + const char* name = 0; + + if (isGCEnabled()) + name = "Leak of returned object when using garbage collection"; + else if (getLangOptions().getGCMode() == LangOptions::HybridGC) + name = "Leak of returned object when not using garbage collection (GC) in " + "dual GC/non-GC code"; + else { + assert(getLangOptions().getGCMode() == LangOptions::NonGC); + name = "Leak of returned object"; + } + + // Leaks should not be reported if they are post-dominated by a sink. + leakAtReturn = new LeakAtReturn(this, name); + leakAtReturn->setSuppressOnSink(true); + BR.Register(leakAtReturn); + + // Second, register leaks within a function/method. + if (isGCEnabled()) + name = "Leak of object when using garbage collection"; + else if (getLangOptions().getGCMode() == LangOptions::HybridGC) + name = "Leak of object when not using garbage collection (GC) in " + "dual GC/non-GC code"; + else { + assert(getLangOptions().getGCMode() == LangOptions::NonGC); + name = "Leak"; + } + + // Leaks should not be reported if they are post-dominated by sinks. + leakWithinFunction = new LeakWithinFunction(this, name); + leakWithinFunction->setSuppressOnSink(true); + BR.Register(leakWithinFunction); + + // Save the reference to the BugReporter. + this->BR = &BR; + + // Register the RetainReleaseChecker with the GRExprEngine object. + // Functionality in CFRefCount will be migrated to RetainReleaseChecker + // over time. + Eng.registerCheck(new RetainReleaseChecker(this)); +} + +GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, + const LangOptions& lopts) { + return new CFRefCount(Ctx, GCEnabled, lopts); +} diff --git a/lib/Checker/CMakeLists.txt b/lib/Checker/CMakeLists.txt new file mode 100644 index 000000000000..7b21d08dcb71 --- /dev/null +++ b/lib/Checker/CMakeLists.txt @@ -0,0 +1,67 @@ +set(LLVM_NO_RTTI 1) + +add_clang_library(clangChecker + AdjustedReturnValueChecker.cpp + ArrayBoundChecker.cpp + AttrNonNullChecker.cpp + BasicConstraintManager.cpp + BasicObjCFoundationChecks.cpp + BasicStore.cpp + BasicValueFactory.cpp + BugReporter.cpp + BugReporterVisitors.cpp + BuiltinFunctionChecker.cpp + CFRefCount.cpp + CallAndMessageChecker.cpp + CallInliner.cpp + CastToStructChecker.cpp + CheckDeadStores.cpp + CheckObjCDealloc.cpp + CheckObjCInstMethSignature.cpp + CheckObjCUnusedIVars.cpp + CheckSecuritySyntaxOnly.cpp + CheckSizeofPointer.cpp + Checker.cpp + CocoaConventions.cpp + DereferenceChecker.cpp + DivZeroChecker.cpp + Environment.cpp + ExplodedGraph.cpp + FixedAddressChecker.cpp + FlatStore.cpp + GRBlockCounter.cpp + GRCoreEngine.cpp + GRExprEngine.cpp + GRExprEngineExperimentalChecks.cpp + GRState.cpp + LLVMConventionsChecker.cpp + MallocChecker.cpp + ManagerRegistry.cpp + MemRegion.cpp + NSAutoreleasePoolChecker.cpp + NSErrorChecker.cpp + NoReturnFunctionChecker.cpp + OSAtomicChecker.cpp + PathDiagnostic.cpp + PointerArithChecker.cpp + PointerSubChecker.cpp + PthreadLockChecker.cpp + RangeConstraintManager.cpp + RegionStore.cpp + ReturnPointerRangeChecker.cpp + ReturnStackAddressChecker.cpp + ReturnUndefChecker.cpp + SVals.cpp + SValuator.cpp + SimpleConstraintManager.cpp + SimpleSValuator.cpp + Store.cpp + SymbolManager.cpp + UndefBranchChecker.cpp + UndefCapturedBlockVarChecker.cpp + UndefResultChecker.cpp + UndefinedArraySubscriptChecker.cpp + UndefinedAssignmentChecker.cpp + VLASizeChecker.cpp + ValueManager.cpp + ) diff --git a/lib/Checker/CallAndMessageChecker.cpp b/lib/Checker/CallAndMessageChecker.cpp new file mode 100644 index 000000000000..9013c3818b0c --- /dev/null +++ b/lib/Checker/CallAndMessageChecker.cpp @@ -0,0 +1,252 @@ +//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines CallAndMessageChecker, a builtin checker that checks for various +// errors of call and objc message expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/TargetInfo.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/AST/ParentMap.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class CallAndMessageChecker + : public CheckerVisitor { + BugType *BT_call_null; + BugType *BT_call_undef; + BugType *BT_call_arg; + BugType *BT_msg_undef; + BugType *BT_msg_arg; + BugType *BT_msg_ret; +public: + CallAndMessageChecker() : + BT_call_null(0), BT_call_undef(0), BT_call_arg(0), + BT_msg_undef(0), BT_msg_arg(0), BT_msg_ret(0) {} + + static void *getTag() { + static int x = 0; + return &x; + } + + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); + bool EvalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME); + +private: + void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); + void EmitNilReceiverBug(CheckerContext &C, const ObjCMessageExpr *ME, + ExplodedNode *N); + + void HandleNilReceiver(CheckerContext &C, const GRState *state, + const ObjCMessageExpr *ME); +}; +} // end anonymous namespace + +void clang::RegisterCallAndMessageChecker(GRExprEngine &Eng) { + Eng.registerCheck(new CallAndMessageChecker()); +} + +void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, + const CallExpr *CE) { + ExplodedNode *N = C.GenerateSink(); + if (!N) + return; + + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetCalleeExpr(N)); + C.EmitReport(R); +} + +void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE){ + + const Expr *Callee = CE->getCallee()->IgnoreParens(); + SVal L = C.getState()->getSVal(Callee); + + if (L.isUndef()) { + if (!BT_call_undef) + BT_call_undef = + new BuiltinBug("Called function pointer is an undefined pointer value"); + EmitBadCall(BT_call_undef, C, CE); + return; + } + + if (isa(L)) { + if (!BT_call_null) + BT_call_null = + new BuiltinBug("Called function pointer is null (null dereference)"); + EmitBadCall(BT_call_null, C, CE); + } + + for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); + I != E; ++I) { + if (C.getState()->getSVal(*I).isUndef()) { + if (ExplodedNode *N = C.GenerateSink()) { + if (!BT_call_arg) + BT_call_arg = new BuiltinBug("Pass-by-value argument in function call" + " is undefined"); + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT_call_arg, + BT_call_arg->getName(), N); + R->addRange((*I)->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); + C.EmitReport(R); + return; + } + } + } +} + +void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + const GRState *state = C.getState(); + + if (const Expr *receiver = ME->getReceiver()) + if (state->getSVal(receiver).isUndef()) { + if (ExplodedNode *N = C.GenerateSink()) { + if (!BT_msg_undef) + BT_msg_undef = + new BuiltinBug("Receiver in message expression is a garbage value"); + EnhancedBugReport *R = + new EnhancedBugReport(*BT_msg_undef, BT_msg_undef->getName(), N); + R->addRange(receiver->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + receiver); + C.EmitReport(R); + } + return; + } + + // Check for any arguments that are uninitialized/undefined. + for (ObjCMessageExpr::const_arg_iterator I = ME->arg_begin(), + E = ME->arg_end(); I != E; ++I) { + if (state->getSVal(*I).isUndef()) { + if (ExplodedNode *N = C.GenerateSink()) { + if (!BT_msg_arg) + BT_msg_arg = + new BuiltinBug("Pass-by-value argument in message expression" + " is undefined"); + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT_msg_arg, + BT_msg_arg->getName(), N); + R->addRange((*I)->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); + C.EmitReport(R); + return; + } + } + } +} + +bool CallAndMessageChecker::EvalNilReceiver(CheckerContext &C, + const ObjCMessageExpr *ME) { + HandleNilReceiver(C, C.getState(), ME); + return true; // Nil receiver is not handled elsewhere. +} + +void CallAndMessageChecker::EmitNilReceiverBug(CheckerContext &C, + const ObjCMessageExpr *ME, + ExplodedNode *N) { + + if (!BT_msg_ret) + BT_msg_ret = + new BuiltinBug("Receiver in message expression is " + "'nil' and returns a garbage value"); + + llvm::SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << "The receiver of message '" << ME->getSelector().getAsString() + << "' is nil and returns a value of type '" + << ME->getType().getAsString() << "' that will be garbage"; + + EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N); + const Expr *receiver = ME->getReceiver(); + report->addRange(receiver->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + receiver); + C.EmitReport(report); +} + +static bool SupportsNilWithFloatRet(const llvm::Triple &triple) { + return triple.getVendor() == llvm::Triple::Apple && + triple.getDarwinMajorNumber() >= 9; +} + +void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, + const GRState *state, + const ObjCMessageExpr *ME) { + + // Check the return type of the message expression. A message to nil will + // return different values depending on the return type and the architecture. + QualType RetTy = ME->getType(); + + ASTContext &Ctx = C.getASTContext(); + CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); + + if (CanRetTy->isStructureType()) { + // FIXME: At some point we shouldn't rely on isConsumedExpr(), but instead + // have the "use of undefined value" be smarter about where the + // undefined value came from. + if (C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { + if (ExplodedNode* N = C.GenerateSink(state)) + EmitNilReceiverBug(C, ME, N); + return; + } + + // The result is not consumed by a surrounding expression. Just propagate + // the current state. + C.addTransition(state); + return; + } + + // Other cases: check if the return type is smaller than void*. + if (CanRetTy != Ctx.VoidTy && + C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { + // Compute: sizeof(void *) and sizeof(return type) + const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); + const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); + + if (voidPtrSize < returnTypeSize && + !(SupportsNilWithFloatRet(Ctx.Target.getTriple()) && + (Ctx.FloatTy == CanRetTy || + Ctx.DoubleTy == CanRetTy || + Ctx.LongDoubleTy == CanRetTy || + Ctx.LongLongTy == CanRetTy))) { + if (ExplodedNode* N = C.GenerateSink(state)) + EmitNilReceiverBug(C, ME, N); + return; + } + + // Handle the safe cases where the return value is 0 if the + // receiver is nil. + // + // FIXME: For now take the conservative approach that we only + // return null values if we *know* that the receiver is nil. + // This is because we can have surprises like: + // + // ... = [[NSScreens screens] objectAtIndex:0]; + // + // What can happen is that [... screens] could return nil, but + // it most likely isn't nil. We should assume the semantics + // of this case unless we have *a lot* more knowledge. + // + SVal V = C.getValueManager().makeZeroVal(ME->getType()); + C.GenerateNode(state->BindExpr(ME, V)); + return; + } + + C.addTransition(state); +} diff --git a/lib/Checker/CallInliner.cpp b/lib/Checker/CallInliner.cpp new file mode 100644 index 000000000000..d94994b19437 --- /dev/null +++ b/lib/Checker/CallInliner.cpp @@ -0,0 +1,113 @@ +//===--- CallInliner.cpp - Transfer function that inlines callee ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the callee inlining transfer function. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Checker/Checkers/LocalCheckers.h" + +using namespace clang; + +namespace { +class CallInliner : public Checker { +public: + static void *getTag() { + static int x; + return &x; + } + + virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); + virtual void EvalEndPath(GREndPathNodeBuilder &B,void *tag,GRExprEngine &Eng); +}; +} + +void clang::RegisterCallInliner(GRExprEngine &Eng) { + Eng.registerCheck(new CallInliner()); +} + +bool CallInliner::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + if (!FD->isThisDeclarationADefinition()) + return false; + + GRStmtNodeBuilder &Builder = C.getNodeBuilder(); + // Make a new LocationContext. + const StackFrameContext *LocCtx = C.getAnalysisManager().getStackFrame(FD, + C.getPredecessor()->getLocationContext(), CE, + Builder.getBlock(), Builder.getIndex()); + + CFGBlock const *Entry = &(LocCtx->getCFG()->getEntry()); + + assert (Entry->empty() && "Entry block must be empty."); + + assert (Entry->succ_size() == 1 && "Entry block must have 1 successor."); + + // Get the solitary successor. + CFGBlock const *SuccB = *(Entry->succ_begin()); + + // Construct an edge representing the starting location in the function. + BlockEdge Loc(Entry, SuccB, LocCtx); + + state = C.getStoreManager().EnterStackFrame(state, LocCtx); + // This is a hack. We really should not use the GRStmtNodeBuilder. + bool isNew; + GRExprEngine &Eng = C.getEngine(); + ExplodedNode *Pred = C.getPredecessor(); + + + ExplodedNode *SuccN = Eng.getGraph().getNode(Loc, state, &isNew); + SuccN->addPredecessor(Pred, Eng.getGraph()); + C.getNodeBuilder().Deferred.erase(Pred); + + if (isNew) + Builder.getWorkList()->Enqueue(SuccN); + + Builder.HasGeneratedNode = true; + + return true; +} + +void CallInliner::EvalEndPath(GREndPathNodeBuilder &B, void *tag, + GRExprEngine &Eng) { + const GRState *state = B.getState(); + ExplodedNode *Pred = B.getPredecessor(); + const StackFrameContext *LocCtx = + cast(Pred->getLocationContext()); + + const Stmt *CE = LocCtx->getCallSite(); + + // Check if this is the top level stack frame. + if (!LocCtx->getParent()) + return; + + PostStmt NodeLoc(CE, LocCtx->getParent()); + + bool isNew; + ExplodedNode *Succ = Eng.getGraph().getNode(NodeLoc, state, &isNew); + Succ->addPredecessor(Pred, Eng.getGraph()); + + // When creating the new work list unit, increment the statement index to + // point to the statement after the CallExpr. + if (isNew) + B.getWorkList().Enqueue(Succ, + *const_cast(LocCtx->getCallSiteBlock()), + LocCtx->getIndex() + 1); + + B.HasGeneratedNode = true; +} diff --git a/lib/Checker/CastToStructChecker.cpp b/lib/Checker/CastToStructChecker.cpp new file mode 100644 index 000000000000..bef5bc285ee2 --- /dev/null +++ b/lib/Checker/CastToStructChecker.cpp @@ -0,0 +1,77 @@ +//=== CastToStructChecker.cpp - Fixed address usage checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines CastToStructChecker, a builtin checker that checks for +// cast from non-struct pointer to struct pointer. +// This check corresponds to CWE-588. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class CastToStructChecker + : public CheckerVisitor { + BuiltinBug *BT; +public: + CastToStructChecker() : BT(0) {} + static void *getTag(); + void PreVisitCastExpr(CheckerContext &C, const CastExpr *B); +}; +} + +void *CastToStructChecker::getTag() { + static int x; + return &x; +} + +void CastToStructChecker::PreVisitCastExpr(CheckerContext &C, + const CastExpr *CE) { + const Expr *E = CE->getSubExpr(); + ASTContext &Ctx = C.getASTContext(); + QualType OrigTy = Ctx.getCanonicalType(E->getType()); + QualType ToTy = Ctx.getCanonicalType(CE->getType()); + + PointerType *OrigPTy = dyn_cast(OrigTy.getTypePtr()); + PointerType *ToPTy = dyn_cast(ToTy.getTypePtr()); + + if (!ToPTy || !OrigPTy) + return; + + QualType OrigPointeeTy = OrigPTy->getPointeeType(); + QualType ToPointeeTy = ToPTy->getPointeeType(); + + if (!ToPointeeTy->isStructureType()) + return; + + // We allow cast from void*. + if (OrigPointeeTy->isVoidType()) + return; + + // Now the cast-to-type is struct pointer, the original type is not void*. + if (!OrigPointeeTy->isRecordType()) { + if (ExplodedNode *N = C.GenerateNode()) { + if (!BT) + BT = new BuiltinBug("Cast from non-struct type to struct type", + "Casting a non-structure type to a structure type " + "and accessing a field can lead to memory access " + "errors or data corruption."); + RangedBugReport *R = new RangedBugReport(*BT,BT->getDescription(), N); + R->addRange(CE->getSourceRange()); + C.EmitReport(R); + } + } +} + +void clang::RegisterCastToStructChecker(GRExprEngine &Eng) { + Eng.registerCheck(new CastToStructChecker()); +} diff --git a/lib/Checker/CheckDeadStores.cpp b/lib/Checker/CheckDeadStores.cpp new file mode 100644 index 000000000000..4a7ca705488a --- /dev/null +++ b/lib/Checker/CheckDeadStores.cpp @@ -0,0 +1,279 @@ +//==- DeadStores.cpp - Check for stores to dead variables --------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a DeadStores, a flow-sensitive checker that looks for +// stores to variables that are no longer live. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/Checkers/LocalCheckers.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ParentMap.h" +#include "llvm/ADT/SmallPtrSet.h" + +using namespace clang; + +namespace { + +class DeadStoreObs : public LiveVariables::ObserverTy { + ASTContext &Ctx; + BugReporter& BR; + ParentMap& Parents; + llvm::SmallPtrSet Escaped; + + enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit }; + +public: + DeadStoreObs(ASTContext &ctx, BugReporter& br, ParentMap& parents, + llvm::SmallPtrSet &escaped) + : Ctx(ctx), BR(br), Parents(parents), Escaped(escaped) {} + + virtual ~DeadStoreObs() {} + + void Report(VarDecl* V, DeadStoreKind dsk, SourceLocation L, SourceRange R) { + if (Escaped.count(V)) + return; + + std::string name = V->getNameAsString(); + + const char* BugType = 0; + std::string msg; + + switch (dsk) { + default: + assert(false && "Impossible dead store type."); + + case DeadInit: + BugType = "Dead initialization"; + msg = "Value stored to '" + name + + "' during its initialization is never read"; + break; + + case DeadIncrement: + BugType = "Dead increment"; + case Standard: + if (!BugType) BugType = "Dead assignment"; + msg = "Value stored to '" + name + "' is never read"; + break; + + case Enclosing: + BugType = "Dead nested assignment"; + msg = "Although the value stored to '" + name + + "' is used in the enclosing expression, the value is never actually" + " read from '" + name + "'"; + break; + } + + BR.EmitBasicReport(BugType, "Dead store", msg, L, R); + } + + void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val, + DeadStoreKind dsk, + const LiveVariables::AnalysisDataTy& AD, + const LiveVariables::ValTy& Live) { + + if (!VD->hasLocalStorage()) + return; + // Reference types confuse the dead stores checker. Skip them + // for now. + if (VD->getType()->getAs()) + return; + + if (!Live(VD, AD) && + !(VD->getAttr() || VD->getAttr())) + Report(VD, dsk, Ex->getSourceRange().getBegin(), + Val->getSourceRange()); + } + + void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk, + const LiveVariables::AnalysisDataTy& AD, + const LiveVariables::ValTy& Live) { + if (VarDecl* VD = dyn_cast(DR->getDecl())) + CheckVarDecl(VD, DR, Val, dsk, AD, Live); + } + + bool isIncrement(VarDecl* VD, BinaryOperator* B) { + if (B->isCompoundAssignmentOp()) + return true; + + Expr* RHS = B->getRHS()->IgnoreParenCasts(); + BinaryOperator* BRHS = dyn_cast(RHS); + + if (!BRHS) + return false; + + DeclRefExpr *DR; + + if ((DR = dyn_cast(BRHS->getLHS()->IgnoreParenCasts()))) + if (DR->getDecl() == VD) + return true; + + if ((DR = dyn_cast(BRHS->getRHS()->IgnoreParenCasts()))) + if (DR->getDecl() == VD) + return true; + + return false; + } + + virtual void ObserveStmt(Stmt* S, + const LiveVariables::AnalysisDataTy& AD, + const LiveVariables::ValTy& Live) { + + // Skip statements in macros. + if (S->getLocStart().isMacroID()) + return; + + if (BinaryOperator* B = dyn_cast(S)) { + if (!B->isAssignmentOp()) return; // Skip non-assignments. + + if (DeclRefExpr* DR = dyn_cast(B->getLHS())) + if (VarDecl *VD = dyn_cast(DR->getDecl())) { + // Special case: check for assigning null to a pointer. + // This is a common form of defensive programming. + if (VD->getType()->isPointerType()) { + if (B->getRHS()->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) + return; + } + + Expr* RHS = B->getRHS()->IgnoreParenCasts(); + // Special case: self-assignments. These are often used to shut up + // "unused variable" compiler warnings. + if (DeclRefExpr* RhsDR = dyn_cast(RHS)) + if (VD == dyn_cast(RhsDR->getDecl())) + return; + + // Otherwise, issue a warning. + DeadStoreKind dsk = Parents.isConsumedExpr(B) + ? Enclosing + : (isIncrement(VD,B) ? DeadIncrement : Standard); + + CheckVarDecl(VD, DR, B->getRHS(), dsk, AD, Live); + } + } + else if (UnaryOperator* U = dyn_cast(S)) { + if (!U->isIncrementOp()) + return; + + // Handle: ++x within a subexpression. The solution is not warn + // about preincrements to dead variables when the preincrement occurs + // as a subexpression. This can lead to false negatives, e.g. "(++x);" + // A generalized dead code checker should find such issues. + if (U->isPrefix() && Parents.isConsumedExpr(U)) + return; + + Expr *Ex = U->getSubExpr()->IgnoreParenCasts(); + + if (DeclRefExpr* DR = dyn_cast(Ex)) + CheckDeclRef(DR, U, DeadIncrement, AD, Live); + } + else if (DeclStmt* DS = dyn_cast(S)) + // Iterate through the decls. Warn if any initializers are complex + // expressions that are not live (never used). + for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end(); + DI != DE; ++DI) { + + VarDecl* V = dyn_cast(*DI); + + if (!V) + continue; + + if (V->hasLocalStorage()) { + // Reference types confuse the dead stores checker. Skip them + // for now. + if (V->getType()->getAs()) + return; + + if (Expr* E = V->getInit()) { + // Don't warn on C++ objects (yet) until we can show that their + // constructors/destructors don't have side effects. + if (isa(E)) + return; + + if (isa(E)) + return; + + // A dead initialization is a variable that is dead after it + // is initialized. We don't flag warnings for those variables + // marked 'unused'. + if (!Live(V, AD) && V->getAttr() == 0) { + // Special case: check for initializations with constants. + // + // e.g. : int x = 0; + // + // If x is EVER assigned a new value later, don't issue + // a warning. This is because such initialization can be + // due to defensive programming. + if (E->isConstantInitializer(Ctx)) + return; + + // Special case: check for initializations from constant + // variables. + // + // e.g. extern const int MyConstant; + // int x = MyConstant; + // + if (DeclRefExpr *DRE=dyn_cast(E->IgnoreParenCasts())) + if (VarDecl *VD = dyn_cast(DRE->getDecl())) + if (VD->hasGlobalStorage() && + VD->getType().isConstQualified()) return; + + Report(V, DeadInit, V->getLocation(), E->getSourceRange()); + } + } + } + } + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Driver function to invoke the Dead-Stores checker on a CFG. +//===----------------------------------------------------------------------===// + +namespace { +class FindEscaped : public CFGRecStmtDeclVisitor{ + CFG *cfg; +public: + FindEscaped(CFG *c) : cfg(c) {} + + CFG& getCFG() { return *cfg; } + + llvm::SmallPtrSet Escaped; + + void VisitUnaryOperator(UnaryOperator* U) { + // Check for '&'. Any VarDecl whose value has its address-taken we + // treat as escaped. + Expr* E = U->getSubExpr()->IgnoreParenCasts(); + if (U->getOpcode() == UnaryOperator::AddrOf) + if (DeclRefExpr* DR = dyn_cast(E)) + if (VarDecl* VD = dyn_cast(DR->getDecl())) { + Escaped.insert(VD); + return; + } + Visit(E); + } +}; +} // end anonymous namespace + + +void clang::CheckDeadStores(CFG &cfg, LiveVariables &L, ParentMap &pmap, + BugReporter& BR) { + FindEscaped FS(&cfg); + FS.getCFG().VisitBlockStmts(FS); + DeadStoreObs A(BR.getContext(), BR, pmap, FS.Escaped); + L.runOnAllBlocks(cfg, &A); +} diff --git a/lib/Checker/CheckObjCDealloc.cpp b/lib/Checker/CheckObjCDealloc.cpp new file mode 100644 index 000000000000..d9606f1306d4 --- /dev/null +++ b/lib/Checker/CheckObjCDealloc.cpp @@ -0,0 +1,258 @@ +//==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckObjCDealloc, a checker that +// analyzes an Objective-C class's implementation to determine if it +// correctly implements -dealloc. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/Checkers/LocalCheckers.h" +#include "clang/Checker/BugReporter/PathDiagnostic.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +static bool scan_dealloc(Stmt* S, Selector Dealloc) { + + if (ObjCMessageExpr* ME = dyn_cast(S)) + if (ME->getSelector() == Dealloc) + if (ME->getReceiver()) + if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts()) + return isa(Receiver); + + // Recurse to children. + + for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) + if (*I && scan_dealloc(*I, Dealloc)) + return true; + + return false; +} + +static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID, + const ObjCPropertyDecl* PD, + Selector Release, + IdentifierInfo* SelfII, + ASTContext& Ctx) { + + // [mMyIvar release] + if (ObjCMessageExpr* ME = dyn_cast(S)) + if (ME->getSelector() == Release) + if (ME->getReceiver()) + if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts()) + if (ObjCIvarRefExpr* E = dyn_cast(Receiver)) + if (E->getDecl() == ID) + return true; + + // [self setMyIvar:nil]; + if (ObjCMessageExpr* ME = dyn_cast(S)) + if (ME->getReceiver()) + if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts()) + if (DeclRefExpr* E = dyn_cast(Receiver)) + if (E->getDecl()->getIdentifier() == SelfII) + if (ME->getMethodDecl() == PD->getSetterMethodDecl() && + ME->getNumArgs() == 1 && + ME->getArg(0)->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) + return true; + + // self.myIvar = nil; + if (BinaryOperator* BO = dyn_cast(S)) + if (BO->isAssignmentOp()) + if (ObjCPropertyRefExpr* PRE = + dyn_cast(BO->getLHS()->IgnoreParenCasts())) + if (PRE->getProperty() == PD) + if (BO->getRHS()->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) { + // This is only a 'release' if the property kind is not + // 'assign'. + return PD->getSetterKind() != ObjCPropertyDecl::Assign;; + } + + // Recurse to children. + for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) + if (*I && scan_ivar_release(*I, ID, PD, Release, SelfII, Ctx)) + return true; + + return false; +} + +void clang::CheckObjCDealloc(const ObjCImplementationDecl* D, + const LangOptions& LOpts, BugReporter& BR) { + + assert (LOpts.getGCMode() != LangOptions::GCOnly); + + ASTContext& Ctx = BR.getContext(); + const ObjCInterfaceDecl* ID = D->getClassInterface(); + + // Does the class contain any ivars that are pointers (or id<...>)? + // If not, skip the check entirely. + // NOTE: This is motivated by PR 2517: + // http://llvm.org/bugs/show_bug.cgi?id=2517 + + bool containsPointerIvar = false; + + for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end(); + I!=E; ++I) { + + ObjCIvarDecl* ID = *I; + QualType T = ID->getType(); + + if (!T->isObjCObjectPointerType() || + ID->getAttr()) // Skip IBOutlets. + continue; + + containsPointerIvar = true; + break; + } + + if (!containsPointerIvar) + return; + + // Determine if the class subclasses NSObject. + IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); + IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); + + + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + + if (II == NSObjectII) + break; + + // FIXME: For now, ignore classes that subclass SenTestCase, as these don't + // need to implement -dealloc. They implement tear down in another way, + // which we should try and catch later. + // http://llvm.org/bugs/show_bug.cgi?id=3187 + if (II == SenTestCaseII) + return; + } + + if (!ID) + return; + + // Get the "dealloc" selector. + IdentifierInfo* II = &Ctx.Idents.get("dealloc"); + Selector S = Ctx.Selectors.getSelector(0, &II); + ObjCMethodDecl* MD = 0; + + // Scan the instance methods for "dealloc". + for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), + E = D->instmeth_end(); I!=E; ++I) { + + if ((*I)->getSelector() == S) { + MD = *I; + break; + } + } + + if (!MD) { // No dealloc found. + + const char* name = LOpts.getGCMode() == LangOptions::NonGC + ? "missing -dealloc" + : "missing -dealloc (Hybrid MM, non-GC)"; + + std::string buf; + llvm::raw_string_ostream os(buf); + os << "Objective-C class '" << D->getNameAsString() + << "' lacks a 'dealloc' instance method"; + + BR.EmitBasicReport(name, os.str(), D->getLocStart()); + return; + } + + // dealloc found. Scan for missing [super dealloc]. + if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) { + + const char* name = LOpts.getGCMode() == LangOptions::NonGC + ? "missing [super dealloc]" + : "missing [super dealloc] (Hybrid MM, non-GC)"; + + std::string buf; + llvm::raw_string_ostream os(buf); + os << "The 'dealloc' instance method in Objective-C class '" + << D->getNameAsString() + << "' does not send a 'dealloc' message to its super class" + " (missing [super dealloc])"; + + BR.EmitBasicReport(name, os.str(), D->getLocStart()); + return; + } + + // Get the "release" selector. + IdentifierInfo* RII = &Ctx.Idents.get("release"); + Selector RS = Ctx.Selectors.getSelector(0, &RII); + + // Get the "self" identifier + IdentifierInfo* SelfII = &Ctx.Idents.get("self"); + + // Scan for missing and extra releases of ivars used by implementations + // of synthesized properties + for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(), + E = D->propimpl_end(); I!=E; ++I) { + + // We can only check the synthesized properties + if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + continue; + + ObjCIvarDecl* ID = (*I)->getPropertyIvarDecl(); + if (!ID) + continue; + + QualType T = ID->getType(); + if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars + continue; + + const ObjCPropertyDecl* PD = (*I)->getPropertyDecl(); + if (!PD) + continue; + + // ivars cannot be set via read-only properties, so we'll skip them + if (PD->isReadOnly()) + continue; + + // ivar must be released if and only if the kind of setter was not 'assign' + bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; + if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) + != requiresRelease) { + const char *name; + const char* category = "Memory (Core Foundation/Objective-C)"; + + std::string buf; + llvm::raw_string_ostream os(buf); + + if (requiresRelease) { + name = LOpts.getGCMode() == LangOptions::NonGC + ? "missing ivar release (leak)" + : "missing ivar release (Hybrid MM, non-GC)"; + + os << "The '" << ID->getNameAsString() + << "' instance variable was retained by a synthesized property but " + "wasn't released in 'dealloc'"; + } else { + name = LOpts.getGCMode() == LangOptions::NonGC + ? "extra ivar release (use-after-release)" + : "extra ivar release (Hybrid MM, non-GC)"; + + os << "The '" << ID->getNameAsString() + << "' instance variable was not retained by a synthesized property " + "but was released in 'dealloc'"; + } + + BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation()); + } + } +} + diff --git a/lib/Checker/CheckObjCInstMethSignature.cpp b/lib/Checker/CheckObjCInstMethSignature.cpp new file mode 100644 index 000000000000..8c43a45d92c0 --- /dev/null +++ b/lib/Checker/CheckObjCInstMethSignature.cpp @@ -0,0 +1,119 @@ +//=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckObjCInstMethSignature, a flow-insenstive check +// that determines if an Objective-C class interface incorrectly redefines +// the method signature in a subclass. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/Checkers/LocalCheckers.h" +#include "clang/Checker/BugReporter/PathDiagnostic.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Type.h" +#include "clang/AST/ASTContext.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +static bool AreTypesCompatible(QualType Derived, QualType Ancestor, + ASTContext& C) { + + // Right now don't compare the compatibility of pointers. That involves + // looking at subtyping relationships. FIXME: Future patch. + if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType()) + return true; + + return C.typesAreCompatible(Derived, Ancestor); +} + +static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, + const ObjCMethodDecl *MethAncestor, + BugReporter &BR, ASTContext &Ctx, + const ObjCImplementationDecl *ID) { + + QualType ResDerived = MethDerived->getResultType(); + QualType ResAncestor = MethAncestor->getResultType(); + + if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "The Objective-C class '" + << MethDerived->getClassInterface()->getNameAsString() + << "', which is derived from class '" + << MethAncestor->getClassInterface()->getNameAsString() + << "', defines the instance method '" + << MethDerived->getSelector().getAsString() + << "' whose return type is '" + << ResDerived.getAsString() + << "'. A method with the same name (same selector) is also defined in " + "class '" + << MethAncestor->getClassInterface()->getNameAsString() + << "' and has a return type of '" + << ResAncestor.getAsString() + << "'. These two types are incompatible, and may result in undefined " + "behavior for clients of these classes."; + + BR.EmitBasicReport("Incompatible instance method return type", + os.str(), MethDerived->getLocStart()); + } +} + +void clang::CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, + BugReporter& BR) { + + const ObjCInterfaceDecl* D = ID->getClassInterface(); + const ObjCInterfaceDecl* C = D->getSuperClass(); + + if (!C) + return; + + ASTContext& Ctx = BR.getContext(); + + // Build a DenseMap of the methods for quick querying. + typedef llvm::DenseMap MapTy; + MapTy IMeths; + unsigned NumMethods = 0; + + for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), + E=ID->instmeth_end(); I!=E; ++I) { + + ObjCMethodDecl* M = *I; + IMeths[M->getSelector()] = M; + ++NumMethods; + } + + // Now recurse the class hierarchy chain looking for methods with the + // same signatures. + while (C && NumMethods) { + for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), + E=C->instmeth_end(); I!=E; ++I) { + + ObjCMethodDecl* M = *I; + Selector S = M->getSelector(); + + MapTy::iterator MI = IMeths.find(S); + + if (MI == IMeths.end() || MI->second == 0) + continue; + + --NumMethods; + ObjCMethodDecl* MethDerived = MI->second; + MI->second = 0; + + CompareReturnTypes(MethDerived, M, BR, Ctx, ID); + } + + C = C->getSuperClass(); + } +} diff --git a/lib/Checker/CheckObjCUnusedIVars.cpp b/lib/Checker/CheckObjCUnusedIVars.cpp new file mode 100644 index 000000000000..f2cf58191632 --- /dev/null +++ b/lib/Checker/CheckObjCUnusedIVars.cpp @@ -0,0 +1,162 @@ +//==- CheckObjCUnusedIVars.cpp - Check for unused ivars ----------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckObjCUnusedIvars, a checker that +// analyzes an Objective-C class's interface/implementation to determine if it +// has any ivars that are never accessed. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/Checkers/LocalCheckers.h" +#include "clang/Checker/BugReporter/PathDiagnostic.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" + +using namespace clang; + +enum IVarState { Unused, Used }; +typedef llvm::DenseMap IvarUsageMap; + +static void Scan(IvarUsageMap& M, const Stmt* S) { + if (!S) + return; + + if (const ObjCIvarRefExpr *Ex = dyn_cast(S)) { + const ObjCIvarDecl *D = Ex->getDecl(); + IvarUsageMap::iterator I = M.find(D); + if (I != M.end()) + I->second = Used; + return; + } + + // Blocks can reference an instance variable of a class. + if (const BlockExpr *BE = dyn_cast(S)) { + Scan(M, BE->getBody()); + return; + } + + for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) + Scan(M, *I); +} + +static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) { + if (!D) + return; + + const ObjCIvarDecl* ID = D->getPropertyIvarDecl(); + + if (!ID) + return; + + IvarUsageMap::iterator I = M.find(ID); + if (I != M.end()) + I->second = Used; +} + +static void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) { + // Scan the methods for accesses. + for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), + E = D->instmeth_end(); I!=E; ++I) + Scan(M, (*I)->getBody()); + + if (const ObjCImplementationDecl *ID = dyn_cast(D)) { + // Scan for @synthesized property methods that act as setters/getters + // to an ivar. + for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), + E = ID->propimpl_end(); I!=E; ++I) + Scan(M, *I); + + // Scan the associated categories as well. + for (const ObjCCategoryDecl *CD = + ID->getClassInterface()->getCategoryList(); CD ; + CD = CD->getNextClassCategory()) { + if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) + Scan(M, CID); + } + } +} + +static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, + SourceManager &SM) { + for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); + I!=E; ++I) + if (const FunctionDecl *FD = dyn_cast(*I)) { + SourceLocation L = FD->getLocStart(); + if (SM.getFileID(L) == FID) + Scan(M, FD->getBody()); + } +} + +void clang::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, + BugReporter &BR) { + + const ObjCInterfaceDecl* ID = D->getClassInterface(); + IvarUsageMap M; + + // Iterate over the ivars. + for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), + E=ID->ivar_end(); I!=E; ++I) { + + const ObjCIvarDecl* ID = *I; + + // Ignore ivars that aren't private. + if (ID->getAccessControl() != ObjCIvarDecl::Private) + continue; + + // Skip IB Outlets. + if (ID->getAttr()) + continue; + + M[ID] = Unused; + } + + if (M.empty()) + return; + + // Now scan the implementation declaration. + Scan(M, D); + + + // Any potentially unused ivars? + bool hasUnused = false; + for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) + if (I->second == Unused) { + hasUnused = true; + break; + } + + if (!hasUnused) + return; + + // We found some potentially unused ivars. Scan the entire translation unit + // for functions inside the @implementation that reference these ivars. + // FIXME: In the future hopefully we can just use the lexical DeclContext + // to go from the ObjCImplementationDecl to the lexically "nested" + // C functions. + SourceManager &SM = BR.getSourceManager(); + Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); + + // Find ivars that are unused. + for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) + if (I->second == Unused) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Instance variable '" << I->first->getNameAsString() + << "' in class '" << ID->getNameAsString() + << "' is never used by the methods in its @implementation " + "(although it may be used by category methods)."; + + BR.EmitBasicReport("Unused instance variable", "Optimization", + os.str(), I->first->getLocation()); + } +} diff --git a/lib/Checker/CheckSecuritySyntaxOnly.cpp b/lib/Checker/CheckSecuritySyntaxOnly.cpp new file mode 100644 index 000000000000..923baf50f3f6 --- /dev/null +++ b/lib/Checker/CheckSecuritySyntaxOnly.cpp @@ -0,0 +1,502 @@ +//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a set of flow-insensitive security checks. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/TargetInfo.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/Checkers/LocalCheckers.h" +#include "clang/AST/StmtVisitor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +static bool isArc4RandomAvailable(const ASTContext &Ctx) { + const llvm::Triple &T = Ctx.Target.getTriple(); + return T.getVendor() == llvm::Triple::Apple || + T.getOS() == llvm::Triple::FreeBSD; +} + +namespace { +class WalkAST : public StmtVisitor { + BugReporter &BR; + IdentifierInfo *II_gets; + IdentifierInfo *II_getpw; + IdentifierInfo *II_mktemp; + enum { num_rands = 9 }; + IdentifierInfo *II_rand[num_rands]; + IdentifierInfo *II_random; + enum { num_setids = 6 }; + IdentifierInfo *II_setid[num_setids]; + + const bool CheckRand; + +public: + WalkAST(BugReporter &br) : BR(br), + II_gets(0), II_getpw(0), II_mktemp(0), + II_rand(), II_random(0), II_setid(), + CheckRand(isArc4RandomAvailable(BR.getContext())) {} + + // Statement visitor methods. + void VisitCallExpr(CallExpr *CE); + void VisitForStmt(ForStmt *S); + void VisitCompoundStmt (CompoundStmt *S); + void VisitStmt(Stmt *S) { VisitChildren(S); } + + void VisitChildren(Stmt *S); + + // Helpers. + IdentifierInfo *GetIdentifier(IdentifierInfo *& II, const char *str); + + // Checker-specific methods. + void CheckLoopConditionForFloat(const ForStmt *FS); + void CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD); + void CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD); + void CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); + void CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD); + void CheckCall_random(const CallExpr *CE, const FunctionDecl *FD); + void CheckUncheckedReturnValue(CallExpr *CE); +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Helper methods. +//===----------------------------------------------------------------------===// + +IdentifierInfo *WalkAST::GetIdentifier(IdentifierInfo *& II, const char *str) { + if (!II) + II = &BR.getContext().Idents.get(str); + + return II; +} + +//===----------------------------------------------------------------------===// +// AST walking. +//===----------------------------------------------------------------------===// + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) + Visit(child); +} + +void WalkAST::VisitCallExpr(CallExpr *CE) { + if (const FunctionDecl *FD = CE->getDirectCallee()) { + CheckCall_gets(CE, FD); + CheckCall_getpw(CE, FD); + CheckCall_mktemp(CE, FD); + if (CheckRand) { + CheckCall_rand(CE, FD); + CheckCall_random(CE, FD); + } + } + + // Recurse and check children. + VisitChildren(CE); +} + +void WalkAST::VisitCompoundStmt(CompoundStmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) { + if (CallExpr *CE = dyn_cast(child)) + CheckUncheckedReturnValue(CE); + Visit(child); + } +} + +void WalkAST::VisitForStmt(ForStmt *FS) { + CheckLoopConditionForFloat(FS); + + // Recurse and check children. + VisitChildren(FS); +} + +//===----------------------------------------------------------------------===// +// Check: floating poing variable used as loop counter. +// Originally: +// Implements: CERT security coding advisory FLP-30. +//===----------------------------------------------------------------------===// + +static const DeclRefExpr* +GetIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { + expr = expr->IgnoreParenCasts(); + + if (const BinaryOperator *B = dyn_cast(expr)) { + if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || + B->getOpcode() == BinaryOperator::Comma)) + return NULL; + + if (const DeclRefExpr *lhs = GetIncrementedVar(B->getLHS(), x, y)) + return lhs; + + if (const DeclRefExpr *rhs = GetIncrementedVar(B->getRHS(), x, y)) + return rhs; + + return NULL; + } + + if (const DeclRefExpr *DR = dyn_cast(expr)) { + const NamedDecl *ND = DR->getDecl(); + return ND == x || ND == y ? DR : NULL; + } + + if (const UnaryOperator *U = dyn_cast(expr)) + return U->isIncrementDecrementOp() + ? GetIncrementedVar(U->getSubExpr(), x, y) : NULL; + + return NULL; +} + +/// CheckLoopConditionForFloat - This check looks for 'for' statements that +/// use a floating point variable as a loop counter. +/// CERT: FLP30-C, FLP30-CPP. +/// +void WalkAST::CheckLoopConditionForFloat(const ForStmt *FS) { + // Does the loop have a condition? + const Expr *condition = FS->getCond(); + + if (!condition) + return; + + // Does the loop have an increment? + const Expr *increment = FS->getInc(); + + if (!increment) + return; + + // Strip away '()' and casts. + condition = condition->IgnoreParenCasts(); + increment = increment->IgnoreParenCasts(); + + // Is the loop condition a comparison? + const BinaryOperator *B = dyn_cast(condition); + + if (!B) + return; + + // Is this a comparison? + if (!(B->isRelationalOp() || B->isEqualityOp())) + return; + + // Are we comparing variables? + const DeclRefExpr *drLHS = dyn_cast(B->getLHS()->IgnoreParens()); + const DeclRefExpr *drRHS = dyn_cast(B->getRHS()->IgnoreParens()); + + // Does at least one of the variables have a floating point type? + drLHS = drLHS && drLHS->getType()->isFloatingType() ? drLHS : NULL; + drRHS = drRHS && drRHS->getType()->isFloatingType() ? drRHS : NULL; + + if (!drLHS && !drRHS) + return; + + const VarDecl *vdLHS = drLHS ? dyn_cast(drLHS->getDecl()) : NULL; + const VarDecl *vdRHS = drRHS ? dyn_cast(drRHS->getDecl()) : NULL; + + if (!vdLHS && !vdRHS) + return; + + // Does either variable appear in increment? + const DeclRefExpr *drInc = GetIncrementedVar(increment, vdLHS, vdRHS); + + if (!drInc) + return; + + // Emit the error. First figure out which DeclRefExpr in the condition + // referenced the compared variable. + const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; + + llvm::SmallVector ranges; + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "Variable '" << drCond->getDecl()->getNameAsCString() + << "' with floating point type '" << drCond->getType().getAsString() + << "' should not be used as a loop counter"; + + ranges.push_back(drCond->getSourceRange()); + ranges.push_back(drInc->getSourceRange()); + + const char *bugType = "Floating point variable used as loop counter"; + BR.EmitBasicReport(bugType, "Security", os.str(), + FS->getLocStart(), ranges.data(), ranges.size()); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'gets' is insecure. +// Originally: +// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) +// CWE-242: Use of Inherently Dangerous Function +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_gets, "gets")) + return; + + const FunctionProtoType *FPT = dyn_cast(FD->getType()); + if (!FPT) + return; + + // Verify that the function takes a single argument. + if (FPT->getNumArgs() != 1) + return; + + // Is the argument a 'char*'? + const PointerType *PT = dyn_cast(FPT->getArgType(0)); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("Potential buffer overflow in call to 'gets'", + "Security", + "Call to function 'gets' is extremely insecure as it can " + "always result in a buffer overflow", + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'getpwd' is insecure. +// CWE-477: Use of Obsolete Functions +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_getpw, "getpw")) + return; + + const FunctionProtoType *FPT = dyn_cast(FD->getType()); + if (!FPT) + return; + + // Verify that the function takes two arguments. + if (FPT->getNumArgs() != 2) + return; + + // Verify the first argument type is integer. + if (!FPT->getArgType(0)->isIntegerType()) + return; + + // Verify the second argument type is char*. + const PointerType *PT = dyn_cast(FPT->getArgType(1)); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'", + "Security", + "The getpw() function is dangerous as it may overflow the " + "provided buffer. It is obsoleted by getpwuid().", + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'mktemp' is insecure.It is obsoleted by mkstemp(). +// CWE-377: Insecure Temporary File +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_mktemp, "mktemp")) + return; + + const FunctionProtoType *FPT = dyn_cast(FD->getType()); + if(!FPT) + return; + + // Verify that the funcion takes a single argument. + if (FPT->getNumArgs() != 1) + return; + + // Verify that the argument is Pointer Type. + const PointerType *PT = dyn_cast(FPT->getArgType(0)); + if (!PT) + return; + + // Verify that the argument is a 'char*'. + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a waring. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'", + "Security", + "Call to function 'mktemp' is insecure as it always " + "creates or uses insecure temporary file", + CE->getLocStart(), &R, 1); +} + + +//===----------------------------------------------------------------------===// +// Check: Linear congruent random number generators should not be used +// Originally: +// CWE-338: Use of cryptographically weak prng +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD) { + if (II_rand[0] == NULL) { + // This check applies to these functions + static const char * const identifiers[num_rands] = { + "drand48", "erand48", "jrand48", "lrand48", "mrand48", "nrand48", + "lcong48", + "rand", "rand_r" + }; + + for (size_t i = 0; i < num_rands; i++) + II_rand[i] = &BR.getContext().Idents.get(identifiers[i]); + } + + const IdentifierInfo *id = FD->getIdentifier(); + size_t identifierid; + + for (identifierid = 0; identifierid < num_rands; identifierid++) + if (id == II_rand[identifierid]) + break; + + if (identifierid >= num_rands) + return; + + const FunctionProtoType *FTP = dyn_cast(FD->getType()); + if (!FTP) + return; + + if (FTP->getNumArgs() == 1) { + // Is the argument an 'unsigned short *'? + // (Actually any integer type is allowed.) + const PointerType *PT = dyn_cast(FTP->getArgType(0)); + if (!PT) + return; + + if (! PT->getPointeeType()->isIntegerType()) + return; + } + else if (FTP->getNumArgs() != 0) + return; + + // Issue a warning. + std::string buf1; + llvm::raw_string_ostream os1(buf1); + os1 << "'" << FD->getNameAsString() << "' is a poor random number generator"; + + std::string buf2; + llvm::raw_string_ostream os2(buf2); + os2 << "Function '" << FD->getNameAsString() + << "' is obsolete because it implements a poor random number generator." + << " Use 'arc4random' instead"; + + SourceRange R = CE->getCallee()->getSourceRange(); + + BR.EmitBasicReport(os1.str(), "Security", os2.str(), + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: 'random' should not be used +// Originally: +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_random(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_random, "random")) + return; + + const FunctionProtoType *FTP = dyn_cast(FD->getType()); + if (!FTP) + return; + + // Verify that the function takes no argument. + if (FTP->getNumArgs() != 0) + return; + + // Issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("'random' is not a secure random number generator", + "Security", + "The 'random' function produces a sequence of values that " + "an adversary may be able to predict. Use 'arc4random' " + "instead", + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: Should check whether privileges are dropped successfully. +// Originally: +//===----------------------------------------------------------------------===// + +void WalkAST::CheckUncheckedReturnValue(CallExpr *CE) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return; + + if (II_setid[0] == NULL) { + static const char * const identifiers[num_setids] = { + "setuid", "setgid", "seteuid", "setegid", + "setreuid", "setregid" + }; + + for (size_t i = 0; i < num_setids; i++) + II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); + } + + const IdentifierInfo *id = FD->getIdentifier(); + size_t identifierid; + + for (identifierid = 0; identifierid < num_setids; identifierid++) + if (id == II_setid[identifierid]) + break; + + if (identifierid >= num_setids) + return; + + const FunctionProtoType *FTP = dyn_cast(FD->getType()); + if (!FTP) + return; + + // Verify that the function takes one or two arguments (depending on + // the function). + if (FTP->getNumArgs() != (identifierid < 4 ? 1 : 2)) + return; + + // The arguments must be integers. + for (unsigned i = 0; i < FTP->getNumArgs(); i++) + if (! FTP->getArgType(i)->isIntegerType()) + return; + + // Issue a warning. + std::string buf1; + llvm::raw_string_ostream os1(buf1); + os1 << "Return value is not checked in call to '" << FD->getNameAsString() + << "'"; + + std::string buf2; + llvm::raw_string_ostream os2(buf2); + os2 << "The return value from the call to '" << FD->getNameAsString() + << "' is not checked. If an error occurs in '" + << FD->getNameAsString() + << "', the following code may execute with unexpected privileges"; + + SourceRange R = CE->getCallee()->getSourceRange(); + + BR.EmitBasicReport(os1.str(), "Security", os2.str(), + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Entry point for check. +//===----------------------------------------------------------------------===// + +void clang::CheckSecuritySyntaxOnly(const Decl *D, BugReporter &BR) { + WalkAST walker(BR); + walker.Visit(D->getBody()); +} diff --git a/lib/Checker/CheckSizeofPointer.cpp b/lib/Checker/CheckSizeofPointer.cpp new file mode 100644 index 000000000000..bbe494c99da5 --- /dev/null +++ b/lib/Checker/CheckSizeofPointer.cpp @@ -0,0 +1,71 @@ +//==- CheckSizeofPointer.cpp - Check for sizeof on pointers ------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a check for unintended use of sizeof() on pointer +// expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Checker/Checkers/LocalCheckers.h" + +using namespace clang; + +namespace { +class WalkAST : public StmtVisitor { + BugReporter &BR; + +public: + WalkAST(BugReporter &br) : BR(br) {} + void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitChildren(Stmt *S); +}; +} + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) + Visit(child); +} + +// CWE-467: Use of sizeof() on a Pointer Type +void WalkAST::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { + if (!E->isSizeOf()) + return; + + // If an explicit type is used in the code, usually the coder knows what he is + // doing. + if (E->isArgumentType()) + return; + + QualType T = E->getTypeOfArgument(); + if (T->isPointerType()) { + + // Many false positives have the form 'sizeof *p'. This is reasonable + // because people know what they are doing when they intentionally + // dereference the pointer. + Expr *ArgEx = E->getArgumentExpr(); + if (!isa(ArgEx->IgnoreParens())) + return; + + SourceRange R = ArgEx->getSourceRange(); + BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type", + "Logic", + "The code calls sizeof() on a pointer type. " + "This can produce an unexpected result.", + E->getLocStart(), &R, 1); + } +} + +void clang::CheckSizeofPointer(const Decl *D, BugReporter &BR) { + WalkAST walker(BR); + walker.Visit(D->getBody()); +} diff --git a/lib/Checker/Checker.cpp b/lib/Checker/Checker.cpp new file mode 100644 index 000000000000..36323b9efb61 --- /dev/null +++ b/lib/Checker/Checker.cpp @@ -0,0 +1,35 @@ +//== Checker.h - Abstract interface for checkers -----------------*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines Checker and CheckerVisitor, classes used for creating +// domain-specific checks. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/Checker.h" +using namespace clang; + +Checker::~Checker() {} + +CheckerContext::~CheckerContext() { + // Do we need to autotransition? 'Dst' can get populated in a variety of + // ways, including 'addTransition()' adding the predecessor node to Dst + // without actually generated a new node. We also shouldn't autotransition + // if we are building sinks or we generated a node and decided to not + // add it as a transition. + if (Dst.size() == size && !B.BuildSinks && !B.HasGeneratedNode) { + if (ST && ST != B.GetState(Pred)) { + static int autoTransitionTag = 0; + B.Tag = &autoTransitionTag; + addTransition(ST); + } + else + Dst.Add(Pred); + } +} diff --git a/lib/Checker/CocoaConventions.cpp b/lib/Checker/CocoaConventions.cpp new file mode 100644 index 000000000000..3ba887ccc7e3 --- /dev/null +++ b/lib/Checker/CocoaConventions.cpp @@ -0,0 +1,195 @@ +//===- CocoaConventions.h - Special handling of Cocoa conventions -*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/DomainSpecific/CocoaConventions.h" +#include "clang/AST/Type.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "llvm/ADT/StringExtras.h" + +using namespace clang; + +using llvm::StringRef; + +// The "fundamental rule" for naming conventions of methods: +// (url broken into two lines) +// http://developer.apple.com/documentation/Cocoa/Conceptual/ +// MemoryMgmt/Tasks/MemoryManagementRules.html +// +// "You take ownership of an object if you create it using a method whose name +// begins with "alloc" or "new" or contains "copy" (for example, alloc, +// newObject, or mutableCopy), or if you send it a retain message. You are +// responsible for relinquishing ownership of objects you own using release +// or autorelease. Any other time you receive an object, you must +// not release it." +// + +static bool isWordEnd(char ch, char prev, char next) { + return ch == '\0' + || (islower(prev) && isupper(ch)) // xxxC + || (isupper(prev) && isupper(ch) && islower(next)) // XXCreate + || !isalpha(ch); +} + +static const char* parseWord(const char* s) { + char ch = *s, prev = '\0'; + assert(ch != '\0'); + char next = *(s+1); + while (!isWordEnd(ch, prev, next)) { + prev = ch; + ch = next; + next = *((++s)+1); + } + return s; +} + +cocoa::NamingConvention cocoa::deriveNamingConvention(Selector S) { + IdentifierInfo *II = S.getIdentifierInfoForSlot(0); + + if (!II) + return NoConvention; + + const char *s = II->getNameStart(); + + // A method/function name may contain a prefix. We don't know it is there, + // however, until we encounter the first '_'. + bool InPossiblePrefix = true; + bool AtBeginning = true; + NamingConvention C = NoConvention; + + while (*s != '\0') { + // Skip '_'. + if (*s == '_') { + if (InPossiblePrefix) { + // If we already have a convention, return it. Otherwise, skip + // the prefix as if it wasn't there. + if (C != NoConvention) + break; + + InPossiblePrefix = false; + AtBeginning = true; + assert(C == NoConvention); + } + ++s; + continue; + } + + // Skip numbers, ':', etc. + if (!isalpha(*s)) { + ++s; + continue; + } + + const char *wordEnd = parseWord(s); + assert(wordEnd > s); + unsigned len = wordEnd - s; + + switch (len) { + default: + break; + case 3: + // Methods starting with 'new' follow the create rule. + if (AtBeginning && StringRef(s, len).equals_lower("new")) + C = CreateRule; + break; + case 4: + // Methods starting with 'alloc' or contain 'copy' follow the + // create rule + if (C == NoConvention && StringRef(s, len).equals_lower("copy")) + C = CreateRule; + else // Methods starting with 'init' follow the init rule. + if (AtBeginning && StringRef(s, len).equals_lower("init")) + C = InitRule; + break; + case 5: + if (AtBeginning && StringRef(s, len).equals_lower("alloc")) + C = CreateRule; + break; + } + + // If we aren't in the prefix and have a derived convention then just + // return it now. + if (!InPossiblePrefix && C != NoConvention) + return C; + + AtBeginning = false; + s = wordEnd; + } + + // We will get here if there wasn't more than one word + // after the prefix. + return C; +} + +bool cocoa::isRefType(QualType RetTy, llvm::StringRef Prefix, + llvm::StringRef Name) { + // Recursively walk the typedef stack, allowing typedefs of reference types. + while (TypedefType* TD = dyn_cast(RetTy.getTypePtr())) { + llvm::StringRef TDName = TD->getDecl()->getIdentifier()->getName(); + if (TDName.startswith(Prefix) && TDName.endswith("Ref")) + return true; + + RetTy = TD->getDecl()->getUnderlyingType(); + } + + if (Name.empty()) + return false; + + // Is the type void*? + const PointerType* PT = RetTy->getAs(); + if (!(PT->getPointeeType().getUnqualifiedType()->isVoidType())) + return false; + + // Does the name start with the prefix? + return Name.startswith(Prefix); +} + +bool cocoa::isCFObjectRef(QualType T) { + return isRefType(T, "CF") || // Core Foundation. + isRefType(T, "CG") || // Core Graphics. + isRefType(T, "DADisk") || // Disk Arbitration API. + isRefType(T, "DADissenter") || + isRefType(T, "DASessionRef"); +} + + +bool cocoa::isCocoaObjectRef(QualType Ty) { + if (!Ty->isObjCObjectPointerType()) + return false; + + const ObjCObjectPointerType *PT = Ty->getAs(); + + // Can be true for objects with the 'NSObject' attribute. + if (!PT) + return true; + + // We assume that id<..>, id, and "Class" all represent tracked objects. + if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() || + PT->isObjCClassType()) + return true; + + // Does the interface subclass NSObject? + // FIXME: We can memoize here if this gets too expensive. + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); + + // Assume that anything declared with a forward declaration and no + // @interface subclasses NSObject. + if (ID->isForwardDecl()) + return true; + + for ( ; ID ; ID = ID->getSuperClass()) + if (ID->getIdentifier()->getName() == "NSObject") + return true; + + return false; +} diff --git a/lib/Checker/DereferenceChecker.cpp b/lib/Checker/DereferenceChecker.cpp new file mode 100644 index 000000000000..0cbc4086701a --- /dev/null +++ b/lib/Checker/DereferenceChecker.cpp @@ -0,0 +1,135 @@ +//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines NullDerefChecker, a builtin check in GRExprEngine that performs +// checks for null pointers at loads and stores. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/Checkers/DereferenceChecker.h" +#include "clang/Checker/PathSensitive/Checker.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class DereferenceChecker : public Checker { + BuiltinBug *BT_null; + BuiltinBug *BT_undef; + llvm::SmallVector ImplicitNullDerefNodes; +public: + DereferenceChecker() : BT_null(0), BT_undef(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void VisitLocation(CheckerContext &C, const Stmt *S, SVal location); + + std::pair + getImplicitNodes() const { + return std::make_pair(ImplicitNullDerefNodes.data(), + ImplicitNullDerefNodes.data() + + ImplicitNullDerefNodes.size()); + } +}; +} // end anonymous namespace + +void clang::RegisterDereferenceChecker(GRExprEngine &Eng) { + Eng.registerCheck(new DereferenceChecker()); +} + +std::pair +clang::GetImplicitNullDereferences(GRExprEngine &Eng) { + DereferenceChecker *checker = Eng.getChecker(); + if (!checker) + return std::make_pair((ExplodedNode * const *) 0, + (ExplodedNode * const *) 0); + return checker->getImplicitNodes(); +} + +void DereferenceChecker::VisitLocation(CheckerContext &C, const Stmt *S, + SVal l) { + // Check for dereference of an undefined value. + if (l.isUndef()) { + if (ExplodedNode *N = C.GenerateSink()) { + if (!BT_undef) + BT_undef = new BuiltinBug("Dereference of undefined pointer value"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + C.EmitReport(report); + } + return; + } + + DefinedOrUnknownSVal location = cast(l); + + // Check for null dereferences. + if (!isa(location)) + return; + + const GRState *state = C.getState(); + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->Assume(location); + + // The explicit NULL case. + if (nullState) { + if (!notNullState) { + // Generate an error node. + ExplodedNode *N = C.GenerateSink(nullState); + if (!N) + return; + + // We know that 'location' cannot be non-null. This is what + // we call an "explicit" null dereference. + if (!BT_null) + BT_null = new BuiltinBug("Dereference of null pointer"); + + llvm::SmallString<100> buf; + + switch (S->getStmtClass()) { + case Stmt::UnaryOperatorClass: { + const UnaryOperator *U = cast(S); + const Expr *SU = U->getSubExpr()->IgnoreParens(); + if (const DeclRefExpr *DR = dyn_cast(SU)) { + if (const VarDecl *VD = dyn_cast(DR->getDecl())) { + llvm::raw_svector_ostream os(buf); + os << "Dereference of null pointer loaded from variable '" + << VD->getName() << '\''; + } + } + } + default: + break; + } + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_null, + buf.empty() ? BT_null->getDescription():buf.str(), + N); + + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + + C.EmitReport(report); + return; + } + else { + // Otherwise, we have the case where the location could either be + // null or not-null. Record the error node as an "implicit" null + // dereference. + if (ExplodedNode *N = C.GenerateSink(nullState)) + ImplicitNullDerefNodes.push_back(N); + } + } + + // From this point forward, we know that the location is not null. + C.addTransition(notNullState); +} diff --git a/lib/Checker/DivZeroChecker.cpp b/lib/Checker/DivZeroChecker.cpp new file mode 100644 index 000000000000..e1346e11b6fd --- /dev/null +++ b/lib/Checker/DivZeroChecker.cpp @@ -0,0 +1,84 @@ +//== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines DivZeroChecker, a builtin check in GRExprEngine that performs +// checks for division by zeros. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class DivZeroChecker : public CheckerVisitor { + BuiltinBug *BT; +public: + DivZeroChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} // end anonymous namespace + +void clang::RegisterDivZeroChecker(GRExprEngine &Eng) { + Eng.registerCheck(new DivZeroChecker()); +} + +void *DivZeroChecker::getTag() { + static int x; + return &x; +} + +void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + BinaryOperator::Opcode Op = B->getOpcode(); + if (Op != BinaryOperator::Div && + Op != BinaryOperator::Rem && + Op != BinaryOperator::DivAssign && + Op != BinaryOperator::RemAssign) + return; + + if (!B->getRHS()->getType()->isIntegerType() || + !B->getRHS()->getType()->isScalarType()) + return; + + SVal Denom = C.getState()->getSVal(B->getRHS()); + const DefinedSVal *DV = dyn_cast(&Denom); + + // Divide-by-undefined handled in the generic checking for uses of + // undefined values. + if (!DV) + return; + + // Check for divide by zero. + ConstraintManager &CM = C.getConstraintManager(); + const GRState *stateNotZero, *stateZero; + llvm::tie(stateNotZero, stateZero) = CM.AssumeDual(C.getState(), *DV); + + if (stateZero && !stateNotZero) { + if (ExplodedNode *N = C.GenerateSink(stateZero)) { + if (!BT) + BT = new BuiltinBug("Division by zero"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, BT->getDescription(), N); + + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDenomExpr(N)); + + C.EmitReport(R); + } + return; + } + + // If we get here, then the denom should not be zero. We abandon the implicit + // zero denom case for now. + C.addTransition(stateNotZero); +} diff --git a/lib/Checker/Environment.cpp b/lib/Checker/Environment.cpp new file mode 100644 index 000000000000..c2c9190fc9ff --- /dev/null +++ b/lib/Checker/Environment.cpp @@ -0,0 +1,164 @@ +//== Environment.cpp - Map from Stmt* to Locations/Values -------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defined the Environment and EnvironmentManager classes. +// +//===----------------------------------------------------------------------===// +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "llvm/ADT/ImmutableMap.h" + +using namespace clang; + +SVal Environment::GetSVal(const Stmt *E, ValueManager& ValMgr) const { + + for (;;) { + + switch (E->getStmtClass()) { + + case Stmt::AddrLabelExprClass: + return ValMgr.makeLoc(cast(E)); + + // ParenExprs are no-ops. + + case Stmt::ParenExprClass: + E = cast(E)->getSubExpr(); + continue; + + case Stmt::CharacterLiteralClass: { + const CharacterLiteral* C = cast(E); + return ValMgr.makeIntVal(C->getValue(), C->getType()); + } + + case Stmt::IntegerLiteralClass: { + // In C++, this expression may have been bound to a temporary object. + SVal const *X = ExprBindings.lookup(E); + if (X) + return *X; + else + return ValMgr.makeIntVal(cast(E)); + } + + // Casts where the source and target type are the same + // are no-ops. We blast through these to get the descendant + // subexpression that has a value. + + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: { + const CastExpr* C = cast(E); + QualType CT = C->getType(); + + if (CT->isVoidType()) + return UnknownVal(); + + break; + } + + // Handle all other Stmt* using a lookup. + + default: + break; + }; + + break; + } + + return LookupExpr(E); +} + +Environment EnvironmentManager::BindExpr(Environment Env, const Stmt *S, + SVal V, bool Invalidate) { + assert(S); + + if (V.isUnknown()) { + if (Invalidate) + return Environment(F.Remove(Env.ExprBindings, S), Env.ACtx); + else + return Env; + } + + return Environment(F.Add(Env.ExprBindings, S, V), Env.ACtx); +} + +namespace { +class MarkLiveCallback : public SymbolVisitor { + SymbolReaper &SymReaper; +public: + MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} + bool VisitSymbol(SymbolRef sym) { SymReaper.markLive(sym); return true; } +}; +} // end anonymous namespace + +// RemoveDeadBindings: +// - Remove subexpression bindings. +// - Remove dead block expression bindings. +// - Keep live block expression bindings: +// - Mark their reachable symbols live in SymbolReaper, +// see ScanReachableSymbols. +// - Mark the region in DRoots if the binding is a loc::MemRegionVal. + +Environment +EnvironmentManager::RemoveDeadBindings(Environment Env, const Stmt *S, + SymbolReaper &SymReaper, + const GRState *ST, + llvm::SmallVectorImpl &DRoots) { + + CFG &C = *Env.getAnalysisContext().getCFG(); + + // We construct a new Environment object entirely, as this is cheaper than + // individually removing all the subexpression bindings (which will greatly + // outnumber block-level expression bindings). + Environment NewEnv = getInitialEnvironment(&Env.getAnalysisContext()); + + // Iterate over the block-expr bindings. + for (Environment::iterator I = Env.begin(), E = Env.end(); + I != E; ++I) { + + const Stmt *BlkExpr = I.getKey(); + + // Not a block-level expression? + if (!C.isBlkExpr(BlkExpr)) + continue; + + const SVal &X = I.getData(); + + if (SymReaper.isLive(S, BlkExpr)) { + // Copy the binding to the new map. + NewEnv.ExprBindings = F.Add(NewEnv.ExprBindings, BlkExpr, X); + + // If the block expr's value is a memory region, then mark that region. + if (isa(X)) { + const MemRegion* R = cast(X).getRegion(); + DRoots.push_back(R); + // Mark the super region of the RX as live. + // e.g.: int x; char *y = (char*) &x; if (*y) ... + // 'y' => element region. 'x' is its super region. + // We only add one level super region for now. + + // FIXME: maybe multiple level of super regions should be added. + if (const SubRegion *SR = dyn_cast(R)) + DRoots.push_back(SR->getSuperRegion()); + } + + // Mark all symbols in the block expr's value live. + MarkLiveCallback cb(SymReaper); + ST->scanReachableSymbols(X, cb); + continue; + } + + // Otherwise the expression is dead with a couple exceptions. + // Do not misclean LogicalExpr or ConditionalOperator. It is dead at the + // beginning of itself, but we need its UndefinedVal to determine its + // SVal. + if (X.isUndef() && cast(X).getData()) + NewEnv.ExprBindings = F.Add(NewEnv.ExprBindings, BlkExpr, X); + } + + return NewEnv; +} diff --git a/lib/Checker/ExplodedGraph.cpp b/lib/Checker/ExplodedGraph.cpp new file mode 100644 index 000000000000..20429b951992 --- /dev/null +++ b/lib/Checker/ExplodedGraph.cpp @@ -0,0 +1,281 @@ +//=-- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -*- C++ -*------=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the template classes ExplodedNode and ExplodedGraph, +// which represent a path-sensitive, intra-procedural "exploded graph." +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/ExplodedGraph.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/AST/Stmt.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Node auditing. +//===----------------------------------------------------------------------===// + +// An out of line virtual method to provide a home for the class vtable. +ExplodedNode::Auditor::~Auditor() {} + +#ifndef NDEBUG +static ExplodedNode::Auditor* NodeAuditor = 0; +#endif + +void ExplodedNode::SetAuditor(ExplodedNode::Auditor* A) { +#ifndef NDEBUG + NodeAuditor = A; +#endif +} + +//===----------------------------------------------------------------------===// +// ExplodedNode. +//===----------------------------------------------------------------------===// + +static inline BumpVector& getVector(void* P) { + return *reinterpret_cast*>(P); +} + +void ExplodedNode::addPredecessor(ExplodedNode* V, ExplodedGraph &G) { + assert (!V->isSink()); + Preds.addNode(V, G); + V->Succs.addNode(this, G); +#ifndef NDEBUG + if (NodeAuditor) NodeAuditor->AddEdge(V, this); +#endif +} + +void ExplodedNode::NodeGroup::addNode(ExplodedNode* N, ExplodedGraph &G) { + assert((reinterpret_cast(N) & Mask) == 0x0); + assert(!getFlag()); + + if (getKind() == Size1) { + if (ExplodedNode* NOld = getNode()) { + BumpVectorContext &Ctx = G.getNodeAllocator(); + BumpVector *V = + G.getAllocator().Allocate >(); + new (V) BumpVector(Ctx, 4); + + assert((reinterpret_cast(V) & Mask) == 0x0); + V->push_back(NOld, Ctx); + V->push_back(N, Ctx); + P = reinterpret_cast(V) | SizeOther; + assert(getPtr() == (void*) V); + assert(getKind() == SizeOther); + } + else { + P = reinterpret_cast(N); + assert(getKind() == Size1); + } + } + else { + assert(getKind() == SizeOther); + getVector(getPtr()).push_back(N, G.getNodeAllocator()); + } +} + +unsigned ExplodedNode::NodeGroup::size() const { + if (getFlag()) + return 0; + + if (getKind() == Size1) + return getNode() ? 1 : 0; + else + return getVector(getPtr()).size(); +} + +ExplodedNode **ExplodedNode::NodeGroup::begin() const { + if (getFlag()) + return NULL; + + if (getKind() == Size1) + return (ExplodedNode**) (getPtr() ? &P : NULL); + else + return const_cast(&*(getVector(getPtr()).begin())); +} + +ExplodedNode** ExplodedNode::NodeGroup::end() const { + if (getFlag()) + return NULL; + + if (getKind() == Size1) + return (ExplodedNode**) (getPtr() ? &P+1 : NULL); + else { + // Dereferencing end() is undefined behaviour. The vector is not empty, so + // we can dereference the last elem and then add 1 to the result. + return const_cast(getVector(getPtr()).end()); + } +} + +ExplodedNode *ExplodedGraph::getNode(const ProgramPoint& L, + const GRState* State, bool* IsNew) { + // Profile 'State' to determine if we already have an existing node. + llvm::FoldingSetNodeID profile; + void* InsertPos = 0; + + NodeTy::Profile(profile, L, State); + NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos); + + if (!V) { + // Allocate a new node. + V = (NodeTy*) getAllocator().Allocate(); + new (V) NodeTy(L, State); + + // Insert the node into the node set and return it. + Nodes.InsertNode(V, InsertPos); + + ++NumNodes; + + if (IsNew) *IsNew = true; + } + else + if (IsNew) *IsNew = false; + + return V; +} + +std::pair +ExplodedGraph::Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd, + llvm::DenseMap *InverseMap) const { + + if (NBeg == NEnd) + return std::make_pair((ExplodedGraph*) 0, + (InterExplodedGraphMap*) 0); + + assert (NBeg < NEnd); + + llvm::OwningPtr M(new InterExplodedGraphMap()); + + ExplodedGraph* G = TrimInternal(NBeg, NEnd, M.get(), InverseMap); + + return std::make_pair(static_cast(G), M.take()); +} + +ExplodedGraph* +ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, + const ExplodedNode* const* EndSources, + InterExplodedGraphMap* M, + llvm::DenseMap *InverseMap) const { + + typedef llvm::DenseSet Pass1Ty; + Pass1Ty Pass1; + + typedef llvm::DenseMap Pass2Ty; + Pass2Ty& Pass2 = M->M; + + llvm::SmallVector WL1, WL2; + + // ===- Pass 1 (reverse DFS) -=== + for (const ExplodedNode* const* I = BeginSources; I != EndSources; ++I) { + assert(*I); + WL1.push_back(*I); + } + + // Process the first worklist until it is empty. Because it is a std::list + // it acts like a FIFO queue. + while (!WL1.empty()) { + const ExplodedNode *N = WL1.back(); + WL1.pop_back(); + + // Have we already visited this node? If so, continue to the next one. + if (Pass1.count(N)) + continue; + + // Otherwise, mark this node as visited. + Pass1.insert(N); + + // If this is a root enqueue it to the second worklist. + if (N->Preds.empty()) { + WL2.push_back(N); + continue; + } + + // Visit our predecessors and enqueue them. + for (ExplodedNode** I=N->Preds.begin(), **E=N->Preds.end(); I!=E; ++I) + WL1.push_back(*I); + } + + // We didn't hit a root? Return with a null pointer for the new graph. + if (WL2.empty()) + return 0; + + // Create an empty graph. + ExplodedGraph* G = MakeEmptyGraph(); + + // ===- Pass 2 (forward DFS to construct the new graph) -=== + while (!WL2.empty()) { + const ExplodedNode* N = WL2.back(); + WL2.pop_back(); + + // Skip this node if we have already processed it. + if (Pass2.find(N) != Pass2.end()) + continue; + + // Create the corresponding node in the new graph and record the mapping + // from the old node to the new node. + ExplodedNode* NewN = G->getNode(N->getLocation(), N->State, NULL); + Pass2[N] = NewN; + + // Also record the reverse mapping from the new node to the old node. + if (InverseMap) (*InverseMap)[NewN] = N; + + // If this node is a root, designate it as such in the graph. + if (N->Preds.empty()) + G->addRoot(NewN); + + // In the case that some of the intended predecessors of NewN have already + // been created, we should hook them up as predecessors. + + // Walk through the predecessors of 'N' and hook up their corresponding + // nodes in the new graph (if any) to the freshly created node. + for (ExplodedNode **I=N->Preds.begin(), **E=N->Preds.end(); I!=E; ++I) { + Pass2Ty::iterator PI = Pass2.find(*I); + if (PI == Pass2.end()) + continue; + + NewN->addPredecessor(PI->second, *G); + } + + // In the case that some of the intended successors of NewN have already + // been created, we should hook them up as successors. Otherwise, enqueue + // the new nodes from the original graph that should have nodes created + // in the new graph. + for (ExplodedNode **I=N->Succs.begin(), **E=N->Succs.end(); I!=E; ++I) { + Pass2Ty::iterator PI = Pass2.find(*I); + if (PI != Pass2.end()) { + PI->second->addPredecessor(NewN, *G); + continue; + } + + // Enqueue nodes to the worklist that were marked during pass 1. + if (Pass1.count(*I)) + WL2.push_back(*I); + } + + // Finally, explictly mark all nodes without any successors as sinks. + if (N->isSink()) + NewN->markAsSink(); + } + + return G; +} + +ExplodedNode* +InterExplodedGraphMap::getMappedNode(const ExplodedNode* N) const { + llvm::DenseMap::const_iterator I = + M.find(N); + + return I == M.end() ? 0 : I->second; +} + diff --git a/lib/Checker/FixedAddressChecker.cpp b/lib/Checker/FixedAddressChecker.cpp new file mode 100644 index 000000000000..04c17d6d7abb --- /dev/null +++ b/lib/Checker/FixedAddressChecker.cpp @@ -0,0 +1,70 @@ +//=== FixedAddressChecker.cpp - Fixed address usage checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines FixedAddressChecker, a builtin checker that checks for +// assignment of a fixed address to a pointer. +// This check corresponds to CWE-587. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class FixedAddressChecker + : public CheckerVisitor { + BuiltinBug *BT; +public: + FixedAddressChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *FixedAddressChecker::getTag() { + static int x; + return &x; +} + +void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + // Using a fixed address is not portable because that address will probably + // not be valid in all environments or platforms. + + if (B->getOpcode() != BinaryOperator::Assign) + return; + + QualType T = B->getType(); + if (!T->isPointerType()) + return; + + const GRState *state = C.getState(); + + SVal RV = state->getSVal(B->getRHS()); + + if (!RV.isConstant() || RV.isZeroConstant()) + return; + + if (ExplodedNode *N = C.GenerateNode()) { + if (!BT) + BT = new BuiltinBug("Use fixed address", + "Using a fixed address is not portable because that " + "address will probably not be valid in all " + "environments or platforms."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getRHS()->getSourceRange()); + C.EmitReport(R); + } +} + +void clang::RegisterFixedAddressChecker(GRExprEngine &Eng) { + Eng.registerCheck(new FixedAddressChecker()); +} diff --git a/lib/Checker/FlatStore.cpp b/lib/Checker/FlatStore.cpp new file mode 100644 index 000000000000..dac66def5dc9 --- /dev/null +++ b/lib/Checker/FlatStore.cpp @@ -0,0 +1,166 @@ +//=== FlatStore.cpp - Flat region-based store model -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/GRState.h" +#include "llvm/ADT/ImmutableIntervalMap.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using llvm::Interval; + +// The actual store type. +typedef llvm::ImmutableIntervalMap BindingVal; +typedef llvm::ImmutableMap RegionBindings; + +namespace { +class FlatStoreManager : public StoreManager { + RegionBindings::Factory RBFactory; + BindingVal::Factory BVFactory; + +public: + FlatStoreManager(GRStateManager &mgr) + : StoreManager(mgr), + RBFactory(mgr.getAllocator()), + BVFactory(mgr.getAllocator()) {} + + SVal Retrieve(Store store, Loc L, QualType T); + Store Bind(Store store, Loc L, SVal val); + Store Remove(Store St, Loc L); + Store BindCompoundLiteral(Store store, const CompoundLiteralExpr* cl, + const LocationContext *LC, SVal v); + + Store getInitialStore(const LocationContext *InitLoc) { + return RBFactory.GetEmptyMap().getRoot(); + } + + SubRegionMap *getSubRegionMap(Store store) { + return 0; + } + + SVal ArrayToPointer(Loc Array); + Store RemoveDeadBindings(Store store, Stmt* Loc, SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots){ + return store; + } + + Store BindDecl(Store store, const VarRegion *VR, SVal initVal); + + Store BindDeclWithNoInit(Store store, const VarRegion *VR); + + typedef llvm::DenseSet InvalidatedSymbols; + + Store InvalidateRegion(Store store, const MemRegion *R, const Expr *E, + unsigned Count, InvalidatedSymbols *IS); + + void print(Store store, llvm::raw_ostream& Out, const char* nl, + const char *sep); + void iterBindings(Store store, BindingsHandler& f); + +private: + static RegionBindings getRegionBindings(Store store) { + return RegionBindings(static_cast(store)); + } + + Interval RegionToInterval(const MemRegion *R); + + SVal RetrieveRegionWithNoBinding(const MemRegion *R, QualType T); +}; +} // end anonymous namespace + +StoreManager *clang::CreateFlatStoreManager(GRStateManager &StMgr) { + return new FlatStoreManager(StMgr); +} + +SVal FlatStoreManager::Retrieve(Store store, Loc L, QualType T) { + const MemRegion *R = cast(L).getRegion(); + Interval I = RegionToInterval(R); + RegionBindings B = getRegionBindings(store); + const BindingVal *BV = B.lookup(R); + if (BV) { + const SVal *V = BVFactory.Lookup(*BV, I); + if (V) + return *V; + else + return RetrieveRegionWithNoBinding(R, T); + } + return RetrieveRegionWithNoBinding(R, T); +} + +SVal FlatStoreManager::RetrieveRegionWithNoBinding(const MemRegion *R, + QualType T) { + if (R->hasStackNonParametersStorage()) + return UndefinedVal(); + else + return ValMgr.getRegionValueSymbolVal(R, T); +} + +Store FlatStoreManager::Bind(Store store, Loc L, SVal val) { + const MemRegion *R = cast(L).getRegion(); + RegionBindings B = getRegionBindings(store); + const BindingVal *V = B.lookup(R); + + BindingVal BV = BVFactory.GetEmptyMap(); + if (V) + BV = *V; + + Interval I = RegionToInterval(R); + BV = BVFactory.Add(BV, I, val); + B = RBFactory.Add(B, R, BV); + return B.getRoot(); +} + +Store FlatStoreManager::Remove(Store store, Loc L) { + return store; +} + +Store FlatStoreManager::BindCompoundLiteral(Store store, + const CompoundLiteralExpr* cl, + const LocationContext *LC, + SVal v) { + return store; +} + +SVal FlatStoreManager::ArrayToPointer(Loc Array) { + return Array; +} + +Store FlatStoreManager::BindDecl(Store store, const VarRegion *VR, + SVal initVal) { + return store; +} + +Store FlatStoreManager::BindDeclWithNoInit(Store store, const VarRegion *VR) { + return store; +} + +Store FlatStoreManager::InvalidateRegion(Store store, const MemRegion *R, + const Expr *E, unsigned Count, + InvalidatedSymbols *IS) { + return store; +} + +void FlatStoreManager::print(Store store, llvm::raw_ostream& Out, + const char* nl, const char *sep) { +} + +void FlatStoreManager::iterBindings(Store store, BindingsHandler& f) { +} + +Interval FlatStoreManager::RegionToInterval(const MemRegion *R) { + switch (R->getKind()) { + case MemRegion::VarRegionKind: { + QualType T = cast(R)->getValueType(Ctx); + uint64_t Size = Ctx.getTypeSize(T); + return Interval(0, Size-1); + } + default: + llvm_unreachable("Region kind unhandled."); + return Interval(0, 0); + } +} diff --git a/lib/Checker/GRBlockCounter.cpp b/lib/Checker/GRBlockCounter.cpp new file mode 100644 index 000000000000..3fa3e1ebb9c6 --- /dev/null +++ b/lib/Checker/GRBlockCounter.cpp @@ -0,0 +1,54 @@ +//==- GRBlockCounter.h - ADT for counting block visits -------------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines GRBlockCounter, an abstract data type used to count +// the number of times a given block has been visited along a path +// analyzed by GRCoreEngine. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/GRBlockCounter.h" +#include "llvm/ADT/ImmutableMap.h" + +using namespace clang; + +typedef llvm::ImmutableMap CountMap; + +static inline CountMap GetMap(void* D) { + return CountMap(static_cast(D)); +} + +static inline CountMap::Factory& GetFactory(void* F) { + return *static_cast(F); +} + +unsigned GRBlockCounter::getNumVisited(unsigned BlockID) const { + CountMap M = GetMap(Data); + CountMap::data_type* T = M.lookup(BlockID); + return T ? *T : 0; +} + +GRBlockCounter::Factory::Factory(llvm::BumpPtrAllocator& Alloc) { + F = new CountMap::Factory(Alloc); +} + +GRBlockCounter::Factory::~Factory() { + delete static_cast(F); +} + +GRBlockCounter +GRBlockCounter::Factory::IncrementCount(GRBlockCounter BC, unsigned BlockID) { + return GRBlockCounter(GetFactory(F).Add(GetMap(BC.Data), BlockID, + BC.getNumVisited(BlockID)+1).getRoot()); +} + +GRBlockCounter +GRBlockCounter::Factory::GetEmptyCounter() { + return GRBlockCounter(GetFactory(F).GetEmptyMap().getRoot()); +} diff --git a/lib/Checker/GRCoreEngine.cpp b/lib/Checker/GRCoreEngine.cpp new file mode 100644 index 000000000000..d54b0777eda7 --- /dev/null +++ b/lib/Checker/GRCoreEngine.cpp @@ -0,0 +1,599 @@ +//==- GRCoreEngine.cpp - Path-Sensitive Dataflow Engine ------------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a generic engine for intraprocedural, path-sensitive, +// dataflow analysis via graph reachability engine. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/GRCoreEngine.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/AST/Expr.h" +#include "llvm/Support/Casting.h" +#include "llvm/ADT/DenseMap.h" +#include +#include + +using llvm::cast; +using llvm::isa; +using namespace clang; + +//===----------------------------------------------------------------------===// +// Worklist classes for exploration of reachable states. +//===----------------------------------------------------------------------===// + +namespace { +class DFS : public GRWorkList { + llvm::SmallVector Stack; +public: + virtual bool hasWork() const { + return !Stack.empty(); + } + + virtual void Enqueue(const GRWorkListUnit& U) { + Stack.push_back(U); + } + + virtual GRWorkListUnit Dequeue() { + assert (!Stack.empty()); + const GRWorkListUnit& U = Stack.back(); + Stack.pop_back(); // This technically "invalidates" U, but we are fine. + return U; + } +}; + +class BFS : public GRWorkList { + std::queue Queue; +public: + virtual bool hasWork() const { + return !Queue.empty(); + } + + virtual void Enqueue(const GRWorkListUnit& U) { + Queue.push(U); + } + + virtual GRWorkListUnit Dequeue() { + // Don't use const reference. The subsequent pop_back() might make it + // unsafe. + GRWorkListUnit U = Queue.front(); + Queue.pop(); + return U; + } +}; + +} // end anonymous namespace + +// Place the dstor for GRWorkList here because it contains virtual member +// functions, and we the code for the dstor generated in one compilation unit. +GRWorkList::~GRWorkList() {} + +GRWorkList *GRWorkList::MakeDFS() { return new DFS(); } +GRWorkList *GRWorkList::MakeBFS() { return new BFS(); } + +namespace { + class BFSBlockDFSContents : public GRWorkList { + std::queue Queue; + llvm::SmallVector Stack; + public: + virtual bool hasWork() const { + return !Queue.empty() || !Stack.empty(); + } + + virtual void Enqueue(const GRWorkListUnit& U) { + if (isa(U.getNode()->getLocation())) + Queue.push(U); + else + Stack.push_back(U); + } + + virtual GRWorkListUnit Dequeue() { + // Process all basic blocks to completion. + if (!Stack.empty()) { + const GRWorkListUnit& U = Stack.back(); + Stack.pop_back(); // This technically "invalidates" U, but we are fine. + return U; + } + + assert(!Queue.empty()); + // Don't use const reference. The subsequent pop_back() might make it + // unsafe. + GRWorkListUnit U = Queue.front(); + Queue.pop(); + return U; + } + }; +} // end anonymous namespace + +GRWorkList* GRWorkList::MakeBFSBlockDFSContents() { + return new BFSBlockDFSContents(); +} + +//===----------------------------------------------------------------------===// +// Core analysis engine. +//===----------------------------------------------------------------------===// +void GRCoreEngine::ProcessEndPath(GREndPathNodeBuilder& Builder) { + SubEngine.ProcessEndPath(Builder); +} + +void GRCoreEngine::ProcessStmt(CFGElement E, GRStmtNodeBuilder& Builder) { + SubEngine.ProcessStmt(E, Builder); +} + +bool GRCoreEngine::ProcessBlockEntrance(CFGBlock* Blk, const GRState* State, + GRBlockCounter BC) { + return SubEngine.ProcessBlockEntrance(Blk, State, BC); +} + +void GRCoreEngine::ProcessBranch(Stmt* Condition, Stmt* Terminator, + GRBranchNodeBuilder& Builder) { + SubEngine.ProcessBranch(Condition, Terminator, Builder); +} + +void GRCoreEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& Builder) { + SubEngine.ProcessIndirectGoto(Builder); +} + +void GRCoreEngine::ProcessSwitch(GRSwitchNodeBuilder& Builder) { + SubEngine.ProcessSwitch(Builder); +} + +/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. +bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) { + + if (G->num_roots() == 0) { // Initialize the analysis by constructing + // the root if none exists. + + CFGBlock* Entry = &(L->getCFG()->getEntry()); + + assert (Entry->empty() && + "Entry block must be empty."); + + assert (Entry->succ_size() == 1 && + "Entry block must have 1 successor."); + + // Get the solitary successor. + CFGBlock* Succ = *(Entry->succ_begin()); + + // Construct an edge representing the + // starting location in the function. + BlockEdge StartLoc(Entry, Succ, L); + + // Set the current block counter to being empty. + WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); + + // Generate the root. + GenerateNode(StartLoc, getInitialState(L), 0); + } + + while (Steps && WList->hasWork()) { + --Steps; + const GRWorkListUnit& WU = WList->Dequeue(); + + // Set the current block counter. + WList->setBlockCounter(WU.getBlockCounter()); + + // Retrieve the node. + ExplodedNode* Node = WU.getNode(); + + // Dispatch on the location type. + switch (Node->getLocation().getKind()) { + case ProgramPoint::BlockEdgeKind: + HandleBlockEdge(cast(Node->getLocation()), Node); + break; + + case ProgramPoint::BlockEntranceKind: + HandleBlockEntrance(cast(Node->getLocation()), Node); + break; + + case ProgramPoint::BlockExitKind: + assert (false && "BlockExit location never occur in forward analysis."); + break; + + default: + assert(isa(Node->getLocation())); + HandlePostStmt(cast(Node->getLocation()), WU.getBlock(), + WU.getIndex(), Node); + break; + } + } + + return WList->hasWork(); +} + + +void GRCoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { + + CFGBlock* Blk = L.getDst(); + + // Check if we are entering the EXIT block. + if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { + + assert (L.getLocationContext()->getCFG()->getExit().size() == 0 + && "EXIT block cannot contain Stmts."); + + // Process the final state transition. + GREndPathNodeBuilder Builder(Blk, Pred, this); + ProcessEndPath(Builder); + + // This path is done. Don't enqueue any more nodes. + return; + } + + // FIXME: Should we allow ProcessBlockEntrance to also manipulate state? + + if (ProcessBlockEntrance(Blk, Pred->State, WList->getBlockCounter())) + GenerateNode(BlockEntrance(Blk, Pred->getLocationContext()), Pred->State, Pred); +} + +void GRCoreEngine::HandleBlockEntrance(const BlockEntrance& L, + ExplodedNode* Pred) { + + // Increment the block counter. + GRBlockCounter Counter = WList->getBlockCounter(); + Counter = BCounterFactory.IncrementCount(Counter, L.getBlock()->getBlockID()); + WList->setBlockCounter(Counter); + + // Process the entrance of the block. + if (CFGElement E = L.getFirstElement()) { + GRStmtNodeBuilder Builder(L.getBlock(), 0, Pred, this, + SubEngine.getStateManager()); + ProcessStmt(E, Builder); + } + else + HandleBlockExit(L.getBlock(), Pred); +} + +void GRCoreEngine::HandleBlockExit(CFGBlock * B, ExplodedNode* Pred) { + + if (Stmt* Term = B->getTerminator()) { + switch (Term->getStmtClass()) { + default: + assert(false && "Analysis for this terminator not implemented."); + break; + + case Stmt::BinaryOperatorClass: // '&&' and '||' + HandleBranch(cast(Term)->getLHS(), Term, B, Pred); + return; + + case Stmt::ConditionalOperatorClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + // FIXME: Use constant-folding in CFG construction to simplify this + // case. + + case Stmt::ChooseExprClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::DoStmtClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::ForStmtClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::ContinueStmtClass: + case Stmt::BreakStmtClass: + case Stmt::GotoStmtClass: + break; + + case Stmt::IfStmtClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::IndirectGotoStmtClass: { + // Only 1 successor: the indirect goto dispatch block. + assert (B->succ_size() == 1); + + GRIndirectGotoNodeBuilder + builder(Pred, B, cast(Term)->getTarget(), + *(B->succ_begin()), this); + + ProcessIndirectGoto(builder); + return; + } + + case Stmt::ObjCForCollectionStmtClass: { + // In the case of ObjCForCollectionStmt, it appears twice in a CFG: + // + // (1) inside a basic block, which represents the binding of the + // 'element' variable to a value. + // (2) in a terminator, which represents the branch. + // + // For (1), subengines will bind a value (i.e., 0 or 1) indicating + // whether or not collection contains any more elements. We cannot + // just test to see if the element is nil because a container can + // contain nil elements. + HandleBranch(Term, Term, B, Pred); + return; + } + + case Stmt::SwitchStmtClass: { + GRSwitchNodeBuilder builder(Pred, B, cast(Term)->getCond(), + this); + + ProcessSwitch(builder); + return; + } + + case Stmt::WhileStmtClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + } + } + + assert (B->succ_size() == 1 && + "Blocks with no terminator should have at most 1 successor."); + + GenerateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()), + Pred->State, Pred); +} + +void GRCoreEngine::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B, + ExplodedNode* Pred) { + assert (B->succ_size() == 2); + + GRBranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), + Pred, this); + + ProcessBranch(Cond, Term, Builder); +} + +void GRCoreEngine::HandlePostStmt(const PostStmt& L, CFGBlock* B, + unsigned StmtIdx, ExplodedNode* Pred) { + + assert (!B->empty()); + + if (StmtIdx == B->size()) + HandleBlockExit(B, Pred); + else { + GRStmtNodeBuilder Builder(B, StmtIdx, Pred, this, + SubEngine.getStateManager()); + ProcessStmt((*B)[StmtIdx], Builder); + } +} + +/// GenerateNode - Utility method to generate nodes, hook up successors, +/// and add nodes to the worklist. +void GRCoreEngine::GenerateNode(const ProgramPoint& Loc, + const GRState* State, ExplodedNode* Pred) { + + bool IsNew; + ExplodedNode* Node = G->getNode(Loc, State, &IsNew); + + if (Pred) + Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. + else { + assert (IsNew); + G->addRoot(Node); // 'Node' has no predecessor. Make it a root. + } + + // Only add 'Node' to the worklist if it was freshly generated. + if (IsNew) WList->Enqueue(Node); +} + +GRStmtNodeBuilder::GRStmtNodeBuilder(CFGBlock* b, unsigned idx, + ExplodedNode* N, GRCoreEngine* e, + GRStateManager &mgr) + : Eng(*e), B(*b), Idx(idx), Pred(N), LastNode(N), Mgr(mgr), Auditor(0), + PurgingDeadSymbols(false), BuildSinks(false), HasGeneratedNode(false), + PointKind(ProgramPoint::PostStmtKind), Tag(0) { + Deferred.insert(N); + CleanedState = getLastNode()->getState(); +} + +GRStmtNodeBuilder::~GRStmtNodeBuilder() { + for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) + if (!(*I)->isSink()) + GenerateAutoTransition(*I); +} + +void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { + assert (!N->isSink()); + + PostStmt Loc(getStmt(), N->getLocationContext()); + + if (Loc == N->getLocation()) { + // Note: 'N' should be a fresh node because otherwise it shouldn't be + // a member of Deferred. + Eng.WList->Enqueue(N, B, Idx+1); + return; + } + + bool IsNew; + ExplodedNode* Succ = Eng.G->getNode(Loc, N->State, &IsNew); + Succ->addPredecessor(N, *Eng.G); + + if (IsNew) + Eng.WList->Enqueue(Succ, B, Idx+1); +} + +static ProgramPoint GetProgramPoint(const Stmt *S, ProgramPoint::Kind K, + const LocationContext *LC, const void *tag){ + switch (K) { + default: + assert(false && "Unhandled ProgramPoint kind"); + case ProgramPoint::PreStmtKind: + return PreStmt(S, LC, tag); + case ProgramPoint::PostStmtKind: + return PostStmt(S, LC, tag); + case ProgramPoint::PreLoadKind: + return PreLoad(S, LC, tag); + case ProgramPoint::PostLoadKind: + return PostLoad(S, LC, tag); + case ProgramPoint::PreStoreKind: + return PreStore(S, LC, tag); + case ProgramPoint::PostStoreKind: + return PostStore(S, LC, tag); + case ProgramPoint::PostLValueKind: + return PostLValue(S, LC, tag); + case ProgramPoint::PostPurgeDeadSymbolsKind: + return PostPurgeDeadSymbols(S, LC, tag); + } +} + +ExplodedNode* +GRStmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* state, + ExplodedNode* Pred, + ProgramPoint::Kind K, + const void *tag) { + + const ProgramPoint &L = GetProgramPoint(S, K, Pred->getLocationContext(),tag); + return generateNodeInternal(L, state, Pred); +} + +ExplodedNode* +GRStmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, + const GRState* State, + ExplodedNode* Pred) { + bool IsNew; + ExplodedNode* N = Eng.G->getNode(Loc, State, &IsNew); + N->addPredecessor(Pred, *Eng.G); + Deferred.erase(Pred); + + if (IsNew) { + Deferred.insert(N); + LastNode = N; + return N; + } + + LastNode = NULL; + return NULL; +} + +ExplodedNode* GRBranchNodeBuilder::generateNode(const GRState* State, + bool branch) { + + // If the branch has been marked infeasible we should not generate a node. + if (!isFeasible(branch)) + return NULL; + + bool IsNew; + + ExplodedNode* Succ = + Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), + State, &IsNew); + + Succ->addPredecessor(Pred, *Eng.G); + + if (branch) + GeneratedTrue = true; + else + GeneratedFalse = true; + + if (IsNew) { + Deferred.push_back(Succ); + return Succ; + } + + return NULL; +} + +GRBranchNodeBuilder::~GRBranchNodeBuilder() { + if (!GeneratedTrue) generateNode(Pred->State, true); + if (!GeneratedFalse) generateNode(Pred->State, false); + + for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) + if (!(*I)->isSink()) Eng.WList->Enqueue(*I); +} + + +ExplodedNode* +GRIndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, + bool isSink) { + bool IsNew; + + ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), + Pred->getLocationContext()), St, &IsNew); + + Succ->addPredecessor(Pred, *Eng.G); + + if (IsNew) { + + if (isSink) + Succ->markAsSink(); + else + Eng.WList->Enqueue(Succ); + + return Succ; + } + + return NULL; +} + + +ExplodedNode* +GRSwitchNodeBuilder::generateCaseStmtNode(const iterator& I, const GRState* St){ + + bool IsNew; + + ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), + Pred->getLocationContext()), St, &IsNew); + Succ->addPredecessor(Pred, *Eng.G); + + if (IsNew) { + Eng.WList->Enqueue(Succ); + return Succ; + } + + return NULL; +} + + +ExplodedNode* +GRSwitchNodeBuilder::generateDefaultCaseNode(const GRState* St, bool isSink) { + + // Get the block for the default case. + assert (Src->succ_rbegin() != Src->succ_rend()); + CFGBlock* DefaultBlock = *Src->succ_rbegin(); + + bool IsNew; + + ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, + Pred->getLocationContext()), St, &IsNew); + Succ->addPredecessor(Pred, *Eng.G); + + if (IsNew) { + if (isSink) + Succ->markAsSink(); + else + Eng.WList->Enqueue(Succ); + + return Succ; + } + + return NULL; +} + +GREndPathNodeBuilder::~GREndPathNodeBuilder() { + // Auto-generate an EOP node if one has not been generated. + if (!HasGeneratedNode) generateNode(Pred->State); +} + +ExplodedNode* +GREndPathNodeBuilder::generateNode(const GRState* State, const void *tag, + ExplodedNode* P) { + HasGeneratedNode = true; + bool IsNew; + + ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B, + Pred->getLocationContext(), tag), State, &IsNew); + + Node->addPredecessor(P ? P : Pred, *Eng.G); + + if (IsNew) { + Eng.G->addEndOfPath(Node); + return Node; + } + + return NULL; +} diff --git a/lib/Checker/GRExprEngine.cpp b/lib/Checker/GRExprEngine.cpp new file mode 100644 index 000000000000..7f863193743b --- /dev/null +++ b/lib/Checker/GRExprEngine.cpp @@ -0,0 +1,3332 @@ +//=-- GRExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- C++ -*-= +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a meta-engine for path-sensitive dataflow analysis that +// is built on GREngine, but provides the boilerplate to execute transfer +// functions and build the ExplodedGraph at the expression level. +// +//===----------------------------------------------------------------------===// +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/PathSensitive/GRExprEngineBuilders.h" +#include "clang/Checker/PathSensitive/Checker.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtObjC.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/PrettyStackTrace.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/StringSwitch.h" + +#ifndef NDEBUG +#include "llvm/Support/GraphWriter.h" +#endif + +using namespace clang; +using llvm::dyn_cast; +using llvm::dyn_cast_or_null; +using llvm::cast; +using llvm::APSInt; + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { + IdentifierInfo* II = &Ctx.Idents.get(name); + return Ctx.Selectors.getSelector(0, &II); +} + + +static QualType GetCalleeReturnType(const CallExpr *CE) { + const Expr *Callee = CE->getCallee(); + QualType T = Callee->getType(); + if (const PointerType *PT = T->getAs()) { + const FunctionType *FT = PT->getPointeeType()->getAs(); + T = FT->getResultType(); + } + else { + const BlockPointerType *BT = T->getAs(); + T = BT->getPointeeType()->getAs()->getResultType(); + } + return T; +} + +static bool CalleeReturnsReference(const CallExpr *CE) { + return (bool) GetCalleeReturnType(CE)->getAs(); +} + +static bool ReceiverReturnsReference(const ObjCMessageExpr *ME) { + const ObjCMethodDecl *MD = ME->getMethodDecl(); + if (!MD) + return false; + return MD->getResultType()->getAs(); +} + +#ifndef NDEBUG +static bool ReceiverReturnsReferenceOrRecord(const ObjCMessageExpr *ME) { + const ObjCMethodDecl *MD = ME->getMethodDecl(); + if (!MD) + return false; + QualType T = MD->getResultType(); + return T->getAs() || T->getAs(); +} + +static bool CalleeReturnsReferenceOrRecord(const CallExpr *CE) { + QualType T = GetCalleeReturnType(CE); + return T->getAs() || T->getAs(); +} +#endif + +//===----------------------------------------------------------------------===// +// Batch auditor. DEPRECATED. +//===----------------------------------------------------------------------===// + +namespace { + +class MappedBatchAuditor : public GRSimpleAPICheck { + typedef llvm::ImmutableList Checks; + typedef llvm::DenseMap MapTy; + + MapTy M; + Checks::Factory F; + Checks AllStmts; + +public: + MappedBatchAuditor(llvm::BumpPtrAllocator& Alloc) : + F(Alloc), AllStmts(F.GetEmptyList()) {} + + virtual ~MappedBatchAuditor() { + llvm::DenseSet AlreadyVisited; + + for (MapTy::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI) + for (Checks::iterator I=MI->second.begin(), E=MI->second.end(); I!=E;++I){ + + GRSimpleAPICheck* check = *I; + + if (AlreadyVisited.count(check)) + continue; + + AlreadyVisited.insert(check); + delete check; + } + } + + void AddCheck(GRSimpleAPICheck *A, Stmt::StmtClass C) { + assert (A && "Check cannot be null."); + void* key = reinterpret_cast((uintptr_t) C); + MapTy::iterator I = M.find(key); + M[key] = F.Concat(A, I == M.end() ? F.GetEmptyList() : I->second); + } + + void AddCheck(GRSimpleAPICheck *A) { + assert (A && "Check cannot be null."); + AllStmts = F.Concat(A, AllStmts); + } + + virtual bool Audit(ExplodedNode* N, GRStateManager& VMgr) { + // First handle the auditors that accept all statements. + bool isSink = false; + for (Checks::iterator I = AllStmts.begin(), E = AllStmts.end(); I!=E; ++I) + isSink |= (*I)->Audit(N, VMgr); + + // Next handle the auditors that accept only specific statements. + const Stmt* S = cast(N->getLocation()).getStmt(); + void* key = reinterpret_cast((uintptr_t) S->getStmtClass()); + MapTy::iterator MI = M.find(key); + if (MI != M.end()) { + for (Checks::iterator I=MI->second.begin(), E=MI->second.end(); I!=E; ++I) + isSink |= (*I)->Audit(N, VMgr); + } + + return isSink; + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Checker worklist routines. +//===----------------------------------------------------------------------===// + +void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, bool isPrevisit) { + + if (Checkers.empty()) { + Dst.insert(Src); + return; + } + + ExplodedNodeSet Tmp; + ExplodedNodeSet *PrevSet = &Src; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) + checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit); + PrevSet = CurrSet; + } + + // Don't autotransition. The CheckerContext objects should do this + // automatically. +} + +void GRExprEngine::CheckerEvalNilReceiver(const ObjCMessageExpr *ME, + ExplodedNodeSet &Dst, + const GRState *state, + ExplodedNode *Pred) { + bool Evaluated = false; + ExplodedNodeSet DstTmp; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + + if (checker->GR_EvalNilReceiver(DstTmp, *Builder, *this, ME, Pred, state, + tag)) { + Evaluated = true; + break; + } else + // The checker didn't evaluate the expr. Restore the Dst. + DstTmp.clear(); + } + + if (Evaluated) + Dst.insert(DstTmp); + else + Dst.insert(Pred); +} + +// CheckerEvalCall returns true if one of the checkers processed the node. +// This may return void when all call evaluation logic goes to some checker +// in the future. +bool GRExprEngine::CheckerEvalCall(const CallExpr *CE, + ExplodedNodeSet &Dst, + ExplodedNode *Pred) { + bool Evaluated = false; + ExplodedNodeSet DstTmp; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + + if (checker->GR_EvalCallExpr(DstTmp, *Builder, *this, CE, Pred, tag)) { + Evaluated = true; + break; + } else + // The checker didn't evaluate the expr. Restore the DstTmp set. + DstTmp.clear(); + } + + if (Evaluated) + Dst.insert(DstTmp); + else + Dst.insert(Pred); + + return Evaluated; +} + +// FIXME: This is largely copy-paste from CheckerVisit(). Need to +// unify. +void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE, + ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + SVal location, SVal val, bool isPrevisit) { + + if (Checkers.empty()) { + Dst.insert(Src); + return; + } + + ExplodedNodeSet Tmp; + ExplodedNodeSet *PrevSet = &Src; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) + { + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) + checker->GR_VisitBind(*CurrSet, *Builder, *this, AssignE, StoreE, + *NI, tag, location, val, isPrevisit); + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } + + // Don't autotransition. The CheckerContext objects should do this + // automatically. +} +//===----------------------------------------------------------------------===// +// Engine construction and deletion. +//===----------------------------------------------------------------------===// + +static void RegisterInternalChecks(GRExprEngine &Eng) { + // Register internal "built-in" BugTypes with the BugReporter. These BugTypes + // are different than what probably many checks will do since they don't + // create BugReports on-the-fly but instead wait until GRExprEngine finishes + // analyzing a function. Generation of BugReport objects is done via a call + // to 'FlushReports' from BugReporter. + // The following checks do not need to have their associated BugTypes + // explicitly registered with the BugReporter. If they issue any BugReports, + // their associated BugType will get registered with the BugReporter + // automatically. Note that the check itself is owned by the GRExprEngine + // object. + RegisterAdjustedReturnValueChecker(Eng); + RegisterAttrNonNullChecker(Eng); + RegisterCallAndMessageChecker(Eng); + RegisterDereferenceChecker(Eng); + RegisterVLASizeChecker(Eng); + RegisterDivZeroChecker(Eng); + RegisterReturnStackAddressChecker(Eng); + RegisterReturnUndefChecker(Eng); + RegisterUndefinedArraySubscriptChecker(Eng); + RegisterUndefinedAssignmentChecker(Eng); + RegisterUndefBranchChecker(Eng); + RegisterUndefCapturedBlockVarChecker(Eng); + RegisterUndefResultChecker(Eng); + + // This is not a checker yet. + RegisterNoReturnFunctionChecker(Eng); + RegisterBuiltinFunctionChecker(Eng); + RegisterOSAtomicChecker(Eng); +} + +GRExprEngine::GRExprEngine(AnalysisManager &mgr, GRTransferFuncs *tf) + : AMgr(mgr), + CoreEngine(mgr.getASTContext(), *this), + G(CoreEngine.getGraph()), + Builder(NULL), + StateMgr(G.getContext(), mgr.getStoreManagerCreator(), + mgr.getConstraintManagerCreator(), G.getAllocator(), + *this), + SymMgr(StateMgr.getSymbolManager()), + ValMgr(StateMgr.getValueManager()), + SVator(ValMgr.getSValuator()), + CurrentStmt(NULL), + NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), + RaiseSel(GetNullarySelector("raise", G.getContext())), + BR(mgr, *this), TF(tf) { + // Register internal checks. + RegisterInternalChecks(*this); + + // FIXME: Eventually remove the TF object entirely. + TF->RegisterChecks(*this); + TF->RegisterPrinters(getStateManager().Printers); +} + +GRExprEngine::~GRExprEngine() { + BR.FlushReports(); + delete [] NSExceptionInstanceRaiseSelectors; + for (CheckersOrdered::iterator I=Checkers.begin(), E=Checkers.end(); I!=E;++I) + delete I->second; +} + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + +void GRExprEngine::AddCheck(GRSimpleAPICheck* A, Stmt::StmtClass C) { + if (!BatchAuditor) + BatchAuditor.reset(new MappedBatchAuditor(getGraph().getAllocator())); + + ((MappedBatchAuditor*) BatchAuditor.get())->AddCheck(A, C); +} + +void GRExprEngine::AddCheck(GRSimpleAPICheck *A) { + if (!BatchAuditor) + BatchAuditor.reset(new MappedBatchAuditor(getGraph().getAllocator())); + + ((MappedBatchAuditor*) BatchAuditor.get())->AddCheck(A); +} + +const GRState* GRExprEngine::getInitialState(const LocationContext *InitLoc) { + const GRState *state = StateMgr.getInitialState(InitLoc); + + // Preconditions. + + // FIXME: It would be nice if we had a more general mechanism to add + // such preconditions. Some day. + do { + const Decl *D = InitLoc->getDecl(); + if (const FunctionDecl *FD = dyn_cast(D)) { + // Precondition: the first argument of 'main' is an integer guaranteed + // to be > 0. + const IdentifierInfo *II = FD->getIdentifier(); + if (!II || !(II->getName() == "main" && FD->getNumParams() > 0)) + break; + + const ParmVarDecl *PD = FD->getParamDecl(0); + QualType T = PD->getType(); + if (!T->isIntegerType()) + break; + + const MemRegion *R = state->getRegion(PD, InitLoc); + if (!R) + break; + + SVal V = state->getSVal(loc::MemRegionVal(R)); + SVal Constraint_untested = EvalBinOp(state, BinaryOperator::GT, V, + ValMgr.makeZeroVal(T), + getContext().IntTy); + + DefinedOrUnknownSVal *Constraint = + dyn_cast(&Constraint_untested); + + if (!Constraint) + break; + + if (const GRState *newState = state->Assume(*Constraint, true)) + state = newState; + + break; + } + + if (const ObjCMethodDecl *MD = dyn_cast(D)) { + // Precondition: 'self' is always non-null upon entry to an Objective-C + // method. + const ImplicitParamDecl *SelfD = MD->getSelfDecl(); + const MemRegion *R = state->getRegion(SelfD, InitLoc); + SVal V = state->getSVal(loc::MemRegionVal(R)); + + if (const Loc *LV = dyn_cast(&V)) { + // Assume that the pointer value in 'self' is non-null. + state = state->Assume(*LV, true); + assert(state && "'self' cannot be null"); + } + } + } while (0); + + return state; +} + +//===----------------------------------------------------------------------===// +// Top-level transfer function logic (Dispatcher). +//===----------------------------------------------------------------------===// + +/// EvalAssume - Called by ConstraintManager. Used to call checker-specific +/// logic for handling assumptions on symbolic values. +const GRState *GRExprEngine::ProcessAssume(const GRState *state, SVal cond, + bool assumption) { + for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); + I != E; ++I) { + + if (!state) + return NULL; + + state = I->second->EvalAssume(state, cond, assumption); + } + + if (!state) + return NULL; + + return TF->EvalAssume(state, cond, assumption); +} + +void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) { + CurrentStmt = CE.getStmt(); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + CurrentStmt->getLocStart(), + "Error evaluating statement"); + + Builder = &builder; + EntryNode = builder.getLastNode(); + + // Set up our simple checks. + if (BatchAuditor) + Builder->setAuditor(BatchAuditor.get()); + + // Create the cleaned state. + const ExplodedNode *BasePred = Builder->getBasePredecessor(); + SymbolReaper SymReaper(BasePred->getLiveVariables(), SymMgr, + BasePred->getLocationContext()->getCurrentStackFrame()); + CleanedState = AMgr.shouldPurgeDead() + ? StateMgr.RemoveDeadBindings(EntryNode->getState(), CurrentStmt, SymReaper) + : EntryNode->getState(); + + // Process any special transfer function for dead symbols. + ExplodedNodeSet Tmp; + + if (!SymReaper.hasDeadSymbols()) + Tmp.Add(EntryNode); + else { + SaveAndRestore OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->HasGeneratedNode); + + SaveAndRestore OldPurgeDeadSymbols(Builder->PurgingDeadSymbols); + Builder->PurgingDeadSymbols = true; + + // FIXME: This should soon be removed. + ExplodedNodeSet Tmp2; + getTF().EvalDeadSymbols(Tmp2, *this, *Builder, EntryNode, CurrentStmt, + CleanedState, SymReaper); + + if (Checkers.empty()) + Tmp.insert(Tmp2); + else { + ExplodedNodeSet Tmp3; + ExplodedNodeSet *SrcSet = &Tmp2; + for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); + I != E; ++I) { + ExplodedNodeSet *DstSet = 0; + if (I+1 == E) + DstSet = &Tmp; + else { + DstSet = (SrcSet == &Tmp2) ? &Tmp3 : &Tmp2; + DstSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + for (ExplodedNodeSet::iterator NI = SrcSet->begin(), NE = SrcSet->end(); + NI != NE; ++NI) + checker->GR_EvalDeadSymbols(*DstSet, *Builder, *this, CurrentStmt, + *NI, SymReaper, tag); + SrcSet = DstSet; + } + } + + if (!Builder->BuildSinks && !Builder->HasGeneratedNode) + Tmp.Add(EntryNode); + } + + bool HasAutoGenerated = false; + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + + ExplodedNodeSet Dst; + + // Set the cleaned state. + Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I)); + + // Visit the statement. + if (CE.asLValue()) + VisitLValue(cast(CurrentStmt), *I, Dst); + else + Visit(CurrentStmt, *I, Dst); + + // Do we need to auto-generate a node? We only need to do this to generate + // a node with a "cleaned" state; GRCoreEngine will actually handle + // auto-transitions for other cases. + if (Dst.size() == 1 && *Dst.begin() == EntryNode + && !Builder->HasGeneratedNode && !HasAutoGenerated) { + HasAutoGenerated = true; + builder.generateNode(CurrentStmt, GetState(EntryNode), *I); + } + } + + // NULL out these variables to cleanup. + CleanedState = NULL; + EntryNode = NULL; + + CurrentStmt = 0; + + Builder = NULL; +} + +void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + S->getLocStart(), + "Error evaluating statement"); + + // FIXME: add metadata to the CFG so that we can disable + // this check when we KNOW that there is no block-level subexpression. + // The motivation is that this check requires a hashtable lookup. + + if (S != CurrentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) { + Dst.Add(Pred); + return; + } + + switch (S->getStmtClass()) { + // C++ stuff we don't support yet. + case Stmt::CXXMemberCallExprClass: + case Stmt::CXXNamedCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::CXXDynamicCastExprClass: + case Stmt::CXXReinterpretCastExprClass: + case Stmt::CXXConstCastExprClass: + case Stmt::CXXFunctionalCastExprClass: + case Stmt::CXXTypeidExprClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::CXXThrowExprClass: + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXZeroInitValueExprClass: + case Stmt::CXXNewExprClass: + case Stmt::CXXDeleteExprClass: + case Stmt::CXXPseudoDestructorExprClass: + case Stmt::UnresolvedLookupExprClass: + case Stmt::UnaryTypeTraitExprClass: + case Stmt::DependentScopeDeclRefExprClass: + case Stmt::CXXConstructExprClass: + case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXExprWithTemporariesClass: + case Stmt::CXXTemporaryObjectExprClass: + case Stmt::CXXUnresolvedConstructExprClass: + case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::UnresolvedMemberExprClass: + case Stmt::CXXCatchStmtClass: + case Stmt::CXXTryStmtClass: { + SaveAndRestore OldSink(Builder->BuildSinks); + Builder->BuildSinks = true; + MakeNode(Dst, S, Pred, GetState(Pred)); + break; + } + + default: + // Cases we intentionally have "default" handle: + // AddrLabelExpr, IntegerLiteral, CharacterLiteral + + Dst.Add(Pred); // No-op. Simply propagate the current state unchanged. + break; + + case Stmt::ArraySubscriptExprClass: + VisitArraySubscriptExpr(cast(S), Pred, Dst, false); + break; + + case Stmt::AsmStmtClass: + VisitAsmStmt(cast(S), Pred, Dst); + break; + + case Stmt::BlockDeclRefExprClass: + VisitBlockDeclRefExpr(cast(S), Pred, Dst, false); + break; + + case Stmt::BlockExprClass: + VisitBlockExpr(cast(S), Pred, Dst); + break; + + case Stmt::BinaryOperatorClass: { + BinaryOperator* B = cast(S); + + if (B->isLogicalOp()) { + VisitLogicalExpr(B, Pred, Dst); + break; + } + else if (B->getOpcode() == BinaryOperator::Comma) { + const GRState* state = GetState(Pred); + MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS()))); + break; + } + + if (AMgr.shouldEagerlyAssume() && + (B->isRelationalOp() || B->isEqualityOp())) { + ExplodedNodeSet Tmp; + VisitBinaryOperator(cast(S), Pred, Tmp, false); + EvalEagerlyAssume(Dst, Tmp, cast(S)); + } + else + VisitBinaryOperator(cast(S), Pred, Dst, false); + + break; + } + + case Stmt::CallExprClass: + case Stmt::CXXOperatorCallExprClass: { + CallExpr* C = cast(S); + VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst, false); + break; + } + + // FIXME: ChooseExpr is really a constant. We need to fix + // the CFG do not model them as explicit control-flow. + + case Stmt::ChooseExprClass: { // __builtin_choose_expr + ChooseExpr* C = cast(S); + VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); + break; + } + + case Stmt::CompoundAssignOperatorClass: + VisitBinaryOperator(cast(S), Pred, Dst, false); + break; + + case Stmt::CompoundLiteralExprClass: + VisitCompoundLiteralExpr(cast(S), Pred, Dst, false); + break; + + case Stmt::ConditionalOperatorClass: { // '?' operator + ConditionalOperator* C = cast(S); + VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); + break; + } + + case Stmt::CXXThisExprClass: + VisitCXXThisExpr(cast(S), Pred, Dst); + break; + + case Stmt::DeclRefExprClass: + VisitDeclRefExpr(cast(S), Pred, Dst, false); + break; + + case Stmt::DeclStmtClass: + VisitDeclStmt(cast(S), Pred, Dst); + break; + + case Stmt::ForStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); + break; + + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: { + CastExpr* C = cast(S); + VisitCast(C, C->getSubExpr(), Pred, Dst, false); + break; + } + + case Stmt::IfStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); + break; + + case Stmt::InitListExprClass: + VisitInitListExpr(cast(S), Pred, Dst); + break; + + case Stmt::MemberExprClass: + VisitMemberExpr(cast(S), Pred, Dst, false); + break; + + case Stmt::ObjCIvarRefExprClass: + VisitObjCIvarRefExpr(cast(S), Pred, Dst, false); + break; + + case Stmt::ObjCForCollectionStmtClass: + VisitObjCForCollectionStmt(cast(S), Pred, Dst); + break; + + case Stmt::ObjCMessageExprClass: + VisitObjCMessageExpr(cast(S), Pred, Dst, false); + break; + + case Stmt::ObjCAtThrowStmtClass: { + // FIXME: This is not complete. We basically treat @throw as + // an abort. + SaveAndRestore OldSink(Builder->BuildSinks); + Builder->BuildSinks = true; + MakeNode(Dst, S, Pred, GetState(Pred)); + break; + } + + case Stmt::ParenExprClass: + Visit(cast(S)->getSubExpr()->IgnoreParens(), Pred, Dst); + break; + + case Stmt::ReturnStmtClass: + VisitReturnStmt(cast(S), Pred, Dst); + break; + + case Stmt::SizeOfAlignOfExprClass: + VisitSizeOfAlignOfExpr(cast(S), Pred, Dst); + break; + + case Stmt::StmtExprClass: { + StmtExpr* SE = cast(S); + + if (SE->getSubStmt()->body_empty()) { + // Empty statement expression. + assert(SE->getType() == getContext().VoidTy + && "Empty statement expression must have void type."); + Dst.Add(Pred); + break; + } + + if (Expr* LastExpr = dyn_cast(*SE->getSubStmt()->body_rbegin())) { + const GRState* state = GetState(Pred); + MakeNode(Dst, SE, Pred, state->BindExpr(SE, state->getSVal(LastExpr))); + } + else + Dst.Add(Pred); + + break; + } + + case Stmt::StringLiteralClass: + VisitLValue(cast(S), Pred, Dst); + break; + + case Stmt::SwitchStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); + break; + + case Stmt::UnaryOperatorClass: { + UnaryOperator *U = cast(S); + if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UnaryOperator::LNot)) { + ExplodedNodeSet Tmp; + VisitUnaryOperator(U, Pred, Tmp, false); + EvalEagerlyAssume(Dst, Tmp, U); + } + else + VisitUnaryOperator(U, Pred, Dst, false); + break; + } + + case Stmt::WhileStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); + break; + } +} + +void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + Ex->getLocStart(), + "Error evaluating statement"); + + + Ex = Ex->IgnoreParens(); + + if (Ex != CurrentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(Ex)){ + Dst.Add(Pred); + return; + } + + switch (Ex->getStmtClass()) { + // C++ stuff we don't support yet. + case Stmt::CXXExprWithTemporariesClass: + case Stmt::CXXMemberCallExprClass: + case Stmt::CXXZeroInitValueExprClass: { + SaveAndRestore OldSink(Builder->BuildSinks); + Builder->BuildSinks = true; + MakeNode(Dst, Ex, Pred, GetState(Pred)); + break; + } + + case Stmt::ArraySubscriptExprClass: + VisitArraySubscriptExpr(cast(Ex), Pred, Dst, true); + return; + + case Stmt::BinaryOperatorClass: + case Stmt::CompoundAssignOperatorClass: + VisitBinaryOperator(cast(Ex), Pred, Dst, true); + return; + + case Stmt::BlockDeclRefExprClass: + VisitBlockDeclRefExpr(cast(Ex), Pred, Dst, true); + return; + + case Stmt::CallExprClass: + case Stmt::CXXOperatorCallExprClass: { + CallExpr *C = cast(Ex); + assert(CalleeReturnsReferenceOrRecord(C)); + VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst, true); + break; + } + + case Stmt::CompoundLiteralExprClass: + VisitCompoundLiteralExpr(cast(Ex), Pred, Dst, true); + return; + + case Stmt::DeclRefExprClass: + VisitDeclRefExpr(cast(Ex), Pred, Dst, true); + return; + + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: { + CastExpr *C = cast(Ex); + QualType T = Ex->getType(); + VisitCast(C, C->getSubExpr(), Pred, Dst, true); + break; + } + + case Stmt::MemberExprClass: + VisitMemberExpr(cast(Ex), Pred, Dst, true); + return; + + case Stmt::ObjCIvarRefExprClass: + VisitObjCIvarRefExpr(cast(Ex), Pred, Dst, true); + return; + + case Stmt::ObjCMessageExprClass: { + ObjCMessageExpr *ME = cast(Ex); + assert(ReceiverReturnsReferenceOrRecord(ME)); + VisitObjCMessageExpr(ME, Pred, Dst, true); + return; + } + + case Stmt::ObjCPropertyRefExprClass: + case Stmt::ObjCImplicitSetterGetterRefExprClass: + // FIXME: Property assignments are lvalues, but not really "locations". + // e.g.: self.x = something; + // Here the "self.x" really can translate to a method call (setter) when + // the assignment is made. Moreover, the entire assignment expression + // evaluate to whatever "something" is, not calling the "getter" for + // the property (which would make sense since it can have side effects). + // We'll probably treat this as a location, but not one that we can + // take the address of. Perhaps we need a new SVal class for cases + // like thsis? + // Note that we have a similar problem for bitfields, since they don't + // have "locations" in the sense that we can take their address. + Dst.Add(Pred); + return; + + case Stmt::StringLiteralClass: { + const GRState* state = GetState(Pred); + SVal V = state->getLValue(cast(Ex)); + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); + return; + } + + case Stmt::UnaryOperatorClass: + VisitUnaryOperator(cast(Ex), Pred, Dst, true); + return; + + // In C++, binding an rvalue to a reference requires to create an object. + case Stmt::IntegerLiteralClass: + CreateCXXTemporaryObject(Ex, Pred, Dst); + return; + + default: + // Arbitrary subexpressions can return aggregate temporaries that + // can be used in a lvalue context. We need to enhance our support + // of such temporaries in both the environment and the store, so right + // now we just do a regular visit. + assert ((Ex->getType()->isAggregateType()) && + "Other kinds of expressions with non-aggregate/union types do" + " not have lvalues."); + + Visit(Ex, Pred, Dst); + } +} + +//===----------------------------------------------------------------------===// +// Block entrance. (Update counters). +//===----------------------------------------------------------------------===// + +bool GRExprEngine::ProcessBlockEntrance(CFGBlock* B, const GRState*, + GRBlockCounter BC) { + + return BC.getNumVisited(B->getBlockID()) < 3; +} + +//===----------------------------------------------------------------------===// +// Generic node creation. +//===----------------------------------------------------------------------===// + +ExplodedNode* GRExprEngine::MakeNode(ExplodedNodeSet& Dst, Stmt* S, + ExplodedNode* Pred, const GRState* St, + ProgramPoint::Kind K, const void *tag) { + assert (Builder && "GRStmtNodeBuilder not present."); + SaveAndRestore OldTag(Builder->Tag); + Builder->Tag = tag; + return Builder->MakeNode(Dst, S, Pred, St, K); +} + +//===----------------------------------------------------------------------===// +// Branch processing. +//===----------------------------------------------------------------------===// + +const GRState* GRExprEngine::MarkBranch(const GRState* state, + Stmt* Terminator, + bool branchTaken) { + + switch (Terminator->getStmtClass()) { + default: + return state; + + case Stmt::BinaryOperatorClass: { // '&&' and '||' + + BinaryOperator* B = cast(Terminator); + BinaryOperator::Opcode Op = B->getOpcode(); + + assert (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr); + + // For &&, if we take the true branch, then the value of the whole + // expression is that of the RHS expression. + // + // For ||, if we take the false branch, then the value of the whole + // expression is that of the RHS expression. + + Expr* Ex = (Op == BinaryOperator::LAnd && branchTaken) || + (Op == BinaryOperator::LOr && !branchTaken) + ? B->getRHS() : B->getLHS(); + + return state->BindExpr(B, UndefinedVal(Ex)); + } + + case Stmt::ConditionalOperatorClass: { // ?: + + ConditionalOperator* C = cast(Terminator); + + // For ?, if branchTaken == true then the value is either the LHS or + // the condition itself. (GNU extension). + + Expr* Ex; + + if (branchTaken) + Ex = C->getLHS() ? C->getLHS() : C->getCond(); + else + Ex = C->getRHS(); + + return state->BindExpr(C, UndefinedVal(Ex)); + } + + case Stmt::ChooseExprClass: { // ?: + + ChooseExpr* C = cast(Terminator); + + Expr* Ex = branchTaken ? C->getLHS() : C->getRHS(); + return state->BindExpr(C, UndefinedVal(Ex)); + } + } +} + +/// RecoverCastedSymbol - A helper function for ProcessBranch that is used +/// to try to recover some path-sensitivity for casts of symbolic +/// integers that promote their values (which are currently not tracked well). +/// This function returns the SVal bound to Condition->IgnoreCasts if all the +// cast(s) did was sign-extend the original value. +static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state, + Stmt* Condition, ASTContext& Ctx) { + + Expr *Ex = dyn_cast(Condition); + if (!Ex) + return UnknownVal(); + + uint64_t bits = 0; + bool bitsInit = false; + + while (CastExpr *CE = dyn_cast(Ex)) { + QualType T = CE->getType(); + + if (!T->isIntegerType()) + return UnknownVal(); + + uint64_t newBits = Ctx.getTypeSize(T); + if (!bitsInit || newBits < bits) { + bitsInit = true; + bits = newBits; + } + + Ex = CE->getSubExpr(); + } + + // We reached a non-cast. Is it a symbolic value? + QualType T = Ex->getType(); + + if (!bitsInit || !T->isIntegerType() || Ctx.getTypeSize(T) > bits) + return UnknownVal(); + + return state->getSVal(Ex); +} + +void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term, + GRBranchNodeBuilder& builder) { + + // Check for NULL conditions; e.g. "for(;;)" + if (!Condition) { + builder.markInfeasible(false); + return; + } + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + Condition->getLocStart(), + "Error evaluating branch"); + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + checker->VisitBranchCondition(builder, *this, Condition, tag); + } + + // If the branch condition is undefined, return; + if (!builder.isFeasible(true) && !builder.isFeasible(false)) + return; + + const GRState* PrevState = builder.getState(); + SVal X = PrevState->getSVal(Condition); + + if (X.isUnknown()) { + // Give it a chance to recover from unknown. + if (const Expr *Ex = dyn_cast(Condition)) { + if (Ex->getType()->isIntegerType()) { + // Try to recover some path-sensitivity. Right now casts of symbolic + // integers that promote their values are currently not tracked well. + // If 'Condition' is such an expression, try and recover the + // underlying value and use that instead. + SVal recovered = RecoverCastedSymbol(getStateManager(), + builder.getState(), Condition, + getContext()); + + if (!recovered.isUnknown()) { + X = recovered; + } + } + } + // If the condition is still unknown, give up. + if (X.isUnknown()) { + builder.generateNode(MarkBranch(PrevState, Term, true), true); + builder.generateNode(MarkBranch(PrevState, Term, false), false); + return; + } + } + + DefinedSVal V = cast(X); + + // Process the true branch. + if (builder.isFeasible(true)) { + if (const GRState *state = PrevState->Assume(V, true)) + builder.generateNode(MarkBranch(state, Term, true), true); + else + builder.markInfeasible(true); + } + + // Process the false branch. + if (builder.isFeasible(false)) { + if (const GRState *state = PrevState->Assume(V, false)) + builder.generateNode(MarkBranch(state, Term, false), false); + else + builder.markInfeasible(false); + } +} + +/// ProcessIndirectGoto - Called by GRCoreEngine. Used to generate successor +/// nodes by processing the 'effects' of a computed goto jump. +void GRExprEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& builder) { + + const GRState *state = builder.getState(); + SVal V = state->getSVal(builder.getTarget()); + + // Three possibilities: + // + // (1) We know the computed label. + // (2) The label is NULL (or some other constant), or Undefined. + // (3) We have no clue about the label. Dispatch to all targets. + // + + typedef GRIndirectGotoNodeBuilder::iterator iterator; + + if (isa(V)) { + LabelStmt* L = cast(V).getLabel(); + + for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) { + if (I.getLabel() == L) { + builder.generateNode(I, state); + return; + } + } + + assert (false && "No block with label."); + return; + } + + if (isa(V) || isa(V)) { + // Dispatch to the first target and mark it as a sink. + //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); + // FIXME: add checker visit. + // UndefBranches.insert(N); + return; + } + + // This is really a catch-all. We don't support symbolics yet. + // FIXME: Implement dispatch for symbolic pointers. + + for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) + builder.generateNode(I, state); +} + + +void GRExprEngine::VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { + + assert(Ex == CurrentStmt && + Pred->getLocationContext()->getCFG()->isBlkExpr(Ex)); + + const GRState* state = GetState(Pred); + SVal X = state->getSVal(Ex); + + assert (X.isUndef()); + + Expr *SE = (Expr*) cast(X).getData(); + assert(SE); + X = state->getSVal(SE); + + // Make sure that we invalidate the previous binding. + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true)); +} + +/// ProcessEndPath - Called by GRCoreEngine. Used to generate end-of-path +/// nodes when the control reaches the end of a function. +void GRExprEngine::ProcessEndPath(GREndPathNodeBuilder& builder) { + getTF().EvalEndPath(*this, builder); + StateMgr.EndPath(builder.getState()); + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ + void *tag = I->first; + Checker *checker = I->second; + checker->EvalEndPath(builder, tag, *this); + } +} + +/// ProcessSwitch - Called by GRCoreEngine. Used to generate successor +/// nodes by processing the 'effects' of a switch statement. +void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) { + typedef GRSwitchNodeBuilder::iterator iterator; + const GRState* state = builder.getState(); + Expr* CondE = builder.getCondition(); + SVal CondV_untested = state->getSVal(CondE); + + if (CondV_untested.isUndef()) { + //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); + // FIXME: add checker + //UndefBranches.insert(N); + + return; + } + DefinedOrUnknownSVal CondV = cast(CondV_untested); + + const GRState *DefaultSt = state; + bool defaultIsFeasible = false; + + for (iterator I = builder.begin(), EI = builder.end(); I != EI; ++I) { + CaseStmt* Case = cast(I.getCase()); + + // Evaluate the LHS of the case value. + Expr::EvalResult V1; + bool b = Case->getLHS()->Evaluate(V1, getContext()); + + // Sanity checks. These go away in Release builds. + assert(b && V1.Val.isInt() && !V1.HasSideEffects + && "Case condition must evaluate to an integer constant."); + b = b; // silence unused variable warning + assert(V1.Val.getInt().getBitWidth() == + getContext().getTypeSize(CondE->getType())); + + // Get the RHS of the case, if it exists. + Expr::EvalResult V2; + + if (Expr* E = Case->getRHS()) { + b = E->Evaluate(V2, getContext()); + assert(b && V2.Val.isInt() && !V2.HasSideEffects + && "Case condition must evaluate to an integer constant."); + b = b; // silence unused variable warning + } + else + V2 = V1; + + // FIXME: Eventually we should replace the logic below with a range + // comparison, rather than concretize the values within the range. + // This should be easy once we have "ranges" for NonLVals. + + do { + nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1.Val.getInt())); + DefinedOrUnknownSVal Res = SVator.EvalEQ(DefaultSt ? DefaultSt : state, + CondV, CaseVal); + + // Now "assume" that the case matches. + if (const GRState* stateNew = state->Assume(Res, true)) { + builder.generateCaseStmtNode(I, stateNew); + + // If CondV evaluates to a constant, then we know that this + // is the *only* case that we can take, so stop evaluating the + // others. + if (isa(CondV)) + return; + } + + // Now "assume" that the case doesn't match. Add this state + // to the default state (if it is feasible). + if (DefaultSt) { + if (const GRState *stateNew = DefaultSt->Assume(Res, false)) { + defaultIsFeasible = true; + DefaultSt = stateNew; + } + else { + defaultIsFeasible = false; + DefaultSt = NULL; + } + } + + // Concretize the next value in the range. + if (V1.Val.getInt() == V2.Val.getInt()) + break; + + ++V1.Val.getInt(); + assert (V1.Val.getInt() <= V2.Val.getInt()); + + } while (true); + } + + // If we reach here, than we know that the default branch is + // possible. + if (defaultIsFeasible) builder.generateDefaultCaseNode(DefaultSt); +} + +//===----------------------------------------------------------------------===// +// Transfer functions: logical operations ('&&', '||'). +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + assert(B->getOpcode() == BinaryOperator::LAnd || + B->getOpcode() == BinaryOperator::LOr); + + assert(B==CurrentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(B)); + + const GRState* state = GetState(Pred); + SVal X = state->getSVal(B); + assert(X.isUndef()); + + const Expr *Ex = (const Expr*) cast(X).getData(); + assert(Ex); + + if (Ex == B->getRHS()) { + X = state->getSVal(Ex); + + // Handle undefined values. + if (X.isUndef()) { + MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + return; + } + + DefinedOrUnknownSVal XD = cast(X); + + // We took the RHS. Because the value of the '&&' or '||' expression must + // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0 + // or 1. Alternatively, we could take a lazy approach, and calculate this + // value later when necessary. We don't have the machinery in place for + // this right now, and since most logical expressions are used for branches, + // the payoff is not likely to be large. Instead, we do eager evaluation. + if (const GRState *newState = state->Assume(XD, true)) + MakeNode(Dst, B, Pred, + newState->BindExpr(B, ValMgr.makeIntVal(1U, B->getType()))); + + if (const GRState *newState = state->Assume(XD, false)) + MakeNode(Dst, B, Pred, + newState->BindExpr(B, ValMgr.makeIntVal(0U, B->getType()))); + } + else { + // We took the LHS expression. Depending on whether we are '&&' or + // '||' we know what the value of the expression is via properties of + // the short-circuiting. + X = ValMgr.makeIntVal(B->getOpcode() == BinaryOperator::LAnd ? 0U : 1U, + B->getType()); + MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + } +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Loads and stores. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitBlockExpr(BlockExpr *BE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + ExplodedNodeSet Tmp; + + CanQualType T = getContext().getCanonicalType(BE->getType()); + SVal V = ValMgr.getBlockPointer(BE->getBlockDecl(), T, + Pred->getLocationContext()); + + MakeNode(Tmp, BE, Pred, GetState(Pred)->BindExpr(BE, V), + ProgramPoint::PostLValueKind); + + // Post-visit the BlockExpr. + CheckerVisit(BE, Dst, Tmp, false); +} + +void GRExprEngine::VisitDeclRefExpr(DeclRefExpr *Ex, ExplodedNode *Pred, + ExplodedNodeSet &Dst, bool asLValue) { + VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue); +} + +void GRExprEngine::VisitBlockDeclRefExpr(BlockDeclRefExpr *Ex, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, bool asLValue) { + VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue); +} + +void GRExprEngine::VisitCommonDeclRefExpr(Expr *Ex, const NamedDecl *D, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, bool asLValue) { + + const GRState *state = GetState(Pred); + + if (const VarDecl* VD = dyn_cast(D)) { + + SVal V = state->getLValue(VD, Pred->getLocationContext()); + + if (asLValue) { + // For references, the 'lvalue' is the pointer address stored in the + // reference region. + if (VD->getType()->isReferenceType()) { + if (const MemRegion *R = V.getAsRegion()) + V = state->getSVal(R); + else + V = UnknownVal(); + } + + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), + ProgramPoint::PostLValueKind); + } + else + EvalLoad(Dst, Ex, Pred, state, V); + + return; + } else if (const EnumConstantDecl* ED = dyn_cast(D)) { + assert(!asLValue && "EnumConstantDecl does not have lvalue."); + + SVal V = ValMgr.makeIntVal(ED->getInitVal()); + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); + return; + + } else if (const FunctionDecl* FD = dyn_cast(D)) { + // This code is valid regardless of the value of 'isLValue'. + SVal V = ValMgr.getFunctionPointer(FD); + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), + ProgramPoint::PostLValueKind); + return; + } + + assert (false && + "ValueDecl support for this ValueDecl not implemented."); +} + +/// VisitArraySubscriptExpr - Transfer function for array accesses +void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A, + ExplodedNode* Pred, + ExplodedNodeSet& Dst, bool asLValue){ + + Expr* Base = A->getBase()->IgnoreParens(); + Expr* Idx = A->getIdx()->IgnoreParens(); + ExplodedNodeSet Tmp; + + if (Base->getType()->isVectorType()) { + // For vector types get its lvalue. + // FIXME: This may not be correct. Is the rvalue of a vector its location? + // In fact, I think this is just a hack. We need to get the right + // semantics. + VisitLValue(Base, Pred, Tmp); + } + else + Visit(Base, Pred, Tmp); // Get Base's rvalue, which should be an LocVal. + + for (ExplodedNodeSet::iterator I1=Tmp.begin(), E1=Tmp.end(); I1!=E1; ++I1) { + ExplodedNodeSet Tmp2; + Visit(Idx, *I1, Tmp2); // Evaluate the index. + + ExplodedNodeSet Tmp3; + CheckerVisit(A, Tmp3, Tmp2, true); + + for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) { + const GRState* state = GetState(*I2); + SVal V = state->getLValue(A->getType(), state->getSVal(Idx), + state->getSVal(Base)); + + if (asLValue) + MakeNode(Dst, A, *I2, state->BindExpr(A, V), + ProgramPoint::PostLValueKind); + else + EvalLoad(Dst, A, *I2, state, V); + } + } +} + +/// VisitMemberExpr - Transfer function for member expressions. +void GRExprEngine::VisitMemberExpr(MemberExpr* M, ExplodedNode* Pred, + ExplodedNodeSet& Dst, bool asLValue) { + + Expr* Base = M->getBase()->IgnoreParens(); + ExplodedNodeSet Tmp; + + if (M->isArrow()) + Visit(Base, Pred, Tmp); // p->f = ... or ... = p->f + else + VisitLValue(Base, Pred, Tmp); // x.f = ... or ... = x.f + + FieldDecl *Field = dyn_cast(M->getMemberDecl()); + if (!Field) // FIXME: skipping member expressions for non-fields + return; + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { + const GRState* state = GetState(*I); + // FIXME: Should we insert some assumption logic in here to determine + // if "Base" is a valid piece of memory? Before we put this assumption + // later when using FieldOffset lvals (which we no longer have). + SVal L = state->getLValue(Field, state->getSVal(Base)); + + if (asLValue) + MakeNode(Dst, M, *I, state->BindExpr(M, L), ProgramPoint::PostLValueKind); + else + EvalLoad(Dst, M, *I, state, L); + } +} + +/// EvalBind - Handle the semantics of binding a value to a specific location. +/// This method is used by EvalStore and (soon) VisitDeclStmt, and others. +void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt *AssignE, + Stmt* StoreE, ExplodedNode* Pred, + const GRState* state, SVal location, SVal Val, + bool atDeclInit) { + + + // Do a previsit of the bind. + ExplodedNodeSet CheckedSet, Src; + Src.Add(Pred); + CheckerVisitBind(AssignE, StoreE, CheckedSet, Src, location, Val, true); + + for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); + I!=E; ++I) { + + if (Pred != *I) + state = GetState(*I); + + const GRState* newState = 0; + + if (atDeclInit) { + const VarRegion *VR = + cast(cast(location).getRegion()); + + newState = state->bindDecl(VR, Val); + } + else { + if (location.isUnknown()) { + // We know that the new state will be the same as the old state since + // the location of the binding is "unknown". Consequently, there + // is no reason to just create a new node. + newState = state; + } + else { + // We are binding to a value other than 'unknown'. Perform the binding + // using the StoreManager. + newState = state->bindLoc(cast(location), Val); + } + } + + // The next thing to do is check if the GRTransferFuncs object wants to + // update the state based on the new binding. If the GRTransferFunc object + // doesn't do anything, just auto-propagate the current state. + GRStmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, StoreE, + newState != state); + + getTF().EvalBind(BuilderRef, location, Val); + } +} + +/// EvalStore - Handle the semantics of a store via an assignment. +/// @param Dst The node set to store generated state nodes +/// @param Ex The expression representing the location of the store +/// @param state The current simulation state +/// @param location The location to store the value +/// @param Val The value to be stored +void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, Expr *AssignE, + Expr* StoreE, + ExplodedNode* Pred, + const GRState* state, SVal location, SVal Val, + const void *tag) { + + assert(Builder && "GRStmtNodeBuilder must be defined."); + + // Evaluate the location (checks for bad dereferences). + ExplodedNodeSet Tmp; + EvalLocation(Tmp, StoreE, Pred, state, location, tag, false); + + if (Tmp.empty()) + return; + + assert(!location.isUndef()); + + SaveAndRestore OldSPointKind(Builder->PointKind, + ProgramPoint::PostStoreKind); + SaveAndRestore OldTag(Builder->Tag, tag); + + // Proceed with the store. + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) + EvalBind(Dst, AssignE, StoreE, *NI, GetState(*NI), location, Val); +} + +void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, QualType LoadTy) { + + // Are we loading from a region? This actually results in two loads; one + // to fetch the address of the referenced value and one to fetch the + // referenced value. + if (const TypedRegion *TR = + dyn_cast_or_null(location.getAsRegion())) { + + QualType ValTy = TR->getValueType(getContext()); + if (const ReferenceType *RT = ValTy->getAs()) { + static int loadReferenceTag = 0; + ExplodedNodeSet Tmp; + EvalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag, + getContext().getPointerType(RT->getPointeeType())); + + // Perform the load from the referenced value. + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { + state = GetState(*I); + location = state->getSVal(Ex); + EvalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy); + } + return; + } + } + + EvalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy); +} + +void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, Expr *Ex, + ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, QualType LoadTy) { + + // Evaluate the location (checks for bad dereferences). + ExplodedNodeSet Tmp; + EvalLocation(Tmp, Ex, Pred, state, location, tag, true); + + if (Tmp.empty()) + return; + + assert(!location.isUndef()); + + SaveAndRestore OldSPointKind(Builder->PointKind); + SaveAndRestore OldTag(Builder->Tag); + + // Proceed with the load. + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { + state = GetState(*NI); + if (location.isUnknown()) { + // This is important. We must nuke the old binding. + MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, UnknownVal()), + ProgramPoint::PostLoadKind, tag); + } + else { + SVal V = state->getSVal(cast(location), LoadTy.isNull() ? + Ex->getType() : LoadTy); + MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, V), ProgramPoint::PostLoadKind, + tag); + } + } +} + +void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S, + ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, bool isLoad) { + // Early checks for performance reason. + if (location.isUnknown() || Checkers.empty()) { + Dst.Add(Pred); + return; + } + + ExplodedNodeSet Src, Tmp; + Src.Add(Pred); + ExplodedNodeSet *PrevSet = &Src; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) + { + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) { + // Use the 'state' argument only when the predecessor node is the + // same as Pred. This allows us to catch updates to the state. + checker->GR_VisitLocation(*CurrSet, *Builder, *this, S, *NI, + *NI == Pred ? state : GetState(*NI), + location, tag, isLoad); + } + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Function calls. +//===----------------------------------------------------------------------===// + +namespace { +class CallExprWLItem { +public: + CallExpr::arg_iterator I; + ExplodedNode *N; + + CallExprWLItem(const CallExpr::arg_iterator &i, ExplodedNode *n) + : I(i), N(n) {} +}; +} // end anonymous namespace + +void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, + CallExpr::arg_iterator AI, + CallExpr::arg_iterator AE, + ExplodedNodeSet& Dst, bool asLValue) { + + // Determine the type of function we're calling (if available). + const FunctionProtoType *Proto = NULL; + QualType FnType = CE->getCallee()->IgnoreParens()->getType(); + if (const PointerType *FnTypePtr = FnType->getAs()) + Proto = FnTypePtr->getPointeeType()->getAs(); + + // Create a worklist to process the arguments. + llvm::SmallVector WorkList; + WorkList.reserve(AE - AI); + WorkList.push_back(CallExprWLItem(AI, Pred)); + + ExplodedNodeSet ArgsEvaluated; + + while (!WorkList.empty()) { + CallExprWLItem Item = WorkList.back(); + WorkList.pop_back(); + + if (Item.I == AE) { + ArgsEvaluated.insert(Item.N); + continue; + } + + // Evaluate the argument. + ExplodedNodeSet Tmp; + const unsigned ParamIdx = Item.I - AI; + + bool VisitAsLvalue = false; + if (Proto && ParamIdx < Proto->getNumArgs()) + VisitAsLvalue = Proto->getArgType(ParamIdx)->isReferenceType(); + + if (VisitAsLvalue) + VisitLValue(*Item.I, Item.N, Tmp); + else + Visit(*Item.I, Item.N, Tmp); + + // Enqueue evaluating the next argument on the worklist. + ++(Item.I); + + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) + WorkList.push_back(CallExprWLItem(Item.I, *NI)); + } + + // Now process the call itself. + ExplodedNodeSet DstTmp; + Expr* Callee = CE->getCallee()->IgnoreParens(); + + for (ExplodedNodeSet::iterator NI=ArgsEvaluated.begin(), + NE=ArgsEvaluated.end(); NI != NE; ++NI) { + // Evaluate the callee. + ExplodedNodeSet DstTmp2; + Visit(Callee, *NI, DstTmp2); + // Perform the previsit of the CallExpr, storing the results in DstTmp. + CheckerVisit(CE, DstTmp, DstTmp2, true); + } + + // Finally, evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call. + ExplodedNodeSet DstTmp3; + + + for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); + DI != DE; ++DI) { + + const GRState* state = GetState(*DI); + SVal L = state->getSVal(Callee); + + // FIXME: Add support for symbolic function calls (calls involving + // function pointer values that are symbolic). + SaveAndRestore OldSink(Builder->BuildSinks); + ExplodedNodeSet DstChecker; + + // If the callee is processed by a checker, skip the rest logic. + if (CheckerEvalCall(CE, DstChecker, *DI)) + DstTmp3.insert(DstChecker); + else { + for (ExplodedNodeSet::iterator DI_Checker = DstChecker.begin(), + DE_Checker = DstChecker.end(); + DI_Checker != DE_Checker; ++DI_Checker) { + + // Dispatch to the plug-in transfer function. + unsigned OldSize = DstTmp3.size(); + SaveOr OldHasGen(Builder->HasGeneratedNode); + Pred = *DI_Checker; + + // Dispatch to transfer function logic to handle the call itself. + // FIXME: Allow us to chain together transfer functions. + assert(Builder && "GRStmtNodeBuilder must be defined."); + getTF().EvalCall(DstTmp3, *this, *Builder, CE, L, Pred); + + // Handle the case where no nodes where generated. Auto-generate that + // contains the updated state if we aren't generating sinks. + if (!Builder->BuildSinks && DstTmp3.size() == OldSize && + !Builder->HasGeneratedNode) + MakeNode(DstTmp3, CE, Pred, state); + } + } + } + + // Finally, perform the post-condition check of the CallExpr and store + // the created nodes in 'Dst'. + + if (!(!asLValue && CalleeReturnsReference(CE))) { + CheckerVisit(CE, Dst, DstTmp3, false); + return; + } + + // Handle the case where the called function returns a reference but + // we expect an rvalue. For such cases, convert the reference to + // an rvalue. + // FIXME: This conversion doesn't actually happen unless the result + // of CallExpr is consumed by another expression. + ExplodedNodeSet DstTmp4; + CheckerVisit(CE, DstTmp4, DstTmp3, false); + QualType LoadTy = CE->getType(); + + static int *ConvertToRvalueTag = 0; + for (ExplodedNodeSet::iterator NI = DstTmp4.begin(), NE = DstTmp4.end(); + NI!=NE; ++NI) { + const GRState *state = GetState(*NI); + EvalLoad(Dst, CE, *NI, state, state->getSVal(CE), + &ConvertToRvalueTag, LoadTy); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C ivar references. +//===----------------------------------------------------------------------===// + +static std::pair EagerlyAssumeTag + = std::pair(&EagerlyAssumeTag,0); + +void GRExprEngine::EvalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, + Expr *Ex) { + for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { + ExplodedNode *Pred = *I; + + // Test if the previous node was as the same expression. This can happen + // when the expression fails to evaluate to anything meaningful and + // (as an optimization) we don't generate a node. + ProgramPoint P = Pred->getLocation(); + if (!isa(P) || cast(P).getStmt() != Ex) { + Dst.Add(Pred); + continue; + } + + const GRState* state = Pred->getState(); + SVal V = state->getSVal(Ex); + if (nonloc::SymExprVal *SEV = dyn_cast(&V)) { + // First assume that the condition is true. + if (const GRState *stateTrue = state->Assume(*SEV, true)) { + stateTrue = stateTrue->BindExpr(Ex, + ValMgr.makeIntVal(1U, Ex->getType())); + Dst.Add(Builder->generateNode(PostStmtCustom(Ex, + &EagerlyAssumeTag, Pred->getLocationContext()), + stateTrue, Pred)); + } + + // Next, assume that the condition is false. + if (const GRState *stateFalse = state->Assume(*SEV, false)) { + stateFalse = stateFalse->BindExpr(Ex, + ValMgr.makeIntVal(0U, Ex->getType())); + Dst.Add(Builder->generateNode(PostStmtCustom(Ex, &EagerlyAssumeTag, + Pred->getLocationContext()), + stateFalse, Pred)); + } + } + else + Dst.Add(Pred); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C ivar references. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitObjCIvarRefExpr(ObjCIvarRefExpr* Ex, ExplodedNode* Pred, + ExplodedNodeSet& Dst, bool asLValue) { + + Expr* Base = cast(Ex->getBase()); + ExplodedNodeSet Tmp; + Visit(Base, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + SVal BaseVal = state->getSVal(Base); + SVal location = state->getLValue(Ex->getDecl(), BaseVal); + + if (asLValue) + MakeNode(Dst, Ex, *I, state->BindExpr(Ex, location)); + else + EvalLoad(Dst, Ex, *I, state, location); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C fast enumeration 'for' statements. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { + + // ObjCForCollectionStmts are processed in two places. This method + // handles the case where an ObjCForCollectionStmt* occurs as one of the + // statements within a basic block. This transfer function does two things: + // + // (1) binds the next container value to 'element'. This creates a new + // node in the ExplodedGraph. + // + // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating + // whether or not the container has any more elements. This value + // will be tested in ProcessBranch. We need to explicitly bind + // this value because a container can contain nil elements. + // + // FIXME: Eventually this logic should actually do dispatches to + // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration). + // This will require simulating a temporary NSFastEnumerationState, either + // through an SVal or through the use of MemRegions. This value can + // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop + // terminates we reclaim the temporary (it goes out of scope) and we + // we can test if the SVal is 0 or if the MemRegion is null (depending + // on what approach we take). + // + // For now: simulate (1) by assigning either a symbol or nil if the + // container is empty. Thus this transfer function will by default + // result in state splitting. + + Stmt* elem = S->getElement(); + SVal ElementV; + + if (DeclStmt* DS = dyn_cast(elem)) { + VarDecl* ElemD = cast(DS->getSingleDecl()); + assert (ElemD->getInit() == 0); + ElementV = GetState(Pred)->getLValue(ElemD, Pred->getLocationContext()); + VisitObjCForCollectionStmtAux(S, Pred, Dst, ElementV); + return; + } + + ExplodedNodeSet Tmp; + VisitLValue(cast(elem), Pred, Tmp); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + VisitObjCForCollectionStmtAux(S, *I, Dst, state->getSVal(elem)); + } +} + +void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S, + ExplodedNode* Pred, ExplodedNodeSet& Dst, + SVal ElementV) { + + // Check if the location we are writing back to is a null pointer. + Stmt* elem = S->getElement(); + ExplodedNodeSet Tmp; + EvalLocation(Tmp, elem, Pred, GetState(Pred), ElementV, NULL, false); + + if (Tmp.empty()) + return; + + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { + Pred = *NI; + const GRState *state = GetState(Pred); + + // Handle the case where the container still has elements. + SVal TrueV = ValMgr.makeTruthVal(1); + const GRState *hasElems = state->BindExpr(S, TrueV); + + // Handle the case where the container has no elements. + SVal FalseV = ValMgr.makeTruthVal(0); + const GRState *noElems = state->BindExpr(S, FalseV); + + if (loc::MemRegionVal* MV = dyn_cast(&ElementV)) + if (const TypedRegion* R = dyn_cast(MV->getRegion())) { + // FIXME: The proper thing to do is to really iterate over the + // container. We will do this with dispatch logic to the store. + // For now, just 'conjure' up a symbolic value. + QualType T = R->getValueType(getContext()); + assert(Loc::IsLocType(T)); + unsigned Count = Builder->getCurrentBlockCount(); + SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); + SVal V = ValMgr.makeLoc(Sym); + hasElems = hasElems->bindLoc(ElementV, V); + + // Bind the location to 'nil' on the false branch. + SVal nilV = ValMgr.makeIntVal(0, T); + noElems = noElems->bindLoc(ElementV, nilV); + } + + // Create the new nodes. + MakeNode(Dst, S, Pred, hasElems); + MakeNode(Dst, S, Pred, noElems); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C message expressions. +//===----------------------------------------------------------------------===// + +namespace { +class ObjCMsgWLItem { +public: + ObjCMessageExpr::arg_iterator I; + ExplodedNode *N; + + ObjCMsgWLItem(const ObjCMessageExpr::arg_iterator &i, ExplodedNode *n) + : I(i), N(n) {} +}; +} // end anonymous namespace + +void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred, + ExplodedNodeSet& Dst, bool asLValue){ + + // Create a worklist to process both the arguments. + llvm::SmallVector WL; + + // But first evaluate the receiver (if any). + ObjCMessageExpr::arg_iterator AI = ME->arg_begin(), AE = ME->arg_end(); + if (Expr *Receiver = ME->getReceiver()) { + ExplodedNodeSet Tmp; + Visit(Receiver, Pred, Tmp); + + if (Tmp.empty()) + return; + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) + WL.push_back(ObjCMsgWLItem(AI, *I)); + } + else + WL.push_back(ObjCMsgWLItem(AI, Pred)); + + // Evaluate the arguments. + ExplodedNodeSet ArgsEvaluated; + while (!WL.empty()) { + ObjCMsgWLItem Item = WL.back(); + WL.pop_back(); + + if (Item.I == AE) { + ArgsEvaluated.insert(Item.N); + continue; + } + + // Evaluate the subexpression. + ExplodedNodeSet Tmp; + + // FIXME: [Objective-C++] handle arguments that are references + Visit(*Item.I, Item.N, Tmp); + + // Enqueue evaluating the next argument on the worklist. + ++(Item.I); + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) + WL.push_back(ObjCMsgWLItem(Item.I, *NI)); + } + + // Now that the arguments are processed, handle the previsits checks. + ExplodedNodeSet DstPrevisit; + CheckerVisit(ME, DstPrevisit, ArgsEvaluated, true); + + // Proceed with evaluate the message expression. + ExplodedNodeSet DstEval; + + for (ExplodedNodeSet::iterator DI = DstPrevisit.begin(), + DE = DstPrevisit.end(); DI != DE; ++DI) { + + Pred = *DI; + bool RaisesException = false; + unsigned OldSize = DstEval.size(); + SaveAndRestore OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->HasGeneratedNode); + + if (const Expr *Receiver = ME->getReceiver()) { + const GRState *state = Pred->getState(); + + // Bifurcate the state into nil and non-nil ones. + DefinedOrUnknownSVal receiverVal = + cast(state->getSVal(Receiver)); + + const GRState *notNilState, *nilState; + llvm::tie(notNilState, nilState) = state->Assume(receiverVal); + + // There are three cases: can be nil or non-nil, must be nil, must be + // non-nil. We handle must be nil, and merge the rest two into non-nil. + if (nilState && !notNilState) { + CheckerEvalNilReceiver(ME, DstEval, nilState, Pred); + continue; + } + + // Check if the "raise" message was sent. + assert(notNilState); + if (ME->getSelector() == RaiseSel) + RaisesException = true; + + // Check if we raise an exception. For now treat these as sinks. + // Eventually we will want to handle exceptions properly. + if (RaisesException) + Builder->BuildSinks = true; + + // Dispatch to plug-in transfer function. + EvalObjCMessageExpr(DstEval, ME, Pred, notNilState); + } + else { + IdentifierInfo* ClsName = ME->getClassName(); + Selector S = ME->getSelector(); + + // Check for special instance methods. + if (!NSExceptionII) { + ASTContext& Ctx = getContext(); + NSExceptionII = &Ctx.Idents.get("NSException"); + } + + if (ClsName == NSExceptionII) { + enum { NUM_RAISE_SELECTORS = 2 }; + + // Lazily create a cache of the selectors. + if (!NSExceptionInstanceRaiseSelectors) { + ASTContext& Ctx = getContext(); + NSExceptionInstanceRaiseSelectors = + new Selector[NUM_RAISE_SELECTORS]; + llvm::SmallVector II; + unsigned idx = 0; + + // raise:format: + II.push_back(&Ctx.Idents.get("raise")); + II.push_back(&Ctx.Idents.get("format")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + + // raise:format::arguments: + II.push_back(&Ctx.Idents.get("arguments")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + } + + for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) + if (S == NSExceptionInstanceRaiseSelectors[i]) { + RaisesException = true; + break; + } + } + + // Check if we raise an exception. For now treat these as sinks. + // Eventually we will want to handle exceptions properly. + if (RaisesException) + Builder->BuildSinks = true; + + // Dispatch to plug-in transfer function. + EvalObjCMessageExpr(DstEval, ME, Pred, Builder->GetState(Pred)); + } + + // Handle the case where no nodes where generated. Auto-generate that + // contains the updated state if we aren't generating sinks. + if (!Builder->BuildSinks && DstEval.size() == OldSize && + !Builder->HasGeneratedNode) + MakeNode(DstEval, ME, Pred, GetState(Pred)); + } + + // Finally, perform the post-condition check of the ObjCMessageExpr and store + // the created nodes in 'Dst'. + if (!(!asLValue && ReceiverReturnsReference(ME))) { + CheckerVisit(ME, Dst, DstEval, false); + return; + } + + // Handle the case where the message expression returns a reference but + // we expect an rvalue. For such cases, convert the reference to + // an rvalue. + // FIXME: This conversion doesn't actually happen unless the result + // of ObjCMessageExpr is consumed by another expression. + ExplodedNodeSet DstRValueConvert; + CheckerVisit(ME, DstRValueConvert, DstEval, false); + QualType LoadTy = ME->getType(); + + static int *ConvertToRvalueTag = 0; + for (ExplodedNodeSet::iterator NI = DstRValueConvert.begin(), + NE = DstRValueConvert.end(); NI != NE; ++NI) { + const GRState *state = GetState(*NI); + EvalLoad(Dst, ME, *NI, state, state->getSVal(ME), + &ConvertToRvalueTag, LoadTy); + } +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Miscellaneous statements. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred, + ExplodedNodeSet &Dst, bool asLValue) { + ExplodedNodeSet S1; + QualType T = CastE->getType(); + QualType ExTy = Ex->getType(); + + if (const ExplicitCastExpr *ExCast=dyn_cast_or_null(CastE)) + T = ExCast->getTypeAsWritten(); + + if (ExTy->isArrayType() || ExTy->isFunctionType() || T->isReferenceType() || + asLValue) + VisitLValue(Ex, Pred, S1); + else + Visit(Ex, Pred, S1); + + ExplodedNodeSet S2; + CheckerVisit(CastE, S2, S1, true); + + // If we are evaluating the cast in an lvalue context, we implicitly want + // the cast to evaluate to a location. + if (asLValue) { + ASTContext &Ctx = getContext(); + T = Ctx.getPointerType(Ctx.getCanonicalType(T)); + ExTy = Ctx.getPointerType(Ctx.getCanonicalType(ExTy)); + } + + switch (CastE->getCastKind()) { + case CastExpr::CK_ToVoid: + assert(!asLValue); + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) + Dst.Add(*I); + return; + + case CastExpr::CK_NoOp: + case CastExpr::CK_FunctionToPointerDecay: + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { + // Copy the SVal of Ex to CastE. + ExplodedNode *N = *I; + const GRState *state = GetState(N); + SVal V = state->getSVal(Ex); + state = state->BindExpr(CastE, V); + MakeNode(Dst, CastE, N, state); + } + return; + + case CastExpr::CK_Unknown: + case CastExpr::CK_ArrayToPointerDecay: + case CastExpr::CK_BitCast: + case CastExpr::CK_IntegralCast: + case CastExpr::CK_IntegralToPointer: + case CastExpr::CK_PointerToIntegral: + case CastExpr::CK_IntegralToFloating: + case CastExpr::CK_FloatingToIntegral: + case CastExpr::CK_FloatingCast: + case CastExpr::CK_AnyPointerToObjCPointerCast: + case CastExpr::CK_AnyPointerToBlockPointerCast: + case CastExpr::CK_DerivedToBase: + // Delegate to SValuator to process. + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { + ExplodedNode* N = *I; + const GRState* state = GetState(N); + SVal V = state->getSVal(Ex); + V = SVator.EvalCast(V, T, ExTy); + state = state->BindExpr(CastE, V); + MakeNode(Dst, CastE, N, state); + } + return; + + default: + llvm::errs() << "Cast kind " << CastE->getCastKind() << " not handled.\n"; + assert(0); + } +} + +void GRExprEngine::VisitCompoundLiteralExpr(CompoundLiteralExpr* CL, + ExplodedNode* Pred, + ExplodedNodeSet& Dst, + bool asLValue) { + InitListExpr* ILE = cast(CL->getInitializer()->IgnoreParens()); + ExplodedNodeSet Tmp; + Visit(ILE, Pred, Tmp); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I!=EI; ++I) { + const GRState* state = GetState(*I); + SVal ILV = state->getSVal(ILE); + const LocationContext *LC = (*I)->getLocationContext(); + state = state->bindCompoundLiteral(CL, LC, ILV); + + if (asLValue) { + MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL, LC))); + } + else + MakeNode(Dst, CL, *I, state->BindExpr(CL, ILV)); + } +} + +void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, + ExplodedNodeSet& Dst) { + + // The CFG has one DeclStmt per Decl. + Decl* D = *DS->decl_begin(); + + if (!D || !isa(D)) + return; + + const VarDecl* VD = dyn_cast(D); + Expr* InitEx = const_cast(VD->getInit()); + + // FIXME: static variables may have an initializer, but the second + // time a function is called those values may not be current. + ExplodedNodeSet Tmp; + + if (InitEx) { + if (VD->getType()->isReferenceType()) + VisitLValue(InitEx, Pred, Tmp); + else + Visit(InitEx, Pred, Tmp); + } + else + Tmp.Add(Pred); + + ExplodedNodeSet Tmp2; + CheckerVisit(DS, Tmp2, Tmp, true); + + for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) { + ExplodedNode *N = *I; + const GRState *state = GetState(N); + + // Decls without InitExpr are not initialized explicitly. + const LocationContext *LC = N->getLocationContext(); + + if (InitEx) { + SVal InitVal = state->getSVal(InitEx); + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if (InitVal.isUnknown() || + !getConstraintManager().canReasonAbout(InitVal)) { + InitVal = ValMgr.getConjuredSymbolVal(NULL, InitEx, + Builder->getCurrentBlockCount()); + } + + EvalBind(Dst, DS, DS, *I, state, + loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); + } + else { + state = state->bindDeclWithNoInit(state->getRegion(VD, LC)); + MakeNode(Dst, DS, *I, state); + } + } +} + +void GRExprEngine::VisitCondInit(VarDecl *VD, Stmt *S, + ExplodedNode *Pred, ExplodedNodeSet& Dst) { + + Expr* InitEx = VD->getInit(); + ExplodedNodeSet Tmp; + Visit(InitEx, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + ExplodedNode *N = *I; + const GRState *state = GetState(N); + + const LocationContext *LC = N->getLocationContext(); + SVal InitVal = state->getSVal(InitEx); + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if (InitVal.isUnknown() || + !getConstraintManager().canReasonAbout(InitVal)) { + InitVal = ValMgr.getConjuredSymbolVal(NULL, InitEx, + Builder->getCurrentBlockCount()); + } + + EvalBind(Dst, S, S, N, state, + loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); + } +} + +namespace { + // This class is used by VisitInitListExpr as an item in a worklist + // for processing the values contained in an InitListExpr. +class InitListWLItem { +public: + llvm::ImmutableList Vals; + ExplodedNode* N; + InitListExpr::reverse_iterator Itr; + + InitListWLItem(ExplodedNode* n, llvm::ImmutableList vals, + InitListExpr::reverse_iterator itr) + : Vals(vals), N(n), Itr(itr) {} +}; +} + + +void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + const GRState* state = GetState(Pred); + QualType T = getContext().getCanonicalType(E->getType()); + unsigned NumInitElements = E->getNumInits(); + + if (T->isArrayType() || T->isStructureType() || + T->isUnionType() || T->isVectorType()) { + + llvm::ImmutableList StartVals = getBasicVals().getEmptySValList(); + + // Handle base case where the initializer has no elements. + // e.g: static int* myArray[] = {}; + if (NumInitElements == 0) { + SVal V = ValMgr.makeCompoundVal(T, StartVals); + MakeNode(Dst, E, Pred, state->BindExpr(E, V)); + return; + } + + // Create a worklist to process the initializers. + llvm::SmallVector WorkList; + WorkList.reserve(NumInitElements); + WorkList.push_back(InitListWLItem(Pred, StartVals, E->rbegin())); + InitListExpr::reverse_iterator ItrEnd = E->rend(); + assert(!(E->rbegin() == E->rend())); + + // Process the worklist until it is empty. + while (!WorkList.empty()) { + InitListWLItem X = WorkList.back(); + WorkList.pop_back(); + + ExplodedNodeSet Tmp; + Visit(*X.Itr, X.N, Tmp); + + InitListExpr::reverse_iterator NewItr = X.Itr + 1; + + for (ExplodedNodeSet::iterator NI=Tmp.begin(),NE=Tmp.end();NI!=NE;++NI) { + // Get the last initializer value. + state = GetState(*NI); + SVal InitV = state->getSVal(cast(*X.Itr)); + + // Construct the new list of values by prepending the new value to + // the already constructed list. + llvm::ImmutableList NewVals = + getBasicVals().consVals(InitV, X.Vals); + + if (NewItr == ItrEnd) { + // Now we have a list holding all init values. Make CompoundValData. + SVal V = ValMgr.makeCompoundVal(T, NewVals); + + // Make final state and node. + MakeNode(Dst, E, *NI, state->BindExpr(E, V)); + } + else { + // Still some initializer values to go. Push them onto the worklist. + WorkList.push_back(InitListWLItem(*NI, NewVals, NewItr)); + } + } + } + + return; + } + + if (Loc::IsLocType(T) || T->isIntegerType()) { + assert (E->getNumInits() == 1); + ExplodedNodeSet Tmp; + Expr* Init = E->getInit(0); + Visit(Init, Pred, Tmp); + for (ExplodedNodeSet::iterator I=Tmp.begin(), EI=Tmp.end(); I != EI; ++I) { + state = GetState(*I); + MakeNode(Dst, E, *I, state->BindExpr(E, state->getSVal(Init))); + } + return; + } + + assert(0 && "unprocessed InitListExpr type"); +} + +/// VisitSizeOfAlignOfExpr - Transfer function for sizeof(type). +void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + QualType T = Ex->getTypeOfArgument(); + CharUnits amt; + + if (Ex->isSizeOf()) { + if (T == getContext().VoidTy) { + // sizeof(void) == 1 byte. + amt = CharUnits::One(); + } + else if (!T.getTypePtr()->isConstantSizeType()) { + // FIXME: Add support for VLAs. + Dst.Add(Pred); + return; + } + else if (T->isObjCInterfaceType()) { + // Some code tries to take the sizeof an ObjCInterfaceType, relying that + // the compiler has laid out its representation. Just report Unknown + // for these. + Dst.Add(Pred); + return; + } + else { + // All other cases. + amt = getContext().getTypeSizeInChars(T); + } + } + else // Get alignment of the type. + amt = getContext().getTypeAlignInChars(T); + + MakeNode(Dst, Ex, Pred, + GetState(Pred)->BindExpr(Ex, + ValMgr.makeIntVal(amt.getQuantity(), Ex->getType()))); +} + + +void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, + ExplodedNodeSet& Dst, bool asLValue) { + + switch (U->getOpcode()) { + + default: + break; + + case UnaryOperator::Deref: { + + Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + + const GRState* state = GetState(*I); + SVal location = state->getSVal(Ex); + + if (asLValue) + MakeNode(Dst, U, *I, state->BindExpr(U, location), + ProgramPoint::PostLValueKind); + else + EvalLoad(Dst, U, *I, state, location); + } + + return; + } + + case UnaryOperator::Real: { + + Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + Dst.Add(*I); + continue; + } + + // For all other types, UnaryOperator::Real is an identity operation. + assert (U->getType() == Ex->getType()); + const GRState* state = GetState(*I); + MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); + } + + return; + } + + case UnaryOperator::Imag: { + + Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + Dst.Add(*I); + continue; + } + + // For all other types, UnaryOperator::Float returns 0. + assert (Ex->getType()->isIntegerType()); + const GRState* state = GetState(*I); + SVal X = ValMgr.makeZeroVal(Ex->getType()); + MakeNode(Dst, U, *I, state->BindExpr(U, X)); + } + + return; + } + + case UnaryOperator::OffsetOf: { + Expr::EvalResult Res; + if (U->Evaluate(Res, getContext()) && Res.Val.isInt()) { + const APSInt &IV = Res.Val.getInt(); + assert(IV.getBitWidth() == getContext().getTypeSize(U->getType())); + assert(U->getType()->isIntegerType()); + assert(IV.isSigned() == U->getType()->isSignedIntegerType()); + SVal X = ValMgr.makeIntVal(IV); + MakeNode(Dst, U, Pred, GetState(Pred)->BindExpr(U, X)); + return; + } + // FIXME: Handle the case where __builtin_offsetof is not a constant. + Dst.Add(Pred); + return; + } + + case UnaryOperator::Plus: assert (!asLValue); // FALL-THROUGH. + case UnaryOperator::Extension: { + + // Unary "+" is a no-op, similar to a parentheses. We still have places + // where it may be a block-level expression, so we need to + // generate an extra node that just propagates the value of the + // subexpression. + + Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); + } + + return; + } + + case UnaryOperator::AddrOf: { + + assert(!asLValue); + Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + VisitLValue(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + SVal V = state->getSVal(Ex); + state = state->BindExpr(U, V); + MakeNode(Dst, U, *I, state); + } + + return; + } + + case UnaryOperator::LNot: + case UnaryOperator::Minus: + case UnaryOperator::Not: { + + assert (!asLValue); + Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + + // Get the value of the subexpression. + SVal V = state->getSVal(Ex); + + if (V.isUnknownOrUndef()) { + MakeNode(Dst, U, *I, state->BindExpr(U, V)); + continue; + } + +// QualType DstT = getContext().getCanonicalType(U->getType()); +// QualType SrcT = getContext().getCanonicalType(Ex->getType()); +// +// if (DstT != SrcT) // Perform promotions. +// V = EvalCast(V, DstT); +// +// if (V.isUnknownOrUndef()) { +// MakeNode(Dst, U, *I, BindExpr(St, U, V)); +// continue; +// } + + switch (U->getOpcode()) { + default: + assert(false && "Invalid Opcode."); + break; + + case UnaryOperator::Not: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, EvalComplement(cast(V))); + break; + + case UnaryOperator::Minus: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, EvalMinus(cast(V))); + break; + + case UnaryOperator::LNot: + + // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." + // + // Note: technically we do "E == 0", but this is the same in the + // transfer functions as "0 == E". + SVal Result; + + if (isa(V)) { + Loc X = ValMgr.makeNull(); + Result = EvalBinOp(state, BinaryOperator::EQ, cast(V), X, + U->getType()); + } + else { + nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); + Result = EvalBinOp(state, BinaryOperator::EQ, cast(V), X, + U->getType()); + } + + state = state->BindExpr(U, Result); + + break; + } + + MakeNode(Dst, U, *I, state); + } + + return; + } + } + + // Handle ++ and -- (both pre- and post-increment). + + assert (U->isIncrementDecrementOp()); + ExplodedNodeSet Tmp; + Expr* Ex = U->getSubExpr()->IgnoreParens(); + VisitLValue(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + + const GRState* state = GetState(*I); + SVal V1 = state->getSVal(Ex); + + // Perform a load. + ExplodedNodeSet Tmp2; + EvalLoad(Tmp2, Ex, *I, state, V1); + + for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) { + + state = GetState(*I2); + SVal V2_untested = state->getSVal(Ex); + + // Propagate unknown and undefined values. + if (V2_untested.isUnknownOrUndef()) { + MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested)); + continue; + } + DefinedSVal V2 = cast(V2_untested); + + // Handle all other values. + BinaryOperator::Opcode Op = U->isIncrementOp() ? BinaryOperator::Add + : BinaryOperator::Sub; + + // If the UnaryOperator has non-location type, use its type to create the + // constant value. If the UnaryOperator has location type, create the + // constant with int type and pointer width. + SVal RHS; + + if (U->getType()->isAnyPointerType()) + RHS = ValMgr.makeIntValWithPtrWidth(1, false); + else + RHS = ValMgr.makeIntVal(1, U->getType()); + + SVal Result = EvalBinOp(state, Op, V2, RHS, U->getType()); + + // Conjure a new symbol if necessary to recover precision. + if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){ + DefinedOrUnknownSVal SymVal = + ValMgr.getConjuredSymbolVal(NULL, Ex, + Builder->getCurrentBlockCount()); + Result = SymVal; + + // If the value is a location, ++/-- should always preserve + // non-nullness. Check if the original value was non-null, and if so + // propagate that constraint. + if (Loc::IsLocType(U->getType())) { + DefinedOrUnknownSVal Constraint = + SVator.EvalEQ(state, V2, ValMgr.makeZeroVal(U->getType())); + + if (!state->Assume(Constraint, true)) { + // It isn't feasible for the original value to be null. + // Propagate this constraint. + Constraint = SVator.EvalEQ(state, SymVal, + ValMgr.makeZeroVal(U->getType())); + + + state = state->Assume(Constraint, false); + assert(state); + } + } + } + + state = state->BindExpr(U, U->isPostfix() ? V2 : Result); + + // Perform the store. + EvalStore(Dst, NULL, U, *I2, state, V1, Result); + } + } +} + + +void GRExprEngine::VisitCXXThisExpr(CXXThisExpr *TE, ExplodedNode *Pred, + ExplodedNodeSet & Dst) { + // Get the this object region from StoreManager. + const MemRegion *R = + ValMgr.getRegionManager().getCXXThisRegion(TE->getType(), + Pred->getLocationContext()); + + const GRState *state = GetState(Pred); + SVal V = state->getSVal(loc::MemRegionVal(R)); + MakeNode(Dst, TE, Pred, state->BindExpr(TE, V)); +} + +void GRExprEngine::VisitAsmStmt(AsmStmt* A, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst); +} + +void GRExprEngine::VisitAsmStmtHelperOutputs(AsmStmt* A, + AsmStmt::outputs_iterator I, + AsmStmt::outputs_iterator E, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { + if (I == E) { + VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst); + return; + } + + ExplodedNodeSet Tmp; + VisitLValue(*I, Pred, Tmp); + + ++I; + + for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end();NI != NE;++NI) + VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst); +} + +void GRExprEngine::VisitAsmStmtHelperInputs(AsmStmt* A, + AsmStmt::inputs_iterator I, + AsmStmt::inputs_iterator E, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + if (I == E) { + + // We have processed both the inputs and the outputs. All of the outputs + // should evaluate to Locs. Nuke all of their values. + + // FIXME: Some day in the future it would be nice to allow a "plug-in" + // which interprets the inline asm and stores proper results in the + // outputs. + + const GRState* state = GetState(Pred); + + for (AsmStmt::outputs_iterator OI = A->begin_outputs(), + OE = A->end_outputs(); OI != OE; ++OI) { + + SVal X = state->getSVal(*OI); + assert (!isa(X)); // Should be an Lval, or unknown, undef. + + if (isa(X)) + state = state->bindLoc(cast(X), UnknownVal()); + } + + MakeNode(Dst, A, Pred, state); + return; + } + + ExplodedNodeSet Tmp; + Visit(*I, Pred, Tmp); + + ++I; + + for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI!=NE; ++NI) + VisitAsmStmtHelperInputs(A, I, E, *NI, Dst); +} + +void GRExprEngine::VisitReturnStmt(ReturnStmt *RS, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + ExplodedNodeSet Src; + if (Expr *RetE = RS->getRetValue()) { + Visit(RetE, Pred, Src); + } + else { + Src.Add(Pred); + } + + ExplodedNodeSet CheckedSet; + CheckerVisit(RS, CheckedSet, Src, true); + + for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); + I != E; ++I) { + + assert(Builder && "GRStmtNodeBuilder must be defined."); + + Pred = *I; + unsigned size = Dst.size(); + + SaveAndRestore OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->HasGeneratedNode); + + getTF().EvalReturn(Dst, *this, *Builder, RS, Pred); + + // Handle the case where no nodes where generated. + if (!Builder->BuildSinks && Dst.size() == size && + !Builder->HasGeneratedNode) + MakeNode(Dst, RS, Pred, GetState(Pred)); + } +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Binary operators. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, + ExplodedNode* Pred, + ExplodedNodeSet& Dst, bool asLValue) { + + ExplodedNodeSet Tmp1; + Expr* LHS = B->getLHS()->IgnoreParens(); + Expr* RHS = B->getRHS()->IgnoreParens(); + + // FIXME: Add proper support for ObjCImplicitSetterGetterRefExpr. + if (isa(LHS)) { + Visit(RHS, Pred, Dst); + return; + } + + if (B->isAssignmentOp()) + VisitLValue(LHS, Pred, Tmp1); + else + Visit(LHS, Pred, Tmp1); + + ExplodedNodeSet Tmp3; + + for (ExplodedNodeSet::iterator I1=Tmp1.begin(), E1=Tmp1.end(); I1!=E1; ++I1) { + SVal LeftV = (*I1)->getState()->getSVal(LHS); + ExplodedNodeSet Tmp2; + Visit(RHS, *I1, Tmp2); + + ExplodedNodeSet CheckedSet; + CheckerVisit(B, CheckedSet, Tmp2, true); + + // With both the LHS and RHS evaluated, process the operation itself. + + for (ExplodedNodeSet::iterator I2=CheckedSet.begin(), E2=CheckedSet.end(); + I2 != E2; ++I2) { + + const GRState *state = GetState(*I2); + const GRState *OldSt = state; + SVal RightV = state->getSVal(RHS); + + BinaryOperator::Opcode Op = B->getOpcode(); + + if (Op == BinaryOperator::Assign) { + // EXPERIMENTAL: "Conjured" symbols. + // FIXME: Handle structs. + QualType T = RHS->getType(); + + if ((RightV.isUnknown()||!getConstraintManager().canReasonAbout(RightV)) + && (Loc::IsLocType(T) || (T->isScalarType()&&T->isIntegerType()))) { + unsigned Count = Builder->getCurrentBlockCount(); + RightV = ValMgr.getConjuredSymbolVal(NULL, B->getRHS(), Count); + } + + SVal ExprVal = asLValue ? LeftV : RightV; + + // Simulate the effects of a "store": bind the value of the RHS + // to the L-Value represented by the LHS. + EvalStore(Tmp3, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV,RightV); + continue; + } + + if (!B->isAssignmentOp()) { + // Process non-assignments except commas or short-circuited + // logical expressions (LAnd and LOr). + SVal Result = EvalBinOp(state, Op, LeftV, RightV, B->getType()); + + if (Result.isUnknown()) { + if (OldSt != state) { + // Generate a new node if we have already created a new state. + MakeNode(Tmp3, B, *I2, state); + } + else + Tmp3.Add(*I2); + + continue; + } + + state = state->BindExpr(B, Result); + + MakeNode(Tmp3, B, *I2, state); + continue; + } + + assert (B->isCompoundAssignmentOp()); + + switch (Op) { + default: + assert(0 && "Invalid opcode for compound assignment."); + case BinaryOperator::MulAssign: Op = BinaryOperator::Mul; break; + case BinaryOperator::DivAssign: Op = BinaryOperator::Div; break; + case BinaryOperator::RemAssign: Op = BinaryOperator::Rem; break; + case BinaryOperator::AddAssign: Op = BinaryOperator::Add; break; + case BinaryOperator::SubAssign: Op = BinaryOperator::Sub; break; + case BinaryOperator::ShlAssign: Op = BinaryOperator::Shl; break; + case BinaryOperator::ShrAssign: Op = BinaryOperator::Shr; break; + case BinaryOperator::AndAssign: Op = BinaryOperator::And; break; + case BinaryOperator::XorAssign: Op = BinaryOperator::Xor; break; + case BinaryOperator::OrAssign: Op = BinaryOperator::Or; break; + } + + // Perform a load (the LHS). This performs the checks for + // null dereferences, and so on. + ExplodedNodeSet Tmp4; + SVal location = state->getSVal(LHS); + EvalLoad(Tmp4, LHS, *I2, state, location); + + for (ExplodedNodeSet::iterator I4=Tmp4.begin(), E4=Tmp4.end(); I4!=E4; + ++I4) { + state = GetState(*I4); + SVal V = state->getSVal(LHS); + + // Get the computation type. + QualType CTy = + cast(B)->getComputationResultType(); + CTy = getContext().getCanonicalType(CTy); + + QualType CLHSTy = + cast(B)->getComputationLHSType(); + CLHSTy = getContext().getCanonicalType(CLHSTy); + + QualType LTy = getContext().getCanonicalType(LHS->getType()); + QualType RTy = getContext().getCanonicalType(RHS->getType()); + + // Promote LHS. + V = SVator.EvalCast(V, CLHSTy, LTy); + + // Compute the result of the operation. + SVal Result = SVator.EvalCast(EvalBinOp(state, Op, V, RightV, CTy), + B->getType(), CTy); + + // EXPERIMENTAL: "Conjured" symbols. + // FIXME: Handle structs. + + SVal LHSVal; + + if ((Result.isUnknown() || + !getConstraintManager().canReasonAbout(Result)) + && (Loc::IsLocType(CTy) + || (CTy->isScalarType() && CTy->isIntegerType()))) { + + unsigned Count = Builder->getCurrentBlockCount(); + + // The symbolic value is actually for the type of the left-hand side + // expression, not the computation type, as this is the value the + // LValue on the LHS will bind to. + LHSVal = ValMgr.getConjuredSymbolVal(NULL, B->getRHS(), LTy, Count); + + // However, we need to convert the symbol to the computation type. + Result = SVator.EvalCast(LHSVal, CTy, LTy); + } + else { + // The left-hand side may bind to a different value then the + // computation type. + LHSVal = SVator.EvalCast(Result, LTy, CTy); + } + + EvalStore(Tmp3, B, LHS, *I4, state->BindExpr(B, Result), + location, LHSVal); + } + } + } + + CheckerVisit(B, Dst, Tmp3, false); +} + +void GRExprEngine::CreateCXXTemporaryObject(Expr *Ex, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { + const GRState *state = GetState(*I); + + // Bind the temporary object to the value of the expression. Then bind + // the expression to the location of the object. + SVal V = state->getSVal(Ex); + + const MemRegion *R = + ValMgr.getRegionManager().getCXXObjectRegion(Ex, + Pred->getLocationContext()); + + state = state->bindLoc(loc::MemRegionVal(R), V); + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, loc::MemRegionVal(R))); + } +} + +//===----------------------------------------------------------------------===// +// Checker registration/lookup. +//===----------------------------------------------------------------------===// + +Checker *GRExprEngine::lookupChecker(void *tag) const { + CheckerMap::const_iterator I = CheckerM.find(tag); + return (I == CheckerM.end()) ? NULL : Checkers[I->second].second; +} + +//===----------------------------------------------------------------------===// +// Visualization. +//===----------------------------------------------------------------------===// + +#ifndef NDEBUG +static GRExprEngine* GraphPrintCheckerState; +static SourceManager* GraphPrintSourceManager; + +namespace llvm { +template<> +struct DOTGraphTraits : + public DefaultDOTGraphTraits { + + DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} + + // FIXME: Since we do not cache error nodes in GRExprEngine now, this does not + // work. + static std::string getNodeAttributes(const ExplodedNode* N, void*) { + +#if 0 + // FIXME: Replace with a general scheme to tell if the node is + // an error node. + if (GraphPrintCheckerState->isImplicitNullDeref(N) || + GraphPrintCheckerState->isExplicitNullDeref(N) || + GraphPrintCheckerState->isUndefDeref(N) || + GraphPrintCheckerState->isUndefStore(N) || + GraphPrintCheckerState->isUndefControlFlow(N) || + GraphPrintCheckerState->isUndefResult(N) || + GraphPrintCheckerState->isBadCall(N) || + GraphPrintCheckerState->isUndefArg(N)) + return "color=\"red\",style=\"filled\""; + + if (GraphPrintCheckerState->isNoReturnCall(N)) + return "color=\"blue\",style=\"filled\""; +#endif + return ""; + } + + static std::string getNodeLabel(const ExplodedNode* N, void*){ + + std::string sbuf; + llvm::raw_string_ostream Out(sbuf); + + // Program Location. + ProgramPoint Loc = N->getLocation(); + + switch (Loc.getKind()) { + case ProgramPoint::BlockEntranceKind: + Out << "Block Entrance: B" + << cast(Loc).getBlock()->getBlockID(); + break; + + case ProgramPoint::BlockExitKind: + assert (false); + break; + + default: { + if (StmtPoint *L = dyn_cast(&Loc)) { + const Stmt* S = L->getStmt(); + SourceLocation SLoc = S->getLocStart(); + + Out << S->getStmtClassName() << ' ' << (void*) S << ' '; + LangOptions LO; // FIXME. + S->printPretty(Out, 0, PrintingPolicy(LO)); + + if (SLoc.isFileID()) { + Out << "\\lline=" + << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) + << " col=" + << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc) + << "\\l"; + } + + if (isa(Loc)) + Out << "\\lPreStmt\\l;"; + else if (isa(Loc)) + Out << "\\lPostLoad\\l;"; + else if (isa(Loc)) + Out << "\\lPostStore\\l"; + else if (isa(Loc)) + Out << "\\lPostLValue\\l"; + +#if 0 + // FIXME: Replace with a general scheme to determine + // the name of the check. + if (GraphPrintCheckerState->isImplicitNullDeref(N)) + Out << "\\|Implicit-Null Dereference.\\l"; + else if (GraphPrintCheckerState->isExplicitNullDeref(N)) + Out << "\\|Explicit-Null Dereference.\\l"; + else if (GraphPrintCheckerState->isUndefDeref(N)) + Out << "\\|Dereference of undefialied value.\\l"; + else if (GraphPrintCheckerState->isUndefStore(N)) + Out << "\\|Store to Undefined Loc."; + else if (GraphPrintCheckerState->isUndefResult(N)) + Out << "\\|Result of operation is undefined."; + else if (GraphPrintCheckerState->isNoReturnCall(N)) + Out << "\\|Call to function marked \"noreturn\"."; + else if (GraphPrintCheckerState->isBadCall(N)) + Out << "\\|Call to NULL/Undefined."; + else if (GraphPrintCheckerState->isUndefArg(N)) + Out << "\\|Argument in call is undefined"; +#endif + + break; + } + + const BlockEdge& E = cast(Loc); + Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" + << E.getDst()->getBlockID() << ')'; + + if (Stmt* T = E.getSrc()->getTerminator()) { + + SourceLocation SLoc = T->getLocStart(); + + Out << "\\|Terminator: "; + LangOptions LO; // FIXME. + E.getSrc()->printTerminator(Out, LO); + + if (SLoc.isFileID()) { + Out << "\\lline=" + << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) + << " col=" + << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc); + } + + if (isa(T)) { + Stmt* Label = E.getDst()->getLabel(); + + if (Label) { + if (CaseStmt* C = dyn_cast(Label)) { + Out << "\\lcase "; + LangOptions LO; // FIXME. + C->getLHS()->printPretty(Out, 0, PrintingPolicy(LO)); + + if (Stmt* RHS = C->getRHS()) { + Out << " .. "; + RHS->printPretty(Out, 0, PrintingPolicy(LO)); + } + + Out << ":"; + } + else { + assert (isa(Label)); + Out << "\\ldefault:"; + } + } + else + Out << "\\l(implicit) default:"; + } + else if (isa(T)) { + // FIXME + } + else { + Out << "\\lCondition: "; + if (*E.getSrc()->succ_begin() == E.getDst()) + Out << "true"; + else + Out << "false"; + } + + Out << "\\l"; + } + +#if 0 + // FIXME: Replace with a general scheme to determine + // the name of the check. + if (GraphPrintCheckerState->isUndefControlFlow(N)) { + Out << "\\|Control-flow based on\\lUndefined value.\\l"; + } +#endif + } + } + + Out << "\\|StateID: " << (void*) N->getState() << "\\|"; + + const GRState *state = N->getState(); + state->printDOT(Out); + + Out << "\\l"; + return Out.str(); + } +}; +} // end llvm namespace +#endif + +#ifndef NDEBUG +template +ExplodedNode* GetGraphNode(ITERATOR I) { return *I; } + +template <> ExplodedNode* +GetGraphNode::iterator> + (llvm::DenseMap::iterator I) { + return I->first; +} +#endif + +void GRExprEngine::ViewGraph(bool trim) { +#ifndef NDEBUG + if (trim) { + std::vector Src; + + // Flush any outstanding reports to make sure we cover all the nodes. + // This does not cause them to get displayed. + for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) + const_cast(*I)->FlushReports(BR); + + // Iterate through the reports and get their nodes. + for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) { + for (BugType::const_iterator I2=(*I)->begin(), E2=(*I)->end(); + I2!=E2; ++I2) { + const BugReportEquivClass& EQ = *I2; + const BugReport &R = **EQ.begin(); + ExplodedNode *N = const_cast(R.getEndNode()); + if (N) Src.push_back(N); + } + } + + ViewGraph(&Src[0], &Src[0]+Src.size()); + } + else { + GraphPrintCheckerState = this; + GraphPrintSourceManager = &getContext().getSourceManager(); + + llvm::ViewGraph(*G.roots_begin(), "GRExprEngine"); + + GraphPrintCheckerState = NULL; + GraphPrintSourceManager = NULL; + } +#endif +} + +void GRExprEngine::ViewGraph(ExplodedNode** Beg, ExplodedNode** End) { +#ifndef NDEBUG + GraphPrintCheckerState = this; + GraphPrintSourceManager = &getContext().getSourceManager(); + + std::auto_ptr TrimmedG(G.Trim(Beg, End).first); + + if (!TrimmedG.get()) + llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; + else + llvm::ViewGraph(*TrimmedG->roots_begin(), "TrimmedGRExprEngine"); + + GraphPrintCheckerState = NULL; + GraphPrintSourceManager = NULL; +#endif +} diff --git a/lib/Checker/GRExprEngineExperimentalChecks.cpp b/lib/Checker/GRExprEngineExperimentalChecks.cpp new file mode 100644 index 000000000000..89b4e4b6392f --- /dev/null +++ b/lib/Checker/GRExprEngineExperimentalChecks.cpp @@ -0,0 +1,40 @@ +//=-- GRExprEngineExperimentalChecks.h ------------------------------*- C++ -*-= +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines functions to instantiate and register experimental +// checks in GRExprEngine. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "GRExprEngineExperimentalChecks.h" +#include "clang/Checker/Checkers/LocalCheckers.h" + +using namespace clang; + +void clang::RegisterExperimentalChecks(GRExprEngine &Eng) { + // These are checks that never belong as internal checks + // within GRExprEngine. + RegisterPthreadLockChecker(Eng); + RegisterMallocChecker(Eng); +} + +void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) { + // These are internal checks that should eventually migrate to + // RegisterInternalChecks() once they have been further tested. + + // Note that this must be registered after ReturnStackAddresEngsChecker. + RegisterReturnPointerRangeChecker(Eng); + + RegisterFixedAddressChecker(Eng); + RegisterPointerSubChecker(Eng); + RegisterPointerArithChecker(Eng); + RegisterCastToStructChecker(Eng); + RegisterArrayBoundChecker(Eng); +} diff --git a/lib/Checker/GRExprEngineExperimentalChecks.h b/lib/Checker/GRExprEngineExperimentalChecks.h new file mode 100644 index 000000000000..9a9da32e556e --- /dev/null +++ b/lib/Checker/GRExprEngineExperimentalChecks.h @@ -0,0 +1,26 @@ +//=-- GRExprEngineExperimentalChecks.h ------------------------------*- C++ -*-= +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines functions to instantiate and register experimental +// checks in GRExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GREXPRENGINE_EXPERIMENTAL_CHECKS +#define LLVM_CLANG_GREXPRENGINE_EXPERIMENTAL_CHECKS + +namespace clang { + +class GRExprEngine; + +void RegisterPthreadLockChecker(GRExprEngine &Eng); +void RegisterMallocChecker(GRExprEngine &Eng); + +} // end clang namespace +#endif diff --git a/lib/Checker/GRExprEngineInternalChecks.h b/lib/Checker/GRExprEngineInternalChecks.h new file mode 100644 index 000000000000..64a930d504cf --- /dev/null +++ b/lib/Checker/GRExprEngineInternalChecks.h @@ -0,0 +1,45 @@ +//=-- GRExprEngineInternalChecks.h- Builtin GRExprEngine Checks -----*- C++ -*-= +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines functions to instantiate and register the "built-in" +// checks in GRExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GREXPRENGINE_INTERNAL_CHECKS +#define LLVM_CLANG_GREXPRENGINE_INTERNAL_CHECKS + +namespace clang { + +class GRExprEngine; + +void RegisterAdjustedReturnValueChecker(GRExprEngine &Eng); +void RegisterAttrNonNullChecker(GRExprEngine &Eng); +void RegisterDereferenceChecker(GRExprEngine &Eng); +void RegisterDivZeroChecker(GRExprEngine &Eng); +void RegisterReturnPointerRangeChecker(GRExprEngine &Eng); +void RegisterReturnStackAddressChecker(GRExprEngine &Eng); +void RegisterReturnUndefChecker(GRExprEngine &Eng); +void RegisterVLASizeChecker(GRExprEngine &Eng); +void RegisterPointerSubChecker(GRExprEngine &Eng); +void RegisterPointerArithChecker(GRExprEngine &Eng); +void RegisterFixedAddressChecker(GRExprEngine &Eng); +void RegisterCastToStructChecker(GRExprEngine &Eng); +void RegisterCallAndMessageChecker(GRExprEngine &Eng); +void RegisterArrayBoundChecker(GRExprEngine &Eng); +void RegisterUndefinedArraySubscriptChecker(GRExprEngine &Eng); +void RegisterUndefinedAssignmentChecker(GRExprEngine &Eng); +void RegisterUndefBranchChecker(GRExprEngine &Eng); +void RegisterUndefCapturedBlockVarChecker(GRExprEngine &Eng); +void RegisterUndefResultChecker(GRExprEngine &Eng); +void RegisterNoReturnFunctionChecker(GRExprEngine &Eng); +void RegisterBuiltinFunctionChecker(GRExprEngine &Eng); +void RegisterOSAtomicChecker(GRExprEngine &Eng); +} // end clang namespace +#endif diff --git a/lib/Checker/GRState.cpp b/lib/Checker/GRState.cpp new file mode 100644 index 000000000000..592f930316e1 --- /dev/null +++ b/lib/Checker/GRState.cpp @@ -0,0 +1,360 @@ +//= GRState.cpp - Path-Sensitive "State" for tracking values -----*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements GRState and GRStateManager. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/GRStateTrait.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Checker/PathSensitive/GRTransferFuncs.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +// Give the vtable for ConstraintManager somewhere to live. +// FIXME: Move this elsewhere. +ConstraintManager::~ConstraintManager() {} + +GRStateManager::~GRStateManager() { + for (std::vector::iterator I=Printers.begin(), + E=Printers.end(); I!=E; ++I) + delete *I; + + for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end(); + I!=E; ++I) + I->second.second(I->second.first); +} + +const GRState* +GRStateManager::RemoveDeadBindings(const GRState* state, Stmt* Loc, + SymbolReaper& SymReaper) { + + // This code essentially performs a "mark-and-sweep" of the VariableBindings. + // The roots are any Block-level exprs and Decls that our liveness algorithm + // tells us are live. We then see what Decls they may reference, and keep + // those around. This code more than likely can be made faster, and the + // frequency of which this method is called should be experimented with + // for optimum performance. + llvm::SmallVector RegionRoots; + GRState NewState = *state; + + NewState.Env = EnvMgr.RemoveDeadBindings(NewState.Env, Loc, SymReaper, + state, RegionRoots); + + // Clean up the store. + NewState.St = StoreMgr->RemoveDeadBindings(NewState.St, Loc, SymReaper, + RegionRoots); + + return ConstraintMgr->RemoveDeadBindings(getPersistentState(NewState), + SymReaper); +} + +const GRState *GRState::unbindLoc(Loc LV) const { + Store OldStore = getStore(); + Store NewStore = getStateManager().StoreMgr->Remove(OldStore, LV); + + if (NewStore == OldStore) + return this; + + GRState NewSt = *this; + NewSt.St = NewStore; + return getStateManager().getPersistentState(NewSt); +} + +SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const { + // We only want to do fetches from regions that we can actually bind + // values. For example, SymbolicRegions of type 'id<...>' cannot + // have direct bindings (but their can be bindings on their subregions). + if (!R->isBoundable()) + return UnknownVal(); + + if (const TypedRegion *TR = dyn_cast(R)) { + QualType T = TR->getValueType(getStateManager().getContext()); + if (Loc::IsLocType(T) || T->isIntegerType()) + return getSVal(R); + } + + return UnknownVal(); +} + + +const GRState *GRState::BindExpr(const Stmt* Ex, SVal V, bool Invalidate) const{ + Environment NewEnv = getStateManager().EnvMgr.BindExpr(Env, Ex, V, + Invalidate); + if (NewEnv == Env) + return this; + + GRState NewSt = *this; + NewSt.Env = NewEnv; + return getStateManager().getPersistentState(NewSt); +} + +const GRState* GRStateManager::getInitialState(const LocationContext *InitLoc) { + GRState State(this, + EnvMgr.getInitialEnvironment(InitLoc->getAnalysisContext()), + StoreMgr->getInitialStore(InitLoc), + GDMFactory.GetEmptyMap()); + + return getPersistentState(State); +} + +const GRState* GRStateManager::getPersistentState(GRState& State) { + + llvm::FoldingSetNodeID ID; + State.Profile(ID); + void* InsertPos; + + if (GRState* I = StateSet.FindNodeOrInsertPos(ID, InsertPos)) + return I; + + GRState* I = (GRState*) Alloc.Allocate(); + new (I) GRState(State); + StateSet.InsertNode(I, InsertPos); + return I; +} + +const GRState* GRState::makeWithStore(Store store) const { + GRState NewSt = *this; + NewSt.St = store; + return getStateManager().getPersistentState(NewSt); +} + +//===----------------------------------------------------------------------===// +// State pretty-printing. +//===----------------------------------------------------------------------===// + +void GRState::print(llvm::raw_ostream& Out, const char* nl, + const char* sep) const { + // Print the store. + GRStateManager &Mgr = getStateManager(); + Mgr.getStoreManager().print(getStore(), Out, nl, sep); + + CFG &C = *getAnalysisContext().getCFG(); + + // Print Subexpression bindings. + bool isFirst = true; + + for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { + if (C.isBlkExpr(I.getKey())) + continue; + + if (isFirst) { + Out << nl << nl << "Sub-Expressions:" << nl; + isFirst = false; + } + else { Out << nl; } + + Out << " (" << (void*) I.getKey() << ") "; + LangOptions LO; // FIXME. + I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); + Out << " : " << I.getData(); + } + + // Print block-expression bindings. + isFirst = true; + + for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { + if (!C.isBlkExpr(I.getKey())) + continue; + + if (isFirst) { + Out << nl << nl << "Block-level Expressions:" << nl; + isFirst = false; + } + else { Out << nl; } + + Out << " (" << (void*) I.getKey() << ") "; + LangOptions LO; // FIXME. + I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); + Out << " : " << I.getData(); + } + + Mgr.getConstraintManager().print(this, Out, nl, sep); + + // Print checker-specific data. + for (std::vector::iterator I = Mgr.Printers.begin(), + E = Mgr.Printers.end(); I != E; ++I) { + (*I)->Print(Out, this, nl, sep); + } +} + +void GRState::printDOT(llvm::raw_ostream& Out) const { + print(Out, "\\l", "\\|"); +} + +void GRState::printStdErr() const { + print(llvm::errs()); +} + +//===----------------------------------------------------------------------===// +// Generic Data Map. +//===----------------------------------------------------------------------===// + +void* const* GRState::FindGDM(void* K) const { + return GDM.lookup(K); +} + +void* +GRStateManager::FindGDMContext(void* K, + void* (*CreateContext)(llvm::BumpPtrAllocator&), + void (*DeleteContext)(void*)) { + + std::pair& p = GDMContexts[K]; + if (!p.first) { + p.first = CreateContext(Alloc); + p.second = DeleteContext; + } + + return p.first; +} + +const GRState* GRStateManager::addGDM(const GRState* St, void* Key, void* Data){ + GRState::GenericDataMap M1 = St->getGDM(); + GRState::GenericDataMap M2 = GDMFactory.Add(M1, Key, Data); + + if (M1 == M2) + return St; + + GRState NewSt = *St; + NewSt.GDM = M2; + return getPersistentState(NewSt); +} + +//===----------------------------------------------------------------------===// +// Utility. +//===----------------------------------------------------------------------===// + +namespace { +class ScanReachableSymbols : public SubRegionMap::Visitor { + typedef llvm::DenseSet VisitedRegionsTy; + + VisitedRegionsTy visited; + const GRState *state; + SymbolVisitor &visitor; + llvm::OwningPtr SRM; +public: + + ScanReachableSymbols(const GRState *st, SymbolVisitor& v) + : state(st), visitor(v) {} + + bool scan(nonloc::CompoundVal val); + bool scan(SVal val); + bool scan(const MemRegion *R); + + // From SubRegionMap::Visitor. + bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) { + return scan(SubRegion); + } +}; +} + +bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { + for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I) + if (!scan(*I)) + return false; + + return true; +} + +bool ScanReachableSymbols::scan(SVal val) { + if (loc::MemRegionVal *X = dyn_cast(&val)) + return scan(X->getRegion()); + + if (nonloc::LocAsInteger *X = dyn_cast(&val)) + return scan(X->getLoc()); + + if (SymbolRef Sym = val.getAsSymbol()) + return visitor.VisitSymbol(Sym); + + if (nonloc::CompoundVal *X = dyn_cast(&val)) + return scan(*X); + + return true; +} + +bool ScanReachableSymbols::scan(const MemRegion *R) { + if (isa(R) || visited.count(R)) + return true; + + visited.insert(R); + + // If this is a symbolic region, visit the symbol for the region. + if (const SymbolicRegion *SR = dyn_cast(R)) + if (!visitor.VisitSymbol(SR->getSymbol())) + return false; + + // If this is a subregion, also visit the parent regions. + if (const SubRegion *SR = dyn_cast(R)) + if (!scan(SR->getSuperRegion())) + return false; + + // Now look at the binding to this region (if any). + if (!scan(state->getSValAsScalarOrLoc(R))) + return false; + + // Now look at the subregions. + if (!SRM.get()) + SRM.reset(state->getStateManager().getStoreManager(). + getSubRegionMap(state->getStore())); + + return SRM->iterSubRegions(R, *this); +} + +bool GRState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { + ScanReachableSymbols S(this, visitor); + return S.scan(val); +} + +bool GRState::scanReachableSymbols(const SVal *I, const SVal *E, + SymbolVisitor &visitor) const { + ScanReachableSymbols S(this, visitor); + for ( ; I != E; ++I) { + if (!S.scan(*I)) + return false; + } + return true; +} + +bool GRState::scanReachableSymbols(const MemRegion * const *I, + const MemRegion * const *E, + SymbolVisitor &visitor) const { + ScanReachableSymbols S(this, visitor); + for ( ; I != E; ++I) { + if (!S.scan(*I)) + return false; + } + return true; +} + +//===----------------------------------------------------------------------===// +// Queries. +//===----------------------------------------------------------------------===// + +bool GRStateManager::isEqual(const GRState* state, const Expr* Ex, + const llvm::APSInt& Y) { + + SVal V = state->getSVal(Ex); + + if (loc::ConcreteInt* X = dyn_cast(&V)) + return X->getValue() == Y; + + if (nonloc::ConcreteInt* X = dyn_cast(&V)) + return X->getValue() == Y; + + if (SymbolRef Sym = V.getAsSymbol()) + return ConstraintMgr->isEqual(state, Sym, Y); + + return false; +} + +bool GRStateManager::isEqual(const GRState* state, const Expr* Ex, uint64_t x) { + return isEqual(state, Ex, getBasicVals().getValue(x, Ex->getType())); +} diff --git a/lib/Checker/LLVMConventionsChecker.cpp b/lib/Checker/LLVMConventionsChecker.cpp new file mode 100644 index 000000000000..14f0fc1280df --- /dev/null +++ b/lib/Checker/LLVMConventionsChecker.cpp @@ -0,0 +1,335 @@ +//=== LLVMConventionsChecker.cpp - Check LLVM codebase conventions ---*- C++ -*- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines LLVMConventionsChecker, a bunch of small little checks +// for checking specific coding conventions in the LLVM/Clang codebase. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Checker/Checkers/LocalCheckers.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include +#include "llvm/ADT/StringRef.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Generic type checking routines. +//===----------------------------------------------------------------------===// + +static bool IsLLVMStringRef(QualType T) { + const RecordType *RT = T->getAs(); + if (!RT) + return false; + + return llvm::StringRef(QualType(RT, 0).getAsString()) == + "class llvm::StringRef"; +} + +static bool InStdNamespace(const Decl *D) { + const DeclContext *DC = D->getDeclContext(); + const NamespaceDecl *ND = dyn_cast(D->getDeclContext()); + if (!ND) + return false; + const IdentifierInfo *II = ND->getIdentifier(); + if (!II || II->getName() != "std") + return false; + DC = ND->getDeclContext(); + return isa(DC); +} + +static bool IsStdString(QualType T) { + if (const QualifiedNameType *QT = T->getAs()) + T = QT->getNamedType(); + + const TypedefType *TT = T->getAs(); + if (!TT) + return false; + + const TypedefDecl *TD = TT->getDecl(); + + if (!InStdNamespace(TD)) + return false; + + return TD->getName() == "string"; +} + +static bool InClangNamespace(const Decl *D) { + const DeclContext *DC = D->getDeclContext(); + const NamespaceDecl *ND = dyn_cast(D->getDeclContext()); + if (!ND) + return false; + const IdentifierInfo *II = ND->getIdentifier(); + if (!II || II->getName() != "clang") + return false; + DC = ND->getDeclContext(); + return isa(DC); +} + +static bool InLLVMNamespace(const Decl *D) { + const DeclContext *DC = D->getDeclContext(); + const NamespaceDecl *ND = dyn_cast(D->getDeclContext()); + if (!ND) + return false; + const IdentifierInfo *II = ND->getIdentifier(); + if (!II || II->getName() != "llvm") + return false; + DC = ND->getDeclContext(); + return isa(DC); +} + +static bool IsClangType(const RecordDecl *RD) { + return RD->getName() == "Type" && InClangNamespace(RD); +} + +static bool IsClangDecl(const RecordDecl *RD) { + return RD->getName() == "Decl" && InClangNamespace(RD); +} + +static bool IsClangStmt(const RecordDecl *RD) { + return RD->getName() == "Stmt" && InClangNamespace(RD); +} + +static bool isClangAttr(const RecordDecl *RD) { + return RD->getName() == "Attr" && InClangNamespace(RD); +} + +static bool IsStdVector(QualType T) { + const TemplateSpecializationType *TS = T->getAs(); + if (!TS) + return false; + + TemplateName TM = TS->getTemplateName(); + TemplateDecl *TD = TM.getAsTemplateDecl(); + + if (!TD || !InStdNamespace(TD)) + return false; + + return TD->getName() == "vector"; +} + +static bool IsSmallVector(QualType T) { + const TemplateSpecializationType *TS = T->getAs(); + if (!TS) + return false; + + TemplateName TM = TS->getTemplateName(); + TemplateDecl *TD = TM.getAsTemplateDecl(); + + if (!TD || !InLLVMNamespace(TD)) + return false; + + return TD->getName() == "SmallVector"; +} + +//===----------------------------------------------------------------------===// +// CHECK: a llvm::StringRef should not be bound to a temporary std::string whose +// lifetime is shorter than the StringRef's. +//===----------------------------------------------------------------------===// + +namespace { +class StringRefCheckerVisitor : public StmtVisitor { + BugReporter &BR; +public: + StringRefCheckerVisitor(BugReporter &br) : BR(br) {} + void VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ; + I != E; ++I) + if (Stmt *child = *I) + Visit(child); + } + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitDeclStmt(DeclStmt *DS); +private: + void VisitVarDecl(VarDecl *VD); + void CheckStringRefBoundtoTemporaryString(VarDecl *VD); +}; +} // end anonymous namespace + +static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) { + StringRefCheckerVisitor walker(BR); + walker.Visit(D->getBody()); +} + +void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { + VisitChildren(S); + + for (DeclStmt::decl_iterator I = S->decl_begin(), E = S->decl_end();I!=E; ++I) + if (VarDecl *VD = dyn_cast(*I)) + VisitVarDecl(VD); +} + +void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { + Expr *Init = VD->getInit(); + if (!Init) + return; + + // Pattern match for: + // llvm::StringRef x = call() (where call returns std::string) + if (!IsLLVMStringRef(VD->getType())) + return; + CXXExprWithTemporaries *Ex1 = dyn_cast(Init); + if (!Ex1) + return; + CXXConstructExpr *Ex2 = dyn_cast(Ex1->getSubExpr()); + if (!Ex2 || Ex2->getNumArgs() != 1) + return; + ImplicitCastExpr *Ex3 = dyn_cast(Ex2->getArg(0)); + if (!Ex3) + return; + CXXConstructExpr *Ex4 = dyn_cast(Ex3->getSubExpr()); + if (!Ex4 || Ex4->getNumArgs() != 1) + return; + ImplicitCastExpr *Ex5 = dyn_cast(Ex4->getArg(0)); + if (!Ex5) + return; + CXXBindTemporaryExpr *Ex6 = dyn_cast(Ex5->getSubExpr()); + if (!Ex6 || !IsStdString(Ex6->getType())) + return; + + // Okay, badness! Report an error. + const char *desc = "StringRef should not be bound to temporary " + "std::string that it outlives"; + + BR.EmitBasicReport(desc, "LLVM Conventions", desc, + VD->getLocStart(), Init->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// CHECK: Clang AST nodes should not have fields that can allocate +// memory. +//===----------------------------------------------------------------------===// + +static bool AllocatesMemory(QualType T) { + return IsStdVector(T) || IsStdString(T) || IsSmallVector(T); +} + +// This type checking could be sped up via dynamic programming. +static bool IsPartOfAST(const CXXRecordDecl *R) { + if (IsClangStmt(R) || IsClangType(R) || IsClangDecl(R) || isClangAttr(R)) + return true; + + for (CXXRecordDecl::base_class_const_iterator I = R->bases_begin(), + E = R->bases_end(); I!=E; ++I) { + CXXBaseSpecifier BS = *I; + QualType T = BS.getType(); + if (const RecordType *baseT = T->getAs()) { + CXXRecordDecl *baseD = cast(baseT->getDecl()); + if (IsPartOfAST(baseD)) + return true; + } + } + + return false; +} + +namespace { +class ASTFieldVisitor { + llvm::SmallVector FieldChain; + CXXRecordDecl *Root; + BugReporter &BR; +public: + ASTFieldVisitor(CXXRecordDecl *root, BugReporter &br) + : Root(root), BR(br) {} + + void Visit(FieldDecl *D); + void ReportError(QualType T); +}; +} // end anonymous namespace + +static void CheckASTMemory(CXXRecordDecl *R, BugReporter &BR) { + if (!IsPartOfAST(R)) + return; + + for (RecordDecl::field_iterator I = R->field_begin(), E = R->field_end(); + I != E; ++I) { + ASTFieldVisitor walker(R, BR); + walker.Visit(*I); + } +} + +void ASTFieldVisitor::Visit(FieldDecl *D) { + FieldChain.push_back(D); + + QualType T = D->getType(); + + if (AllocatesMemory(T)) + ReportError(T); + + if (const RecordType *RT = T->getAs()) { + const RecordDecl *RD = RT->getDecl()->getDefinition(); + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I) + Visit(*I); + } + + FieldChain.pop_back(); +} + +void ASTFieldVisitor::ReportError(QualType T) { + llvm::SmallString<1024> buf; + llvm::raw_svector_ostream os(buf); + + os << "AST class '" << Root->getName() << "' has a field '" + << FieldChain.front()->getName() << "' that allocates heap memory"; + if (FieldChain.size() > 1) { + os << " via the following chain: "; + bool isFirst = true; + for (llvm::SmallVectorImpl::iterator I=FieldChain.begin(), + E=FieldChain.end(); I!=E; ++I) { + if (!isFirst) + os << '.'; + else + isFirst = false; + os << (*I)->getName(); + } + } + os << " (type " << FieldChain.back()->getType().getAsString() << ")"; + os.flush(); + + // Note that this will fire for every translation unit that uses this + // class. This is suboptimal, but at least scan-build will merge + // duplicate HTML reports. In the future we need a unified way of merging + // duplicate reports across translation units. For C++ classes we cannot + // just report warnings when we see an out-of-line method definition for a + // class, as that heuristic doesn't always work (the complete definition of + // the class may be in the header file, for example). + BR.EmitBasicReport("AST node allocates heap memory", "LLVM Conventions", + os.str(), FieldChain.front()->getLocStart()); +} + +//===----------------------------------------------------------------------===// +// Entry point for all checks. +//===----------------------------------------------------------------------===// + +static void ScanCodeDecls(DeclContext *DC, BugReporter &BR) { + for (DeclContext::decl_iterator I=DC->decls_begin(), E=DC->decls_end(); + I!=E ; ++I) { + + Decl *D = *I; + + if (D->getBody()) + CheckStringRefAssignedTemporary(D, BR); + + if (CXXRecordDecl *R = dyn_cast(D)) + if (R->isDefinition()) + CheckASTMemory(R, BR); + + if (DeclContext *DC_child = dyn_cast(D)) + ScanCodeDecls(DC_child, BR); + } +} + +void clang::CheckLLVMConventions(TranslationUnitDecl &TU, + BugReporter &BR) { + ScanCodeDecls(&TU, BR); +} + diff --git a/lib/Checker/Makefile b/lib/Checker/Makefile new file mode 100644 index 000000000000..673d152270ca --- /dev/null +++ b/lib/Checker/Makefile @@ -0,0 +1,21 @@ +##===- clang/lib/Checker/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements analyses built on top of source-level CFGs. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. +LIBRARYNAME := clangChecker +BUILD_ARCHIVE = 1 + +CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include + +include $(LEVEL)/Makefile.common + diff --git a/lib/Checker/MallocChecker.cpp b/lib/Checker/MallocChecker.cpp new file mode 100644 index 000000000000..4ff98642e1c2 --- /dev/null +++ b/lib/Checker/MallocChecker.cpp @@ -0,0 +1,341 @@ +//=== MallocChecker.cpp - A malloc/free checker -------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines malloc/free checker, which checks for potential memory +// leaks, double free, and use-after-free problems. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineExperimentalChecks.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Checker/PathSensitive/GRStateTrait.h" +#include "clang/Checker/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.h" +using namespace clang; + +namespace { + +class RefState { + enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped } K; + const Stmt *S; + +public: + RefState(Kind k, const Stmt *s) : K(k), S(s) {} + + bool isAllocated() const { return K == AllocateUnchecked; } + bool isReleased() const { return K == Released; } + bool isEscaped() const { return K == Escaped; } + + bool operator==(const RefState &X) const { + return K == X.K && S == X.S; + } + + static RefState getAllocateUnchecked(const Stmt *s) { + return RefState(AllocateUnchecked, s); + } + static RefState getAllocateFailed() { + return RefState(AllocateFailed, 0); + } + static RefState getReleased(const Stmt *s) { return RefState(Released, s); } + static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + ID.AddPointer(S); + } +}; + +class RegionState {}; + +class MallocChecker : public CheckerVisitor { + BuiltinBug *BT_DoubleFree; + BuiltinBug *BT_Leak; + IdentifierInfo *II_malloc, *II_free, *II_realloc; + +public: + MallocChecker() + : BT_DoubleFree(0), BT_Leak(0), II_malloc(0), II_free(0), II_realloc(0) {} + static void *getTag(); + bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); + void EvalDeadSymbols(CheckerContext &C,const Stmt *S,SymbolReaper &SymReaper); + void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); + const GRState *EvalAssume(const GRState *state, SVal Cond, bool Assumption); + +private: + void MallocMem(CheckerContext &C, const CallExpr *CE); + const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, + const Expr *SizeEx, const GRState *state); + void FreeMem(CheckerContext &C, const CallExpr *CE); + const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE, + const GRState *state); + + void ReallocMem(CheckerContext &C, const CallExpr *CE); +}; +} // end anonymous namespace + +typedef llvm::ImmutableMap RegionStateTy; + +namespace clang { + template <> + struct GRStateTrait + : public GRStatePartialTrait > { + static void *GDMIndex() { return MallocChecker::getTag(); } + }; +} + +void clang::RegisterMallocChecker(GRExprEngine &Eng) { + Eng.registerCheck(new MallocChecker()); +} + +void *MallocChecker::getTag() { + static int x; + return &x; +} + +bool MallocChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + ASTContext &Ctx = C.getASTContext(); + if (!II_malloc) + II_malloc = &Ctx.Idents.get("malloc"); + if (!II_free) + II_free = &Ctx.Idents.get("free"); + if (!II_realloc) + II_realloc = &Ctx.Idents.get("realloc"); + + if (FD->getIdentifier() == II_malloc) { + MallocMem(C, CE); + return true; + } + + if (FD->getIdentifier() == II_free) { + FreeMem(C, CE); + return true; + } + + if (FD->getIdentifier() == II_realloc) { + ReallocMem(C, CE); + return true; + } + + return false; +} + +void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = MallocMemAux(C, CE, CE->getArg(0), C.getState()); + C.addTransition(state); +} + +const GRState *MallocChecker::MallocMemAux(CheckerContext &C, + const CallExpr *CE, + const Expr *SizeEx, + const GRState *state) { + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + ValueManager &ValMgr = C.getValueManager(); + + SVal RetVal = ValMgr.getConjuredSymbolVal(NULL, CE, CE->getType(), Count); + + SVal Size = state->getSVal(SizeEx); + + state = C.getEngine().getStoreManager().setExtent(state, RetVal.getAsRegion(), + Size); + + state = state->BindExpr(CE, RetVal); + + SymbolRef Sym = RetVal.getAsLocSymbol(); + assert(Sym); + // Set the symbol's state to Allocated. + return state->set(Sym, RefState::getAllocateUnchecked(CE)); +} + +void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = FreeMemAux(C, CE, C.getState()); + + if (state) + C.addTransition(state); +} + +const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, + const GRState *state) { + SVal ArgVal = state->getSVal(CE->getArg(0)); + + // If ptr is NULL, no operation is preformed. + if (ArgVal.isZeroConstant()) + return state; + + SymbolRef Sym = ArgVal.getAsLocSymbol(); + assert(Sym); + + const RefState *RS = state->get(Sym); + + // If the symbol has not been tracked, return. This is possible when free() is + // called on a pointer that does not get its pointee directly from malloc(). + // Full support of this requires inter-procedural analysis. + if (!RS) + return state; + + // Check double free. + if (RS->isReleased()) { + ExplodedNode *N = C.GenerateSink(); + if (N) { + if (!BT_DoubleFree) + BT_DoubleFree = new BuiltinBug("Double free", + "Try to free a memory block that has been released"); + // FIXME: should find where it's freed last time. + BugReport *R = new BugReport(*BT_DoubleFree, + BT_DoubleFree->getDescription(), N); + C.EmitReport(R); + } + return NULL; + } + + // Normal free. + return state->set(Sym, RefState::getReleased(CE)); +} + +void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Arg0 = CE->getArg(0); + DefinedOrUnknownSVal Arg0Val=cast(state->getSVal(Arg0)); + + ValueManager &ValMgr = C.getValueManager(); + SValuator &SVator = C.getSValuator(); + + DefinedOrUnknownSVal PtrEQ = SVator.EvalEQ(state, Arg0Val, ValMgr.makeNull()); + + // If the ptr is NULL, the call is equivalent to malloc(size). + if (const GRState *stateEqual = state->Assume(PtrEQ, true)) { + // Hack: set the NULL symbolic region to released to suppress false warning. + // In the future we should add more states for allocated regions, e.g., + // CheckedNull, CheckedNonNull. + + SymbolRef Sym = Arg0Val.getAsLocSymbol(); + if (Sym) + stateEqual = stateEqual->set(Sym, RefState::getReleased(CE)); + + const GRState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1), stateEqual); + C.addTransition(stateMalloc); + } + + if (const GRState *stateNotEqual = state->Assume(PtrEQ, false)) { + const Expr *Arg1 = CE->getArg(1); + DefinedOrUnknownSVal Arg1Val = + cast(stateNotEqual->getSVal(Arg1)); + DefinedOrUnknownSVal SizeZero = SVator.EvalEQ(stateNotEqual, Arg1Val, + ValMgr.makeIntValWithPtrWidth(0, false)); + + if (const GRState *stateSizeZero = stateNotEqual->Assume(SizeZero, true)) { + const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero); + if (stateFree) + C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true)); + } + + if (const GRState *stateSizeNotZero=stateNotEqual->Assume(SizeZero,false)) { + const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero); + if (stateFree) { + // FIXME: We should copy the content of the original buffer. + const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1), + stateFree); + C.addTransition(stateRealloc); + } + } + } +} + +void MallocChecker::EvalDeadSymbols(CheckerContext &C, const Stmt *S, + SymbolReaper &SymReaper) { + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I != E; ++I) { + SymbolRef Sym = *I; + const GRState *state = C.getState(); + const RefState *RS = state->get(Sym); + if (!RS) + return; + + if (RS->isAllocated()) { + ExplodedNode *N = C.GenerateSink(); + if (N) { + if (!BT_Leak) + BT_Leak = new BuiltinBug("Memory leak", + "Allocated memory never released. Potential memory leak."); + // FIXME: where it is allocated. + BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); + C.EmitReport(R); + } + } + } +} + +void MallocChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag, + GRExprEngine &Eng) { + SaveAndRestore OldHasGen(B.HasGeneratedNode); + const GRState *state = B.getState(); + typedef llvm::ImmutableMap SymMap; + SymMap M = state->get(); + + for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { + RefState RS = I->second; + if (RS.isAllocated()) { + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (N) { + if (!BT_Leak) + BT_Leak = new BuiltinBug("Memory leak", + "Allocated memory never released. Potential memory leak."); + BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); + Eng.getBugReporter().EmitReport(R); + } + } + } +} + +void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { + const Expr *RetE = S->getRetValue(); + if (!RetE) + return; + + const GRState *state = C.getState(); + + SymbolRef Sym = state->getSVal(RetE).getAsSymbol(); + + if (!Sym) + return; + + const RefState *RS = state->get(Sym); + if (!RS) + return; + + // FIXME: check other cases. + if (RS->isAllocated()) + state = state->set(Sym, RefState::getEscaped(S)); + + C.addTransition(state); +} + +const GRState *MallocChecker::EvalAssume(const GRState *state, SVal Cond, + bool Assumption) { + // If a symblic region is assumed to NULL, set its state to AllocateFailed. + // FIXME: should also check symbols assumed to non-null. + + RegionStateTy RS = state->get(); + + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + if (state->getSymVal(I.getKey())) + state = state->set(I.getKey(),RefState::getAllocateFailed()); + } + + return state; +} diff --git a/lib/Checker/ManagerRegistry.cpp b/lib/Checker/ManagerRegistry.cpp new file mode 100644 index 000000000000..d11a997cc0f6 --- /dev/null +++ b/lib/Checker/ManagerRegistry.cpp @@ -0,0 +1,20 @@ +//===- ManagerRegistry.cpp - Pluggble Analyzer module creators --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the pluggable analyzer module creators. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/ManagerRegistry.h" + +using namespace clang; + +StoreManagerCreator ManagerRegistry::StoreMgrCreator = 0; + +ConstraintManagerCreator ManagerRegistry::ConstraintMgrCreator = 0; diff --git a/lib/Checker/MemRegion.cpp b/lib/Checker/MemRegion.cpp new file mode 100644 index 000000000000..194015a11b11 --- /dev/null +++ b/lib/Checker/MemRegion.cpp @@ -0,0 +1,799 @@ +//== MemRegion.cpp - Abstract memory regions for static analysis --*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines MemRegion and its subclasses. MemRegion defines a +// partially-typed abstraction of memory useful for path-sensitive dataflow +// analyses. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Checker/PathSensitive/MemRegion.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/StmtVisitor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// MemRegion Construction. +//===----------------------------------------------------------------------===// + +template struct MemRegionManagerTrait; + +template +RegionTy* MemRegionManager::getRegion(const A1 a1) { + + const typename MemRegionManagerTrait::SuperRegionTy *superRegion = + MemRegionManagerTrait::getSuperRegion(*this, a1); + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate(); + new (R) RegionTy(a1, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template +RegionTy* MemRegionManager::getSubRegion(const A1 a1, + const MemRegion *superRegion) { + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate(); + new (R) RegionTy(a1, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template +RegionTy* MemRegionManager::getRegion(const A1 a1, const A2 a2) { + + const typename MemRegionManagerTrait::SuperRegionTy *superRegion = + MemRegionManagerTrait::getSuperRegion(*this, a1, a2); + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, a2, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate(); + new (R) RegionTy(a1, a2, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template +RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, + const MemRegion *superRegion) { + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, a2, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate(); + new (R) RegionTy(a1, a2, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template +RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3, + const MemRegion *superRegion) { + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, a2, a3, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate(); + new (R) RegionTy(a1, a2, a3, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +//===----------------------------------------------------------------------===// +// Object destruction. +//===----------------------------------------------------------------------===// + +MemRegion::~MemRegion() {} + +MemRegionManager::~MemRegionManager() { + // All regions and their data are BumpPtrAllocated. No need to call + // their destructors. +} + +//===----------------------------------------------------------------------===// +// Basic methods. +//===----------------------------------------------------------------------===// + +bool SubRegion::isSubRegionOf(const MemRegion* R) const { + const MemRegion* r = getSuperRegion(); + while (r != 0) { + if (r == R) + return true; + if (const SubRegion* sr = dyn_cast(r)) + r = sr->getSuperRegion(); + else + break; + } + return false; +} + +MemRegionManager* SubRegion::getMemRegionManager() const { + const SubRegion* r = this; + do { + const MemRegion *superRegion = r->getSuperRegion(); + if (const SubRegion *sr = dyn_cast(superRegion)) { + r = sr; + continue; + } + return superRegion->getMemRegionManager(); + } while (1); +} + +const StackFrameContext *VarRegion::getStackFrame() const { + const StackSpaceRegion *SSR = dyn_cast(getMemorySpace()); + return SSR ? SSR->getStackFrame() : NULL; +} + +//===----------------------------------------------------------------------===// +// FoldingSet profiling. +//===----------------------------------------------------------------------===// + +void MemSpaceRegion::Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddInteger((unsigned)getKind()); +} + +void StackSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger((unsigned)getKind()); + ID.AddPointer(getStackFrame()); +} + +void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const StringLiteral* Str, + const MemRegion* superRegion) { + ID.AddInteger((unsigned) StringRegionKind); + ID.AddPointer(Str); + ID.AddPointer(superRegion); +} + +void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const Expr* Ex, unsigned cnt, + const MemRegion *) { + ID.AddInteger((unsigned) AllocaRegionKind); + ID.AddPointer(Ex); + ID.AddInteger(cnt); +} + +void AllocaRegion::Profile(llvm::FoldingSetNodeID& ID) const { + ProfileRegion(ID, Ex, Cnt, superRegion); +} + +void CompoundLiteralRegion::Profile(llvm::FoldingSetNodeID& ID) const { + CompoundLiteralRegion::ProfileRegion(ID, CL, superRegion); +} + +void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const CompoundLiteralExpr* CL, + const MemRegion* superRegion) { + ID.AddInteger((unsigned) CompoundLiteralRegionKind); + ID.AddPointer(CL); + ID.AddPointer(superRegion); +} + +void CXXThisRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const PointerType *PT, + const MemRegion *sRegion) { + ID.AddInteger((unsigned) CXXThisRegionKind); + ID.AddPointer(PT); + ID.AddPointer(sRegion); +} + +void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const { + CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion); +} + +void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl* D, + const MemRegion* superRegion, Kind k) { + ID.AddInteger((unsigned) k); + ID.AddPointer(D); + ID.AddPointer(superRegion); +} + +void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const { + DeclRegion::ProfileRegion(ID, D, superRegion, getKind()); +} + +void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const { + VarRegion::ProfileRegion(ID, getDecl(), superRegion); +} + +void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym, + const MemRegion *sreg) { + ID.AddInteger((unsigned) MemRegion::SymbolicRegionKind); + ID.Add(sym); + ID.AddPointer(sreg); +} + +void SymbolicRegion::Profile(llvm::FoldingSetNodeID& ID) const { + SymbolicRegion::ProfileRegion(ID, sym, getSuperRegion()); +} + +void ElementRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + QualType ElementType, SVal Idx, + const MemRegion* superRegion) { + ID.AddInteger(MemRegion::ElementRegionKind); + ID.Add(ElementType); + ID.AddPointer(superRegion); + Idx.Profile(ID); +} + +void ElementRegion::Profile(llvm::FoldingSetNodeID& ID) const { + ElementRegion::ProfileRegion(ID, ElementType, Index, superRegion); +} + +void FunctionTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const FunctionDecl *FD, + const MemRegion*) { + ID.AddInteger(MemRegion::FunctionTextRegionKind); + ID.AddPointer(FD); +} + +void FunctionTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { + FunctionTextRegion::ProfileRegion(ID, FD, superRegion); +} + +void BlockTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const BlockDecl *BD, CanQualType, + const AnalysisContext *AC, + const MemRegion*) { + ID.AddInteger(MemRegion::BlockTextRegionKind); + ID.AddPointer(BD); +} + +void BlockTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { + BlockTextRegion::ProfileRegion(ID, BD, locTy, AC, superRegion); +} + +void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const BlockTextRegion *BC, + const LocationContext *LC, + const MemRegion *sReg) { + ID.AddInteger(MemRegion::BlockDataRegionKind); + ID.AddPointer(BC); + ID.AddPointer(LC); + ID.AddPointer(sReg); +} + +void BlockDataRegion::Profile(llvm::FoldingSetNodeID& ID) const { + BlockDataRegion::ProfileRegion(ID, BC, LC, getSuperRegion()); +} + +void CXXObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + Expr const *Ex, + const MemRegion *sReg) { + ID.AddPointer(Ex); + ID.AddPointer(sReg); +} + +void CXXObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ProfileRegion(ID, Ex, getSuperRegion()); +} + +//===----------------------------------------------------------------------===// +// Region pretty-printing. +//===----------------------------------------------------------------------===// + +void MemRegion::dump() const { + dumpToStream(llvm::errs()); +} + +std::string MemRegion::getString() const { + std::string s; + llvm::raw_string_ostream os(s); + dumpToStream(os); + return os.str(); +} + +void MemRegion::dumpToStream(llvm::raw_ostream& os) const { + os << ""; +} + +void AllocaRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "alloca{" << (void*) Ex << ',' << Cnt << '}'; +} + +void FunctionTextRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "code{" << getDecl()->getDeclName().getAsString() << '}'; +} + +void BlockTextRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "block_code{" << (void*) this << '}'; +} + +void BlockDataRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "block_data{" << BC << '}'; +} + + +void CompoundLiteralRegion::dumpToStream(llvm::raw_ostream& os) const { + // FIXME: More elaborate pretty-printing. + os << "{ " << (void*) CL << " }"; +} + +void CXXThisRegion::dumpToStream(llvm::raw_ostream &os) const { + os << "this"; +} + +void ElementRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "element{" << superRegion << ',' + << Index << ',' << getElementType().getAsString() << '}'; +} + +void FieldRegion::dumpToStream(llvm::raw_ostream& os) const { + os << superRegion << "->" << getDecl()->getNameAsString(); +} + +void ObjCIvarRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "ivar{" << superRegion << ',' << getDecl()->getNameAsString() << '}'; +} + +void StringRegion::dumpToStream(llvm::raw_ostream& os) const { + Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOptions())); +} + +void SymbolicRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "SymRegion{" << sym << '}'; +} + +void VarRegion::dumpToStream(llvm::raw_ostream& os) const { + os << cast(D)->getNameAsString(); +} + +void RegionRawOffset::dump() const { + dumpToStream(llvm::errs()); +} + +void RegionRawOffset::dumpToStream(llvm::raw_ostream& os) const { + os << "raw_offset{" << getRegion() << ',' << getByteOffset() << '}'; +} + +//===----------------------------------------------------------------------===// +// MemRegionManager methods. +//===----------------------------------------------------------------------===// + +template +const REG *MemRegionManager::LazyAllocate(REG*& region) { + if (!region) { + region = (REG*) A.Allocate(); + new (region) REG(this); + } + + return region; +} + +template +const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) { + if (!region) { + region = (REG*) A.Allocate(); + new (region) REG(this, a); + } + + return region; +} + +const StackLocalsSpaceRegion* +MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) { + assert(STC); + if (STC == cachedStackLocalsFrame) + return cachedStackLocalsRegion; + cachedStackLocalsFrame = STC; + return LazyAllocate(cachedStackLocalsRegion, STC); +} + +const StackArgumentsSpaceRegion * +MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) { + assert(STC); + if (STC == cachedStackArgumentsFrame) + return cachedStackArgumentsRegion; + + cachedStackArgumentsFrame = STC; + return LazyAllocate(cachedStackArgumentsRegion, STC); +} + +const GlobalsSpaceRegion *MemRegionManager::getGlobalsRegion() { + return LazyAllocate(globals); +} + +const HeapSpaceRegion *MemRegionManager::getHeapRegion() { + return LazyAllocate(heap); +} + +const MemSpaceRegion *MemRegionManager::getUnknownRegion() { + return LazyAllocate(unknown); +} + +const MemSpaceRegion *MemRegionManager::getCodeRegion() { + return LazyAllocate(code); +} + +//===----------------------------------------------------------------------===// +// Constructing regions. +//===----------------------------------------------------------------------===// + +const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str) { + return getSubRegion(Str, getGlobalsRegion()); +} + +const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, + const LocationContext *LC) { + const MemRegion *sReg = 0; + + if (D->hasLocalStorage()) { + // FIXME: Once we implement scope handling, we will need to properly lookup + // 'D' to the proper LocationContext. + const DeclContext *DC = D->getDeclContext(); + const StackFrameContext *STC = LC->getStackFrameForDeclContext(DC); + + if (!STC) + sReg = getUnknownRegion(); + else { + sReg = isa(D) || isa(D) + ? static_cast(getStackArgumentsRegion(STC)) + : static_cast(getStackLocalsRegion(STC)); + } + } + else { + sReg = getGlobalsRegion(); + } + + return getSubRegion(D, sReg); +} + +const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, + const MemRegion *superR) { + return getSubRegion(D, superR); +} + +const BlockDataRegion * +MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC, + const LocationContext *LC) { + const MemRegion *sReg = 0; + + if (LC) { + // FIXME: Once we implement scope handling, we want the parent region + // to be the scope. + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + sReg = getStackLocalsRegion(STC); + } + else { + // We allow 'LC' to be NULL for cases where want BlockDataRegions + // without context-sensitivity. + sReg = getUnknownRegion(); + } + + return getSubRegion(BC, LC, sReg); +} + +const CompoundLiteralRegion* +MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL, + const LocationContext *LC) { + + const MemRegion *sReg = 0; + + if (CL->isFileScope()) + sReg = getGlobalsRegion(); + else { + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + sReg = getStackLocalsRegion(STC); + } + + return getSubRegion(CL, sReg); +} + +const ElementRegion* +MemRegionManager::getElementRegion(QualType elementType, SVal Idx, + const MemRegion* superRegion, + ASTContext& Ctx){ + + QualType T = Ctx.getCanonicalType(elementType); + + llvm::FoldingSetNodeID ID; + ElementRegion::ProfileRegion(ID, T, Idx, superRegion); + + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + ElementRegion* R = cast_or_null(data); + + if (!R) { + R = (ElementRegion*) A.Allocate(); + new (R) ElementRegion(T, Idx, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +const FunctionTextRegion * +MemRegionManager::getFunctionTextRegion(const FunctionDecl *FD) { + return getSubRegion(FD, getCodeRegion()); +} + +const BlockTextRegion * +MemRegionManager::getBlockTextRegion(const BlockDecl *BD, CanQualType locTy, + AnalysisContext *AC) { + return getSubRegion(BD, locTy, AC, getCodeRegion()); +} + + +/// getSymbolicRegion - Retrieve or create a "symbolic" memory region. +const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { + return getSubRegion(sym, getUnknownRegion()); +} + +const FieldRegion* +MemRegionManager::getFieldRegion(const FieldDecl* d, + const MemRegion* superRegion){ + return getSubRegion(d, superRegion); +} + +const ObjCIvarRegion* +MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl* d, + const MemRegion* superRegion) { + return getSubRegion(d, superRegion); +} + +const CXXObjectRegion* +MemRegionManager::getCXXObjectRegion(Expr const *E, + LocationContext const *LC) { + const StackFrameContext *SFC = LC->getCurrentStackFrame(); + assert(SFC); + return getSubRegion(E, getStackLocalsRegion(SFC)); +} + +const CXXThisRegion* +MemRegionManager::getCXXThisRegion(QualType thisPointerTy, + const LocationContext *LC) { + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + const PointerType *PT = thisPointerTy->getAs(); + assert(PT); + return getSubRegion(PT, getStackArgumentsRegion(STC)); +} + +const AllocaRegion* +MemRegionManager::getAllocaRegion(const Expr* E, unsigned cnt, + const LocationContext *LC) { + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + return getSubRegion(E, cnt, getStackLocalsRegion(STC)); +} + +const MemSpaceRegion *MemRegion::getMemorySpace() const { + const MemRegion *R = this; + const SubRegion* SR = dyn_cast(this); + + while (SR) { + R = SR->getSuperRegion(); + SR = dyn_cast(R); + } + + return dyn_cast(R); +} + +bool MemRegion::hasStackStorage() const { + return isa(getMemorySpace()); +} + +bool MemRegion::hasStackNonParametersStorage() const { + return isa(getMemorySpace()); +} + +bool MemRegion::hasStackParametersStorage() const { + return isa(getMemorySpace()); +} + +bool MemRegion::hasGlobalsOrParametersStorage() const { + const MemSpaceRegion *MS = getMemorySpace(); + return isa(MS) || + isa(MS); +} + +// getBaseRegion strips away all elements and fields, and get the base region +// of them. +const MemRegion *MemRegion::getBaseRegion() const { + const MemRegion *R = this; + while (true) { + if (const ElementRegion *ER = dyn_cast(R)) { + R = ER->getSuperRegion(); + continue; + } + if (const FieldRegion *FR = dyn_cast(R)) { + R = FR->getSuperRegion(); + continue; + } + break; + } + return R; +} + +//===----------------------------------------------------------------------===// +// View handling. +//===----------------------------------------------------------------------===// + +const MemRegion *MemRegion::StripCasts() const { + const MemRegion *R = this; + while (true) { + if (const ElementRegion *ER = dyn_cast(R)) { + // FIXME: generalize. Essentially we want to strip away ElementRegions + // that were layered on a symbolic region because of casts. We only + // want to strip away ElementRegions, however, where the index is 0. + SVal index = ER->getIndex(); + if (nonloc::ConcreteInt *CI = dyn_cast(&index)) { + if (CI->getValue().getSExtValue() == 0) { + R = ER->getSuperRegion(); + continue; + } + } + } + break; + } + return R; +} + +// FIXME: Merge with the implementation of the same method in Store.cpp +static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { + if (const RecordType *RT = Ty->getAs()) { + const RecordDecl *D = RT->getDecl(); + if (!D->getDefinition()) + return false; + } + + return true; +} + +RegionRawOffset ElementRegion::getAsRawOffset() const { + CharUnits offset = CharUnits::Zero(); + const ElementRegion *ER = this; + const MemRegion *superR = NULL; + ASTContext &C = getContext(); + + // FIXME: Handle multi-dimensional arrays. + + while (ER) { + superR = ER->getSuperRegion(); + + // FIXME: generalize to symbolic offsets. + SVal index = ER->getIndex(); + if (nonloc::ConcreteInt *CI = dyn_cast(&index)) { + // Update the offset. + int64_t i = CI->getValue().getSExtValue(); + + if (i != 0) { + QualType elemType = ER->getElementType(); + + // If we are pointing to an incomplete type, go no further. + if (!IsCompleteType(C, elemType)) { + superR = ER; + break; + } + + CharUnits size = C.getTypeSizeInChars(elemType); + offset += (i * size); + } + + // Go to the next ElementRegion (if any). + ER = dyn_cast(superR); + continue; + } + + return NULL; + } + + assert(superR && "super region cannot be NULL"); + return RegionRawOffset(superR, offset.getQuantity()); +} + +//===----------------------------------------------------------------------===// +// BlockDataRegion +//===----------------------------------------------------------------------===// + +void BlockDataRegion::LazyInitializeReferencedVars() { + if (ReferencedVars) + return; + + AnalysisContext *AC = getCodeRegion()->getAnalysisContext(); + AnalysisContext::referenced_decls_iterator I, E; + llvm::tie(I, E) = AC->getReferencedBlockVars(BC->getDecl()); + + if (I == E) { + ReferencedVars = (void*) 0x1; + return; + } + + MemRegionManager &MemMgr = *getMemRegionManager(); + llvm::BumpPtrAllocator &A = MemMgr.getAllocator(); + BumpVectorContext BC(A); + + typedef BumpVector VarVec; + VarVec *BV = (VarVec*) A.Allocate(); + new (BV) VarVec(BC, E - I); + + for ( ; I != E; ++I) { + const VarDecl *VD = *I; + const VarRegion *VR = 0; + + if (!VD->getAttr() && VD->hasLocalStorage()) + VR = MemMgr.getVarRegion(VD, this); + else { + if (LC) + VR = MemMgr.getVarRegion(VD, LC); + else { + VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); + } + } + + assert(VR); + BV->push_back(VR, BC); + } + + ReferencedVars = BV; +} + +BlockDataRegion::referenced_vars_iterator +BlockDataRegion::referenced_vars_begin() const { + const_cast(this)->LazyInitializeReferencedVars(); + + BumpVector *Vec = + static_cast*>(ReferencedVars); + + return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? + NULL : Vec->begin()); +} + +BlockDataRegion::referenced_vars_iterator +BlockDataRegion::referenced_vars_end() const { + const_cast(this)->LazyInitializeReferencedVars(); + + BumpVector *Vec = + static_cast*>(ReferencedVars); + + return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? + NULL : Vec->end()); +} diff --git a/lib/Checker/NSAutoreleasePoolChecker.cpp b/lib/Checker/NSAutoreleasePoolChecker.cpp new file mode 100644 index 000000000000..29bac9c384c2 --- /dev/null +++ b/lib/Checker/NSAutoreleasePoolChecker.cpp @@ -0,0 +1,86 @@ +//=- NSAutoreleasePoolChecker.cpp --------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a NSAutoreleasePoolChecker, a small checker that warns +// about subpar uses of NSAutoreleasePool. Note that while the check itself +// (in it's current form) could be written as a flow-insensitive check, in +// can be potentially enhanced in the future with flow-sensitive information. +// It is also a good example of the CheckerVisitor interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "BasicObjCFoundationChecks.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Decl.h" + +using namespace clang; + +namespace { +class NSAutoreleasePoolChecker + : public CheckerVisitor { + + Selector releaseS; + +public: + NSAutoreleasePoolChecker(Selector release_s) : releaseS(release_s) {} + + static void *getTag() { + static int x = 0; + return &x; + } + + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); +}; + +} // end anonymous namespace + + +void clang::RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng) { + ASTContext &Ctx = Eng.getContext(); + if (Ctx.getLangOptions().getGCMode() != LangOptions::NonGC) { + Eng.registerCheck(new NSAutoreleasePoolChecker(GetNullarySelector("release", + Ctx))); + } +} + +void +NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + const Expr *receiver = ME->getReceiver(); + if (!receiver) + return; + + // FIXME: Enhance with value-tracking information instead of consulting + // the type of the expression. + const ObjCObjectPointerType* PT = + receiver->getType()->getAs(); + + if (!PT) + return; + const ObjCInterfaceDecl* OD = PT->getInterfaceDecl(); + if (!OD) + return; + if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) + return; + + // Sending 'release' message? + if (ME->getSelector() != releaseS) + return; + + SourceRange R = ME->getSourceRange(); + + C.getBugReporter().EmitBasicReport("Use -drain instead of -release", + "API Upgrade (Apple)", + "Use -drain instead of -release when using NSAutoreleasePool " + "and garbage collection", ME->getLocStart(), &R, 1); +} diff --git a/lib/Checker/NSErrorChecker.cpp b/lib/Checker/NSErrorChecker.cpp new file mode 100644 index 000000000000..e428e2e83f2a --- /dev/null +++ b/lib/Checker/NSErrorChecker.cpp @@ -0,0 +1,237 @@ +//=- NSErrorCheckerer.cpp - Coding conventions for uses of NSError -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckNSError, a flow-insenstive check +// that determines if an Objective-C class interface correctly returns +// a non-void return type. +// +// File under feature request PR 2600. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/Checkers/LocalCheckers.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/Checkers/DereferenceChecker.h" +#include "BasicObjCFoundationChecks.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Decl.h" +#include "llvm/ADT/SmallVector.h" + +using namespace clang; + +namespace { +class NSErrorChecker : public BugType { + const Decl &CodeDecl; + const bool isNSErrorWarning; + IdentifierInfo * const II; + GRExprEngine &Eng; + + void CheckSignature(const ObjCMethodDecl& MD, QualType& ResultTy, + llvm::SmallVectorImpl& ErrorParams); + + void CheckSignature(const FunctionDecl& MD, QualType& ResultTy, + llvm::SmallVectorImpl& ErrorParams); + + bool CheckNSErrorArgument(QualType ArgTy); + bool CheckCFErrorArgument(QualType ArgTy); + + void CheckParamDeref(const VarDecl *V, const LocationContext *LC, + const GRState *state, BugReporter& BR); + + void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl); + +public: + NSErrorChecker(const Decl &D, bool isNSError, GRExprEngine& eng) + : BugType(isNSError ? "NSError** null dereference" + : "CFErrorRef* null dereference", + "Coding conventions (Apple)"), + CodeDecl(D), + isNSErrorWarning(isNSError), + II(&eng.getContext().Idents.get(isNSErrorWarning ? "NSError":"CFErrorRef")), + Eng(eng) {} + + void FlushReports(BugReporter& BR); +}; + +} // end anonymous namespace + +void clang::RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng, + const Decl &D) { + BR.Register(new NSErrorChecker(D, true, Eng)); + BR.Register(new NSErrorChecker(D, false, Eng)); +} + +void NSErrorChecker::FlushReports(BugReporter& BR) { + // Get the analysis engine and the exploded analysis graph. + ExplodedGraph& G = Eng.getGraph(); + + // Get the ASTContext, which is useful for querying type information. + ASTContext &Ctx = BR.getContext(); + + QualType ResultTy; + llvm::SmallVector ErrorParams; + + if (const ObjCMethodDecl* MD = dyn_cast(&CodeDecl)) + CheckSignature(*MD, ResultTy, ErrorParams); + else if (const FunctionDecl* FD = dyn_cast(&CodeDecl)) + CheckSignature(*FD, ResultTy, ErrorParams); + else + return; + + if (ErrorParams.empty()) + return; + + if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl); + + for (ExplodedGraph::roots_iterator RI=G.roots_begin(), RE=G.roots_end(); + RI!=RE; ++RI) { + // Scan the parameters for an implicit null dereference. + for (llvm::SmallVectorImpl::iterator I=ErrorParams.begin(), + E=ErrorParams.end(); I!=E; ++I) + CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR); + } +} + +void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + if (isa(CodeDecl)) + os << "Method"; + else + os << "Function"; + + os << " accepting "; + os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*"); + os << " should have a non-void return value to indicate whether or not an " + "error occurred"; + + BR.EmitBasicReport(isNSErrorWarning + ? "Bad return type when passing NSError**" + : "Bad return type when passing CFError*", + getCategory(), os.str(), + CodeDecl.getLocation()); +} + +void +NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, + llvm::SmallVectorImpl& ErrorParams) { + + ResultTy = M.getResultType(); + + for (ObjCMethodDecl::param_iterator I=M.param_begin(), + E=M.param_end(); I!=E; ++I) { + + QualType T = (*I)->getType(); + + if (isNSErrorWarning) { + if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); + } + else if (CheckCFErrorArgument(T)) + ErrorParams.push_back(*I); + } +} + +void +NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy, + llvm::SmallVectorImpl& ErrorParams) { + + ResultTy = F.getResultType(); + + for (FunctionDecl::param_const_iterator I = F.param_begin(), + E = F.param_end(); I != E; ++I) { + + QualType T = (*I)->getType(); + + if (isNSErrorWarning) { + if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); + } + else if (CheckCFErrorArgument(T)) + ErrorParams.push_back(*I); + } +} + + +bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) { + + const PointerType* PPT = ArgTy->getAs(); + if (!PPT) + return false; + + const ObjCObjectPointerType* PT = + PPT->getPointeeType()->getAs(); + + if (!PT) + return false; + + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); + + // FIXME: Can ID ever be NULL? + if (ID) + return II == ID->getIdentifier(); + + return false; +} + +bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) { + + const PointerType* PPT = ArgTy->getAs(); + if (!PPT) return false; + + const TypedefType* TT = PPT->getPointeeType()->getAs(); + if (!TT) return false; + + return TT->getDecl()->getIdentifier() == II; +} + +void NSErrorChecker::CheckParamDeref(const VarDecl *Param, + const LocationContext *LC, + const GRState *rootState, + BugReporter& BR) { + + SVal ParamL = rootState->getLValue(Param, LC); + const MemRegion* ParamR = cast(ParamL).getRegionAs(); + assert (ParamR && "Parameters always have VarRegions."); + SVal ParamSVal = rootState->getSVal(ParamR); + + // FIXME: For now assume that ParamSVal is symbolic. We need to generalize + // this later. + SymbolRef ParamSym = ParamSVal.getAsLocSymbol(); + if (!ParamSym) + return; + + // Iterate over the implicit-null dereferences. + ExplodedNode *const* I, *const* E; + llvm::tie(I, E) = GetImplicitNullDereferences(Eng); + for ( ; I != E; ++I) { + const GRState *state = (*I)->getState(); + SVal location = state->getSVal((*I)->getLocationAs()->getStmt()); + if (location.getAsSymbol() != ParamSym) + continue; + + // Emit an error. + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Potential null dereference. According to coding standards "; + + if (isNSErrorWarning) + os << "in 'Creating and Returning NSError Objects' the parameter '"; + else + os << "documented in CoreFoundation/CFError.h the parameter '"; + + os << Param->getNameAsString() << "' may be null."; + + BugReport *report = new BugReport(*this, os.str(), *I); + // FIXME: Notable symbols are now part of the report. We should + // add support for notable symbols in BugReport. + // BR.addNotableSymbol(SV->getSymbol()); + BR.EmitReport(report); + } +} diff --git a/lib/Checker/NoReturnFunctionChecker.cpp b/lib/Checker/NoReturnFunctionChecker.cpp new file mode 100644 index 000000000000..1455d87665db --- /dev/null +++ b/lib/Checker/NoReturnFunctionChecker.cpp @@ -0,0 +1,79 @@ +//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines NoReturnFunctionChecker, which evaluates functions that do not +// return to the caller. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/Checker.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; + +namespace { + +class NoReturnFunctionChecker : public Checker { +public: + static void *getTag() { static int tag = 0; return &tag; } + virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); +}; + +} + +void clang::RegisterNoReturnFunctionChecker(GRExprEngine &Eng) { + Eng.registerCheck(new NoReturnFunctionChecker()); +} + +bool NoReturnFunctionChecker::EvalCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + bool BuildSinks = false; + + if (FD->getAttr() || FD->getAttr()) + BuildSinks = true; + else if (const IdentifierInfo *II = FD->getIdentifier()) { + // HACK: Some functions are not marked noreturn, and don't return. + // Here are a few hardwired ones. If this takes too long, we can + // potentially cache these results. + BuildSinks + = llvm::StringSwitch(llvm::StringRef(II->getName())) + .Case("exit", true) + .Case("panic", true) + .Case("error", true) + .Case("Assert", true) + // FIXME: This is just a wrapper around throwing an exception. + // Eventually inter-procedural analysis should handle this easily. + .Case("ziperr", true) + .Case("assfail", true) + .Case("db_error", true) + .Case("__assert", true) + .Case("__assert_rtn", true) + .Case("__assert_fail", true) + .Case("dtrace_assfail", true) + .Case("yy_fatal_error", true) + .Case("_XCAssertionFailureHandler", true) + .Case("_DTAssertionFailureHandler", true) + .Case("_TSAssertionFailureHandler", true) + .Default(false); + } + + if (!BuildSinks) + return false; + + C.GenerateSink(CE); + return true; +} diff --git a/lib/Checker/OSAtomicChecker.cpp b/lib/Checker/OSAtomicChecker.cpp new file mode 100644 index 000000000000..7f4aeca33178 --- /dev/null +++ b/lib/Checker/OSAtomicChecker.cpp @@ -0,0 +1,197 @@ +//=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker evaluates OSAtomic functions. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/Checker.h" +#include "clang/Basic/Builtins.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; + +namespace { + +class OSAtomicChecker : public Checker { +public: + static void *getTag() { static int tag = 0; return &tag; } + virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); + +private: + bool EvalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE); +}; + +} + +void clang::RegisterOSAtomicChecker(GRExprEngine &Eng) { + Eng.registerCheck(new OSAtomicChecker()); +} + +bool OSAtomicChecker::EvalCallExpr(CheckerContext &C,const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl* FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + const IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return false; + + llvm::StringRef FName(II->getName()); + + // Check for compare and swap. + if (FName.startswith("OSAtomicCompareAndSwap") || + FName.startswith("objc_atomicCompareAndSwap")) + return EvalOSAtomicCompareAndSwap(C, CE); + + // FIXME: Other atomics. + return false; +} + +bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C, + const CallExpr *CE) { + // Not enough arguments to match OSAtomicCompareAndSwap? + if (CE->getNumArgs() != 3) + return false; + + ASTContext &Ctx = C.getASTContext(); + const Expr *oldValueExpr = CE->getArg(0); + QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); + + const Expr *newValueExpr = CE->getArg(1); + QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType()); + + // Do the types of 'oldValue' and 'newValue' match? + if (oldValueType != newValueType) + return false; + + const Expr *theValueExpr = CE->getArg(2); + const PointerType *theValueType=theValueExpr->getType()->getAs(); + + // theValueType not a pointer? + if (!theValueType) + return false; + + QualType theValueTypePointee = + Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType(); + + // The pointee must match newValueType and oldValueType. + if (theValueTypePointee != newValueType) + return false; + + static unsigned magic_load = 0; + static unsigned magic_store = 0; + + const void *OSAtomicLoadTag = &magic_load; + const void *OSAtomicStoreTag = &magic_store; + + // Load 'theValue'. + GRExprEngine &Engine = C.getEngine(); + const GRState *state = C.getState(); + ExplodedNodeSet Tmp; + SVal location = state->getSVal(theValueExpr); + // Here we should use the value type of the region as the load type. + QualType LoadTy; + if (const TypedRegion *TR = + dyn_cast_or_null(location.getAsRegion())) { + LoadTy = TR->getValueType(Ctx); + } + Engine.EvalLoad(Tmp, const_cast(theValueExpr), C.getPredecessor(), + state, location, OSAtomicLoadTag, LoadTy); + + if (Tmp.empty()) { + // If no nodes were generated, other checkers must generated sinks. But + // since the builder state was restored, we set it manually to prevent + // auto transition. + // FIXME: there should be a better approach. + C.getNodeBuilder().BuildSinks = true; + return true; + } + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); + I != E; ++I) { + + ExplodedNode *N = *I; + const GRState *stateLoad = N->getState(); + SVal theValueVal_untested = stateLoad->getSVal(theValueExpr); + SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr); + + // FIXME: Issue an error. + if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { + return false; + } + + DefinedOrUnknownSVal theValueVal = + cast(theValueVal_untested); + DefinedOrUnknownSVal oldValueVal = + cast(oldValueVal_untested); + + SValuator &SVator = Engine.getSValuator(); + + // Perform the comparison. + DefinedOrUnknownSVal Cmp = SVator.EvalEQ(stateLoad,theValueVal,oldValueVal); + + const GRState *stateEqual = stateLoad->Assume(Cmp, true); + + // Were they equal? + if (stateEqual) { + // Perform the store. + ExplodedNodeSet TmpStore; + SVal val = stateEqual->getSVal(newValueExpr); + + // Handle implicit value casts. + if (const TypedRegion *R = + dyn_cast_or_null(location.getAsRegion())) { + val = SVator.EvalCast(val,R->getValueType(Ctx),newValueExpr->getType()); + } + + Engine.EvalStore(TmpStore, NULL, const_cast(theValueExpr), N, + stateEqual, location, val, OSAtomicStoreTag); + + if (TmpStore.empty()) { + // If no nodes were generated, other checkers must generated sinks. But + // since the builder state was restored, we set it manually to prevent + // auto transition. + // FIXME: there should be a better approach. + C.getNodeBuilder().BuildSinks = true; + return true; + } + + // Now bind the result of the comparison. + for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), + E2 = TmpStore.end(); I2 != E2; ++I2) { + ExplodedNode *predNew = *I2; + const GRState *stateNew = predNew->getState(); + // Check for 'void' return type if we have a bogus function prototype. + SVal Res = UnknownVal(); + QualType T = CE->getType(); + if (!T->isVoidType()) + Res = Engine.getValueManager().makeTruthVal(true, T); + C.GenerateNode(stateNew->BindExpr(CE, Res), predNew); + } + } + + // Were they not equal? + if (const GRState *stateNotEqual = stateLoad->Assume(Cmp, false)) { + // Check for 'void' return type if we have a bogus function prototype. + SVal Res = UnknownVal(); + QualType T = CE->getType(); + if (!T->isVoidType()) + Res = Engine.getValueManager().makeTruthVal(false, CE->getType()); + C.GenerateNode(stateNotEqual->BindExpr(CE, Res), N); + } + } + + return true; +} diff --git a/lib/Checker/PathDiagnostic.cpp b/lib/Checker/PathDiagnostic.cpp new file mode 100644 index 000000000000..97500d95785c --- /dev/null +++ b/lib/Checker/PathDiagnostic.cpp @@ -0,0 +1,281 @@ +//===--- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PathDiagnostic-related interfaces. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/BugReporter/PathDiagnostic.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/StmtCXX.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Casting.h" + +using namespace clang; +using llvm::dyn_cast; +using llvm::isa; + +bool PathDiagnosticMacroPiece::containsEvent() const { + for (const_iterator I = begin(), E = end(); I!=E; ++I) { + if (isa(*I)) + return true; + + if (PathDiagnosticMacroPiece *MP = dyn_cast(*I)) + if (MP->containsEvent()) + return true; + } + + return false; +} + +static llvm::StringRef StripTrailingDots(llvm::StringRef s) { + for (llvm::StringRef::size_type i = s.size(); i != 0; --i) + if (s[i - 1] != '.') + return s.substr(0, i); + return ""; +} + +PathDiagnosticPiece::PathDiagnosticPiece(llvm::StringRef s, + Kind k, DisplayHint hint) + : str(StripTrailingDots(s)), kind(k), Hint(hint) {} + +PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) + : kind(k), Hint(hint) {} + +PathDiagnosticPiece::~PathDiagnosticPiece() {} +PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {} +PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} + +PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() { + for (iterator I = begin(), E = end(); I != E; ++I) delete *I; +} + +PathDiagnostic::PathDiagnostic() : Size(0) {} + +PathDiagnostic::~PathDiagnostic() { + for (iterator I = begin(), E = end(); I != E; ++I) delete &*I; +} + +void PathDiagnostic::resetPath(bool deletePieces) { + Size = 0; + + if (deletePieces) + for (iterator I=begin(), E=end(); I!=E; ++I) + delete &*I; + + path.clear(); +} + + +PathDiagnostic::PathDiagnostic(llvm::StringRef bugtype, llvm::StringRef desc, + llvm::StringRef category) + : Size(0), + BugType(StripTrailingDots(bugtype)), + Desc(StripTrailingDots(desc)), + Category(StripTrailingDots(category)) {} + +void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, + const DiagnosticInfo &Info) { + + // Create a PathDiagnostic with a single piece. + + PathDiagnostic* D = new PathDiagnostic(); + + const char *LevelStr; + switch (DiagLevel) { + default: + case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); + case Diagnostic::Note: LevelStr = "note: "; break; + case Diagnostic::Warning: LevelStr = "warning: "; break; + case Diagnostic::Error: LevelStr = "error: "; break; + case Diagnostic::Fatal: LevelStr = "fatal error: "; break; + } + + llvm::SmallString<100> StrC; + StrC += LevelStr; + Info.FormatDiagnostic(StrC); + + PathDiagnosticPiece *P = + new PathDiagnosticEventPiece(Info.getLocation(), StrC.str()); + + for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) + P->addRange(Info.getRange(i)); + for (unsigned i = 0, e = Info.getNumCodeModificationHints(); i != e; ++i) + P->addCodeModificationHint(Info.getCodeModificationHint(i)); + D->push_front(P); + + HandlePathDiagnostic(D); +} + +//===----------------------------------------------------------------------===// +// PathDiagnosticLocation methods. +//===----------------------------------------------------------------------===// + +FullSourceLoc PathDiagnosticLocation::asLocation() const { + assert(isValid()); + // Note that we want a 'switch' here so that the compiler can warn us in + // case we add more cases. + switch (K) { + case SingleLocK: + case RangeK: + break; + case StmtK: + return FullSourceLoc(S->getLocStart(), const_cast(*SM)); + case DeclK: + return FullSourceLoc(D->getLocation(), const_cast(*SM)); + } + + return FullSourceLoc(R.getBegin(), const_cast(*SM)); +} + +PathDiagnosticRange PathDiagnosticLocation::asRange() const { + assert(isValid()); + // Note that we want a 'switch' here so that the compiler can warn us in + // case we add more cases. + switch (K) { + case SingleLocK: + return PathDiagnosticRange(R, true); + case RangeK: + break; + case StmtK: { + const Stmt *S = asStmt(); + switch (S->getStmtClass()) { + default: + break; + case Stmt::DeclStmtClass: { + const DeclStmt *DS = cast(S); + if (DS->isSingleDecl()) { + // Should always be the case, but we'll be defensive. + return SourceRange(DS->getLocStart(), + DS->getSingleDecl()->getLocation()); + } + break; + } + // FIXME: Provide better range information for different + // terminators. + case Stmt::IfStmtClass: + case Stmt::WhileStmtClass: + case Stmt::DoStmtClass: + case Stmt::ForStmtClass: + case Stmt::ChooseExprClass: + case Stmt::IndirectGotoStmtClass: + case Stmt::SwitchStmtClass: + case Stmt::ConditionalOperatorClass: + case Stmt::ObjCForCollectionStmtClass: { + SourceLocation L = S->getLocStart(); + return SourceRange(L, L); + } + } + + return S->getSourceRange(); + } + case DeclK: + if (const ObjCMethodDecl *MD = dyn_cast(D)) + return MD->getSourceRange(); + if (const FunctionDecl *FD = dyn_cast(D)) { + // FIXME: We would like to always get the function body, even + // when it needs to be de-serialized, but getting the + // ASTContext here requires significant changes. + if (Stmt *Body = FD->getBody()) { + if (CompoundStmt *CS = dyn_cast(Body)) + return CS->getSourceRange(); + else + return cast(Body)->getSourceRange(); + } + } + else { + SourceLocation L = D->getLocation(); + return PathDiagnosticRange(SourceRange(L, L), true); + } + } + + return R; +} + +void PathDiagnosticLocation::flatten() { + if (K == StmtK) { + R = asRange(); + K = RangeK; + S = 0; + D = 0; + } + else if (K == DeclK) { + SourceLocation L = D->getLocation(); + R = SourceRange(L, L); + K = SingleLocK; + S = 0; + D = 0; + } +} + +//===----------------------------------------------------------------------===// +// FoldingSet profiling methods. +//===----------------------------------------------------------------------===// + +void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger((unsigned) K); + switch (K) { + case RangeK: + ID.AddInteger(R.getBegin().getRawEncoding()); + ID.AddInteger(R.getEnd().getRawEncoding()); + break; + case SingleLocK: + ID.AddInteger(R.getBegin().getRawEncoding()); + break; + case StmtK: + ID.Add(S); + break; + case DeclK: + ID.Add(D); + break; + } + return; +} + +void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger((unsigned) getKind()); + ID.AddString(str); + // FIXME: Add profiling support for code hints. + ID.AddInteger((unsigned) getDisplayHint()); + for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) { + ID.AddInteger(I->getBegin().getRawEncoding()); + ID.AddInteger(I->getEnd().getRawEncoding()); + } +} + +void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticPiece::Profile(ID); + ID.Add(Pos); +} + +void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticPiece::Profile(ID); + for (const_iterator I = begin(), E = end(); I != E; ++I) + ID.Add(*I); +} + +void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticSpotPiece::Profile(ID); + for (const_iterator I = begin(), E = end(); I != E; ++I) + ID.Add(**I); +} + +void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(Size); + ID.AddString(BugType); + ID.AddString(Desc); + ID.AddString(Category); + for (const_iterator I = begin(), E = end(); I != E; ++I) + ID.Add(*I); + + for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) + ID.AddString(*I); +} diff --git a/lib/Checker/PointerArithChecker.cpp b/lib/Checker/PointerArithChecker.cpp new file mode 100644 index 000000000000..3d62d0c7b9d7 --- /dev/null +++ b/lib/Checker/PointerArithChecker.cpp @@ -0,0 +1,71 @@ +//=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines PointerArithChecker, a builtin checker that checks for +// pointer arithmetic on locations other than array elements. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class PointerArithChecker + : public CheckerVisitor { + BuiltinBug *BT; +public: + PointerArithChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *PointerArithChecker::getTag() { + static int x; + return &x; +} + +void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + if (B->getOpcode() != BinaryOperator::Sub && + B->getOpcode() != BinaryOperator::Add) + return; + + const GRState *state = C.getState(); + SVal LV = state->getSVal(B->getLHS()); + SVal RV = state->getSVal(B->getRHS()); + + const MemRegion *LR = LV.getAsRegion(); + + if (!LR || !RV.isConstant()) + return; + + // If pointer arithmetic is done on variables of non-array type, this often + // means behavior rely on memory organization, which is dangerous. + if (isa(LR) || isa(LR) || + isa(LR)) { + + if (ExplodedNode *N = C.GenerateNode()) { + if (!BT) + BT = new BuiltinBug("Dangerous pointer arithmetic", + "Pointer arithmetic done on non-array variables " + "means reliance on memory layout, which is " + "dangerous."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getSourceRange()); + C.EmitReport(R); + } + } +} + +void clang::RegisterPointerArithChecker(GRExprEngine &Eng) { + Eng.registerCheck(new PointerArithChecker()); +} diff --git a/lib/Checker/PointerSubChecker.cpp b/lib/Checker/PointerSubChecker.cpp new file mode 100644 index 000000000000..acc848ac8edb --- /dev/null +++ b/lib/Checker/PointerSubChecker.cpp @@ -0,0 +1,77 @@ +//=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines PointerSubChecker, a builtin checker that checks for +// pointer subtractions on two pointers pointing to different memory chunks. +// This check corresponds to CWE-469. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class PointerSubChecker + : public CheckerVisitor { + BuiltinBug *BT; +public: + PointerSubChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *PointerSubChecker::getTag() { + static int x; + return &x; +} + +void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + // When doing pointer subtraction, if the two pointers do not point to the + // same memory chunk, emit a warning. + if (B->getOpcode() != BinaryOperator::Sub) + return; + + const GRState *state = C.getState(); + SVal LV = state->getSVal(B->getLHS()); + SVal RV = state->getSVal(B->getRHS()); + + const MemRegion *LR = LV.getAsRegion(); + const MemRegion *RR = RV.getAsRegion(); + + if (!(LR && RR)) + return; + + const MemRegion *BaseLR = LR->getBaseRegion(); + const MemRegion *BaseRR = RR->getBaseRegion(); + + if (BaseLR == BaseRR) + return; + + // Allow arithmetic on different symbolic regions. + if (isa(BaseLR) || isa(BaseRR)) + return; + + if (ExplodedNode *N = C.GenerateNode()) { + if (!BT) + BT = new BuiltinBug("Pointer subtraction", + "Subtraction of two pointers that do not point to " + "the same memory chunk may cause incorrect result."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getSourceRange()); + C.EmitReport(R); + } +} + +void clang::RegisterPointerSubChecker(GRExprEngine &Eng) { + Eng.registerCheck(new PointerSubChecker()); +} diff --git a/lib/Checker/PthreadLockChecker.cpp b/lib/Checker/PthreadLockChecker.cpp new file mode 100644 index 000000000000..74e266c3edfc --- /dev/null +++ b/lib/Checker/PthreadLockChecker.cpp @@ -0,0 +1,141 @@ +//===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually +// this shouldn't be registered with GRExprEngineInternalChecks. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/GRStateTrait.h" +#include "GRExprEngineExperimentalChecks.h" +#include "llvm/ADT/ImmutableSet.h" + +using namespace clang; + +namespace { +class PthreadLockChecker + : public CheckerVisitor { + BugType *BT; +public: + PthreadLockChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + + void AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock); + + void ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock); + +}; +} // end anonymous namespace + +// GDM Entry for tracking lock state. +namespace { class LockSet {}; } +namespace clang { +template <> struct GRStateTrait : + public GRStatePartialTrait > { + static void* GDMIndex() { return PthreadLockChecker::getTag(); } +}; +} // end clang namespace + +void clang::RegisterPthreadLockChecker(GRExprEngine &Eng) { + Eng.registerCheck(new PthreadLockChecker()); +} + + +void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionTextRegion *R = + dyn_cast_or_null(state->getSVal(Callee).getAsRegion()); + + if (!R) + return; + + llvm::StringRef FName = R->getDecl()->getName(); + + if (FName == "pthread_mutex_lock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false); + } + else if (FName == "pthread_mutex_trylock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true); + } + else if (FName == "pthread_mutex_unlock") { + if (CE->getNumArgs() != 1) + return; + ReleaseLock(C, CE, state->getSVal(CE->getArg(0))); + } +} + +void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + SVal X = state->getSVal(CE); + if (X.isUnknownOrUndef()) + return; + + DefinedSVal retVal = cast(X); + const GRState *lockSucc = state; + + if (isTryLock) { + // Bifurcate the state, and allow a mode where the lock acquisition fails. + const GRState *lockFail; + llvm::tie(lockFail, lockSucc) = state->Assume(retVal); + assert(lockFail && lockSucc); + C.addTransition(C.GenerateNode(CE, lockFail)); + } + else { + // Assume that the return value was 0. + lockSucc = state->Assume(retVal, false); + assert(lockSucc); + } + + // Record that the lock was acquired. + lockSucc = lockSucc->add(lockR); + + C.addTransition(lockSucc != state ? C.GenerateNode(CE, lockSucc) : + C.getPredecessor()); +} + +void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + // Record that the lock was released. + // FIXME: Handle unlocking locks that were never acquired. This may + // require IPA for wrappers. + const GRState *unlockState = state->remove(lockR); + + if (state == unlockState) + return; + + C.addTransition(C.GenerateNode(CE, unlockState)); +} diff --git a/lib/Checker/RangeConstraintManager.cpp b/lib/Checker/RangeConstraintManager.cpp new file mode 100644 index 000000000000..c904c33e08d2 --- /dev/null +++ b/lib/Checker/RangeConstraintManager.cpp @@ -0,0 +1,359 @@ +//== RangeConstraintManager.cpp - Manage range constraints.------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines RangeConstraintManager, a class that tracks simple +// equality and inequality constraints on symbolic values of GRState. +// +//===----------------------------------------------------------------------===// + +#include "SimpleConstraintManager.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Checker/PathSensitive/GRStateTrait.h" +#include "clang/Checker/PathSensitive/GRTransferFuncs.h" +#include "clang/Checker/ManagerRegistry.h" +#include "llvm/Support/Debug.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +namespace { class ConstraintRange {}; } +static int ConstraintRangeIndex = 0; + +/// A Range represents the closed range [from, to]. The caller must +/// guarantee that from <= to. Note that Range is immutable, so as not +/// to subvert RangeSet's immutability. +namespace { +class Range : public std::pair { +public: + Range(const llvm::APSInt &from, const llvm::APSInt &to) + : std::pair(&from, &to) { + assert(from <= to); + } + bool Includes(const llvm::APSInt &v) const { + return *first <= v && v <= *second; + } + const llvm::APSInt &From() const { + return *first; + } + const llvm::APSInt &To() const { + return *second; + } + const llvm::APSInt *getConcreteValue() const { + return &From() == &To() ? &From() : NULL; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(&From()); + ID.AddPointer(&To()); + } +}; + + +class RangeTrait : public llvm::ImutContainerInfo { +public: + // When comparing if one Range is less than another, we should compare + // the actual APSInt values instead of their pointers. This keeps the order + // consistent (instead of comparing by pointer values) and can potentially + // be used to speed up some of the operations in RangeSet. + static inline bool isLess(key_type_ref lhs, key_type_ref rhs) { + return *lhs.first < *rhs.first || (!(*rhs.first < *lhs.first) && + *lhs.second < *rhs.second); + } +}; + +/// RangeSet contains a set of ranges. If the set is empty, then +/// there the value of a symbol is overly constrained and there are no +/// possible values for that symbol. +class RangeSet { + typedef llvm::ImmutableSet PrimRangeSet; + PrimRangeSet ranges; // no need to make const, since it is an + // ImmutableSet - this allows default operator= + // to work. +public: + typedef PrimRangeSet::Factory Factory; + typedef PrimRangeSet::iterator iterator; + + RangeSet(PrimRangeSet RS) : ranges(RS) {} + RangeSet(Factory& F) : ranges(F.GetEmptySet()) {} + + iterator begin() const { return ranges.begin(); } + iterator end() const { return ranges.end(); } + + bool isEmpty() const { return ranges.isEmpty(); } + + /// Construct a new RangeSet representing '{ [from, to] }'. + RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to) + : ranges(F.Add(F.GetEmptySet(), Range(from, to))) {} + + /// Profile - Generates a hash profile of this RangeSet for use + /// by FoldingSet. + void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); } + + /// getConcreteValue - If a symbol is contrained to equal a specific integer + /// constant then this method returns that value. Otherwise, it returns + /// NULL. + const llvm::APSInt* getConcreteValue() const { + return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : 0; + } + + /// AddEQ - Create a new RangeSet with the additional constraint that the + /// value be equal to V. + RangeSet AddEQ(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { + // Search for a range that includes 'V'. If so, return a new RangeSet + // representing { [V, V] }. + for (PrimRangeSet::iterator i = begin(), e = end(); i!=e; ++i) + if (i->Includes(V)) + return RangeSet(F, V, V); + + return RangeSet(F); + } + + /// AddNE - Create a new RangeSet with the additional constraint that the + /// value be not be equal to V. + RangeSet AddNE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { + PrimRangeSet newRanges = ranges; + + // FIXME: We can perhaps enhance ImmutableSet to do this search for us + // in log(N) time using the sorted property of the internal AVL tree. + for (iterator i = begin(), e = end(); i != e; ++i) { + if (i->Includes(V)) { + // Remove the old range. + newRanges = F.Remove(newRanges, *i); + // Split the old range into possibly one or two ranges. + if (V != i->From()) + newRanges = F.Add(newRanges, Range(i->From(), BV.Sub1(V))); + if (V != i->To()) + newRanges = F.Add(newRanges, Range(BV.Add1(V), i->To())); + // All of the ranges are non-overlapping, so we can stop. + break; + } + } + + return newRanges; + } + + /// AddNE - Create a new RangeSet with the additional constraint that the + /// value be less than V. + RangeSet AddLT(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { + PrimRangeSet newRanges = F.GetEmptySet(); + + for (iterator i = begin(), e = end() ; i != e ; ++i) { + if (i->Includes(V) && i->From() < V) + newRanges = F.Add(newRanges, Range(i->From(), BV.Sub1(V))); + else if (i->To() < V) + newRanges = F.Add(newRanges, *i); + } + + return newRanges; + } + + RangeSet AddLE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { + PrimRangeSet newRanges = F.GetEmptySet(); + + for (iterator i = begin(), e = end(); i != e; ++i) { + // Strictly we should test for includes *V + 1, but no harm is + // done by this formulation + if (i->Includes(V)) + newRanges = F.Add(newRanges, Range(i->From(), V)); + else if (i->To() <= V) + newRanges = F.Add(newRanges, *i); + } + + return newRanges; + } + + RangeSet AddGT(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { + PrimRangeSet newRanges = F.GetEmptySet(); + + for (PrimRangeSet::iterator i = begin(), e = end(); i != e; ++i) { + if (i->Includes(V) && i->To() > V) + newRanges = F.Add(newRanges, Range(BV.Add1(V), i->To())); + else if (i->From() > V) + newRanges = F.Add(newRanges, *i); + } + + return newRanges; + } + + RangeSet AddGE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { + PrimRangeSet newRanges = F.GetEmptySet(); + + for (PrimRangeSet::iterator i = begin(), e = end(); i != e; ++i) { + // Strictly we should test for includes *V - 1, but no harm is + // done by this formulation + if (i->Includes(V)) + newRanges = F.Add(newRanges, Range(V, i->To())); + else if (i->From() >= V) + newRanges = F.Add(newRanges, *i); + } + + return newRanges; + } + + void print(llvm::raw_ostream &os) const { + bool isFirst = true; + os << "{ "; + for (iterator i = begin(), e = end(); i != e; ++i) { + if (isFirst) + isFirst = false; + else + os << ", "; + + os << '[' << i->From().toString(10) << ", " << i->To().toString(10) + << ']'; + } + os << " }"; + } + + bool operator==(const RangeSet &other) const { + return ranges == other.ranges; + } +}; +} // end anonymous namespace + +typedef llvm::ImmutableMap ConstraintRangeTy; + +namespace clang { +template<> +struct GRStateTrait + : public GRStatePartialTrait { + static inline void* GDMIndex() { return &ConstraintRangeIndex; } +}; +} + +namespace { +class RangeConstraintManager : public SimpleConstraintManager{ + RangeSet GetRange(const GRState *state, SymbolRef sym); +public: + RangeConstraintManager(GRSubEngine &subengine) + : SimpleConstraintManager(subengine) {} + + const GRState* AssumeSymNE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AssumeSymEQ(const GRState* St, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AssumeSymLT(const GRState* St, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AssumeSymGT(const GRState* St, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AssumeSymGE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V); + + const GRState* AssumeSymLE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V); + + const llvm::APSInt* getSymVal(const GRState* St, SymbolRef sym) const; + + // FIXME: Refactor into SimpleConstraintManager? + bool isEqual(const GRState* St, SymbolRef sym, const llvm::APSInt& V) const { + const llvm::APSInt *i = getSymVal(St, sym); + return i ? *i == V : false; + } + + const GRState* RemoveDeadBindings(const GRState* St, SymbolReaper& SymReaper); + + void print(const GRState* St, llvm::raw_ostream& Out, + const char* nl, const char *sep); + +private: + RangeSet::Factory F; +}; + +} // end anonymous namespace + +ConstraintManager* clang::CreateRangeConstraintManager(GRStateManager&, + GRSubEngine &subeng) { + return new RangeConstraintManager(subeng); +} + +const llvm::APSInt* RangeConstraintManager::getSymVal(const GRState* St, + SymbolRef sym) const { + const ConstraintRangeTy::data_type *T = St->get(sym); + return T ? T->getConcreteValue() : NULL; +} + +/// Scan all symbols referenced by the constraints. If the symbol is not alive +/// as marked in LSymbols, mark it as dead in DSymbols. +const GRState* +RangeConstraintManager::RemoveDeadBindings(const GRState* state, + SymbolReaper& SymReaper) { + + ConstraintRangeTy CR = state->get(); + ConstraintRangeTy::Factory& CRFactory = state->get_context(); + + for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) { + SymbolRef sym = I.getKey(); + if (SymReaper.maybeDead(sym)) + CR = CRFactory.Remove(CR, sym); + } + + return state->set(CR); +} + +//===------------------------------------------------------------------------=== +// AssumeSymX methods: public interface for RangeConstraintManager. +//===------------------------------------------------------------------------===/ + +RangeSet +RangeConstraintManager::GetRange(const GRState *state, SymbolRef sym) { + if (ConstraintRangeTy::data_type* V = state->get(sym)) + return *V; + + // Lazily generate a new RangeSet representing all possible values for the + // given symbol type. + QualType T = state->getSymbolManager().getType(sym); + BasicValueFactory& BV = state->getBasicVals(); + return RangeSet(F, BV.getMinValue(T), BV.getMaxValue(T)); +} + +//===------------------------------------------------------------------------=== +// AssumeSymX methods: public interface for RangeConstraintManager. +//===------------------------------------------------------------------------===/ + +#define AssumeX(OP)\ +const GRState*\ +RangeConstraintManager::AssumeSym ## OP(const GRState* state, SymbolRef sym,\ + const llvm::APSInt& V){\ + const RangeSet& R = GetRange(state, sym).Add##OP(state->getBasicVals(), F, V);\ + return !R.isEmpty() ? state->set(sym, R) : NULL;\ +} + +AssumeX(EQ) +AssumeX(NE) +AssumeX(LT) +AssumeX(GT) +AssumeX(LE) +AssumeX(GE) + +//===------------------------------------------------------------------------=== +// Pretty-printing. +//===------------------------------------------------------------------------===/ + +void RangeConstraintManager::print(const GRState* St, llvm::raw_ostream& Out, + const char* nl, const char *sep) { + + ConstraintRangeTy Ranges = St->get(); + + if (Ranges.isEmpty()) + return; + + Out << nl << sep << "ranges of symbol values:"; + + for (ConstraintRangeTy::iterator I=Ranges.begin(), E=Ranges.end(); I!=E; ++I){ + Out << nl << ' ' << I.getKey() << " : "; + I.getData().print(Out); + } +} diff --git a/lib/Checker/RegionStore.cpp b/lib/Checker/RegionStore.cpp new file mode 100644 index 000000000000..f70105af1379 --- /dev/null +++ b/lib/Checker/RegionStore.cpp @@ -0,0 +1,1905 @@ +//== RegionStore.cpp - Field-sensitive store model --------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a basic region store model. In this model, we do have field +// sensitivity. But we assume nothing about the heap shape. So recursive data +// structures are largely ignored. Basically we do 1-limiting analysis. +// Parameter pointers are assumed with no aliasing. Pointee objects of +// parameters are created lazily. +// +//===----------------------------------------------------------------------===// +#include "clang/Checker/PathSensitive/MemRegion.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Checker/PathSensitive/GRStateTrait.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/Support/Optional.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/AST/CharUnits.h" + +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +#define USE_EXPLICIT_COMPOUND 0 + +//===----------------------------------------------------------------------===// +// Representation of binding keys. +//===----------------------------------------------------------------------===// + +namespace { +class BindingKey { +public: + enum Kind { Direct = 0x0, Default = 0x1 }; +private: + llvm ::PointerIntPair P; + uint64_t Offset; + + explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k) + : P(r, (unsigned) k), Offset(offset) { assert(r); } +public: + + bool isDefault() const { return P.getInt() == Default; } + bool isDirect() const { return P.getInt() == Direct; } + + const MemRegion *getRegion() const { return P.getPointer(); } + uint64_t getOffset() const { return Offset; } + + void Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddPointer(P.getOpaqueValue()); + ID.AddInteger(Offset); + } + + static BindingKey Make(const MemRegion *R, Kind k); + + bool operator<(const BindingKey &X) const { + if (P.getOpaqueValue() < X.P.getOpaqueValue()) + return true; + if (P.getOpaqueValue() > X.P.getOpaqueValue()) + return false; + return Offset < X.Offset; + } + + bool operator==(const BindingKey &X) const { + return P.getOpaqueValue() == X.P.getOpaqueValue() && + Offset == X.Offset; + } +}; +} // end anonymous namespace + +namespace llvm { + static inline + llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BindingKey K) { + os << '(' << K.getRegion() << ',' << K.getOffset() + << ',' << (K.isDirect() ? "direct" : "default") + << ')'; + return os; + } +} // end llvm namespace + +//===----------------------------------------------------------------------===// +// Actual Store type. +//===----------------------------------------------------------------------===// + +typedef llvm::ImmutableMap RegionBindings; + +//===----------------------------------------------------------------------===// +// Fine-grained control of RegionStoreManager. +//===----------------------------------------------------------------------===// + +namespace { +struct minimal_features_tag {}; +struct maximal_features_tag {}; + +class RegionStoreFeatures { + bool SupportsFields; + bool SupportsRemaining; + +public: + RegionStoreFeatures(minimal_features_tag) : + SupportsFields(false), SupportsRemaining(false) {} + + RegionStoreFeatures(maximal_features_tag) : + SupportsFields(true), SupportsRemaining(false) {} + + void enableFields(bool t) { SupportsFields = t; } + + bool supportsFields() const { return SupportsFields; } + bool supportsRemaining() const { return SupportsRemaining; } +}; +} + +//===----------------------------------------------------------------------===// +// Region "Extents" +//===----------------------------------------------------------------------===// +// +// MemRegions represent chunks of memory with a size (their "extent"). This +// GDM entry tracks the extents for regions. Extents are in bytes. +// +namespace { class RegionExtents {}; } +static int RegionExtentsIndex = 0; +namespace clang { + template<> struct GRStateTrait + : public GRStatePartialTrait > { + static void* GDMIndex() { return &RegionExtentsIndex; } + }; +} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static bool IsAnyPointerOrIntptr(QualType ty, ASTContext &Ctx) { + if (ty->isAnyPointerType()) + return true; + + return ty->isIntegerType() && ty->isScalarType() && + Ctx.getTypeSize(ty) == Ctx.getTypeSize(Ctx.VoidPtrTy); +} + +//===----------------------------------------------------------------------===// +// Main RegionStore logic. +//===----------------------------------------------------------------------===// + +namespace { + +class RegionStoreSubRegionMap : public SubRegionMap { +public: + typedef llvm::ImmutableSet Set; + typedef llvm::DenseMap Map; +private: + Set::Factory F; + Map M; +public: + bool add(const MemRegion* Parent, const MemRegion* SubRegion) { + Map::iterator I = M.find(Parent); + + if (I == M.end()) { + M.insert(std::make_pair(Parent, F.Add(F.GetEmptySet(), SubRegion))); + return true; + } + + I->second = F.Add(I->second, SubRegion); + return false; + } + + void process(llvm::SmallVectorImpl &WL, const SubRegion *R); + + ~RegionStoreSubRegionMap() {} + + const Set *getSubRegions(const MemRegion *Parent) const { + Map::const_iterator I = M.find(Parent); + return I == M.end() ? NULL : &I->second; + } + + bool iterSubRegions(const MemRegion* Parent, Visitor& V) const { + Map::const_iterator I = M.find(Parent); + + if (I == M.end()) + return true; + + Set S = I->second; + for (Set::iterator SI=S.begin(),SE=S.end(); SI != SE; ++SI) { + if (!V.Visit(Parent, *SI)) + return false; + } + + return true; + } +}; + + +class RegionStoreManager : public StoreManager { + const RegionStoreFeatures Features; + RegionBindings::Factory RBFactory; + + typedef llvm::DenseMap SMCache; + SMCache SC; + +public: + RegionStoreManager(GRStateManager& mgr, const RegionStoreFeatures &f) + : StoreManager(mgr), + Features(f), + RBFactory(mgr.getAllocator()) {} + + virtual ~RegionStoreManager() { + for (SMCache::iterator I = SC.begin(), E = SC.end(); I != E; ++I) + delete (*I).second; + } + + SubRegionMap *getSubRegionMap(Store store) { + return getRegionStoreSubRegionMap(store); + } + + RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store); + + Optional getBinding(RegionBindings B, const MemRegion *R); + Optional getDirectBinding(RegionBindings B, const MemRegion *R); + /// getDefaultBinding - Returns an SVal* representing an optional default + /// binding associated with a region and its subregions. + Optional getDefaultBinding(RegionBindings B, const MemRegion *R); + + /// setImplicitDefaultValue - Set the default binding for the provided + /// MemRegion to the value implicitly defined for compound literals when + /// the value is not specified. + Store setImplicitDefaultValue(Store store, const MemRegion *R, QualType T); + + /// ArrayToPointer - Emulates the "decay" of an array to a pointer + /// type. 'Array' represents the lvalue of the array being decayed + /// to a pointer, and the returned SVal represents the decayed + /// version of that lvalue (i.e., a pointer to the first element of + /// the array). This is called by GRExprEngine when evaluating + /// casts from arrays to pointers. + SVal ArrayToPointer(Loc Array); + + SVal EvalBinOp(BinaryOperator::Opcode Op,Loc L, NonLoc R, QualType resultTy); + + Store getInitialStore(const LocationContext *InitLoc) { + return RBFactory.GetEmptyMap().getRoot(); + } + + //===-------------------------------------------------------------------===// + // Binding values to regions. + //===-------------------------------------------------------------------===// + + Store InvalidateRegion(Store store, const MemRegion *R, const Expr *E, + unsigned Count, InvalidatedSymbols *IS) { + return RegionStoreManager::InvalidateRegions(store, &R, &R+1, E, Count, IS); + } + + Store InvalidateRegions(Store store, + const MemRegion * const *Begin, + const MemRegion * const *End, + const Expr *E, unsigned Count, + InvalidatedSymbols *IS); + +public: // Made public for helper classes. + + void RemoveSubRegionBindings(RegionBindings &B, const MemRegion *R, + RegionStoreSubRegionMap &M); + + RegionBindings Add(RegionBindings B, BindingKey K, SVal V); + + RegionBindings Add(RegionBindings B, const MemRegion *R, + BindingKey::Kind k, SVal V); + + const SVal *Lookup(RegionBindings B, BindingKey K); + const SVal *Lookup(RegionBindings B, const MemRegion *R, BindingKey::Kind k); + + RegionBindings Remove(RegionBindings B, BindingKey K); + RegionBindings Remove(RegionBindings B, const MemRegion *R, + BindingKey::Kind k); + + RegionBindings Remove(RegionBindings B, const MemRegion *R) { + return Remove(Remove(B, R, BindingKey::Direct), R, BindingKey::Default); + } + + Store Remove(Store store, BindingKey K); + +public: // Part of public interface to class. + + Store Bind(Store store, Loc LV, SVal V); + + Store BindCompoundLiteral(Store store, const CompoundLiteralExpr* CL, + const LocationContext *LC, SVal V); + + Store BindDecl(Store store, const VarRegion *VR, SVal InitVal); + + Store BindDeclWithNoInit(Store store, const VarRegion *) { + return store; + } + + /// BindStruct - Bind a compound value to a structure. + Store BindStruct(Store store, const TypedRegion* R, SVal V); + + Store BindArray(Store store, const TypedRegion* R, SVal V); + + /// KillStruct - Set the entire struct to unknown. + Store KillStruct(Store store, const TypedRegion* R); + + Store Remove(Store store, Loc LV); + + + //===------------------------------------------------------------------===// + // Loading values from regions. + //===------------------------------------------------------------------===// + + /// The high level logic for this method is this: + /// Retrieve (L) + /// if L has binding + /// return L's binding + /// else if L is in killset + /// return unknown + /// else + /// if L is on stack or heap + /// return undefined + /// else + /// return symbolic + SVal Retrieve(Store store, Loc L, QualType T = QualType()); + + SVal RetrieveElement(Store store, const ElementRegion *R); + + SVal RetrieveField(Store store, const FieldRegion *R); + + SVal RetrieveObjCIvar(Store store, const ObjCIvarRegion *R); + + SVal RetrieveVar(Store store, const VarRegion *R); + + SVal RetrieveLazySymbol(const TypedRegion *R); + + SVal RetrieveFieldOrElementCommon(Store store, const TypedRegion *R, + QualType Ty, const MemRegion *superR); + + /// Retrieve the values in a struct and return a CompoundVal, used when doing + /// struct copy: + /// struct s x, y; + /// x = y; + /// y's value is retrieved by this method. + SVal RetrieveStruct(Store store, const TypedRegion* R); + + SVal RetrieveArray(Store store, const TypedRegion* R); + + /// Get the state and region whose binding this region R corresponds to. + std::pair + GetLazyBinding(RegionBindings B, const MemRegion *R); + + Store CopyLazyBindings(nonloc::LazyCompoundVal V, Store store, + const TypedRegion *R); + + const ElementRegion *GetElementZeroRegion(const MemRegion *R, QualType T); + + //===------------------------------------------------------------------===// + // State pruning. + //===------------------------------------------------------------------===// + + /// RemoveDeadBindings - Scans the RegionStore of 'state' for dead values. + /// It returns a new Store with these values removed. + Store RemoveDeadBindings(Store store, Stmt* Loc, SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots); + + const GRState *EnterStackFrame(const GRState *state, + const StackFrameContext *frame); + + //===------------------------------------------------------------------===// + // Region "extents". + //===------------------------------------------------------------------===// + + const GRState *setExtent(const GRState *state,const MemRegion* R,SVal Extent); + DefinedOrUnknownSVal getSizeInElements(const GRState *state, + const MemRegion* R, QualType EleTy); + + //===------------------------------------------------------------------===// + // Utility methods. + //===------------------------------------------------------------------===// + + static inline RegionBindings GetRegionBindings(Store store) { + return RegionBindings(static_cast(store)); + } + + void print(Store store, llvm::raw_ostream& Out, const char* nl, + const char *sep); + + void iterBindings(Store store, BindingsHandler& f) { + // FIXME: Implement. + } + + // FIXME: Remove. + BasicValueFactory& getBasicVals() { + return StateMgr.getBasicVals(); + } + + // FIXME: Remove. + ASTContext& getContext() { return StateMgr.getContext(); } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// RegionStore creation. +//===----------------------------------------------------------------------===// + +StoreManager *clang::CreateRegionStoreManager(GRStateManager& StMgr) { + RegionStoreFeatures F = maximal_features_tag(); + return new RegionStoreManager(StMgr, F); +} + +StoreManager *clang::CreateFieldsOnlyRegionStoreManager(GRStateManager &StMgr) { + RegionStoreFeatures F = minimal_features_tag(); + F.enableFields(true); + return new RegionStoreManager(StMgr, F); +} + +void +RegionStoreSubRegionMap::process(llvm::SmallVectorImpl &WL, + const SubRegion *R) { + const MemRegion *superR = R->getSuperRegion(); + if (add(superR, R)) + if (const SubRegion *sr = dyn_cast(superR)) + WL.push_back(sr); +} + +RegionStoreSubRegionMap* +RegionStoreManager::getRegionStoreSubRegionMap(Store store) { + RegionBindings B = GetRegionBindings(store); + RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap(); + + llvm::SmallVector WL; + + for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) + if (const SubRegion *R = dyn_cast(I.getKey().getRegion())) + M->process(WL, R); + + // We also need to record in the subregion map "intermediate" regions that + // don't have direct bindings but are super regions of those that do. + while (!WL.empty()) { + const SubRegion *R = WL.back(); + WL.pop_back(); + M->process(WL, R); + } + + return M; +} + +//===----------------------------------------------------------------------===// +// Binding invalidation. +//===----------------------------------------------------------------------===// + +void RegionStoreManager::RemoveSubRegionBindings(RegionBindings &B, + const MemRegion *R, + RegionStoreSubRegionMap &M) { + + if (const RegionStoreSubRegionMap::Set *S = M.getSubRegions(R)) + for (RegionStoreSubRegionMap::Set::iterator I = S->begin(), E = S->end(); + I != E; ++I) + RemoveSubRegionBindings(B, *I, M); + + B = Remove(B, R); +} + +namespace { +class InvalidateRegionsWorker { + typedef BumpVector RegionCluster; + typedef llvm::DenseMap ClusterMap; + typedef llvm::SmallVector, 10> + WorkList; + + BumpVectorContext BVC; + ClusterMap ClusterM; + WorkList WL; + + RegionStoreManager &RM; + StoreManager::InvalidatedSymbols *IS; + ASTContext &Ctx; + ValueManager &ValMgr; + +public: + InvalidateRegionsWorker(RegionStoreManager &rm, + StoreManager::InvalidatedSymbols *is, + ASTContext &ctx, ValueManager &valMgr) + : RM(rm), IS(is), Ctx(ctx), ValMgr(valMgr) {} + + Store InvalidateRegions(Store store, const MemRegion * const *I, + const MemRegion * const *E, + const Expr *Ex, unsigned Count); + +private: + void AddToWorkList(BindingKey K); + void AddToWorkList(const MemRegion *R); + void AddToCluster(BindingKey K); + RegionCluster **getCluster(const MemRegion *R); + void VisitBinding(SVal V); +}; +} + +void InvalidateRegionsWorker::AddToCluster(BindingKey K) { + const MemRegion *R = K.getRegion(); + const MemRegion *baseR = R->getBaseRegion(); + RegionCluster **CPtr = getCluster(baseR); + assert(*CPtr); + (*CPtr)->push_back(K, BVC); +} + +void InvalidateRegionsWorker::AddToWorkList(BindingKey K) { + AddToWorkList(K.getRegion()); +} + +void InvalidateRegionsWorker::AddToWorkList(const MemRegion *R) { + const MemRegion *baseR = R->getBaseRegion(); + RegionCluster **CPtr = getCluster(baseR); + if (RegionCluster *C = *CPtr) { + WL.push_back(std::make_pair(baseR, C)); + *CPtr = NULL; + } +} + +InvalidateRegionsWorker::RegionCluster ** +InvalidateRegionsWorker::getCluster(const MemRegion *R) { + RegionCluster *&CRef = ClusterM[R]; + if (!CRef) { + void *Mem = BVC.getAllocator().Allocate(); + CRef = new (Mem) RegionCluster(BVC, 10); + } + return &CRef; +} + +void InvalidateRegionsWorker::VisitBinding(SVal V) { + // A symbol? Mark it touched by the invalidation. + if (IS) + if (SymbolRef Sym = V.getAsSymbol()) + IS->insert(Sym); + + if (const MemRegion *R = V.getAsRegion()) { + AddToWorkList(R); + return; + } + + // Is it a LazyCompoundVal? All references get invalidated as well. + if (const nonloc::LazyCompoundVal *LCS = + dyn_cast(&V)) { + + const MemRegion *LazyR = LCS->getRegion(); + RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + + for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ + const MemRegion *baseR = RI.getKey().getRegion(); + if (cast(baseR)->isSubRegionOf(LazyR)) + VisitBinding(RI.getData()); + } + + return; + } +} + +Store InvalidateRegionsWorker::InvalidateRegions(Store store, + const MemRegion * const *I, + const MemRegion * const *E, + const Expr *Ex, unsigned Count) +{ + RegionBindings B = RegionStoreManager::GetRegionBindings(store); + + // Scan the entire store and make the region clusters. + for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) { + AddToCluster(RI.getKey()); + if (const MemRegion *R = RI.getData().getAsRegion()) { + // Generate a cluster, but don't add the region to the cluster + // if there aren't any bindings. + getCluster(R->getBaseRegion()); + } + } + + // Add the cluster for I .. E to a worklist. + for ( ; I != E; ++I) + AddToWorkList(*I); + + while (!WL.empty()) { + const MemRegion *baseR; + RegionCluster *C; + llvm::tie(baseR, C) = WL.back(); + WL.pop_back(); + + for (RegionCluster::iterator I = C->begin(), E = C->end(); I != E; ++I) { + BindingKey K = *I; + + // Get the old binding. Is it a region? If so, add it to the worklist. + if (const SVal *V = RM.Lookup(B, K)) + VisitBinding(*V); + + B = RM.Remove(B, K); + } + + // Now inspect the base region. + + if (IS) { + // Symbolic region? Mark that symbol touched by the invalidation. + if (const SymbolicRegion *SR = dyn_cast(baseR)) + IS->insert(SR->getSymbol()); + } + + // BlockDataRegion? If so, invalidate captured variables that are passed + // by reference. + if (const BlockDataRegion *BR = dyn_cast(baseR)) { + for (BlockDataRegion::referenced_vars_iterator + BI = BR->referenced_vars_begin(), BE = BR->referenced_vars_end() ; + BI != BE; ++BI) { + const VarRegion *VR = *BI; + const VarDecl *VD = VR->getDecl(); + if (VD->getAttr() || !VD->hasLocalStorage()) + AddToWorkList(VR); + } + continue; + } + + if (isa(baseR) || isa(baseR)) { + // Invalidate the region by setting its default value to + // conjured symbol. The type of the symbol is irrelavant. + DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, + Count); + B = RM.Add(B, baseR, BindingKey::Default, V); + continue; + } + + if (!baseR->isBoundable()) + continue; + + const TypedRegion *TR = cast(baseR); + QualType T = TR->getValueType(Ctx); + + // Invalidate the binding. + if (const RecordType *RT = T->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl()->getDefinition(); + // No record definition. There is nothing we can do. + if (!RD) { + B = RM.Remove(B, baseR); + continue; + } + + // Invalidate the region by setting its default value to + // conjured symbol. The type of the symbol is irrelavant. + DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, + Count); + B = RM.Add(B, baseR, BindingKey::Default, V); + continue; + } + + if (const ArrayType *AT = Ctx.getAsArrayType(T)) { + // Set the default value of the array to conjured symbol. + DefinedOrUnknownSVal V = + ValMgr.getConjuredSymbolVal(baseR, Ex, AT->getElementType(), Count); + B = RM.Add(B, baseR, BindingKey::Default, V); + continue; + } + + DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(baseR, Ex, T, Count); + assert(SymbolManager::canSymbolicate(T) || V.isUnknown()); + B = RM.Add(B, baseR, BindingKey::Direct, V); + } + + // Create a new state with the updated bindings. + return B.getRoot(); +} + +Store RegionStoreManager::InvalidateRegions(Store store, + const MemRegion * const *I, + const MemRegion * const *E, + const Expr *Ex, unsigned Count, + InvalidatedSymbols *IS) { + InvalidateRegionsWorker W(*this, IS, getContext(), + StateMgr.getValueManager()); + return W.InvalidateRegions(store, I, E, Ex, Count); +} + +//===----------------------------------------------------------------------===// +// Extents for regions. +//===----------------------------------------------------------------------===// + +DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state, + const MemRegion *R, + QualType EleTy) { + + switch (R->getKind()) { + case MemRegion::CXXThisRegionKind: + assert(0 && "Cannot get size of 'this' region"); + case MemRegion::GenericMemSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::GlobalsSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: + assert(0 && "Cannot index into a MemSpace"); + return UnknownVal(); + + case MemRegion::FunctionTextRegionKind: + case MemRegion::BlockTextRegionKind: + case MemRegion::BlockDataRegionKind: + // Technically this can happen if people do funny things with casts. + return UnknownVal(); + + // Not yet handled. + case MemRegion::AllocaRegionKind: + case MemRegion::CompoundLiteralRegionKind: + case MemRegion::ElementRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + case MemRegion::CXXObjectRegionKind: + return UnknownVal(); + + case MemRegion::SymbolicRegionKind: { + const SVal *Size = state->get(R); + if (!Size) + return UnknownVal(); + const nonloc::ConcreteInt *CI = dyn_cast(Size); + if (!CI) + return UnknownVal(); + + CharUnits RegionSize = + CharUnits::fromQuantity(CI->getValue().getSExtValue()); + CharUnits EleSize = getContext().getTypeSizeInChars(EleTy); + assert(RegionSize % EleSize == 0); + + return ValMgr.makeIntVal(RegionSize / EleSize, false); + } + + case MemRegion::StringRegionKind: { + const StringLiteral* Str = cast(R)->getStringLiteral(); + // We intentionally made the size value signed because it participates in + // operations with signed indices. + return ValMgr.makeIntVal(Str->getByteLength()+1, false); + } + + case MemRegion::VarRegionKind: { + const VarRegion* VR = cast(R); + // Get the type of the variable. + QualType T = VR->getDesugaredValueType(getContext()); + + // FIXME: Handle variable-length arrays. + if (isa(T)) + return UnknownVal(); + + if (const ConstantArrayType* CAT = dyn_cast(T)) { + // return the size as signed integer. + return ValMgr.makeIntVal(CAT->getSize(), false); + } + + // Clients can use ordinary variables as if they were arrays. These + // essentially are arrays of size 1. + return ValMgr.makeIntVal(1, false); + } + } + + assert(0 && "Unreachable"); + return UnknownVal(); +} + +const GRState *RegionStoreManager::setExtent(const GRState *state, + const MemRegion *region, + SVal extent) { + return state->set(region, extent); +} + +//===----------------------------------------------------------------------===// +// Location and region casting. +//===----------------------------------------------------------------------===// + +/// ArrayToPointer - Emulates the "decay" of an array to a pointer +/// type. 'Array' represents the lvalue of the array being decayed +/// to a pointer, and the returned SVal represents the decayed +/// version of that lvalue (i.e., a pointer to the first element of +/// the array). This is called by GRExprEngine when evaluating casts +/// from arrays to pointers. +SVal RegionStoreManager::ArrayToPointer(Loc Array) { + if (!isa(Array)) + return UnknownVal(); + + const MemRegion* R = cast(&Array)->getRegion(); + const TypedRegion* ArrayR = dyn_cast(R); + + if (!ArrayR) + return UnknownVal(); + + // Strip off typedefs from the ArrayRegion's ValueType. + QualType T = ArrayR->getValueType(getContext()).getDesugaredType(); + ArrayType *AT = cast(T); + T = AT->getElementType(); + + SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); + return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, + getContext())); +} + +//===----------------------------------------------------------------------===// +// Pointer arithmetic. +//===----------------------------------------------------------------------===// + +SVal RegionStoreManager::EvalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R, + QualType resultTy) { + // Assume the base location is MemRegionVal. + if (!isa(L)) + return UnknownVal(); + + const MemRegion* MR = cast(L).getRegion(); + const ElementRegion *ER = 0; + + switch (MR->getKind()) { + case MemRegion::SymbolicRegionKind: { + const SymbolicRegion *SR = cast(MR); + SymbolRef Sym = SR->getSymbol(); + QualType T = Sym->getType(getContext()); + QualType EleTy; + + if (const PointerType *PT = T->getAs()) + EleTy = PT->getPointeeType(); + else + EleTy = T->getAs()->getPointeeType(); + + SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); + ER = MRMgr.getElementRegion(EleTy, ZeroIdx, SR, getContext()); + break; + } + case MemRegion::AllocaRegionKind: { + const AllocaRegion *AR = cast(MR); + QualType T = getContext().CharTy; // Create an ElementRegion of bytes. + QualType EleTy = T->getAs()->getPointeeType(); + SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); + ER = MRMgr.getElementRegion(EleTy, ZeroIdx, AR, getContext()); + break; + } + + case MemRegion::ElementRegionKind: { + ER = cast(MR); + break; + } + + // Not yet handled. + case MemRegion::VarRegionKind: + case MemRegion::StringRegionKind: { + + } + // Fall-through. + case MemRegion::CompoundLiteralRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + case MemRegion::CXXObjectRegionKind: + return UnknownVal(); + + case MemRegion::FunctionTextRegionKind: + case MemRegion::BlockTextRegionKind: + case MemRegion::BlockDataRegionKind: + // Technically this can happen if people do funny things with casts. + return UnknownVal(); + + case MemRegion::CXXThisRegionKind: + assert(0 && + "Cannot perform pointer arithmetic on implicit argument 'this'"); + case MemRegion::GenericMemSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::GlobalsSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: + assert(0 && "Cannot perform pointer arithmetic on a MemSpace"); + return UnknownVal(); + } + + SVal Idx = ER->getIndex(); + nonloc::ConcreteInt* Base = dyn_cast(&Idx); + + // For now, only support: + // (a) concrete integer indices that can easily be resolved + // (b) 0 + symbolic index + if (Base) { + if (nonloc::ConcreteInt *Offset = dyn_cast(&R)) { + // FIXME: Should use SValuator here. + SVal NewIdx = + Base->evalBinOp(ValMgr, Op, + cast(ValMgr.convertToArrayIndex(*Offset))); + const MemRegion* NewER = + MRMgr.getElementRegion(ER->getElementType(), NewIdx, + ER->getSuperRegion(), getContext()); + return ValMgr.makeLoc(NewER); + } + if (0 == Base->getValue()) { + const MemRegion* NewER = + MRMgr.getElementRegion(ER->getElementType(), R, + ER->getSuperRegion(), getContext()); + return ValMgr.makeLoc(NewER); + } + } + + return UnknownVal(); +} + +//===----------------------------------------------------------------------===// +// Loading values from regions. +//===----------------------------------------------------------------------===// + +Optional RegionStoreManager::getDirectBinding(RegionBindings B, + const MemRegion *R) { + if (const SVal *V = Lookup(B, R, BindingKey::Direct)) + return *V; + + return Optional(); +} + +Optional RegionStoreManager::getDefaultBinding(RegionBindings B, + const MemRegion *R) { + if (R->isBoundable()) + if (const TypedRegion *TR = dyn_cast(R)) + if (TR->getValueType(getContext())->isUnionType()) + return UnknownVal(); + + if (const SVal *V = Lookup(B, R, BindingKey::Default)) + return *V; + + return Optional(); +} + +Optional RegionStoreManager::getBinding(RegionBindings B, + const MemRegion *R) { + + if (Optional V = getDirectBinding(B, R)) + return V; + + return getDefaultBinding(B, R); +} + +static bool IsReinterpreted(QualType RTy, QualType UsedTy, ASTContext &Ctx) { + RTy = Ctx.getCanonicalType(RTy); + UsedTy = Ctx.getCanonicalType(UsedTy); + + if (RTy == UsedTy) + return false; + + + // Recursively check the types. We basically want to see if a pointer value + // is ever reinterpreted as a non-pointer, e.g. void** and intptr_t* + // represents a reinterpretation. + if (Loc::IsLocType(RTy) && Loc::IsLocType(UsedTy)) { + const PointerType *PRTy = RTy->getAs(); + const PointerType *PUsedTy = UsedTy->getAs(); + + return PUsedTy && PRTy && + IsReinterpreted(PRTy->getPointeeType(), + PUsedTy->getPointeeType(), Ctx); + } + + return true; +} + +const ElementRegion * +RegionStoreManager::GetElementZeroRegion(const MemRegion *R, QualType T) { + ASTContext &Ctx = getContext(); + SVal idx = ValMgr.makeZeroArrayIndex(); + assert(!T.isNull()); + return MRMgr.getElementRegion(T, idx, R, Ctx); +} + +SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { + assert(!isa(L) && "location unknown"); + assert(!isa(L) && "location undefined"); + + // FIXME: Is this even possible? Shouldn't this be treated as a null + // dereference at a higher level? + if (isa(L)) + return UndefinedVal(); + + const MemRegion *MR = cast(L).getRegion(); + + if (isa(MR) || isa(MR)) + MR = GetElementZeroRegion(MR, T); + + if (isa(MR)) + return UnknownVal(); + + // FIXME: Perhaps this method should just take a 'const MemRegion*' argument + // instead of 'Loc', and have the other Loc cases handled at a higher level. + const TypedRegion *R = cast(MR); + QualType RTy = R->getValueType(getContext()); + + // FIXME: We should eventually handle funny addressing. e.g.: + // + // int x = ...; + // int *p = &x; + // char *q = (char*) p; + // char c = *q; // returns the first byte of 'x'. + // + // Such funny addressing will occur due to layering of regions. + +#if 0 + ASTContext &Ctx = getContext(); + if (!T.isNull() && IsReinterpreted(RTy, T, Ctx)) { + SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); + R = MRMgr.getElementRegion(T, ZeroIdx, R, Ctx); + RTy = T; + assert(Ctx.getCanonicalType(RTy) == + Ctx.getCanonicalType(R->getValueType(Ctx))); + } +#endif + + if (RTy->isStructureType()) + return RetrieveStruct(store, R); + + // FIXME: Handle unions. + if (RTy->isUnionType()) + return UnknownVal(); + + if (RTy->isArrayType()) + return RetrieveArray(store, R); + + // FIXME: handle Vector types. + if (RTy->isVectorType()) + return UnknownVal(); + + if (const FieldRegion* FR = dyn_cast(R)) + return CastRetrievedVal(RetrieveField(store, FR), FR, T, false); + + if (const ElementRegion* ER = dyn_cast(R)) { + // FIXME: Here we actually perform an implicit conversion from the loaded + // value to the element type. Eventually we want to compose these values + // more intelligently. For example, an 'element' can encompass multiple + // bound regions (e.g., several bound bytes), or could be a subset of + // a larger value. + return CastRetrievedVal(RetrieveElement(store, ER), ER, T, false); + } + + if (const ObjCIvarRegion *IVR = dyn_cast(R)) { + // FIXME: Here we actually perform an implicit conversion from the loaded + // value to the ivar type. What we should model is stores to ivars + // that blow past the extent of the ivar. If the address of the ivar is + // reinterpretted, it is possible we stored a different value that could + // fit within the ivar. Either we need to cast these when storing them + // or reinterpret them lazily (as we do here). + return CastRetrievedVal(RetrieveObjCIvar(store, IVR), IVR, T, false); + } + + if (const VarRegion *VR = dyn_cast(R)) { + // FIXME: Here we actually perform an implicit conversion from the loaded + // value to the variable type. What we should model is stores to variables + // that blow past the extent of the variable. If the address of the + // variable is reinterpretted, it is possible we stored a different value + // that could fit within the variable. Either we need to cast these when + // storing them or reinterpret them lazily (as we do here). + return CastRetrievedVal(RetrieveVar(store, VR), VR, T, false); + } + + RegionBindings B = GetRegionBindings(store); + const SVal *V = Lookup(B, R, BindingKey::Direct); + + // Check if the region has a binding. + if (V) + return *V; + + // The location does not have a bound value. This means that it has + // the value it had upon its creation and/or entry to the analyzed + // function/method. These are either symbolic values or 'undefined'. + if (R->hasStackNonParametersStorage()) { + // All stack variables are considered to have undefined values + // upon creation. All heap allocated blocks are considered to + // have undefined values as well unless they are explicitly bound + // to specific values. + return UndefinedVal(); + } + + // All other values are symbolic. + return ValMgr.getRegionValueSymbolVal(R, RTy); +} + +std::pair +RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R) { + if (Optional OV = getDirectBinding(B, R)) + if (const nonloc::LazyCompoundVal *V = + dyn_cast(OV.getPointer())) + return std::make_pair(V->getStore(), V->getRegion()); + + if (const ElementRegion *ER = dyn_cast(R)) { + const std::pair &X = + GetLazyBinding(B, ER->getSuperRegion()); + + if (X.second) + return std::make_pair(X.first, + MRMgr.getElementRegionWithSuper(ER, X.second)); + } + else if (const FieldRegion *FR = dyn_cast(R)) { + const std::pair &X = + GetLazyBinding(B, FR->getSuperRegion()); + + if (X.second) + return std::make_pair(X.first, + MRMgr.getFieldRegionWithSuper(FR, X.second)); + } + // The NULL MemRegion indicates an non-existent lazy binding. A NULL Store is + // possible for a valid lazy binding. + return std::make_pair((Store) 0, (const MemRegion *) 0); +} + +SVal RegionStoreManager::RetrieveElement(Store store, + const ElementRegion* R) { + // Check if the region has a binding. + RegionBindings B = GetRegionBindings(store); + if (Optional V = getDirectBinding(B, R)) + return *V; + + const MemRegion* superR = R->getSuperRegion(); + + // Check if the region is an element region of a string literal. + if (const StringRegion *StrR=dyn_cast(superR)) { + // FIXME: Handle loads from strings where the literal is treated as + // an integer, e.g., *((unsigned int*)"hello") + ASTContext &Ctx = getContext(); + QualType T = Ctx.getAsArrayType(StrR->getValueType(Ctx))->getElementType(); + if (T != Ctx.getCanonicalType(R->getElementType())) + return UnknownVal(); + + const StringLiteral *Str = StrR->getStringLiteral(); + SVal Idx = R->getIndex(); + if (nonloc::ConcreteInt *CI = dyn_cast(&Idx)) { + int64_t i = CI->getValue().getSExtValue(); + int64_t byteLength = Str->getByteLength(); + if (i > byteLength) { + // Buffer overflow checking in GRExprEngine should handle this case, + // but we shouldn't rely on it to not overflow here if that checking + // is disabled. + return UnknownVal(); + } + char c = (i == byteLength) ? '\0' : Str->getStrData()[i]; + return ValMgr.makeIntVal(c, T); + } + } + + // Check if the immediate super region has a direct binding. + if (Optional V = getDirectBinding(B, superR)) { + if (SymbolRef parentSym = V->getAsSymbol()) + return ValMgr.getDerivedRegionValueSymbolVal(parentSym, R); + + if (V->isUnknownOrUndef()) + return *V; + + // Handle LazyCompoundVals for the immediate super region. Other cases + // are handled in 'RetrieveFieldOrElementCommon'. + if (const nonloc::LazyCompoundVal *LCV = + dyn_cast(V)) { + + R = MRMgr.getElementRegionWithSuper(R, LCV->getRegion()); + return RetrieveElement(LCV->getStore(), R); + } + + // Other cases: give up. + return UnknownVal(); + } + + return RetrieveFieldOrElementCommon(store, R, R->getElementType(), superR); +} + +SVal RegionStoreManager::RetrieveField(Store store, + const FieldRegion* R) { + + // Check if the region has a binding. + RegionBindings B = GetRegionBindings(store); + if (Optional V = getDirectBinding(B, R)) + return *V; + + QualType Ty = R->getValueType(getContext()); + return RetrieveFieldOrElementCommon(store, R, Ty, R->getSuperRegion()); +} + +SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, + const TypedRegion *R, + QualType Ty, + const MemRegion *superR) { + + // At this point we have already checked in either RetrieveElement or + // RetrieveField if 'R' has a direct binding. + + RegionBindings B = GetRegionBindings(store); + + while (superR) { + if (const Optional &D = getDefaultBinding(B, superR)) { + if (SymbolRef parentSym = D->getAsSymbol()) + return ValMgr.getDerivedRegionValueSymbolVal(parentSym, R); + + if (D->isZeroConstant()) + return ValMgr.makeZeroVal(Ty); + + if (D->isUnknown()) + return *D; + + assert(0 && "Unknown default value"); + } + + // If our super region is a field or element itself, walk up the region + // hierarchy to see if there is a default value installed in an ancestor. + if (isa(superR) || isa(superR)) { + superR = cast(superR)->getSuperRegion(); + continue; + } + + break; + } + + // Lazy binding? + Store lazyBindingStore = NULL; + const MemRegion *lazyBindingRegion = NULL; + llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R); + + if (lazyBindingRegion) { + if (const ElementRegion *ER = dyn_cast(lazyBindingRegion)) + return RetrieveElement(lazyBindingStore, ER); + return RetrieveField(lazyBindingStore, + cast(lazyBindingRegion)); + } + + if (R->hasStackNonParametersStorage()) { + if (isa(R)) { + // Currently we don't reason specially about Clang-style vectors. Check + // if superR is a vector and if so return Unknown. + if (const TypedRegion *typedSuperR = dyn_cast(superR)) { + if (typedSuperR->getValueType(getContext())->isVectorType()) + return UnknownVal(); + } + } + + return UndefinedVal(); + } + + // All other values are symbolic. + return ValMgr.getRegionValueSymbolVal(R, Ty); +} + +SVal RegionStoreManager::RetrieveObjCIvar(Store store, const ObjCIvarRegion* R){ + + // Check if the region has a binding. + RegionBindings B = GetRegionBindings(store); + + if (Optional V = getDirectBinding(B, R)) + return *V; + + const MemRegion *superR = R->getSuperRegion(); + + // Check if the super region has a default binding. + if (Optional V = getDefaultBinding(B, superR)) { + if (SymbolRef parentSym = V->getAsSymbol()) + return ValMgr.getDerivedRegionValueSymbolVal(parentSym, R); + + // Other cases: give up. + return UnknownVal(); + } + + return RetrieveLazySymbol(R); +} + +SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) { + + // Check if the region has a binding. + RegionBindings B = GetRegionBindings(store); + + if (Optional V = getDirectBinding(B, R)) + return *V; + + // Lazily derive a value for the VarRegion. + const VarDecl *VD = R->getDecl(); + QualType T = VD->getType(); + const MemSpaceRegion *MS = R->getMemorySpace(); + + if (isa(MS) || + isa(MS)) + return ValMgr.getRegionValueSymbolVal(R, T); + + if (isa(MS)) { + if (VD->isFileVarDecl()) + return ValMgr.getRegionValueSymbolVal(R, T); + + if (T->isIntegerType()) + return ValMgr.makeIntVal(0, T); + if (T->isPointerType()) + return ValMgr.makeNull(); + + return UnknownVal(); + } + + return UndefinedVal(); +} + +SVal RegionStoreManager::RetrieveLazySymbol(const TypedRegion *R) { + + QualType valTy = R->getValueType(getContext()); + + // All other values are symbolic. + return ValMgr.getRegionValueSymbolVal(R, valTy); +} + +SVal RegionStoreManager::RetrieveStruct(Store store, const TypedRegion* R) { + QualType T = R->getValueType(getContext()); + assert(T->isStructureType()); + + const RecordType* RT = T->getAsStructureType(); + RecordDecl* RD = RT->getDecl(); + assert(RD->isDefinition()); + (void)RD; +#if USE_EXPLICIT_COMPOUND + llvm::ImmutableList StructVal = getBasicVals().getEmptySValList(); + + // FIXME: We shouldn't use a std::vector. If RecordDecl doesn't have a + // reverse iterator, we should implement one. + std::vector Fields(RD->field_begin(), RD->field_end()); + + for (std::vector::reverse_iterator Field = Fields.rbegin(), + FieldEnd = Fields.rend(); + Field != FieldEnd; ++Field) { + FieldRegion* FR = MRMgr.getFieldRegion(*Field, R); + QualType FTy = (*Field)->getType(); + SVal FieldValue = Retrieve(store, loc::MemRegionVal(FR), FTy).getSVal(); + StructVal = getBasicVals().consVals(FieldValue, StructVal); + } + + return ValMgr.makeCompoundVal(T, StructVal); +#else + return ValMgr.makeLazyCompoundVal(store, R); +#endif +} + +SVal RegionStoreManager::RetrieveArray(Store store, const TypedRegion * R) { +#if USE_EXPLICIT_COMPOUND + QualType T = R->getValueType(getContext()); + ConstantArrayType* CAT = cast(T.getTypePtr()); + + llvm::ImmutableList ArrayVal = getBasicVals().getEmptySValList(); + uint64_t size = CAT->getSize().getZExtValue(); + for (uint64_t i = 0; i < size; ++i) { + SVal Idx = ValMgr.makeArrayIndex(i); + ElementRegion* ER = MRMgr.getElementRegion(CAT->getElementType(), Idx, R, + getContext()); + QualType ETy = ER->getElementType(); + SVal ElementVal = Retrieve(store, loc::MemRegionVal(ER), ETy).getSVal(); + ArrayVal = getBasicVals().consVals(ElementVal, ArrayVal); + } + + return ValMgr.makeCompoundVal(T, ArrayVal); +#else + assert(isa(R->getValueType(getContext()))); + return ValMgr.makeLazyCompoundVal(store, R); +#endif +} + +//===----------------------------------------------------------------------===// +// Binding values to regions. +//===----------------------------------------------------------------------===// + +Store RegionStoreManager::Remove(Store store, Loc L) { + if (isa(L)) + if (const MemRegion* R = cast(L).getRegion()) + return Remove(GetRegionBindings(store), R).getRoot(); + + return store; +} + +Store RegionStoreManager::Bind(Store store, Loc L, SVal V) { + if (isa(L)) + return store; + + // If we get here, the location should be a region. + const MemRegion *R = cast(L).getRegion(); + + // Check if the region is a struct region. + if (const TypedRegion* TR = dyn_cast(R)) + if (TR->getValueType(getContext())->isStructureType()) + return BindStruct(store, TR, V); + + // Special case: the current region represents a cast and it and the super + // region both have pointer types or intptr_t types. If so, perform the + // bind to the super region. + // This is needed to support OSAtomicCompareAndSwap and friends or other + // loads that treat integers as pointers and vis versa. + if (const ElementRegion *ER = dyn_cast(R)) { + if (ER->getIndex().isZeroConstant()) { + if (const TypedRegion *superR = + dyn_cast(ER->getSuperRegion())) { + ASTContext &Ctx = getContext(); + QualType superTy = superR->getValueType(Ctx); + QualType erTy = ER->getValueType(Ctx); + + if (IsAnyPointerOrIntptr(superTy, Ctx) && + IsAnyPointerOrIntptr(erTy, Ctx)) { + V = ValMgr.getSValuator().EvalCast(V, superTy, erTy); + return Bind(store, loc::MemRegionVal(superR), V); + } + // For now, just invalidate the fields of the struct/union/class. + // FIXME: Precisely handle the fields of the record. + if (superTy->isRecordType()) + return InvalidateRegion(store, superR, NULL, 0, NULL); + } + } + } + else if (const SymbolicRegion *SR = dyn_cast(R)) { + // Binding directly to a symbolic region should be treated as binding + // to element 0. + QualType T = SR->getSymbol()->getType(getContext()); + + // FIXME: Is this the right way to handle symbols that are references? + if (const PointerType *PT = T->getAs()) + T = PT->getPointeeType(); + else + T = T->getAs()->getPointeeType(); + + R = GetElementZeroRegion(SR, T); + } + + // Perform the binding. + RegionBindings B = GetRegionBindings(store); + return Add(B, R, BindingKey::Direct, V).getRoot(); +} + +Store RegionStoreManager::BindDecl(Store store, const VarRegion *VR, + SVal InitVal) { + + QualType T = VR->getDecl()->getType(); + + if (T->isArrayType()) + return BindArray(store, VR, InitVal); + if (T->isStructureType()) + return BindStruct(store, VR, InitVal); + + return Bind(store, ValMgr.makeLoc(VR), InitVal); +} + +// FIXME: this method should be merged into Bind(). +Store RegionStoreManager::BindCompoundLiteral(Store store, + const CompoundLiteralExpr *CL, + const LocationContext *LC, + SVal V) { + return Bind(store, loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)), + V); +} + +Store RegionStoreManager::setImplicitDefaultValue(Store store, + const MemRegion *R, + QualType T) { + RegionBindings B = GetRegionBindings(store); + SVal V; + + if (Loc::IsLocType(T)) + V = ValMgr.makeNull(); + else if (T->isIntegerType()) + V = ValMgr.makeZeroVal(T); + else if (T->isStructureType() || T->isArrayType()) { + // Set the default value to a zero constant when it is a structure + // or array. The type doesn't really matter. + V = ValMgr.makeZeroVal(ValMgr.getContext().IntTy); + } + else { + return store; + } + + return Add(B, R, BindingKey::Default, V).getRoot(); +} + +Store RegionStoreManager::BindArray(Store store, const TypedRegion* R, + SVal Init) { + + ASTContext &Ctx = getContext(); + const ArrayType *AT = + cast(Ctx.getCanonicalType(R->getValueType(Ctx))); + QualType ElementTy = AT->getElementType(); + Optional Size; + + if (const ConstantArrayType* CAT = dyn_cast(AT)) + Size = CAT->getSize().getZExtValue(); + + // Check if the init expr is a StringLiteral. + if (isa(Init)) { + const MemRegion* InitR = cast(Init).getRegion(); + const StringLiteral* S = cast(InitR)->getStringLiteral(); + const char* str = S->getStrData(); + unsigned len = S->getByteLength(); + unsigned j = 0; + + // Copy bytes from the string literal into the target array. Trailing bytes + // in the array that are not covered by the string literal are initialized + // to zero. + + // We assume that string constants are bound to + // constant arrays. + uint64_t size = Size.getValue(); + + for (uint64_t i = 0; i < size; ++i, ++j) { + if (j >= len) + break; + + SVal Idx = ValMgr.makeArrayIndex(i); + const ElementRegion* ER = MRMgr.getElementRegion(ElementTy, Idx, R, + getContext()); + + SVal V = ValMgr.makeIntVal(str[j], sizeof(char)*8, true); + store = Bind(store, loc::MemRegionVal(ER), V); + } + + return store; + } + + // Handle lazy compound values. + if (nonloc::LazyCompoundVal *LCV = dyn_cast(&Init)) + return CopyLazyBindings(*LCV, store, R); + + // Remaining case: explicit compound values. + + if (Init.isUnknown()) + return setImplicitDefaultValue(store, R, ElementTy); + + nonloc::CompoundVal& CV = cast(Init); + nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); + uint64_t i = 0; + + for (; Size.hasValue() ? i < Size.getValue() : true ; ++i, ++VI) { + // The init list might be shorter than the array length. + if (VI == VE) + break; + + SVal Idx = ValMgr.makeArrayIndex(i); + const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, getContext()); + + if (ElementTy->isStructureType()) + store = BindStruct(store, ER, *VI); + else + store = Bind(store, ValMgr.makeLoc(ER), *VI); + } + + // If the init list is shorter than the array length, set the + // array default value. + if (Size.hasValue() && i < Size.getValue()) + store = setImplicitDefaultValue(store, R, ElementTy); + + return store; +} + +Store RegionStoreManager::BindStruct(Store store, const TypedRegion* R, + SVal V) { + + if (!Features.supportsFields()) + return store; + + QualType T = R->getValueType(getContext()); + assert(T->isStructureType()); + + const RecordType* RT = T->getAs(); + RecordDecl* RD = RT->getDecl(); + + if (!RD->isDefinition()) + return store; + + // Handle lazy compound values. + if (const nonloc::LazyCompoundVal *LCV=dyn_cast(&V)) + return CopyLazyBindings(*LCV, store, R); + + // We may get non-CompoundVal accidentally due to imprecise cast logic. + // Ignore them and kill the field values. + if (V.isUnknown() || !isa(V)) + return KillStruct(store, R); + + nonloc::CompoundVal& CV = cast(V); + nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); + + RecordDecl::field_iterator FI, FE; + + for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI, ++VI) { + + if (VI == VE) + break; + + QualType FTy = (*FI)->getType(); + const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); + + if (FTy->isArrayType()) + store = BindArray(store, FR, *VI); + else if (FTy->isStructureType()) + store = BindStruct(store, FR, *VI); + else + store = Bind(store, ValMgr.makeLoc(FR), *VI); + } + + // There may be fewer values in the initialize list than the fields of struct. + if (FI != FE) { + RegionBindings B = GetRegionBindings(store); + B = Add(B, R, BindingKey::Default, ValMgr.makeIntVal(0, false)); + store = B.getRoot(); + } + + return store; +} + +Store RegionStoreManager::KillStruct(Store store, const TypedRegion* R) { + RegionBindings B = GetRegionBindings(store); + llvm::OwningPtr + SubRegions(getRegionStoreSubRegionMap(store)); + RemoveSubRegionBindings(B, R, *SubRegions); + + // Set the default value of the struct region to "unknown". + return Add(B, R, BindingKey::Default, UnknownVal()).getRoot(); +} + +Store RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, + Store store, const TypedRegion *R) { + + // Nuke the old bindings stemming from R. + RegionBindings B = GetRegionBindings(store); + + llvm::OwningPtr + SubRegions(getRegionStoreSubRegionMap(store)); + + // B and DVM are updated after the call to RemoveSubRegionBindings. + RemoveSubRegionBindings(B, R, *SubRegions.get()); + + // Now copy the bindings. This amounts to just binding 'V' to 'R'. This + // results in a zero-copy algorithm. + return Add(B, R, BindingKey::Direct, V).getRoot(); +} + +//===----------------------------------------------------------------------===// +// "Raw" retrievals and bindings. +//===----------------------------------------------------------------------===// + +BindingKey BindingKey::Make(const MemRegion *R, Kind k) { + if (const ElementRegion *ER = dyn_cast(R)) { + const RegionRawOffset &O = ER->getAsRawOffset(); + + if (O.getRegion()) + return BindingKey(O.getRegion(), O.getByteOffset(), k); + + // FIXME: There are some ElementRegions for which we cannot compute + // raw offsets yet, including regions with symbolic offsets. + } + + return BindingKey(R, 0, k); +} + +RegionBindings RegionStoreManager::Add(RegionBindings B, BindingKey K, SVal V) { + return RBFactory.Add(B, K, V); +} + +RegionBindings RegionStoreManager::Add(RegionBindings B, const MemRegion *R, + BindingKey::Kind k, SVal V) { + return Add(B, BindingKey::Make(R, k), V); +} + +const SVal *RegionStoreManager::Lookup(RegionBindings B, BindingKey K) { + return B.lookup(K); +} + +const SVal *RegionStoreManager::Lookup(RegionBindings B, + const MemRegion *R, + BindingKey::Kind k) { + return Lookup(B, BindingKey::Make(R, k)); +} + +RegionBindings RegionStoreManager::Remove(RegionBindings B, BindingKey K) { + return RBFactory.Remove(B, K); +} + +RegionBindings RegionStoreManager::Remove(RegionBindings B, const MemRegion *R, + BindingKey::Kind k){ + return Remove(B, BindingKey::Make(R, k)); +} + +Store RegionStoreManager::Remove(Store store, BindingKey K) { + RegionBindings B = GetRegionBindings(store); + return Remove(B, K).getRoot(); +} + +//===----------------------------------------------------------------------===// +// State pruning. +//===----------------------------------------------------------------------===// + +Store RegionStoreManager::RemoveDeadBindings(Store store, Stmt* Loc, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots) +{ + typedef std::pair RBDNode; + + RegionBindings B = GetRegionBindings(store); + + // The backmap from regions to subregions. + llvm::OwningPtr + SubRegions(getRegionStoreSubRegionMap(store)); + + // Do a pass over the regions in the store. For VarRegions we check if + // the variable is still live and if so add it to the list of live roots. + // For other regions we populate our region backmap. + llvm::SmallVector IntermediateRoots; + + // Scan the direct bindings for "intermediate" roots. + for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const MemRegion *R = I.getKey().getRegion(); + IntermediateRoots.push_back(R); + } + + // Process the "intermediate" roots to find if they are referenced by + // real roots. + llvm::SmallVector WorkList; + llvm::SmallVector Postponed; + + llvm::DenseSet IntermediateVisited; + + while (!IntermediateRoots.empty()) { + const MemRegion* R = IntermediateRoots.back(); + IntermediateRoots.pop_back(); + + if (IntermediateVisited.count(R)) + continue; + IntermediateVisited.insert(R); + + if (const VarRegion* VR = dyn_cast(R)) { + if (SymReaper.isLive(Loc, VR)) + WorkList.push_back(std::make_pair(store, VR)); + continue; + } + + if (const SymbolicRegion* SR = dyn_cast(R)) { + llvm::SmallVectorImpl &Q = + SymReaper.isLive(SR->getSymbol()) ? WorkList : Postponed; + + Q.push_back(std::make_pair(store, SR)); + + continue; + } + + // Add the super region for R to the worklist if it is a subregion. + if (const SubRegion* superR = + dyn_cast(cast(R)->getSuperRegion())) + IntermediateRoots.push_back(superR); + } + + // Enqueue the RegionRoots onto WorkList. + for (llvm::SmallVectorImpl::iterator I=RegionRoots.begin(), + E=RegionRoots.end(); I!=E; ++I) { + WorkList.push_back(std::make_pair(store, *I)); + } + RegionRoots.clear(); + + llvm::DenseSet Visited; + +tryAgain: + while (!WorkList.empty()) { + RBDNode N = WorkList.back(); + WorkList.pop_back(); + + // Have we visited this node before? + if (Visited.count(N)) + continue; + Visited.insert(N); + + const MemRegion *R = N.second; + Store store_N = N.first; + + // Enqueue subregions. + RegionStoreSubRegionMap *M; + + if (store == store_N) + M = SubRegions.get(); + else { + RegionStoreSubRegionMap *& SM = SC[store_N]; + if (!SM) + SM = getRegionStoreSubRegionMap(store_N); + M = SM; + } + + if (const RegionStoreSubRegionMap::Set *S = M->getSubRegions(R)) + for (RegionStoreSubRegionMap::Set::iterator I = S->begin(), E = S->end(); + I != E; ++I) + WorkList.push_back(std::make_pair(store_N, *I)); + + // Enqueue the super region. + if (const SubRegion *SR = dyn_cast(R)) { + const MemRegion *superR = SR->getSuperRegion(); + if (!isa(superR)) { + // If 'R' is a field or an element, we want to keep the bindings + // for the other fields and elements around. The reason is that + // pointer arithmetic can get us to the other fields or elements. + assert(isa(R) || isa(R) + || isa(R)); + WorkList.push_back(std::make_pair(store_N, superR)); + } + } + + // Mark the symbol for any live SymbolicRegion as "live". This means we + // should continue to track that symbol. + if (const SymbolicRegion *SymR = dyn_cast(R)) + SymReaper.markLive(SymR->getSymbol()); + + // For BlockDataRegions, enqueue the VarRegions for variables marked + // with __block (passed-by-reference). + // via BlockDeclRefExprs. + if (const BlockDataRegion *BD = dyn_cast(R)) { + for (BlockDataRegion::referenced_vars_iterator + RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end(); + RI != RE; ++RI) { + if ((*RI)->getDecl()->getAttr()) + WorkList.push_back(std::make_pair(store_N, *RI)); + } + // No possible data bindings on a BlockDataRegion. Continue to the + // next region in the worklist. + continue; + } + + RegionBindings B_N = GetRegionBindings(store_N); + + // Get the data binding for R (if any). + Optional V = getBinding(B_N, R); + + if (V) { + // Check for lazy bindings. + if (const nonloc::LazyCompoundVal *LCV = + dyn_cast(V.getPointer())) { + + const LazyCompoundValData *D = LCV->getCVData(); + WorkList.push_back(std::make_pair(D->getStore(), D->getRegion())); + } + else { + // Update the set of live symbols. + for (SVal::symbol_iterator SI=V->symbol_begin(), SE=V->symbol_end(); + SI!=SE;++SI) + SymReaper.markLive(*SI); + + // If V is a region, then add it to the worklist. + if (const MemRegion *RX = V->getAsRegion()) + WorkList.push_back(std::make_pair(store_N, RX)); + } + } + } + + // See if any postponed SymbolicRegions are actually live now, after + // having done a scan. + for (llvm::SmallVectorImpl::iterator I = Postponed.begin(), + E = Postponed.end() ; I != E ; ++I) { + if (const SymbolicRegion *SR = cast_or_null(I->second)) { + if (SymReaper.isLive(SR->getSymbol())) { + WorkList.push_back(*I); + I->second = NULL; + } + } + } + + if (!WorkList.empty()) + goto tryAgain; + + // We have now scanned the store, marking reachable regions and symbols + // as live. We now remove all the regions that are dead from the store + // as well as update DSymbols with the set symbols that are now dead. + Store new_store = store; + for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const MemRegion* R = I.getKey().getRegion(); + // If this region live? Is so, none of its symbols are dead. + if (Visited.count(std::make_pair(store, R))) + continue; + + // Remove this dead region from the store. + new_store = Remove(new_store, I.getKey()); + + // Mark all non-live symbols that this region references as dead. + if (const SymbolicRegion* SymR = dyn_cast(R)) + SymReaper.maybeDead(SymR->getSymbol()); + + SVal X = I.getData(); + SVal::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); + for (; SI != SE; ++SI) + SymReaper.maybeDead(*SI); + } + + return new_store; +} + +GRState const *RegionStoreManager::EnterStackFrame(GRState const *state, + StackFrameContext const *frame) { + FunctionDecl const *FD = cast(frame->getDecl()); + CallExpr const *CE = cast(frame->getCallSite()); + + FunctionDecl::param_const_iterator PI = FD->param_begin(); + + CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); + + // Copy the arg expression value to the arg variables. + Store store = state->getStore(); + for (; AI != AE; ++AI, ++PI) { + SVal ArgVal = state->getSVal(*AI); + store = Bind(store, ValMgr.makeLoc(MRMgr.getVarRegion(*PI, frame)), ArgVal); + } + + return state->makeWithStore(store); +} + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + +void RegionStoreManager::print(Store store, llvm::raw_ostream& OS, + const char* nl, const char *sep) { + RegionBindings B = GetRegionBindings(store); + OS << "Store (direct and default bindings):" << nl; + + for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) + OS << ' ' << I.getKey() << " : " << I.getData() << nl; +} diff --git a/lib/Checker/ReturnPointerRangeChecker.cpp b/lib/Checker/ReturnPointerRangeChecker.cpp new file mode 100644 index 000000000000..949ded507c5b --- /dev/null +++ b/lib/Checker/ReturnPointerRangeChecker.cpp @@ -0,0 +1,97 @@ +//== ReturnPointerRangeChecker.cpp ------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ReturnPointerRangeChecker, which is a path-sensitive check +// which looks for an out-of-bound pointer being returned to callers. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" + +using namespace clang; + +namespace { +class ReturnPointerRangeChecker : + public CheckerVisitor { + BuiltinBug *BT; +public: + ReturnPointerRangeChecker() : BT(0) {} + static void *getTag(); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); +}; +} + +void clang::RegisterReturnPointerRangeChecker(GRExprEngine &Eng) { + Eng.registerCheck(new ReturnPointerRangeChecker()); +} + +void *ReturnPointerRangeChecker::getTag() { + static int x = 0; return &x; +} + +void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + const GRState *state = C.getState(); + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + SVal V = state->getSVal(RetE); + const MemRegion *R = V.getAsRegion(); + if (!R) + return; + + R = R->StripCasts(); + if (!R) + return; + + const ElementRegion *ER = dyn_cast_or_null(R); + if (!ER) + return; + + DefinedOrUnknownSVal &Idx = cast(ER->getIndex()); + + // FIXME: All of this out-of-bounds checking should eventually be refactored + // into a common place. + + DefinedOrUnknownSVal NumElements + = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), + ER->getValueType(C.getASTContext())); + + const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); + const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.GenerateSink(StOutBound); + + if (!N) + return; + + // FIXME: This bug correspond to CWE-466. Eventually we should have bug + // types explicitly reference such exploit categories (when applicable). + if (!BT) + BT = new BuiltinBug("Return of pointer value outside of expected range", + "Returned pointer value points outside the original object " + "(potential buffer overflow)"); + + // FIXME: It would be nice to eventually make this diagnostic more clear, + // e.g., by referencing the original declaration or by saying *why* this + // reference is outside the range. + + // Generate a report for this bug. + RangedBugReport *report = + new RangedBugReport(*BT, BT->getDescription(), N); + + report->addRange(RetE->getSourceRange()); + C.EmitReport(report); + } +} diff --git a/lib/Checker/ReturnStackAddressChecker.cpp b/lib/Checker/ReturnStackAddressChecker.cpp new file mode 100644 index 000000000000..9cbabba4a5f5 --- /dev/null +++ b/lib/Checker/ReturnStackAddressChecker.cpp @@ -0,0 +1,125 @@ +//== ReturnStackAddressChecker.cpp ------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ReturnStackAddressChecker, which is a path-sensitive +// check which looks for the addresses of stack variables being returned to +// callers. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang; + +namespace { +class ReturnStackAddressChecker : + public CheckerVisitor { + BuiltinBug *BT; +public: + ReturnStackAddressChecker() : BT(0) {} + static void *getTag(); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); +private: + void EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE); +}; +} + +void clang::RegisterReturnStackAddressChecker(GRExprEngine &Eng) { + Eng.registerCheck(new ReturnStackAddressChecker()); +} + +void *ReturnStackAddressChecker::getTag() { + static int x = 0; return &x; +} + +void ReturnStackAddressChecker::EmitStackError(CheckerContext &C, + const MemRegion *R, + const Expr *RetE) { + ExplodedNode *N = C.GenerateSink(); + + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Return of address to stack-allocated memory"); + + // Generate a report for this bug. + llvm::SmallString<512> buf; + llvm::raw_svector_ostream os(buf); + SourceRange range; + + // Get the base region, stripping away fields and elements. + R = R->getBaseRegion(); + + // Check if the region is a compound literal. + if (const CompoundLiteralRegion* CR = dyn_cast(R)) { + const CompoundLiteralExpr* CL = CR->getLiteralExpr(); + os << "Address of stack memory associated with a compound literal " + "declared on line " + << C.getSourceManager().getInstantiationLineNumber(CL->getLocStart()) + << " returned to caller"; + range = CL->getSourceRange(); + } + else if (const AllocaRegion* AR = dyn_cast(R)) { + const Expr* ARE = AR->getExpr(); + SourceLocation L = ARE->getLocStart(); + range = ARE->getSourceRange(); + os << "Address of stack memory allocated by call to alloca() on line " + << C.getSourceManager().getInstantiationLineNumber(L) + << " returned to caller"; + } + else if (const BlockDataRegion *BR = dyn_cast(R)) { + const BlockDecl *BD = BR->getCodeRegion()->getDecl(); + SourceLocation L = BD->getLocStart(); + range = BD->getSourceRange(); + os << "Address of stack-allocated block declared on line " + << C.getSourceManager().getInstantiationLineNumber(L) + << " returned to caller"; + } + else if (const VarRegion *VR = dyn_cast(R)) { + os << "Address of stack memory associated with local variable '" + << VR->getString() << "' returned"; + range = VR->getDecl()->getSourceRange(); + } + else { + assert(false && "Invalid region in ReturnStackAddressChecker."); + return; + } + + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + report->addRange(RetE->getSourceRange()); + if (range.isValid()) + report->addRange(range); + + C.EmitReport(report); +} + +void ReturnStackAddressChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + SVal V = C.getState()->getSVal(RetE); + const MemRegion *R = V.getAsRegion(); + + if (!R || !R->hasStackStorage()) + return; + + if (R->hasStackStorage()) { + EmitStackError(C, R, RetE); + return; + } +} diff --git a/lib/Checker/ReturnUndefChecker.cpp b/lib/Checker/ReturnUndefChecker.cpp new file mode 100644 index 000000000000..ee259883e48c --- /dev/null +++ b/lib/Checker/ReturnUndefChecker.cpp @@ -0,0 +1,68 @@ +//== ReturnUndefChecker.cpp -------------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ReturnUndefChecker, which is a path-sensitive +// check which looks for undefined or garbage values being returned to the +// caller. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang; + +namespace { +class ReturnUndefChecker : + public CheckerVisitor { + BuiltinBug *BT; +public: + ReturnUndefChecker() : BT(0) {} + static void *getTag(); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); +}; +} + +void clang::RegisterReturnUndefChecker(GRExprEngine &Eng) { + Eng.registerCheck(new ReturnUndefChecker()); +} + +void *ReturnUndefChecker::getTag() { + static int x = 0; return &x; +} + +void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + if (!C.getState()->getSVal(RetE).isUndef()) + return; + + ExplodedNode *N = C.GenerateSink(); + + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Garbage return value", + "Undefined or garbage value returned to caller"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT, BT->getDescription(), N); + + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE); + + C.EmitReport(report); +} diff --git a/lib/Checker/SVals.cpp b/lib/Checker/SVals.cpp new file mode 100644 index 000000000000..28b3fce050bb --- /dev/null +++ b/lib/Checker/SVals.cpp @@ -0,0 +1,327 @@ +//= RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SVal, Loc, and NonLoc, classes that represent +// abstract r-values for use with path-sensitive value tracking. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Basic/IdentifierTable.h" + +using namespace clang; +using llvm::dyn_cast; +using llvm::cast; +using llvm::APSInt; + +//===----------------------------------------------------------------------===// +// Symbol iteration within an SVal. +//===----------------------------------------------------------------------===// + + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + +bool SVal::hasConjuredSymbol() const { + if (const nonloc::SymbolVal* SV = dyn_cast(this)) { + SymbolRef sym = SV->getSymbol(); + if (isa(sym)) + return true; + } + + if (const loc::MemRegionVal *RV = dyn_cast(this)) { + const MemRegion *R = RV->getRegion(); + if (const SymbolicRegion *SR = dyn_cast(R)) { + SymbolRef sym = SR->getSymbol(); + if (isa(sym)) + return true; + } + } + + return false; +} + +const FunctionDecl *SVal::getAsFunctionDecl() const { + if (const loc::MemRegionVal* X = dyn_cast(this)) { + const MemRegion* R = X->getRegion(); + if (const FunctionTextRegion *CTR = R->getAs()) + return CTR->getDecl(); + } + + return NULL; +} + +/// getAsLocSymbol - If this SVal is a location (subclasses Loc) and +/// wraps a symbol, return that SymbolRef. Otherwise return 0. +// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? +SymbolRef SVal::getAsLocSymbol() const { + if (const loc::MemRegionVal *X = dyn_cast(this)) { + const MemRegion *R = X->StripCasts(); + if (const SymbolicRegion *SymR = dyn_cast(R)) + return SymR->getSymbol(); + } + return NULL; +} + +/// getAsSymbol - If this Sval wraps a symbol return that SymbolRef. +/// Otherwise return 0. +// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? +SymbolRef SVal::getAsSymbol() const { + if (const nonloc::SymbolVal *X = dyn_cast(this)) + return X->getSymbol(); + + if (const nonloc::SymExprVal *X = dyn_cast(this)) + if (SymbolRef Y = dyn_cast(X->getSymbolicExpression())) + return Y; + + return getAsLocSymbol(); +} + +/// getAsSymbolicExpression - If this Sval wraps a symbolic expression then +/// return that expression. Otherwise return NULL. +const SymExpr *SVal::getAsSymbolicExpression() const { + if (const nonloc::SymExprVal *X = dyn_cast(this)) + return X->getSymbolicExpression(); + + return getAsSymbol(); +} + +const MemRegion *SVal::getAsRegion() const { + if (const loc::MemRegionVal *X = dyn_cast(this)) + return X->getRegion(); + + if (const nonloc::LocAsInteger *X = dyn_cast(this)) { + return X->getLoc().getAsRegion(); + } + + return 0; +} + +const MemRegion *loc::MemRegionVal::StripCasts() const { + const MemRegion *R = getRegion(); + return R ? R->StripCasts() : NULL; +} + +bool SVal::symbol_iterator::operator==(const symbol_iterator &X) const { + return itr == X.itr; +} + +bool SVal::symbol_iterator::operator!=(const symbol_iterator &X) const { + return itr != X.itr; +} + +SVal::symbol_iterator::symbol_iterator(const SymExpr *SE) { + itr.push_back(SE); + while (!isa(itr.back())) expand(); +} + +SVal::symbol_iterator& SVal::symbol_iterator::operator++() { + assert(!itr.empty() && "attempting to iterate on an 'end' iterator"); + assert(isa(itr.back())); + itr.pop_back(); + if (!itr.empty()) + while (!isa(itr.back())) expand(); + return *this; +} + +SymbolRef SVal::symbol_iterator::operator*() { + assert(!itr.empty() && "attempting to dereference an 'end' iterator"); + return cast(itr.back()); +} + +void SVal::symbol_iterator::expand() { + const SymExpr *SE = itr.back(); + itr.pop_back(); + + if (const SymIntExpr *SIE = dyn_cast(SE)) { + itr.push_back(SIE->getLHS()); + return; + } + else if (const SymSymExpr *SSE = dyn_cast(SE)) { + itr.push_back(SSE->getLHS()); + itr.push_back(SSE->getRHS()); + return; + } + + assert(false && "unhandled expansion case"); +} + +const void *nonloc::LazyCompoundVal::getStore() const { + return static_cast(Data)->getStore(); +} + +const TypedRegion *nonloc::LazyCompoundVal::getRegion() const { + return static_cast(Data)->getRegion(); +} + +//===----------------------------------------------------------------------===// +// Other Iterators. +//===----------------------------------------------------------------------===// + +nonloc::CompoundVal::iterator nonloc::CompoundVal::begin() const { + return getValue()->begin(); +} + +nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { + return getValue()->end(); +} + +//===----------------------------------------------------------------------===// +// Useful predicates. +//===----------------------------------------------------------------------===// + +bool SVal::isConstant() const { + return isa(this) || isa(this); +} + +bool SVal::isZeroConstant() const { + if (isa(*this)) + return cast(*this).getValue() == 0; + else if (isa(*this)) + return cast(*this).getValue() == 0; + else + return false; +} + + +//===----------------------------------------------------------------------===// +// Transfer function dispatch for Non-Locs. +//===----------------------------------------------------------------------===// + +SVal nonloc::ConcreteInt::evalBinOp(ValueManager &ValMgr, + BinaryOperator::Opcode Op, + const nonloc::ConcreteInt& R) const { + const llvm::APSInt* X = + ValMgr.getBasicValueFactory().EvaluateAPSInt(Op, getValue(), R.getValue()); + + if (X) + return nonloc::ConcreteInt(*X); + else + return UndefinedVal(); +} + +nonloc::ConcreteInt +nonloc::ConcreteInt::evalComplement(ValueManager &ValMgr) const { + return ValMgr.makeIntVal(~getValue()); +} + +nonloc::ConcreteInt nonloc::ConcreteInt::evalMinus(ValueManager &ValMgr) const { + return ValMgr.makeIntVal(-getValue()); +} + +//===----------------------------------------------------------------------===// +// Transfer function dispatch for Locs. +//===----------------------------------------------------------------------===// + +SVal loc::ConcreteInt::EvalBinOp(BasicValueFactory& BasicVals, + BinaryOperator::Opcode Op, + const loc::ConcreteInt& R) const { + + assert (Op == BinaryOperator::Add || Op == BinaryOperator::Sub || + (Op >= BinaryOperator::LT && Op <= BinaryOperator::NE)); + + const llvm::APSInt* X = BasicVals.EvaluateAPSInt(Op, getValue(), R.getValue()); + + if (X) + return loc::ConcreteInt(*X); + else + return UndefinedVal(); +} + +//===----------------------------------------------------------------------===// +// Pretty-Printing. +//===----------------------------------------------------------------------===// + +void SVal::dump() const { dumpToStream(llvm::errs()); } + +void SVal::dumpToStream(llvm::raw_ostream& os) const { + switch (getBaseKind()) { + case UnknownKind: + os << "Invalid"; + break; + case NonLocKind: + cast(this)->dumpToStream(os); + break; + case LocKind: + cast(this)->dumpToStream(os); + break; + case UndefinedKind: + os << "Undefined"; + break; + default: + assert (false && "Invalid SVal."); + } +} + +void NonLoc::dumpToStream(llvm::raw_ostream& os) const { + switch (getSubKind()) { + case nonloc::ConcreteIntKind: + os << cast(this)->getValue().getZExtValue(); + if (cast(this)->getValue().isUnsigned()) + os << 'U'; + break; + case nonloc::SymbolValKind: + os << '$' << cast(this)->getSymbol(); + break; + case nonloc::SymExprValKind: { + const nonloc::SymExprVal& C = *cast(this); + const SymExpr *SE = C.getSymbolicExpression(); + os << SE; + break; + } + case nonloc::LocAsIntegerKind: { + const nonloc::LocAsInteger& C = *cast(this); + os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]"; + break; + } + case nonloc::CompoundValKind: { + const nonloc::CompoundVal& C = *cast(this); + os << "compoundVal{"; + bool first = true; + for (nonloc::CompoundVal::iterator I=C.begin(), E=C.end(); I!=E; ++I) { + if (first) { + os << ' '; first = false; + } + else + os << ", "; + + (*I).dumpToStream(os); + } + os << "}"; + break; + } + case nonloc::LazyCompoundValKind: { + const nonloc::LazyCompoundVal &C = *cast(this); + os << "lazyCompoundVal{" << (void*) C.getStore() << ',' << C.getRegion() + << '}'; + break; + } + default: + assert (false && "Pretty-printed not implemented for this NonLoc."); + break; + } +} + +void Loc::dumpToStream(llvm::raw_ostream& os) const { + switch (getSubKind()) { + case loc::ConcreteIntKind: + os << cast(this)->getValue().getZExtValue() << " (Loc)"; + break; + case loc::GotoLabelKind: + os << "&&" << cast(this)->getLabel()->getID()->getName(); + break; + case loc::MemRegionKind: + os << '&' << cast(this)->getRegion()->getString(); + break; + default: + assert(false && "Pretty-printing not implemented for this Loc."); + break; + } +} diff --git a/lib/Checker/SValuator.cpp b/lib/Checker/SValuator.cpp new file mode 100644 index 000000000000..542fc1b1078d --- /dev/null +++ b/lib/Checker/SValuator.cpp @@ -0,0 +1,157 @@ +// SValuator.cpp - Basic class for all SValuator implementations --*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SValuator, the base class for all (complete) SValuator +// implementations. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/SValuator.h" +#include "clang/Checker/PathSensitive/GRState.h" + +using namespace clang; + + +SVal SValuator::EvalBinOp(const GRState *ST, BinaryOperator::Opcode Op, + SVal L, SVal R, QualType T) { + + if (L.isUndef() || R.isUndef()) + return UndefinedVal(); + + if (L.isUnknown() || R.isUnknown()) + return UnknownVal(); + + if (isa(L)) { + if (isa(R)) + return EvalBinOpLL(Op, cast(L), cast(R), T); + + return EvalBinOpLN(ST, Op, cast(L), cast(R), T); + } + + if (isa(R)) { + // Support pointer arithmetic where the increment/decrement operand + // is on the left and the pointer on the right. + assert(Op == BinaryOperator::Add || Op == BinaryOperator::Sub); + + // Commute the operands. + return EvalBinOpLN(ST, Op, cast(R), cast(L), T); + } + + return EvalBinOpNN(ST, Op, cast(L), cast(R), T); +} + +DefinedOrUnknownSVal SValuator::EvalEQ(const GRState *ST, + DefinedOrUnknownSVal L, + DefinedOrUnknownSVal R) { + return cast(EvalBinOp(ST, BinaryOperator::EQ, L, R, + ValMgr.getContext().IntTy)); +} + +SVal SValuator::EvalCast(SVal val, QualType castTy, QualType originalTy) { + if (val.isUnknownOrUndef() || castTy == originalTy) + return val; + + ASTContext &C = ValMgr.getContext(); + + // For const casts, just propagate the value. + if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) + if (C.hasSameUnqualifiedType(castTy, originalTy)) + return val; + + // Check for casts to real or complex numbers. We don't handle these at all + // right now. + if (castTy->isFloatingType() || castTy->isAnyComplexType()) + return UnknownVal(); + + // Check for casts from integers to integers. + if (castTy->isIntegerType() && originalTy->isIntegerType()) + return EvalCastNL(cast(val), castTy); + + // Check for casts from pointers to integers. + if (castTy->isIntegerType() && Loc::IsLocType(originalTy)) + return EvalCastL(cast(val), castTy); + + // Check for casts from integers to pointers. + if (Loc::IsLocType(castTy) && originalTy->isIntegerType()) { + if (nonloc::LocAsInteger *LV = dyn_cast(&val)) { + if (const MemRegion *R = LV->getLoc().getAsRegion()) { + StoreManager &storeMgr = ValMgr.getStateManager().getStoreManager(); + R = storeMgr.CastRegion(R, castTy); + return R ? SVal(loc::MemRegionVal(R)) : UnknownVal(); + } + return LV->getLoc(); + } + goto DispatchCast; + } + + // Just pass through function and block pointers. + if (originalTy->isBlockPointerType() || originalTy->isFunctionPointerType()) { + assert(Loc::IsLocType(castTy)); + return val; + } + + // Check for casts from array type to another type. + if (originalTy->isArrayType()) { + // We will always decay to a pointer. + val = ValMgr.getStateManager().ArrayToPointer(cast(val)); + + // Are we casting from an array to a pointer? If so just pass on + // the decayed value. + if (castTy->isPointerType()) + return val; + + // Are we casting from an array to an integer? If so, cast the decayed + // pointer value to an integer. + assert(castTy->isIntegerType()); + + // FIXME: Keep these here for now in case we decide soon that we + // need the original decayed type. + // QualType elemTy = cast(originalTy)->getElementType(); + // QualType pointerTy = C.getPointerType(elemTy); + return EvalCastL(cast(val), castTy); + } + + // Check for casts from a region to a specific type. + if (const MemRegion *R = val.getAsRegion()) { + // FIXME: We should handle the case where we strip off view layers to get + // to a desugared type. + + assert(Loc::IsLocType(castTy)); + // We get a symbolic function pointer for a dereference of a function + // pointer, but it is of function type. Example: + + // struct FPRec { + // void (*my_func)(int * x); + // }; + // + // int bar(int x); + // + // int f1_a(struct FPRec* foo) { + // int x; + // (*foo->my_func)(&x); + // return bar(x)+1; // no-warning + // } + + assert(Loc::IsLocType(originalTy) || originalTy->isFunctionType() || + originalTy->isBlockPointerType()); + + StoreManager &storeMgr = ValMgr.getStateManager().getStoreManager(); + + // Delegate to store manager to get the result of casting a region to a + // different type. If the MemRegion* returned is NULL, this expression + // evaluates to UnknownVal. + R = storeMgr.CastRegion(R, castTy); + return R ? SVal(loc::MemRegionVal(R)) : UnknownVal(); + } + +DispatchCast: + // All other cases. + return isa(val) ? EvalCastL(cast(val), castTy) + : EvalCastNL(cast(val), castTy); +} diff --git a/lib/Checker/SimpleConstraintManager.cpp b/lib/Checker/SimpleConstraintManager.cpp new file mode 100644 index 000000000000..8c423a99777d --- /dev/null +++ b/lib/Checker/SimpleConstraintManager.cpp @@ -0,0 +1,249 @@ +//== SimpleConstraintManager.cpp --------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SimpleConstraintManager, a class that holds code shared +// between BasicConstraintManager and RangeConstraintManager. +// +//===----------------------------------------------------------------------===// + +#include "SimpleConstraintManager.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Checker/PathSensitive/Checker.h" + +namespace clang { + +SimpleConstraintManager::~SimpleConstraintManager() {} + +bool SimpleConstraintManager::canReasonAbout(SVal X) const { + if (nonloc::SymExprVal *SymVal = dyn_cast(&X)) { + const SymExpr *SE = SymVal->getSymbolicExpression(); + + if (isa(SE)) + return true; + + if (const SymIntExpr *SIE = dyn_cast(SE)) { + switch (SIE->getOpcode()) { + // We don't reason yet about bitwise-constraints on symbolic values. + case BinaryOperator::And: + case BinaryOperator::Or: + case BinaryOperator::Xor: + return false; + // We don't reason yet about arithmetic constraints on symbolic values. + case BinaryOperator::Mul: + case BinaryOperator::Div: + case BinaryOperator::Rem: + case BinaryOperator::Add: + case BinaryOperator::Sub: + case BinaryOperator::Shl: + case BinaryOperator::Shr: + return false; + // All other cases. + default: + return true; + } + } + + return false; + } + + return true; +} + +const GRState *SimpleConstraintManager::Assume(const GRState *state, + DefinedSVal Cond, + bool Assumption) { + if (isa(Cond)) + return Assume(state, cast(Cond), Assumption); + else + return Assume(state, cast(Cond), Assumption); +} + +const GRState *SimpleConstraintManager::Assume(const GRState *state, Loc cond, + bool assumption) { + state = AssumeAux(state, cond, assumption); + return SU.ProcessAssume(state, cond, assumption); +} + +const GRState *SimpleConstraintManager::AssumeAux(const GRState *state, + Loc Cond, bool Assumption) { + + BasicValueFactory &BasicVals = state->getBasicVals(); + + switch (Cond.getSubKind()) { + default: + assert (false && "'Assume' not implemented for this Loc."); + return state; + + case loc::MemRegionKind: { + // FIXME: Should this go into the storemanager? + + const MemRegion *R = cast(Cond).getRegion(); + const SubRegion *SubR = dyn_cast(R); + + while (SubR) { + // FIXME: now we only find the first symbolic region. + if (const SymbolicRegion *SymR = dyn_cast(SubR)) { + if (Assumption) + return AssumeSymNE(state, SymR->getSymbol(), + BasicVals.getZeroWithPtrWidth()); + else + return AssumeSymEQ(state, SymR->getSymbol(), + BasicVals.getZeroWithPtrWidth()); + } + SubR = dyn_cast(SubR->getSuperRegion()); + } + + // FALL-THROUGH. + } + + case loc::GotoLabelKind: + return Assumption ? state : NULL; + + case loc::ConcreteIntKind: { + bool b = cast(Cond).getValue() != 0; + bool isFeasible = b ? Assumption : !Assumption; + return isFeasible ? state : NULL; + } + } // end switch +} + +const GRState *SimpleConstraintManager::Assume(const GRState *state, + NonLoc cond, + bool assumption) { + state = AssumeAux(state, cond, assumption); + return SU.ProcessAssume(state, cond, assumption); +} + +const GRState *SimpleConstraintManager::AssumeAux(const GRState *state, + NonLoc Cond, + bool Assumption) { + + // We cannot reason about SymIntExpr and SymSymExpr. + if (!canReasonAbout(Cond)) { + // Just return the current state indicating that the path is feasible. + // This may be an over-approximation of what is possible. + return state; + } + + BasicValueFactory &BasicVals = state->getBasicVals(); + SymbolManager &SymMgr = state->getSymbolManager(); + + switch (Cond.getSubKind()) { + default: + assert(false && "'Assume' not implemented for this NonLoc"); + + case nonloc::SymbolValKind: { + nonloc::SymbolVal& SV = cast(Cond); + SymbolRef sym = SV.getSymbol(); + QualType T = SymMgr.getType(sym); + const llvm::APSInt &zero = BasicVals.getValue(0, T); + + return Assumption ? AssumeSymNE(state, sym, zero) + : AssumeSymEQ(state, sym, zero); + } + + case nonloc::SymExprValKind: { + nonloc::SymExprVal V = cast(Cond); + if (const SymIntExpr *SE = dyn_cast(V.getSymbolicExpression())){ + // FIXME: This is a hack. It silently converts the RHS integer to be + // of the same type as on the left side. This should be removed once + // we support truncation/extension of symbolic values. + GRStateManager &StateMgr = state->getStateManager(); + ASTContext &Ctx = StateMgr.getContext(); + QualType LHSType = SE->getLHS()->getType(Ctx); + BasicValueFactory &BasicVals = StateMgr.getBasicVals(); + const llvm::APSInt &RHS = BasicVals.Convert(LHSType, SE->getRHS()); + SymIntExpr SENew(SE->getLHS(), SE->getOpcode(), RHS, SE->getType(Ctx)); + + return AssumeSymInt(state, Assumption, &SENew); + } + + // For all other symbolic expressions, over-approximate and consider + // the constraint feasible. + return state; + } + + case nonloc::ConcreteIntKind: { + bool b = cast(Cond).getValue() != 0; + bool isFeasible = b ? Assumption : !Assumption; + return isFeasible ? state : NULL; + } + + case nonloc::LocAsIntegerKind: + return AssumeAux(state, cast(Cond).getLoc(), + Assumption); + } // end switch +} + +const GRState *SimpleConstraintManager::AssumeSymInt(const GRState *state, + bool Assumption, + const SymIntExpr *SE) { + + + // Here we assume that LHS is a symbol. This is consistent with the + // rest of the constraint manager logic. + SymbolRef Sym = cast(SE->getLHS()); + const llvm::APSInt &Int = SE->getRHS(); + + switch (SE->getOpcode()) { + default: + // No logic yet for other operators. Assume the constraint is feasible. + return state; + + case BinaryOperator::EQ: + return Assumption ? AssumeSymEQ(state, Sym, Int) + : AssumeSymNE(state, Sym, Int); + + case BinaryOperator::NE: + return Assumption ? AssumeSymNE(state, Sym, Int) + : AssumeSymEQ(state, Sym, Int); + case BinaryOperator::GT: + return Assumption ? AssumeSymGT(state, Sym, Int) + : AssumeSymLE(state, Sym, Int); + + case BinaryOperator::GE: + return Assumption ? AssumeSymGE(state, Sym, Int) + : AssumeSymLT(state, Sym, Int); + + case BinaryOperator::LT: + return Assumption ? AssumeSymLT(state, Sym, Int) + : AssumeSymGE(state, Sym, Int); + + case BinaryOperator::LE: + return Assumption ? AssumeSymLE(state, Sym, Int) + : AssumeSymGT(state, Sym, Int); + } // end switch +} + +const GRState *SimpleConstraintManager::AssumeInBound(const GRState *state, + DefinedSVal Idx, + DefinedSVal UpperBound, + bool Assumption) { + + // Only support ConcreteInt for now. + if (!(isa(Idx) && isa(UpperBound))) + return state; + + const llvm::APSInt& Zero = state->getBasicVals().getZeroWithPtrWidth(false); + llvm::APSInt IdxV = cast(Idx).getValue(); + // IdxV might be too narrow. + if (IdxV.getBitWidth() < Zero.getBitWidth()) + IdxV.extend(Zero.getBitWidth()); + // UBV might be too narrow, too. + llvm::APSInt UBV = cast(UpperBound).getValue(); + if (UBV.getBitWidth() < Zero.getBitWidth()) + UBV.extend(Zero.getBitWidth()); + + bool InBound = (Zero <= IdxV) && (IdxV < UBV); + bool isFeasible = Assumption ? InBound : !InBound; + return isFeasible ? state : NULL; +} + +} // end of namespace clang diff --git a/lib/Checker/SimpleConstraintManager.h b/lib/Checker/SimpleConstraintManager.h new file mode 100644 index 000000000000..5f20e0072b20 --- /dev/null +++ b/lib/Checker/SimpleConstraintManager.h @@ -0,0 +1,83 @@ +//== SimpleConstraintManager.h ----------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Code shared between BasicConstraintManager and RangeConstraintManager. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_SIMPLE_CONSTRAINT_MANAGER_H +#define LLVM_CLANG_ANALYSIS_SIMPLE_CONSTRAINT_MANAGER_H + +#include "clang/Checker/PathSensitive/ConstraintManager.h" +#include "clang/Checker/PathSensitive/GRState.h" + +namespace clang { + +class SimpleConstraintManager : public ConstraintManager { + GRSubEngine &SU; +public: + SimpleConstraintManager(GRSubEngine &subengine) : SU(subengine) {} + virtual ~SimpleConstraintManager(); + + //===------------------------------------------------------------------===// + // Common implementation for the interface provided by ConstraintManager. + //===------------------------------------------------------------------===// + + bool canReasonAbout(SVal X) const; + + const GRState *Assume(const GRState *state, DefinedSVal Cond, + bool Assumption); + + const GRState *Assume(const GRState *state, Loc Cond, bool Assumption); + + const GRState *Assume(const GRState *state, NonLoc Cond, bool Assumption); + + const GRState *AssumeSymInt(const GRState *state, bool Assumption, + const SymIntExpr *SE); + + const GRState *AssumeInBound(const GRState *state, DefinedSVal Idx, + DefinedSVal UpperBound, + bool Assumption); + +protected: + + //===------------------------------------------------------------------===// + // Interface that subclasses must implement. + //===------------------------------------------------------------------===// + + virtual const GRState *AssumeSymNE(const GRState *state, SymbolRef sym, + const llvm::APSInt& V) = 0; + + virtual const GRState *AssumeSymEQ(const GRState *state, SymbolRef sym, + const llvm::APSInt& V) = 0; + + virtual const GRState *AssumeSymLT(const GRState *state, SymbolRef sym, + const llvm::APSInt& V) = 0; + + virtual const GRState *AssumeSymGT(const GRState *state, SymbolRef sym, + const llvm::APSInt& V) = 0; + + virtual const GRState *AssumeSymLE(const GRState *state, SymbolRef sym, + const llvm::APSInt& V) = 0; + + virtual const GRState *AssumeSymGE(const GRState *state, SymbolRef sym, + const llvm::APSInt& V) = 0; + + //===------------------------------------------------------------------===// + // Internal implementation. + //===------------------------------------------------------------------===// + + const GRState *AssumeAux(const GRState *state, Loc Cond,bool Assumption); + + const GRState *AssumeAux(const GRState *state, NonLoc Cond, bool Assumption); +}; + +} // end clang namespace + +#endif diff --git a/lib/Checker/SimpleSValuator.cpp b/lib/Checker/SimpleSValuator.cpp new file mode 100644 index 000000000000..fb1d74a99046 --- /dev/null +++ b/lib/Checker/SimpleSValuator.cpp @@ -0,0 +1,428 @@ +// SimpleSValuator.cpp - A basic SValuator ------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SimpleSValuator, a basic implementation of SValuator. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/SValuator.h" +#include "clang/Checker/PathSensitive/GRState.h" + +using namespace clang; + +namespace { +class SimpleSValuator : public SValuator { +protected: + virtual SVal EvalCastNL(NonLoc val, QualType castTy); + virtual SVal EvalCastL(Loc val, QualType castTy); + +public: + SimpleSValuator(ValueManager &valMgr) : SValuator(valMgr) {} + virtual ~SimpleSValuator() {} + + virtual SVal EvalMinus(NonLoc val); + virtual SVal EvalComplement(NonLoc val); + virtual SVal EvalBinOpNN(const GRState *state, BinaryOperator::Opcode op, + NonLoc lhs, NonLoc rhs, QualType resultTy); + virtual SVal EvalBinOpLL(BinaryOperator::Opcode op, Loc lhs, Loc rhs, + QualType resultTy); + virtual SVal EvalBinOpLN(const GRState *state, BinaryOperator::Opcode op, + Loc lhs, NonLoc rhs, QualType resultTy); +}; +} // end anonymous namespace + +SValuator *clang::CreateSimpleSValuator(ValueManager &valMgr) { + return new SimpleSValuator(valMgr); +} + +//===----------------------------------------------------------------------===// +// Transfer function for Casts. +//===----------------------------------------------------------------------===// + +SVal SimpleSValuator::EvalCastNL(NonLoc val, QualType castTy) { + + bool isLocType = Loc::IsLocType(castTy); + + if (nonloc::LocAsInteger *LI = dyn_cast(&val)) { + if (isLocType) + return LI->getLoc(); + + // FIXME: Correctly support promotions/truncations. + ASTContext &Ctx = ValMgr.getContext(); + unsigned castSize = Ctx.getTypeSize(castTy); + if (castSize == LI->getNumBits()) + return val; + + return ValMgr.makeLocAsInteger(LI->getLoc(), castSize); + } + + if (const SymExpr *se = val.getAsSymbolicExpression()) { + ASTContext &Ctx = ValMgr.getContext(); + QualType T = Ctx.getCanonicalType(se->getType(Ctx)); + if (T == Ctx.getCanonicalType(castTy)) + return val; + + // FIXME: Remove this hack when we support symbolic truncation/extension. + // HACK: If both castTy and T are integers, ignore the cast. This is + // not a permanent solution. Eventually we want to precisely handle + // extension/truncation of symbolic integers. This prevents us from losing + // precision when we assign 'x = y' and 'y' is symbolic and x and y are + // different integer types. + if (T->isIntegerType() && castTy->isIntegerType()) + return val; + + return UnknownVal(); + } + + if (!isa(val)) + return UnknownVal(); + + // Only handle casts from integers to integers. + if (!isLocType && !castTy->isIntegerType()) + return UnknownVal(); + + llvm::APSInt i = cast(val).getValue(); + i.setIsUnsigned(castTy->isUnsignedIntegerType() || Loc::IsLocType(castTy)); + i.extOrTrunc(ValMgr.getContext().getTypeSize(castTy)); + + if (isLocType) + return ValMgr.makeIntLocVal(i); + else + return ValMgr.makeIntVal(i); +} + +SVal SimpleSValuator::EvalCastL(Loc val, QualType castTy) { + + // Casts from pointers -> pointers, just return the lval. + // + // Casts from pointers -> references, just return the lval. These + // can be introduced by the frontend for corner cases, e.g + // casting from va_list* to __builtin_va_list&. + // + if (Loc::IsLocType(castTy) || castTy->isReferenceType()) + return val; + + // FIXME: Handle transparent unions where a value can be "transparently" + // lifted into a union type. + if (castTy->isUnionType()) + return UnknownVal(); + + assert(castTy->isIntegerType()); + unsigned BitWidth = ValMgr.getContext().getTypeSize(castTy); + + if (!isa(val)) + return ValMgr.makeLocAsInteger(val, BitWidth); + + llvm::APSInt i = cast(val).getValue(); + i.setIsUnsigned(castTy->isUnsignedIntegerType() || Loc::IsLocType(castTy)); + i.extOrTrunc(BitWidth); + return ValMgr.makeIntVal(i); +} + +//===----------------------------------------------------------------------===// +// Transfer function for unary operators. +//===----------------------------------------------------------------------===// + +SVal SimpleSValuator::EvalMinus(NonLoc val) { + switch (val.getSubKind()) { + case nonloc::ConcreteIntKind: + return cast(val).evalMinus(ValMgr); + default: + return UnknownVal(); + } +} + +SVal SimpleSValuator::EvalComplement(NonLoc X) { + switch (X.getSubKind()) { + case nonloc::ConcreteIntKind: + return cast(X).evalComplement(ValMgr); + default: + return UnknownVal(); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function for binary operators. +//===----------------------------------------------------------------------===// + +static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { + switch (op) { + default: + assert(false && "Invalid opcode."); + case BinaryOperator::LT: return BinaryOperator::GE; + case BinaryOperator::GT: return BinaryOperator::LE; + case BinaryOperator::LE: return BinaryOperator::GT; + case BinaryOperator::GE: return BinaryOperator::LT; + case BinaryOperator::EQ: return BinaryOperator::NE; + case BinaryOperator::NE: return BinaryOperator::EQ; + } +} + +// Equality operators for Locs. +// FIXME: All this logic will be revamped when we have MemRegion::getLocation() +// implemented. + +static SVal EvalEquality(ValueManager &ValMgr, Loc lhs, Loc rhs, bool isEqual, + QualType resultTy) { + + switch (lhs.getSubKind()) { + default: + assert(false && "EQ/NE not implemented for this Loc."); + return UnknownVal(); + + case loc::ConcreteIntKind: { + if (SymbolRef rSym = rhs.getAsSymbol()) + return ValMgr.makeNonLoc(rSym, + isEqual ? BinaryOperator::EQ + : BinaryOperator::NE, + cast(lhs).getValue(), + resultTy); + break; + } + case loc::MemRegionKind: { + if (SymbolRef lSym = lhs.getAsLocSymbol()) { + if (isa(rhs)) { + return ValMgr.makeNonLoc(lSym, + isEqual ? BinaryOperator::EQ + : BinaryOperator::NE, + cast(rhs).getValue(), + resultTy); + } + } + break; + } + + case loc::GotoLabelKind: + break; + } + + return ValMgr.makeTruthVal(isEqual ? lhs == rhs : lhs != rhs, resultTy); +} + +SVal SimpleSValuator::EvalBinOpNN(const GRState *state, + BinaryOperator::Opcode op, + NonLoc lhs, NonLoc rhs, + QualType resultTy) { + // Handle trivial case where left-side and right-side are the same. + if (lhs == rhs) + switch (op) { + default: + break; + case BinaryOperator::EQ: + case BinaryOperator::LE: + case BinaryOperator::GE: + return ValMgr.makeTruthVal(true, resultTy); + case BinaryOperator::LT: + case BinaryOperator::GT: + case BinaryOperator::NE: + return ValMgr.makeTruthVal(false, resultTy); + } + + while (1) { + switch (lhs.getSubKind()) { + default: + return UnknownVal(); + case nonloc::LocAsIntegerKind: { + Loc lhsL = cast(lhs).getLoc(); + switch (rhs.getSubKind()) { + case nonloc::LocAsIntegerKind: + return EvalBinOpLL(op, lhsL, cast(rhs).getLoc(), + resultTy); + case nonloc::ConcreteIntKind: { + // Transform the integer into a location and compare. + ASTContext& Ctx = ValMgr.getContext(); + llvm::APSInt i = cast(rhs).getValue(); + i.setIsUnsigned(true); + i.extOrTrunc(Ctx.getTypeSize(Ctx.VoidPtrTy)); + return EvalBinOpLL(op, lhsL, ValMgr.makeLoc(i), resultTy); + } + default: + switch (op) { + case BinaryOperator::EQ: + return ValMgr.makeTruthVal(false, resultTy); + case BinaryOperator::NE: + return ValMgr.makeTruthVal(true, resultTy); + default: + // This case also handles pointer arithmetic. + return UnknownVal(); + } + } + } + case nonloc::SymExprValKind: { + // Logical not? + if (!(op == BinaryOperator::EQ && rhs.isZeroConstant())) + return UnknownVal(); + + const SymExpr *symExpr = + cast(lhs).getSymbolicExpression(); + + // Only handle ($sym op constant) for now. + if (const SymIntExpr *symIntExpr = dyn_cast(symExpr)) { + BinaryOperator::Opcode opc = symIntExpr->getOpcode(); + switch (opc) { + case BinaryOperator::LAnd: + case BinaryOperator::LOr: + assert(false && "Logical operators handled by branching logic."); + return UnknownVal(); + case BinaryOperator::Assign: + case BinaryOperator::MulAssign: + case BinaryOperator::DivAssign: + case BinaryOperator::RemAssign: + case BinaryOperator::AddAssign: + case BinaryOperator::SubAssign: + case BinaryOperator::ShlAssign: + case BinaryOperator::ShrAssign: + case BinaryOperator::AndAssign: + case BinaryOperator::XorAssign: + case BinaryOperator::OrAssign: + case BinaryOperator::Comma: + assert(false && "'=' and ',' operators handled by GRExprEngine."); + return UnknownVal(); + case BinaryOperator::PtrMemD: + case BinaryOperator::PtrMemI: + assert(false && "Pointer arithmetic not handled here."); + return UnknownVal(); + case BinaryOperator::Mul: + case BinaryOperator::Div: + case BinaryOperator::Rem: + case BinaryOperator::Add: + case BinaryOperator::Sub: + case BinaryOperator::Shl: + case BinaryOperator::Shr: + case BinaryOperator::And: + case BinaryOperator::Xor: + case BinaryOperator::Or: + // Not handled yet. + return UnknownVal(); + case BinaryOperator::LT: + case BinaryOperator::GT: + case BinaryOperator::LE: + case BinaryOperator::GE: + case BinaryOperator::EQ: + case BinaryOperator::NE: + opc = NegateComparison(opc); + assert(symIntExpr->getType(ValMgr.getContext()) == resultTy); + return ValMgr.makeNonLoc(symIntExpr->getLHS(), opc, + symIntExpr->getRHS(), resultTy); + } + } + } + case nonloc::ConcreteIntKind: { + if (isa(rhs)) { + const nonloc::ConcreteInt& lhsInt = cast(lhs); + return lhsInt.evalBinOp(ValMgr, op, cast(rhs)); + } + else { + // Swap the left and right sides and flip the operator if doing so + // allows us to better reason about the expression (this is a form + // of expression canonicalization). + NonLoc tmp = rhs; + rhs = lhs; + lhs = tmp; + + switch (op) { + case BinaryOperator::LT: op = BinaryOperator::GT; continue; + case BinaryOperator::GT: op = BinaryOperator::LT; continue; + case BinaryOperator::LE: op = BinaryOperator::GE; continue; + case BinaryOperator::GE: op = BinaryOperator::LE; continue; + case BinaryOperator::EQ: + case BinaryOperator::NE: + case BinaryOperator::Add: + case BinaryOperator::Mul: + continue; + default: + return UnknownVal(); + } + } + } + case nonloc::SymbolValKind: { + nonloc::SymbolVal *slhs = cast(&lhs); + SymbolRef Sym = slhs->getSymbol(); + + // Does the symbol simplify to a constant? If so, "fold" the constant + // by setting 'lhs' to a ConcreteInt and try again. + if (Sym->getType(ValMgr.getContext())->isIntegerType()) + if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { + // The symbol evaluates to a constant. If necessary, promote the + // folded constant (LHS) to the result type. + BasicValueFactory &BVF = ValMgr.getBasicValueFactory(); + const llvm::APSInt &lhs_I = BVF.Convert(resultTy, *Constant); + lhs = nonloc::ConcreteInt(lhs_I); + + // Also promote the RHS (if necessary). + + // For shifts, it necessary promote the RHS to the result type. + if (BinaryOperator::isShiftOp(op)) + continue; + + // Other operators: do an implicit conversion. This shouldn't be + // necessary once we support truncation/extension of symbolic values. + if (nonloc::ConcreteInt *rhs_I = dyn_cast(&rhs)){ + rhs = nonloc::ConcreteInt(BVF.Convert(resultTy, rhs_I->getValue())); + } + + continue; + } + + if (isa(rhs)) { + return ValMgr.makeNonLoc(slhs->getSymbol(), op, + cast(rhs).getValue(), + resultTy); + } + + return UnknownVal(); + } + } + } +} + +SVal SimpleSValuator::EvalBinOpLL(BinaryOperator::Opcode op, Loc lhs, Loc rhs, + QualType resultTy) { + switch (op) { + default: + return UnknownVal(); + case BinaryOperator::EQ: + case BinaryOperator::NE: + return EvalEquality(ValMgr, lhs, rhs, op == BinaryOperator::EQ, resultTy); + case BinaryOperator::LT: + case BinaryOperator::GT: + // FIXME: Generalize. For now, just handle the trivial case where + // the two locations are identical. + if (lhs == rhs) + return ValMgr.makeTruthVal(false, resultTy); + return UnknownVal(); + } +} + +SVal SimpleSValuator::EvalBinOpLN(const GRState *state, + BinaryOperator::Opcode op, + Loc lhs, NonLoc rhs, QualType resultTy) { + // Special case: 'rhs' is an integer that has the same width as a pointer and + // we are using the integer location in a comparison. Normally this cannot be + // triggered, but transfer functions like those for OSCommpareAndSwapBarrier32 + // can generate comparisons that trigger this code. + // FIXME: Are all locations guaranteed to have pointer width? + if (BinaryOperator::isEqualityOp(op)) { + if (nonloc::ConcreteInt *rhsInt = dyn_cast(&rhs)) { + const llvm::APSInt *x = &rhsInt->getValue(); + ASTContext &ctx = ValMgr.getContext(); + if (ctx.getTypeSize(ctx.VoidPtrTy) == x->getBitWidth()) { + // Convert the signedness of the integer (if necessary). + if (x->isSigned()) + x = &ValMgr.getBasicValueFactory().getValue(*x, true); + + return EvalBinOpLL(op, lhs, loc::ConcreteInt(*x), resultTy); + } + } + } + + // Delegate pointer arithmetic to the StoreManager. + return state->getStateManager().getStoreManager().EvalBinOp(op, lhs, + rhs, resultTy); +} diff --git a/lib/Checker/Store.cpp b/lib/Checker/Store.cpp new file mode 100644 index 000000000000..e524cb3d7cc3 --- /dev/null +++ b/lib/Checker/Store.cpp @@ -0,0 +1,327 @@ +//== Store.cpp - Interface for maps from Locations to Values ----*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defined the types Store and StoreManager. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/Store.h" +#include "clang/Checker/PathSensitive/GRState.h" +#include "clang/AST/CharUnits.h" + +using namespace clang; + +StoreManager::StoreManager(GRStateManager &stateMgr) + : ValMgr(stateMgr.getValueManager()), StateMgr(stateMgr), + MRMgr(ValMgr.getRegionManager()), Ctx(stateMgr.getContext()) {} + +const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, + QualType EleTy, uint64_t index) { + SVal idx = ValMgr.makeArrayIndex(index); + return MRMgr.getElementRegion(EleTy, idx, Base, ValMgr.getContext()); +} + +// FIXME: Merge with the implementation of the same method in MemRegion.cpp +static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { + if (const RecordType *RT = Ty->getAs()) { + const RecordDecl *D = RT->getDecl(); + if (!D->getDefinition()) + return false; + } + + return true; +} + +const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) { + + ASTContext& Ctx = StateMgr.getContext(); + + // Handle casts to Objective-C objects. + if (CastToTy->isObjCObjectPointerType()) + return R->StripCasts(); + + if (CastToTy->isBlockPointerType()) { + // FIXME: We may need different solutions, depending on the symbol + // involved. Blocks can be casted to/from 'id', as they can be treated + // as Objective-C objects. This could possibly be handled by enhancing + // our reasoning of downcasts of symbolic objects. + if (isa(R) || isa(R)) + return R; + + // We don't know what to make of it. Return a NULL region, which + // will be interpretted as UnknownVal. + return NULL; + } + + // Now assume we are casting from pointer to pointer. Other cases should + // already be handled. + QualType PointeeTy = CastToTy->getAs()->getPointeeType(); + QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); + + // Handle casts to void*. We just pass the region through. + if (CanonPointeeTy.getLocalUnqualifiedType() == Ctx.VoidTy) + return R; + + // Handle casts from compatible types. + if (R->isBoundable()) + if (const TypedRegion *TR = dyn_cast(R)) { + QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx)); + if (CanonPointeeTy == ObjTy) + return R; + } + + // Process region cast according to the kind of the region being cast. + switch (R->getKind()) { + case MemRegion::CXXThisRegionKind: + case MemRegion::GenericMemSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: + case MemRegion::GlobalsSpaceRegionKind: { + assert(0 && "Invalid region cast"); + break; + } + + case MemRegion::FunctionTextRegionKind: + case MemRegion::BlockTextRegionKind: + case MemRegion::BlockDataRegionKind: { + // CodeTextRegion should be cast to only a function or block pointer type, + // although they can in practice be casted to anything, e.g, void*, char*, + // etc. + // Just return the region. + return R; + } + + case MemRegion::StringRegionKind: + // FIXME: Need to handle arbitrary downcasts. + case MemRegion::SymbolicRegionKind: + case MemRegion::AllocaRegionKind: + case MemRegion::CompoundLiteralRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + case MemRegion::VarRegionKind: + case MemRegion::CXXObjectRegionKind: + return MakeElementRegion(R, PointeeTy); + + case MemRegion::ElementRegionKind: { + // If we are casting from an ElementRegion to another type, the + // algorithm is as follows: + // + // (1) Compute the "raw offset" of the ElementRegion from the + // base region. This is done by calling 'getAsRawOffset()'. + // + // (2a) If we get a 'RegionRawOffset' after calling + // 'getAsRawOffset()', determine if the absolute offset + // can be exactly divided into chunks of the size of the + // casted-pointee type. If so, create a new ElementRegion with + // the pointee-cast type as the new ElementType and the index + // being the offset divded by the chunk size. If not, create + // a new ElementRegion at offset 0 off the raw offset region. + // + // (2b) If we don't a get a 'RegionRawOffset' after calling + // 'getAsRawOffset()', it means that we are at offset 0. + // + // FIXME: Handle symbolic raw offsets. + + const ElementRegion *elementR = cast(R); + const RegionRawOffset &rawOff = elementR->getAsRawOffset(); + const MemRegion *baseR = rawOff.getRegion(); + + // If we cannot compute a raw offset, throw up our hands and return + // a NULL MemRegion*. + if (!baseR) + return NULL; + + CharUnits off = CharUnits::fromQuantity(rawOff.getByteOffset()); + + if (off.isZero()) { + // Edge case: we are at 0 bytes off the beginning of baseR. We + // check to see if type we are casting to is the same as the base + // region. If so, just return the base region. + if (const TypedRegion *TR = dyn_cast(baseR)) { + QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx)); + QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); + if (CanonPointeeTy == ObjTy) + return baseR; + } + + // Otherwise, create a new ElementRegion at offset 0. + return MakeElementRegion(baseR, PointeeTy); + } + + // We have a non-zero offset from the base region. We want to determine + // if the offset can be evenly divided by sizeof(PointeeTy). If so, + // we create an ElementRegion whose index is that value. Otherwise, we + // create two ElementRegions, one that reflects a raw offset and the other + // that reflects the cast. + + // Compute the index for the new ElementRegion. + int64_t newIndex = 0; + const MemRegion *newSuperR = 0; + + // We can only compute sizeof(PointeeTy) if it is a complete type. + if (IsCompleteType(Ctx, PointeeTy)) { + // Compute the size in **bytes**. + CharUnits pointeeTySize = Ctx.getTypeSizeInChars(PointeeTy); + + // Is the offset a multiple of the size? If so, we can layer the + // ElementRegion (with elementType == PointeeTy) directly on top of + // the base region. + if (off % pointeeTySize == 0) { + newIndex = off / pointeeTySize; + newSuperR = baseR; + } + } + + if (!newSuperR) { + // Create an intermediate ElementRegion to represent the raw byte. + // This will be the super region of the final ElementRegion. + newSuperR = MakeElementRegion(baseR, Ctx.CharTy, off.getQuantity()); + } + + return MakeElementRegion(newSuperR, PointeeTy, newIndex); + } + } + + assert(0 && "unreachable"); + return 0; +} + + +/// CastRetrievedVal - Used by subclasses of StoreManager to implement +/// implicit casts that arise from loads from regions that are reinterpreted +/// as another region. +SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R, + QualType castTy, bool performTestOnly) { + + if (castTy.isNull()) + return V; + + ASTContext &Ctx = ValMgr.getContext(); + + if (performTestOnly) { + // Automatically translate references to pointers. + QualType T = R->getValueType(Ctx); + if (const ReferenceType *RT = T->getAs()) + T = Ctx.getPointerType(RT->getPointeeType()); + + assert(ValMgr.getContext().hasSameUnqualifiedType(castTy, T)); + return V; + } + + if (const Loc *L = dyn_cast(&V)) + return ValMgr.getSValuator().EvalCastL(*L, castTy); + else if (const NonLoc *NL = dyn_cast(&V)) + return ValMgr.getSValuator().EvalCastNL(*NL, castTy); + + return V; +} + +Store StoreManager::InvalidateRegions(Store store, + const MemRegion * const *I, + const MemRegion * const *End, + const Expr *E, unsigned Count, + InvalidatedSymbols *IS) { + for ( ; I != End ; ++I) + store = InvalidateRegion(store, *I, E, Count, IS); + + return store; +} + +SVal StoreManager::getLValueFieldOrIvar(const Decl* D, SVal Base) { + if (Base.isUnknownOrUndef()) + return Base; + + Loc BaseL = cast(Base); + const MemRegion* BaseR = 0; + + switch (BaseL.getSubKind()) { + case loc::MemRegionKind: + BaseR = cast(BaseL).getRegion(); + break; + + case loc::GotoLabelKind: + // These are anormal cases. Flag an undefined value. + return UndefinedVal(); + + case loc::ConcreteIntKind: + // While these seem funny, this can happen through casts. + // FIXME: What we should return is the field offset. For example, + // add the field offset to the integer value. That way funny things + // like this work properly: &(((struct foo *) 0xa)->f) + return Base; + + default: + assert(0 && "Unhandled Base."); + return Base; + } + + // NOTE: We must have this check first because ObjCIvarDecl is a subclass + // of FieldDecl. + if (const ObjCIvarDecl *ID = dyn_cast(D)) + return loc::MemRegionVal(MRMgr.getObjCIvarRegion(ID, BaseR)); + + return loc::MemRegionVal(MRMgr.getFieldRegion(cast(D), BaseR)); +} + +SVal StoreManager::getLValueElement(QualType elementType, SVal Offset, + SVal Base) { + + // If the base is an unknown or undefined value, just return it back. + // FIXME: For absolute pointer addresses, we just return that value back as + // well, although in reality we should return the offset added to that + // value. + if (Base.isUnknownOrUndef() || isa(Base)) + return Base; + + // Only handle integer offsets... for now. + if (!isa(Offset)) + return UnknownVal(); + + const MemRegion* BaseRegion = cast(Base).getRegion(); + + // Pointer of any type can be cast and used as array base. + const ElementRegion *ElemR = dyn_cast(BaseRegion); + + // Convert the offset to the appropriate size and signedness. + Offset = ValMgr.convertToArrayIndex(Offset); + + if (!ElemR) { + // + // If the base region is not an ElementRegion, create one. + // This can happen in the following example: + // + // char *p = __builtin_alloc(10); + // p[1] = 8; + // + // Observe that 'p' binds to an AllocaRegion. + // + return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset, + BaseRegion, Ctx)); + } + + SVal BaseIdx = ElemR->getIndex(); + + if (!isa(BaseIdx)) + return UnknownVal(); + + const llvm::APSInt& BaseIdxI = cast(BaseIdx).getValue(); + const llvm::APSInt& OffI = cast(Offset).getValue(); + assert(BaseIdxI.isSigned()); + + // Compute the new index. + SVal NewIdx = nonloc::ConcreteInt( + ValMgr.getBasicValueFactory().getValue(BaseIdxI + OffI)); + + // Construct the new ElementRegion. + const MemRegion *ArrayR = ElemR->getSuperRegion(); + return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR, + Ctx)); +} diff --git a/lib/Checker/SymbolManager.cpp b/lib/Checker/SymbolManager.cpp new file mode 100644 index 000000000000..40bdcf65bca4 --- /dev/null +++ b/lib/Checker/SymbolManager.cpp @@ -0,0 +1,228 @@ +//== SymbolManager.h - Management of Symbolic Values ------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SymbolManager, a class that manages symbolic values +// created for use by GRExprEngine and related classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/SymbolManager.h" +#include "clang/Checker/PathSensitive/MemRegion.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +void SymExpr::dump() const { + dumpToStream(llvm::errs()); +} + +static void print(llvm::raw_ostream& os, BinaryOperator::Opcode Op) { + switch (Op) { + default: + assert(false && "operator printing not implemented"); + break; + case BinaryOperator::Mul: os << '*' ; break; + case BinaryOperator::Div: os << '/' ; break; + case BinaryOperator::Rem: os << '%' ; break; + case BinaryOperator::Add: os << '+' ; break; + case BinaryOperator::Sub: os << '-' ; break; + case BinaryOperator::Shl: os << "<<" ; break; + case BinaryOperator::Shr: os << ">>" ; break; + case BinaryOperator::LT: os << "<" ; break; + case BinaryOperator::GT: os << '>' ; break; + case BinaryOperator::LE: os << "<=" ; break; + case BinaryOperator::GE: os << ">=" ; break; + case BinaryOperator::EQ: os << "==" ; break; + case BinaryOperator::NE: os << "!=" ; break; + case BinaryOperator::And: os << '&' ; break; + case BinaryOperator::Xor: os << '^' ; break; + case BinaryOperator::Or: os << '|' ; break; + } +} + +void SymIntExpr::dumpToStream(llvm::raw_ostream& os) const { + os << '('; + getLHS()->dumpToStream(os); + os << ") "; + print(os, getOpcode()); + os << ' ' << getRHS().getZExtValue(); + if (getRHS().isUnsigned()) os << 'U'; +} + +void SymSymExpr::dumpToStream(llvm::raw_ostream& os) const { + os << '('; + getLHS()->dumpToStream(os); + os << ") "; + os << '('; + getRHS()->dumpToStream(os); + os << ')'; +} + +void SymbolConjured::dumpToStream(llvm::raw_ostream& os) const { + os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}'; +} + +void SymbolDerived::dumpToStream(llvm::raw_ostream& os) const { + os << "derived_$" << getSymbolID() << '{' + << getParentSymbol() << ',' << getRegion() << '}'; +} + +void SymbolRegionValue::dumpToStream(llvm::raw_ostream& os) const { + os << "reg_$" << getSymbolID() << "<" << R << ">"; +} + +const SymbolRegionValue* +SymbolManager::getRegionValueSymbol(const MemRegion* R, QualType T) { + llvm::FoldingSetNodeID profile; + SymbolRegionValue::Profile(profile, R, T); + void* InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate(); + new (SD) SymbolRegionValue(SymbolCounter, R, T); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast(SD); +} + +const SymbolConjured* +SymbolManager::getConjuredSymbol(const Stmt* E, QualType T, unsigned Count, + const void* SymbolTag) { + + llvm::FoldingSetNodeID profile; + SymbolConjured::Profile(profile, E, T, Count, SymbolTag); + void* InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate(); + new (SD) SymbolConjured(SymbolCounter, E, T, Count, SymbolTag); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast(SD); +} + +const SymbolDerived* +SymbolManager::getDerivedSymbol(SymbolRef parentSymbol, + const TypedRegion *R) { + + llvm::FoldingSetNodeID profile; + SymbolDerived::Profile(profile, parentSymbol, R); + void* InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate(); + new (SD) SymbolDerived(SymbolCounter, parentSymbol, R); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast(SD); +} + +const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs, + BinaryOperator::Opcode op, + const llvm::APSInt& v, + QualType t) { + llvm::FoldingSetNodeID ID; + SymIntExpr::Profile(ID, lhs, op, v, t); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!data) { + data = (SymIntExpr*) BPAlloc.Allocate(); + new (data) SymIntExpr(lhs, op, v, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast(data); +} + +const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs, + BinaryOperator::Opcode op, + const SymExpr *rhs, + QualType t) { + llvm::FoldingSetNodeID ID; + SymSymExpr::Profile(ID, lhs, op, rhs, t); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!data) { + data = (SymSymExpr*) BPAlloc.Allocate(); + new (data) SymSymExpr(lhs, op, rhs, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast(data); +} + +QualType SymbolConjured::getType(ASTContext&) const { + return T; +} + + +QualType SymbolDerived::getType(ASTContext& Ctx) const { + return R->getValueType(Ctx); +} + +QualType SymbolRegionValue::getType(ASTContext& C) const { + if (!T.isNull()) + return T; + + if (const TypedRegion* TR = dyn_cast(R)) + return TR->getValueType(C); + + return QualType(); +} + +SymbolManager::~SymbolManager() {} + +bool SymbolManager::canSymbolicate(QualType T) { + return Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType()); +} + +void SymbolReaper::markLive(SymbolRef sym) { + TheLiving.insert(sym); + TheDead.erase(sym); +} + +bool SymbolReaper::maybeDead(SymbolRef sym) { + if (isLive(sym)) + return false; + + TheDead.insert(sym); + return true; +} + +bool SymbolReaper::isLive(SymbolRef sym) { + if (TheLiving.count(sym)) + return true; + + if (const SymbolDerived *derived = dyn_cast(sym)) { + if (isLive(derived->getParentSymbol())) { + markLive(sym); + return true; + } + return false; + } + + // Interogate the symbol. It may derive from an input value to + // the analyzed function/method. + return isa(sym); +} + +bool SymbolReaper::isLive(const Stmt *Loc, const VarRegion *VR) const { + const StackFrameContext *SFC = VR->getStackFrame(); + return SFC == CurrentStackFrame ? Liveness.isLive(Loc, VR->getDecl()) : true; +} + +SymbolVisitor::~SymbolVisitor() {} diff --git a/lib/Checker/UndefBranchChecker.cpp b/lib/Checker/UndefBranchChecker.cpp new file mode 100644 index 000000000000..e047b187b108 --- /dev/null +++ b/lib/Checker/UndefBranchChecker.cpp @@ -0,0 +1,117 @@ +//=== UndefBranchChecker.cpp -----------------------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines UndefBranchChecker, which checks for undefined branch +// condition. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/Checker.h" + +using namespace clang; + +namespace { + +class UndefBranchChecker : public Checker { + BuiltinBug *BT; + + struct FindUndefExpr { + GRStateManager& VM; + const GRState* St; + + FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} + + Expr* FindExpr(Expr* Ex) { + if (!MatchesCriteria(Ex)) + return 0; + + for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end();I!=E;++I) + if (Expr* ExI = dyn_cast_or_null(*I)) { + Expr* E2 = FindExpr(ExI); + if (E2) return E2; + } + + return Ex; + } + + bool MatchesCriteria(Expr* Ex) { return St->getSVal(Ex).isUndef(); } + }; + +public: + UndefBranchChecker() : BT(0) {} + static void *getTag(); + void VisitBranchCondition(GRBranchNodeBuilder &Builder, GRExprEngine &Eng, + Stmt *Condition, void *tag); +}; + +} + +void clang::RegisterUndefBranchChecker(GRExprEngine &Eng) { + Eng.registerCheck(new UndefBranchChecker()); +} + +void *UndefBranchChecker::getTag() { + static int x; + return &x; +} + +void UndefBranchChecker::VisitBranchCondition(GRBranchNodeBuilder &Builder, + GRExprEngine &Eng, + Stmt *Condition, void *tag) { + const GRState *state = Builder.getState(); + SVal X = state->getSVal(Condition); + if (X.isUndef()) { + ExplodedNode *N = Builder.generateNode(state, true); + if (N) { + N->markAsSink(); + if (!BT) + BT = new BuiltinBug("Branch condition evaluates to a garbage value"); + + // What's going on here: we want to highlight the subexpression of the + // condition that is the most likely source of the "uninitialized + // branch condition." We do a recursive walk of the condition's + // subexpressions and roughly look for the most nested subexpression + // that binds to Undefined. We then highlight that expression's range. + BlockEdge B = cast(N->getLocation()); + Expr* Ex = cast(B.getSrc()->getTerminatorCondition()); + assert (Ex && "Block must have a terminator."); + + // Get the predecessor node and check if is a PostStmt with the Stmt + // being the terminator condition. We want to inspect the state + // of that node instead because it will contain main information about + // the subexpressions. + assert (!N->pred_empty()); + + // Note: any predecessor will do. They should have identical state, + // since all the BlockEdge did was act as an error sink since the value + // had to already be undefined. + ExplodedNode *PrevN = *N->pred_begin(); + ProgramPoint P = PrevN->getLocation(); + const GRState* St = N->getState(); + + if (PostStmt* PS = dyn_cast(&P)) + if (PS->getStmt() == Ex) + St = PrevN->getState(); + + FindUndefExpr FindIt(Eng.getStateManager(), St); + Ex = FindIt.FindExpr(Ex); + + // Emit the bug report. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(),N); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + R->addRange(Ex->getSourceRange()); + + Eng.getBugReporter().EmitReport(R); + } + + Builder.markInfeasible(true); + Builder.markInfeasible(false); + } +} diff --git a/lib/Checker/UndefCapturedBlockVarChecker.cpp b/lib/Checker/UndefCapturedBlockVarChecker.cpp new file mode 100644 index 000000000000..a8d7284b40ac --- /dev/null +++ b/lib/Checker/UndefCapturedBlockVarChecker.cpp @@ -0,0 +1,101 @@ +// UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker detects blocks that capture uninitialized values. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +namespace { +class UndefCapturedBlockVarChecker + : public CheckerVisitor { + BugType *BT; + +public: + UndefCapturedBlockVarChecker() : BT(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PostVisitBlockExpr(CheckerContext &C, const BlockExpr *BE); +}; +} // end anonymous namespace + +void clang::RegisterUndefCapturedBlockVarChecker(GRExprEngine &Eng) { + Eng.registerCheck(new UndefCapturedBlockVarChecker()); +} + +static const BlockDeclRefExpr *FindBlockDeclRefExpr(const Stmt *S, + const VarDecl *VD){ + if (const BlockDeclRefExpr *BR = dyn_cast(S)) + if (BR->getDecl() == VD) + return BR; + + for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); + I!=E; ++I) + if (const Stmt *child = *I) { + const BlockDeclRefExpr *BR = FindBlockDeclRefExpr(child, VD); + if (BR) + return BR; + } + + return NULL; +} + +void +UndefCapturedBlockVarChecker::PostVisitBlockExpr(CheckerContext &C, + const BlockExpr *BE) { + if (!BE->hasBlockDeclRefExprs()) + return; + + const GRState *state = C.getState(); + const BlockDataRegion *R = + cast(state->getSVal(BE).getAsRegion()); + + BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), + E = R->referenced_vars_end(); + + for (; I != E; ++I) { + // This VarRegion is the region associated with the block; we need + // the one associated with the encompassing context. + const VarRegion *VR = *I; + const VarDecl *VD = VR->getDecl(); + + if (VD->getAttr() || !VD->hasLocalStorage()) + continue; + + // Get the VarRegion associated with VD in the local stack frame. + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + VR = C.getValueManager().getRegionManager().getVarRegion(VD, LC); + + if (state->getSVal(VR).isUndef()) + if (ExplodedNode *N = C.GenerateSink()) { + if (!BT) + BT = new BuiltinBug("Captured block variable is uninitialized"); + + // Generate a bug report. + llvm::SmallString<128> buf; + llvm::raw_svector_ostream os(buf); + + os << "Variable '" << VD->getName() << "' is captured by block with " + "a garbage value"; + + EnhancedBugReport *R = new EnhancedBugReport(*BT, os.str(), N); + if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) + R->addRange(Ex->getSourceRange()); + R->addVisitorCreator(bugreporter::registerFindLastStore, VR); + // need location of block + C.EmitReport(R); + } + } +} diff --git a/lib/Checker/UndefResultChecker.cpp b/lib/Checker/UndefResultChecker.cpp new file mode 100644 index 000000000000..fb2283a62044 --- /dev/null +++ b/lib/Checker/UndefResultChecker.cpp @@ -0,0 +1,86 @@ +//=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefResultChecker, a builtin check in GRExprEngine that +// performs checks for undefined results of non-assignment binary operators. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/BugReporter/BugReporter.h" + +using namespace clang; + +namespace { +class UndefResultChecker + : public CheckerVisitor { + + BugType *BT; + +public: + UndefResultChecker() : BT(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} // end anonymous namespace + +void clang::RegisterUndefResultChecker(GRExprEngine &Eng) { + Eng.registerCheck(new UndefResultChecker()); +} + +void UndefResultChecker::PostVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + const GRState *state = C.getState(); + if (state->getSVal(B).isUndef()) { + // Generate an error node. + ExplodedNode *N = C.GenerateSink(); + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Result of operation is garbage or undefined"); + + llvm::SmallString<256> sbuf; + llvm::raw_svector_ostream OS(sbuf); + const Expr *Ex = NULL; + bool isLeft = true; + + if (state->getSVal(B->getLHS()).isUndef()) { + Ex = B->getLHS()->IgnoreParenCasts(); + isLeft = true; + } + else if (state->getSVal(B->getRHS()).isUndef()) { + Ex = B->getRHS()->IgnoreParenCasts(); + isLeft = false; + } + + if (Ex) { + OS << "The " << (isLeft ? "left" : "right") + << " operand of '" + << BinaryOperator::getOpcodeStr(B->getOpcode()) + << "' is a garbage value"; + } + else { + // Neither operand was undefined, but the result is undefined. + OS << "The result of the '" + << BinaryOperator::getOpcodeStr(B->getOpcode()) + << "' expression is undefined"; + } + EnhancedBugReport *report = new EnhancedBugReport(*BT, OS.str(), N); + if (Ex) { + report->addRange(Ex->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + } + else + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, B); + C.EmitReport(report); + } +} diff --git a/lib/Checker/UndefinedArraySubscriptChecker.cpp b/lib/Checker/UndefinedArraySubscriptChecker.cpp new file mode 100644 index 000000000000..a2792ad17ba1 --- /dev/null +++ b/lib/Checker/UndefinedArraySubscriptChecker.cpp @@ -0,0 +1,56 @@ +//===--- UndefinedArraySubscriptChecker.h ----------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedArraySubscriptChecker, a builtin check in GRExprEngine +// that performs checks for undefined array subscripts. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class UndefinedArraySubscriptChecker + : public CheckerVisitor { + BugType *BT; +public: + UndefinedArraySubscriptChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PreVisitArraySubscriptExpr(CheckerContext &C, + const ArraySubscriptExpr *A); +}; +} // end anonymous namespace + +void clang::RegisterUndefinedArraySubscriptChecker(GRExprEngine &Eng) { + Eng.registerCheck(new UndefinedArraySubscriptChecker()); +} + +void +UndefinedArraySubscriptChecker::PreVisitArraySubscriptExpr(CheckerContext &C, + const ArraySubscriptExpr *A) { + if (C.getState()->getSVal(A->getIdx()).isUndef()) { + if (ExplodedNode *N = C.GenerateSink()) { + if (!BT) + BT = new BuiltinBug("Array subscript is undefined"); + + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + R->addRange(A->getIdx()->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + A->getIdx()); + C.EmitReport(R); + } + } +} diff --git a/lib/Checker/UndefinedAssignmentChecker.cpp b/lib/Checker/UndefinedAssignmentChecker.cpp new file mode 100644 index 000000000000..7c33c1d39235 --- /dev/null +++ b/lib/Checker/UndefinedAssignmentChecker.cpp @@ -0,0 +1,79 @@ +//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedAssginmentChecker, a builtin check in GRExprEngine that +// checks for assigning undefined values. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/BugReporter/BugReporter.h" + +using namespace clang; + +namespace { +class UndefinedAssignmentChecker + : public CheckerVisitor { + BugType *BT; +public: + UndefinedAssignmentChecker() : BT(0) {} + static void *getTag(); + virtual void PreVisitBind(CheckerContext &C, const Stmt *AssignE, + const Stmt *StoreE, SVal location, + SVal val); +}; +} + +void clang::RegisterUndefinedAssignmentChecker(GRExprEngine &Eng){ + Eng.registerCheck(new UndefinedAssignmentChecker()); +} + +void *UndefinedAssignmentChecker::getTag() { + static int x = 0; + return &x; +} + +void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, + const Stmt *AssignE, + const Stmt *StoreE, + SVal location, + SVal val) { + if (!val.isUndef()) + return; + + ExplodedNode *N = C.GenerateSink(); + + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Assigned value is garbage or undefined"); + + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + + if (AssignE) { + const Expr *ex = 0; + + if (const BinaryOperator *B = dyn_cast(AssignE)) + ex = B->getRHS(); + else if (const DeclStmt *DS = dyn_cast(AssignE)) { + const VarDecl* VD = dyn_cast(DS->getSingleDecl()); + ex = VD->getInit(); + } + if (ex) { + R->addRange(ex->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, ex); + } + } + + C.EmitReport(R); +} + diff --git a/lib/Checker/VLASizeChecker.cpp b/lib/Checker/VLASizeChecker.cpp new file mode 100644 index 000000000000..51ad1e2daf5e --- /dev/null +++ b/lib/Checker/VLASizeChecker.cpp @@ -0,0 +1,96 @@ +//=== VLASizeChecker.cpp - Undefined dereference checker --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines VLASizeChecker, a builtin check in GRExprEngine that +// performs checks for declaration of VLA of undefined or zero size. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Checker/BugReporter/BugReporter.h" + +using namespace clang; + +namespace { +class VLASizeChecker : public CheckerVisitor { + BugType *BT_zero; + BugType *BT_undef; + +public: + VLASizeChecker() : BT_zero(0), BT_undef(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS); +}; +} // end anonymous namespace + +void clang::RegisterVLASizeChecker(GRExprEngine &Eng) { + Eng.registerCheck(new VLASizeChecker()); +} + +void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { + if (!DS->isSingleDecl()) + return; + + const VarDecl *VD = dyn_cast(DS->getSingleDecl()); + if (!VD) + return; + + const VariableArrayType *VLA + = C.getASTContext().getAsVariableArrayType(VD->getType()); + if (!VLA) + return; + + // FIXME: Handle multi-dimensional VLAs. + const Expr* SE = VLA->getSizeExpr(); + const GRState *state = C.getState(); + SVal sizeV = state->getSVal(SE); + + if (sizeV.isUndef()) { + // Generate an error node. + ExplodedNode *N = C.GenerateSink(); + if (!N) + return; + + if (!BT_undef) + BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " + "garbage value as its size"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); + report->addRange(SE->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + C.EmitReport(report); + return; + } + + // Check if the size is zero. + DefinedOrUnknownSVal sizeD = cast(sizeV); + + const GRState *stateNotZero, *stateZero; + llvm::tie(stateNotZero, stateZero) = state->Assume(sizeD); + + if (stateZero && !stateNotZero) { + ExplodedNode* N = C.GenerateSink(stateZero); + if (!BT_zero) + BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " + "size"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); + report->addRange(SE->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + C.EmitReport(report); + return; + } + + // From this point on, assume that the size is not zero. + C.addTransition(stateNotZero); +} diff --git a/lib/Checker/ValueManager.cpp b/lib/Checker/ValueManager.cpp new file mode 100644 index 000000000000..5359489a2299 --- /dev/null +++ b/lib/Checker/ValueManager.cpp @@ -0,0 +1,153 @@ +//== ValueManager.cpp - Aggregate manager of symbols and SVals --*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ValueManager, a class that manages symbolic values +// and SVals created for use by GRExprEngine and related classes. It +// wraps and owns SymbolManager, MemRegionManager, and BasicValueFactory. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/ValueManager.h" +#include "clang/Analysis/AnalysisContext.h" + +using namespace clang; +using namespace llvm; + +//===----------------------------------------------------------------------===// +// Utility methods for constructing SVals. +//===----------------------------------------------------------------------===// + +DefinedOrUnknownSVal ValueManager::makeZeroVal(QualType T) { + if (Loc::IsLocType(T)) + return makeNull(); + + if (T->isIntegerType()) + return makeIntVal(0, T); + + // FIXME: Handle floats. + // FIXME: Handle structs. + return UnknownVal(); +} + +//===----------------------------------------------------------------------===// +// Utility methods for constructing Non-Locs. +//===----------------------------------------------------------------------===// + +NonLoc ValueManager::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const APSInt& v, QualType T) { + // The Environment ensures we always get a persistent APSInt in + // BasicValueFactory, so we don't need to get the APSInt from + // BasicValueFactory again. + assert(!Loc::IsLocType(T)); + return nonloc::SymExprVal(SymMgr.getSymIntExpr(lhs, op, v, T)); +} + +NonLoc ValueManager::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const SymExpr *rhs, QualType T) { + assert(SymMgr.getType(lhs) == SymMgr.getType(rhs)); + assert(!Loc::IsLocType(T)); + return nonloc::SymExprVal(SymMgr.getSymSymExpr(lhs, op, rhs, T)); +} + + +SVal ValueManager::convertToArrayIndex(SVal V) { + if (V.isUnknownOrUndef()) + return V; + + // Common case: we have an appropriately sized integer. + if (nonloc::ConcreteInt* CI = dyn_cast(&V)) { + const llvm::APSInt& I = CI->getValue(); + if (I.getBitWidth() == ArrayIndexWidth && I.isSigned()) + return V; + } + + return SVator->EvalCastNL(cast(V), ArrayIndexTy); +} + +DefinedOrUnknownSVal ValueManager::getRegionValueSymbolVal(const MemRegion* R, + QualType T) { + + if (T.isNull()) { + const TypedRegion* TR = cast(R); + T = TR->getValueType(SymMgr.getContext()); + } + + if (!SymbolManager::canSymbolicate(T)) + return UnknownVal(); + + SymbolRef sym = SymMgr.getRegionValueSymbol(R, T); + + if (Loc::IsLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedOrUnknownSVal ValueManager::getConjuredSymbolVal(const void *SymbolTag, + const Expr *E, + unsigned Count) { + QualType T = E->getType(); + + if (!SymbolManager::canSymbolicate(T)) + return UnknownVal(); + + SymbolRef sym = SymMgr.getConjuredSymbol(E, Count, SymbolTag); + + if (Loc::IsLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedOrUnknownSVal ValueManager::getConjuredSymbolVal(const void *SymbolTag, + const Expr *E, + QualType T, + unsigned Count) { + + if (!SymbolManager::canSymbolicate(T)) + return UnknownVal(); + + SymbolRef sym = SymMgr.getConjuredSymbol(E, T, Count, SymbolTag); + + if (Loc::IsLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + + +DefinedOrUnknownSVal +ValueManager::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, + const TypedRegion *R) { + QualType T = R->getValueType(R->getContext()); + + if (!SymbolManager::canSymbolicate(T)) + return UnknownVal(); + + SymbolRef sym = SymMgr.getDerivedSymbol(parentSymbol, R); + + if (Loc::IsLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedSVal ValueManager::getFunctionPointer(const FunctionDecl* FD) { + return loc::MemRegionVal(MemMgr.getFunctionTextRegion(FD)); +} + +DefinedSVal ValueManager::getBlockPointer(const BlockDecl *D, + CanQualType locTy, + const LocationContext *LC) { + const BlockTextRegion *BC = + MemMgr.getBlockTextRegion(D, locTy, LC->getAnalysisContext()); + const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, LC); + return loc::MemRegionVal(BD); +} + diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index ca5b6fa97c26..46b62441d6e4 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/lib/CodeGen/CGBlocks.cpp @@ -13,6 +13,7 @@ #include "CGDebugInfo.h" #include "CodeGenFunction.h" +#include "CGObjCRuntime.h" #include "CodeGenModule.h" #include "clang/AST/DeclObjC.h" #include "llvm/Module.h" @@ -176,7 +177,7 @@ llvm::Value *CodeGenFunction::BuildBlockLiteralTmp(const BlockExpr *BE) { // block literal. // __invoke CharUnits subBlockSize; - uint64_t subBlockAlign; + CharUnits subBlockAlign; llvm::SmallVector subBlockDeclRefDecls; bool subBlockHasCopyDispose = false; llvm::Function *Fn @@ -249,7 +250,7 @@ llvm::Value *CodeGenFunction::BuildBlockLiteralTmp(const BlockExpr *BE) { llvm::StructType *Ty = llvm::StructType::get(VMContext, Types, true); llvm::AllocaInst *A = CreateTempAlloca(Ty); - A->setAlignment(subBlockAlign); + A->setAlignment(subBlockAlign.getQuantity()); V = A; std::vector NoteForHelper(subBlockDeclRefDecls.size()); @@ -355,7 +356,21 @@ llvm::Value *CodeGenFunction::BuildBlockLiteralTmp(const BlockExpr *BE) { } QualType BPT = BE->getType(); - return Builder.CreateBitCast(V, ConvertType(BPT)); + V = Builder.CreateBitCast(V, ConvertType(BPT)); + // See if this is a __weak block variable and the must call objc_read_weak + // on it. + const FunctionType *ftype = BPT->getPointeeType()->getAs(); + QualType RES = ftype->getResultType(); + if (RES.isObjCGCWeak()) { + // Must cast argument to id* + const llvm::Type *ObjectPtrTy = + ConvertType(CGM.getContext().getObjCIdType()); + const llvm::Type *PtrObjectPtrTy = + llvm::PointerType::getUnqual(ObjectPtrTy); + V = Builder.CreateBitCast(V, PtrObjectPtrTy); + V = CGM.getObjCRuntime().EmitObjCWeakRead(*this, V); + } + return V; } @@ -496,10 +511,12 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr* E, // Load the function. llvm::Value *Func = Builder.CreateLoad(FuncPtr, "tmp"); - QualType ResultType = FnType->getAs()->getResultType(); + const FunctionType *FuncTy = FnType->getAs(); + QualType ResultType = FuncTy->getResultType(); const CGFunctionInfo &FnInfo = - CGM.getTypes().getFunctionInfo(ResultType, Args); + CGM.getTypes().getFunctionInfo(ResultType, Args, FuncTy->getCallConv(), + FuncTy->getNoReturnAttr()); // Cast the function pointer to the right type. const llvm::Type *BlockFTy = @@ -593,8 +610,8 @@ BlockModule::GetAddrOfGlobalBlock(const BlockExpr *BE, const char * n) { // Block literal size. For global blocks we just use the size of the generic // block literal struct. - CharUnits BlockLiteralSize = CharUnits::fromQuantity( - TheTargetData.getTypeStoreSizeInBits(getGenericBlockLiteralType()) / 8); + CharUnits BlockLiteralSize = + CGM.GetTargetTypeStoreSize(getGenericBlockLiteralType()); DescriptorFields[1] = llvm::ConstantInt::get(UnsignedLongTy,BlockLiteralSize.getQuantity()); @@ -615,7 +632,7 @@ BlockModule::GetAddrOfGlobalBlock(const BlockExpr *BE, const char * n) { CodeGenFunction::BlockInfo Info(0, n); CharUnits subBlockSize; - uint64_t subBlockAlign; + CharUnits subBlockAlign; llvm::SmallVector subBlockDeclRefDecls; bool subBlockHasCopyDispose = false; llvm::DenseMap LocalDeclMap; @@ -678,7 +695,7 @@ CodeGenFunction::GenerateBlockFunction(const BlockExpr *BExpr, const Decl *OuterFuncDecl, llvm::DenseMap ldm, CharUnits &Size, - uint64_t &Align, + CharUnits &Align, llvm::SmallVector &subBlockDeclRefDecls, bool &subBlockHasCopyDispose) { @@ -698,13 +715,14 @@ CodeGenFunction::GenerateBlockFunction(const BlockExpr *BExpr, LocalDeclMap[VD] = i->second; } - BlockOffset = CharUnits::fromQuantity( - CGM.getTargetData() - .getTypeStoreSizeInBits(CGM.getGenericBlockLiteralType()) / 8); - BlockAlign = getContext().getTypeAlign(getContext().VoidPtrTy) / 8; + BlockOffset = + CGM.GetTargetTypeStoreSize(CGM.getGenericBlockLiteralType()); + BlockAlign = getContext().getTypeAlignInChars(getContext().VoidPtrTy); const FunctionType *BlockFunctionType = BExpr->getFunctionType(); QualType ResultType; + CallingConv CC = BlockFunctionType->getCallConv(); + bool NoReturn = BlockFunctionType->getNoReturnAttr(); bool IsVariadic; if (const FunctionProtoType *FTy = dyn_cast(BlockFunctionType)) { @@ -743,7 +761,7 @@ CodeGenFunction::GenerateBlockFunction(const BlockExpr *BExpr, Args.push_back(std::make_pair(*i, (*i)->getType())); const CGFunctionInfo &FI = - CGM.getTypes().getFunctionInfo(ResultType, Args); + CGM.getTypes().getFunctionInfo(ResultType, Args, CC, NoReturn); CodeGenTypes &Types = CGM.getTypes(); const llvm::FunctionType *LTy = Types.GetFunctionType(FI, IsVariadic); @@ -777,10 +795,9 @@ CodeGenFunction::GenerateBlockFunction(const BlockExpr *BExpr, if (CGDebugInfo *DI = getDebugInfo()) { // Emit debug information for all the BlockDeclRefDecls. - for (unsigned i=0; i < BlockDeclRefDecls.size(); ++i) { - const Expr *E = BlockDeclRefDecls[i]; - const BlockDeclRefExpr *BDRE = dyn_cast(E); - if (BDRE) { + for (unsigned i = 0, e = BlockDeclRefDecls.size(); i != e; ++i) { + if (const BlockDeclRefExpr *BDRE = + dyn_cast(BlockDeclRefDecls[i])) { const ValueDecl *D = BDRE->getDecl(); DI->setLocation(D->getLocation()); DI->EmitDeclareOfBlockDeclRefVariable(BDRE, @@ -798,9 +815,10 @@ CodeGenFunction::GenerateBlockFunction(const BlockExpr *BExpr, FinishFunction(cast(BExpr->getBody())->getRBracLoc()); // The runtime needs a minimum alignment of a void *. - uint64_t MinAlign = getContext().getTypeAlign(getContext().VoidPtrTy) / 8; + CharUnits MinAlign = getContext().getTypeAlignInChars(getContext().VoidPtrTy); BlockOffset = CharUnits::fromQuantity( - llvm::RoundUpToAlignment(BlockOffset.getQuantity(), MinAlign)); + llvm::RoundUpToAlignment(BlockOffset.getQuantity(), + MinAlign.getQuantity())); Size = BlockOffset; Align = BlockAlign; @@ -813,20 +831,20 @@ CharUnits BlockFunction::getBlockOffset(const BlockDeclRefExpr *BDRE) { const ValueDecl *D = dyn_cast(BDRE->getDecl()); CharUnits Size = getContext().getTypeSizeInChars(D->getType()); - uint64_t Align = getContext().getDeclAlignInBytes(D); + CharUnits Align = getContext().getDeclAlign(D); if (BDRE->isByRef()) { Size = getContext().getTypeSizeInChars(getContext().VoidPtrTy); - Align = getContext().getTypeAlign(getContext().VoidPtrTy) / 8; + Align = getContext().getTypeAlignInChars(getContext().VoidPtrTy); } - assert ((Align > 0) && "alignment must be 1 byte or more"); + assert ((Align.isPositive()) && "alignment must be 1 byte or more"); CharUnits OldOffset = BlockOffset; // Ensure proper alignment, even if it means we have to have a gap BlockOffset = CharUnits::fromQuantity( - llvm::RoundUpToAlignment(BlockOffset.getQuantity(), Align)); + llvm::RoundUpToAlignment(BlockOffset.getQuantity(), Align.getQuantity())); BlockAlign = std::max(Align, BlockAlign); CharUnits Pad = BlockOffset - OldOffset; @@ -868,7 +886,7 @@ GenerateCopyHelperFunction(bool BlockHasCopyDispose, const llvm::StructType *T, Args.push_back(std::make_pair(Src, Src->getType())); const CGFunctionInfo &FI = - CGM.getTypes().getFunctionInfo(R, Args); + CGM.getTypes().getFunctionInfo(R, Args, CC_Default, false); // FIXME: We'd like to put these into a mergable by content, with // internal linkage. @@ -949,7 +967,7 @@ GenerateDestroyHelperFunction(bool BlockHasCopyDispose, Args.push_back(std::make_pair(Src, Src->getType())); const CGFunctionInfo &FI = - CGM.getTypes().getFunctionInfo(R, Args); + CGM.getTypes().getFunctionInfo(R, Args, CC_Default, false); // FIXME: We'd like to put these into a mergable by content, with // internal linkage. @@ -1033,7 +1051,7 @@ GeneratebyrefCopyHelperFunction(const llvm::Type *T, int flag) { Args.push_back(std::make_pair(Src, Src->getType())); const CGFunctionInfo &FI = - CGM.getTypes().getFunctionInfo(R, Args); + CGM.getTypes().getFunctionInfo(R, Args, CC_Default, false); CodeGenTypes &Types = CGM.getTypes(); const llvm::FunctionType *LTy = Types.GetFunctionType(FI, false); @@ -1096,7 +1114,7 @@ BlockFunction::GeneratebyrefDestroyHelperFunction(const llvm::Type *T, Args.push_back(std::make_pair(Src, Src->getType())); const CGFunctionInfo &FI = - CGM.getTypes().getFunctionInfo(R, Args); + CGM.getTypes().getFunctionInfo(R, Args, CC_Default, false); CodeGenTypes &Types = CGM.getTypes(); const llvm::FunctionType *LTy = Types.GetFunctionType(FI, false); diff --git a/lib/CodeGen/CGBlocks.h b/lib/CodeGen/CGBlocks.h index f42244c52e0e..a9f5ae05c109 100644 --- a/lib/CodeGen/CGBlocks.h +++ b/lib/CodeGen/CGBlocks.h @@ -177,8 +177,9 @@ public: /// BlockOffset - The offset in bytes for the next allocation of an /// imported block variable. CharUnits BlockOffset; - /// BlockAlign - Maximal alignment needed for the Block expressed in bytes. - uint64_t BlockAlign; + /// BlockAlign - Maximal alignment needed for the Block expressed in + /// characters. + CharUnits BlockAlign; /// getBlockOffset - Allocate an offset for the ValueDecl from a /// BlockDeclRefExpr in a block literal (BlockExpr). diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index f11d52e4334c..beaf7b89c003 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -304,6 +304,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Size = Builder.CreateIntCast(Size, llvm::Type::getInt32Ty(VMContext), false, "tmp"); return RValue::get(Builder.CreateAlloca(llvm::Type::getInt8Ty(VMContext), Size, "tmp")); } + case Builtin::BIbzero: case Builtin::BI__builtin_bzero: { Value *Address = EmitScalarExpr(E->getArg(0)); Builder.CreateCall4(CGM.getMemSetFn(), Address, @@ -656,7 +657,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, // Unknown builtin, for now just dump it out and return undef. if (hasAggregateLLVMType(E->getType())) - return RValue::getAggregate(CreateTempAlloca(ConvertType(E->getType()))); + return RValue::getAggregate(CreateMemTemp(E->getType())); return RValue::get(llvm::UndefValue::get(ConvertType(E->getType()))); } diff --git a/lib/CodeGen/CGCXX.cpp b/lib/CodeGen/CGCXX.cpp index 4323f84d9633..28c4c6b4b57b 100644 --- a/lib/CodeGen/CGCXX.cpp +++ b/lib/CodeGen/CGCXX.cpp @@ -165,7 +165,8 @@ CodeGenFunction::GenerateCovariantThunk(llvm::Function *Fn, GlobalDecl GD, bool Extern, const CovariantThunkAdjustment &Adjustment) { const CXXMethodDecl *MD = cast(GD.getDecl()); - QualType ResultType = MD->getType()->getAs()->getResultType(); + const FunctionProtoType *FPT = MD->getType()->getAs(); + QualType ResultType = FPT->getResultType(); FunctionArgList Args; ImplicitParamDecl *ThisDecl = @@ -190,7 +191,6 @@ CodeGenFunction::GenerateCovariantThunk(llvm::Function *Fn, StartFunction(FD, ResultType, Fn, Args, SourceLocation()); // generate body - const FunctionProtoType *FPT = MD->getType()->getAs(); const llvm::Type *Ty = CGM.getTypes().GetFunctionType(CGM.getTypes().getFunctionInfo(MD), FPT->isVariadic()); @@ -232,7 +232,9 @@ CodeGenFunction::GenerateCovariantThunk(llvm::Function *Fn, CallArgs.push_back(std::make_pair(EmitCallArg(Arg, ArgType), ArgType)); } - RValue RV = EmitCall(CGM.getTypes().getFunctionInfo(ResultType, CallArgs), + RValue RV = EmitCall(CGM.getTypes().getFunctionInfo(ResultType, CallArgs, + FPT->getCallConv(), + FPT->getNoReturnAttr()), Callee, ReturnValueSlot(), CallArgs, MD); if (ShouldAdjustReturnPointer && !Adjustment.ReturnAdjustment.isEmpty()) { bool CanBeZero = !(ResultType->isReferenceType() diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 2dda0b883f04..b064c125ad00 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -33,12 +33,19 @@ using namespace CodeGen; // FIXME: Use iterator and sidestep silly type array creation. +static unsigned ClangCallConvToLLVMCallConv(CallingConv CC) { + switch (CC) { + default: return llvm::CallingConv::C; + case CC_X86StdCall: return llvm::CallingConv::X86_StdCall; + case CC_X86FastCall: return llvm::CallingConv::X86_FastCall; + } +} + const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const FunctionNoProtoType *FTNP) { - // FIXME: Set calling convention correctly, it needs to be associated with the - // type somehow. return getFunctionInfo(FTNP->getResultType(), - llvm::SmallVector(), 0); + llvm::SmallVector(), + FTNP->getCallConv(), FTNP->getNoReturnAttr()); } const @@ -47,20 +54,19 @@ CGFunctionInfo &CodeGenTypes::getFunctionInfo(const FunctionProtoType *FTP) { // FIXME: Kill copy. for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i) ArgTys.push_back(FTP->getArgType(i)); - // FIXME: Set calling convention correctly, it needs to be associated with the - // type somehow. - return getFunctionInfo(FTP->getResultType(), ArgTys, 0); + return getFunctionInfo(FTP->getResultType(), ArgTys, + FTP->getCallConv(), FTP->getNoReturnAttr()); } -static unsigned getCallingConventionForDecl(const Decl *D) { +static CallingConv getCallingConventionForDecl(const Decl *D) { // Set the appropriate calling convention for the Function. if (D->hasAttr()) - return llvm::CallingConv::X86_StdCall; + return CC_X86StdCall; if (D->hasAttr()) - return llvm::CallingConv::X86_FastCall; + return CC_X86FastCall; - return llvm::CallingConv::C; + return CC_C; } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const CXXRecordDecl *RD, @@ -75,7 +81,8 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const CXXRecordDecl *RD, // FIXME: Set calling convention correctly, it needs to be associated with the // type somehow. - return getFunctionInfo(FTP->getResultType(), ArgTys, 0); + return getFunctionInfo(FTP->getResultType(), ArgTys, + FTP->getCallConv(), FTP->getNoReturnAttr()); } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const CXXMethodDecl *MD) { @@ -87,8 +94,8 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const CXXMethodDecl *MD) { const FunctionProtoType *FTP = MD->getType()->getAs(); for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i) ArgTys.push_back(FTP->getArgType(i)); - return getFunctionInfo(FTP->getResultType(), ArgTys, - getCallingConventionForDecl(MD)); + return getFunctionInfo(FTP->getResultType(), ArgTys, FTP->getCallConv(), + FTP->getNoReturnAttr()); } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const CXXConstructorDecl *D, @@ -105,8 +112,8 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const CXXConstructorDecl *D, const FunctionProtoType *FTP = D->getType()->getAs(); for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i) ArgTys.push_back(FTP->getArgType(i)); - return getFunctionInfo(FTP->getResultType(), ArgTys, - getCallingConventionForDecl(D)); + return getFunctionInfo(FTP->getResultType(), ArgTys, FTP->getCallConv(), + FTP->getNoReturnAttr()); } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const CXXDestructorDecl *D, @@ -123,8 +130,8 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const CXXDestructorDecl *D, const FunctionProtoType *FTP = D->getType()->getAs(); for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i) ArgTys.push_back(FTP->getArgType(i)); - return getFunctionInfo(FTP->getResultType(), ArgTys, - getCallingConventionForDecl(D)); + return getFunctionInfo(FTP->getResultType(), ArgTys, FTP->getCallConv(), + FTP->getNoReturnAttr()); } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const FunctionDecl *FD) { @@ -132,19 +139,19 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const FunctionDecl *FD) { if (MD->isInstance()) return getFunctionInfo(MD); - unsigned CallingConvention = getCallingConventionForDecl(FD); const FunctionType *FTy = FD->getType()->getAs(); if (const FunctionNoProtoType *FNTP = dyn_cast(FTy)) return getFunctionInfo(FNTP->getResultType(), llvm::SmallVector(), - CallingConvention); + FNTP->getCallConv(), FNTP->getNoReturnAttr()); const FunctionProtoType *FPT = cast(FTy); llvm::SmallVector ArgTys; // FIXME: Kill copy. for (unsigned i = 0, e = FPT->getNumArgs(); i != e; ++i) ArgTys.push_back(FPT->getArgType(i)); - return getFunctionInfo(FPT->getResultType(), ArgTys, CallingConvention); + return getFunctionInfo(FPT->getResultType(), ArgTys, + FPT->getCallConv(), FPT->getNoReturnAttr()); } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const ObjCMethodDecl *MD) { @@ -156,37 +163,56 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const ObjCMethodDecl *MD) { e = MD->param_end(); i != e; ++i) ArgTys.push_back((*i)->getType()); return getFunctionInfo(MD->getResultType(), ArgTys, - getCallingConventionForDecl(MD)); + getCallingConventionForDecl(MD), + /*NoReturn*/ false); +} + +const CGFunctionInfo &CodeGenTypes::getFunctionInfo(GlobalDecl GD) { + // FIXME: Do we need to handle ObjCMethodDecl? + const FunctionDecl *FD = cast(GD.getDecl()); + + if (const CXXConstructorDecl *CD = dyn_cast(FD)) + return getFunctionInfo(CD, GD.getCtorType()); + + if (const CXXDestructorDecl *DD = dyn_cast(FD)) + return getFunctionInfo(DD, GD.getDtorType()); + + return getFunctionInfo(FD); } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(QualType ResTy, const CallArgList &Args, - unsigned CallingConvention){ + CallingConv CC, + bool NoReturn) { // FIXME: Kill copy. llvm::SmallVector ArgTys; for (CallArgList::const_iterator i = Args.begin(), e = Args.end(); i != e; ++i) ArgTys.push_back(i->second); - return getFunctionInfo(ResTy, ArgTys, CallingConvention); + return getFunctionInfo(ResTy, ArgTys, CC, NoReturn); } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(QualType ResTy, const FunctionArgList &Args, - unsigned CallingConvention){ + CallingConv CC, + bool NoReturn) { // FIXME: Kill copy. llvm::SmallVector ArgTys; for (FunctionArgList::const_iterator i = Args.begin(), e = Args.end(); i != e; ++i) ArgTys.push_back(i->second); - return getFunctionInfo(ResTy, ArgTys, CallingConvention); + return getFunctionInfo(ResTy, ArgTys, CC, NoReturn); } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(QualType ResTy, const llvm::SmallVector &ArgTys, - unsigned CallingConvention){ + CallingConv CallConv, + bool NoReturn) { + unsigned CC = ClangCallConvToLLVMCallConv(CallConv); + // Lookup or create unique function info. llvm::FoldingSetNodeID ID; - CGFunctionInfo::Profile(ID, CallingConvention, ResTy, + CGFunctionInfo::Profile(ID, CC, NoReturn, ResTy, ArgTys.begin(), ArgTys.end()); void *InsertPos = 0; @@ -195,7 +221,7 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(QualType ResTy, return *FI; // Construct the function info. - FI = new CGFunctionInfo(CallingConvention, ResTy, ArgTys); + FI = new CGFunctionInfo(CC, NoReturn, ResTy, ArgTys); FunctionInfos.InsertNode(FI, InsertPos); // Compute ABI information. @@ -205,10 +231,12 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(QualType ResTy, } CGFunctionInfo::CGFunctionInfo(unsigned _CallingConvention, + bool _NoReturn, QualType ResTy, const llvm::SmallVector &ArgTys) : CallingConvention(_CallingConvention), - EffectiveCallingConvention(_CallingConvention) + EffectiveCallingConvention(_CallingConvention), + NoReturn(_NoReturn) { NumArgs = ArgTys.size(); Args = new ArgInfo[1 + NumArgs]; @@ -258,7 +286,7 @@ CodeGenFunction::ExpandTypeFromArgs(QualType Ty, LValue LV, QualType FT = FD->getType(); // FIXME: What are the right qualifiers here? - LValue LV = EmitLValueForField(Addr, FD, false, 0); + LValue LV = EmitLValueForField(Addr, FD, 0); if (CodeGenFunction::hasAggregateLLVMType(FT)) { AI = ExpandTypeFromArgs(FT, LV, AI); } else { @@ -285,7 +313,7 @@ CodeGenFunction::ExpandTypeToArgs(QualType Ty, RValue RV, QualType FT = FD->getType(); // FIXME: What are the right qualifiers here? - LValue LV = EmitLValueForField(Addr, FD, false, 0); + LValue LV = EmitLValueForField(Addr, FD, 0); if (CodeGenFunction::hasAggregateLLVMType(FT)) { ExpandTypeToArgs(FT, RValue::getAggregate(LV.getAddress()), Args); } else { @@ -490,6 +518,9 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, CallingConv = FI.getEffectiveCallingConvention(); + if (FI.isNoReturn()) + FuncAttrs |= llvm::Attribute::NoReturn; + // FIXME: handle sseregparm someday... if (TargetDecl) { if (TargetDecl->hasAttr()) @@ -685,7 +716,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, // Create a temporary alloca to hold the argument; the rest of // codegen expects to access aggregates & complex values by // reference. - V = CreateTempAlloca(ConvertTypeForMem(Ty)); + V = CreateMemTemp(Ty); Builder.CreateStore(AI, V); } else { if (!getContext().typesAreCompatible(Ty, Arg->getType())) { @@ -702,8 +733,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, // If this structure was expanded into multiple arguments then // we need to create a temporary and reconstruct it from the // arguments. - llvm::Value *Temp = CreateTempAlloca(ConvertTypeForMem(Ty), - Arg->getName() + ".addr"); + llvm::Value *Temp = CreateMemTemp(Ty, Arg->getName() + ".addr"); // FIXME: What are the right qualifiers here? llvm::Function::arg_iterator End = ExpandTypeFromArgs(Ty, LValue::MakeAddr(Temp, Qualifiers()), AI); @@ -719,7 +749,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, case ABIArgInfo::Ignore: // Initialize the local variable appropriately. if (hasAggregateLLVMType(Ty)) { - EmitParmDecl(*Arg, CreateTempAlloca(ConvertTypeForMem(Ty))); + EmitParmDecl(*Arg, CreateMemTemp(Ty)); } else { EmitParmDecl(*Arg, llvm::UndefValue::get(ConvertType(Arg->getType()))); } @@ -732,7 +762,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, // FIXME: This is very wasteful; EmitParmDecl is just going to drop the // result in a new alloca anyway, so we could just store into that // directly if we broke the abstraction down more. - llvm::Value *V = CreateTempAlloca(ConvertTypeForMem(Ty), "coerce"); + llvm::Value *V = CreateMemTemp(Ty, "coerce"); CreateCoercedStore(AI, V, /*DestIsVolatile=*/false, *this); // Match to what EmitParmDecl is expecting for this type. if (!CodeGenFunction::hasAggregateLLVMType(Ty)) { @@ -803,7 +833,7 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI, RValue CodeGenFunction::EmitCallArg(const Expr *E, QualType ArgType) { if (ArgType->isReferenceType()) - return EmitReferenceBindingToExpr(E, ArgType); + return EmitReferenceBindingToExpr(E); return EmitAnyExprToTemp(E); } @@ -827,7 +857,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (CGM.ReturnTypeUsesSret(CallInfo)) { llvm::Value *Value = ReturnValue.getValue(); if (!Value) - Value = CreateTempAlloca(ConvertTypeForMem(RetTy)); + Value = CreateMemTemp(RetTy); Args.push_back(Value); } @@ -843,7 +873,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, case ABIArgInfo::Indirect: if (RV.isScalar() || RV.isComplex()) { // Make a temporary alloca to pass the argument. - Args.push_back(CreateTempAlloca(ConvertTypeForMem(I->second))); + Args.push_back(CreateMemTemp(I->second)); if (RV.isScalar()) EmitStoreOfScalar(RV.getScalarVal(), Args.back(), false, I->second); else @@ -874,10 +904,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, // FIXME: Avoid the conversion through memory if possible. llvm::Value *SrcPtr; if (RV.isScalar()) { - SrcPtr = CreateTempAlloca(ConvertTypeForMem(I->second), "coerce"); + SrcPtr = CreateMemTemp(I->second, "coerce"); EmitStoreOfScalar(RV.getScalarVal(), SrcPtr, false, I->second); } else if (RV.isComplex()) { - SrcPtr = CreateTempAlloca(ConvertTypeForMem(I->second), "coerce"); + SrcPtr = CreateMemTemp(I->second, "coerce"); StoreComplexToAddr(RV.getComplexVal(), SrcPtr, false); } else SrcPtr = RV.getAggregateAddr(); @@ -982,7 +1012,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, bool DestIsVolatile = ReturnValue.isVolatile(); if (!DestPtr) { - DestPtr = CreateTempAlloca(ConvertTypeForMem(RetTy), "agg.tmp"); + DestPtr = CreateMemTemp(RetTy, "agg.tmp"); DestIsVolatile = false; } Builder.CreateStore(CI, DestPtr, DestIsVolatile); @@ -1000,7 +1030,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, bool DestIsVolatile = ReturnValue.isVolatile(); if (!DestPtr) { - DestPtr = CreateTempAlloca(ConvertTypeForMem(RetTy), "coerce"); + DestPtr = CreateMemTemp(RetTy, "coerce"); DestIsVolatile = false; } diff --git a/lib/CodeGen/CGCall.h b/lib/CodeGen/CGCall.h index 427ab5f4cbc8..9601e9ae9a27 100644 --- a/lib/CodeGen/CGCall.h +++ b/lib/CodeGen/CGCall.h @@ -69,6 +69,9 @@ namespace CodeGen { /// depend on the ABI. unsigned EffectiveCallingConvention; + /// Whether this function is noreturn. + bool NoReturn; + unsigned NumArgs; ArgInfo *Args; @@ -77,6 +80,7 @@ namespace CodeGen { typedef ArgInfo *arg_iterator; CGFunctionInfo(unsigned CallingConvention, + bool NoReturn, QualType ResTy, const llvm::SmallVector &ArgTys); ~CGFunctionInfo() { delete[] Args; } @@ -88,6 +92,8 @@ namespace CodeGen { unsigned arg_size() const { return NumArgs; } + bool isNoReturn() const { return NoReturn; } + /// getCallingConvention - Return the user specified calling /// convention. unsigned getCallingConvention() const { return CallingConvention; } @@ -108,6 +114,7 @@ namespace CodeGen { void Profile(llvm::FoldingSetNodeID &ID) { ID.AddInteger(getCallingConvention()); + ID.AddBoolean(NoReturn); getReturnType().Profile(ID); for (arg_iterator it = arg_begin(), ie = arg_end(); it != ie; ++it) it->type.Profile(ID); @@ -115,10 +122,12 @@ namespace CodeGen { template static void Profile(llvm::FoldingSetNodeID &ID, unsigned CallingConvention, + bool NoReturn, QualType ResTy, Iterator begin, Iterator end) { ID.AddInteger(CallingConvention); + ID.AddBoolean(NoReturn); ResTy.Profile(ID); for (; begin != end; ++begin) begin->Profile(ID); diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index a822ca2b7417..fa5a47f31564 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -19,11 +19,11 @@ using namespace clang; using namespace CodeGen; static uint64_t -ComputeNonVirtualBaseClassOffset(ASTContext &Context, CXXBasePaths &Paths, +ComputeNonVirtualBaseClassOffset(ASTContext &Context, + const CXXBasePath &Path, unsigned Start) { uint64_t Offset = 0; - const CXXBasePath &Path = Paths.front(); for (unsigned i = Start, e = Path.size(); i != e; ++i) { const CXXBasePathElement& Element = Path[i]; @@ -44,20 +44,21 @@ ComputeNonVirtualBaseClassOffset(ASTContext &Context, CXXBasePaths &Paths, } llvm::Constant * -CodeGenModule::GetCXXBaseClassOffset(const CXXRecordDecl *ClassDecl, - const CXXRecordDecl *BaseClassDecl) { - if (ClassDecl == BaseClassDecl) +CodeGenModule::GetNonVirtualBaseClassOffset(const CXXRecordDecl *Class, + const CXXRecordDecl *BaseClass) { + if (Class == BaseClass) return 0; CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true, /*DetectVirtual=*/false); - if (!const_cast(ClassDecl)-> - isDerivedFrom(const_cast(BaseClassDecl), Paths)) { + if (!const_cast(Class)-> + isDerivedFrom(const_cast(BaseClass), Paths)) { assert(false && "Class must be derived from the passed in base class!"); return 0; } - uint64_t Offset = ComputeNonVirtualBaseClassOffset(getContext(), Paths, 0); + uint64_t Offset = ComputeNonVirtualBaseClassOffset(getContext(), + Paths.front(), 0); if (!Offset) return 0; @@ -67,51 +68,6 @@ CodeGenModule::GetCXXBaseClassOffset(const CXXRecordDecl *ClassDecl, return llvm::ConstantInt::get(PtrDiffTy, Offset); } -static llvm::Value *GetCXXBaseClassOffset(CodeGenFunction &CGF, - llvm::Value *BaseValue, - const CXXRecordDecl *ClassDecl, - const CXXRecordDecl *BaseClassDecl) { - CXXBasePaths Paths(/*FindAmbiguities=*/false, - /*RecordPaths=*/true, /*DetectVirtual=*/false); - if (!const_cast(ClassDecl)-> - isDerivedFrom(const_cast(BaseClassDecl), Paths)) { - assert(false && "Class must be derived from the passed in base class!"); - return 0; - } - - unsigned Start = 0; - llvm::Value *VirtualOffset = 0; - - const CXXBasePath &Path = Paths.front(); - const CXXRecordDecl *VBase = 0; - for (unsigned i = 0, e = Path.size(); i != e; ++i) { - const CXXBasePathElement& Element = Path[i]; - if (Element.Base->isVirtual()) { - Start = i+1; - QualType VBaseType = Element.Base->getType(); - VBase = cast(VBaseType->getAs()->getDecl()); - } - } - if (VBase) - VirtualOffset = - CGF.GetVirtualCXXBaseClassOffset(BaseValue, ClassDecl, VBase); - - uint64_t Offset = - ComputeNonVirtualBaseClassOffset(CGF.getContext(), Paths, Start); - - if (!Offset) - return VirtualOffset; - - const llvm::Type *PtrDiffTy = - CGF.ConvertType(CGF.getContext().getPointerDiffType()); - llvm::Value *NonVirtualOffset = llvm::ConstantInt::get(PtrDiffTy, Offset); - - if (VirtualOffset) - return CGF.Builder.CreateAdd(VirtualOffset, NonVirtualOffset); - - return NonVirtualOffset; -} - // FIXME: This probably belongs in CGVtable, but it relies on // the static function ComputeNonVirtualBaseClassOffset, so we should make that // a CodeGenModule member function as well. @@ -144,25 +100,91 @@ CodeGenModule::ComputeThunkAdjustment(const CXXRecordDecl *ClassDecl, getVtableInfo().getVirtualBaseOffsetIndex(ClassDecl, BaseClassDecl); uint64_t Offset = - ComputeNonVirtualBaseClassOffset(getContext(), Paths, Start); + ComputeNonVirtualBaseClassOffset(getContext(), Paths.front(), Start); return ThunkAdjustment(Offset, VirtualOffset); } +/// Gets the address of a virtual base class within a complete object. +/// This should only be used for (1) non-virtual bases or (2) virtual bases +/// when the type is known to be complete (e.g. in complete destructors). +/// +/// The object pointed to by 'This' is assumed to be non-null. +llvm::Value * +CodeGenFunction::GetAddressOfBaseOfCompleteClass(llvm::Value *This, + bool isBaseVirtual, + const CXXRecordDecl *Derived, + const CXXRecordDecl *Base) { + // 'this' must be a pointer (in some address space) to Derived. + assert(This->getType()->isPointerTy() && + cast(This->getType())->getElementType() + == ConvertType(Derived)); + + // Compute the offset of the virtual base. + uint64_t Offset; + const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Derived); + if (isBaseVirtual) + Offset = Layout.getVBaseClassOffset(Base); + else + Offset = Layout.getBaseClassOffset(Base); + + // Shift and cast down to the base type. + // TODO: for complete types, this should be possible with a GEP. + llvm::Value *V = This; + if (Offset) { + const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(getLLVMContext()); + V = Builder.CreateBitCast(V, Int8PtrTy); + V = Builder.CreateConstInBoundsGEP1_64(V, Offset / 8); + } + V = Builder.CreateBitCast(V, ConvertType(Base)->getPointerTo()); + + return V; +} + llvm::Value * CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value, - const CXXRecordDecl *ClassDecl, - const CXXRecordDecl *BaseClassDecl, + const CXXRecordDecl *Class, + const CXXRecordDecl *BaseClass, bool NullCheckValue) { QualType BTy = getContext().getCanonicalType( - getContext().getTypeDeclType(const_cast(BaseClassDecl))); + getContext().getTypeDeclType(BaseClass)); const llvm::Type *BasePtrTy = llvm::PointerType::getUnqual(ConvertType(BTy)); - if (ClassDecl == BaseClassDecl) { + if (Class == BaseClass) { // Just cast back. return Builder.CreateBitCast(Value, BasePtrTy); } + + CXXBasePaths Paths(/*FindAmbiguities=*/false, + /*RecordPaths=*/true, /*DetectVirtual=*/false); + if (!const_cast(Class)-> + isDerivedFrom(const_cast(BaseClass), Paths)) { + assert(false && "Class must be derived from the passed in base class!"); + return 0; + } + + unsigned Start = 0; + llvm::Value *VirtualOffset = 0; + + const CXXBasePath &Path = Paths.front(); + const CXXRecordDecl *VBase = 0; + for (unsigned i = 0, e = Path.size(); i != e; ++i) { + const CXXBasePathElement& Element = Path[i]; + if (Element.Base->isVirtual()) { + Start = i+1; + QualType VBaseType = Element.Base->getType(); + VBase = cast(VBaseType->getAs()->getDecl()); + } + } + + uint64_t Offset = + ComputeNonVirtualBaseClassOffset(getContext(), Paths.front(), Start); + if (!Offset && !VBase) { + // Just cast back. + return Builder.CreateBitCast(Value, BasePtrTy); + } + llvm::BasicBlock *CastNull = 0; llvm::BasicBlock *CastNotNull = 0; llvm::BasicBlock *CastEnd = 0; @@ -179,16 +201,27 @@ CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value, EmitBlock(CastNotNull); } - const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(VMContext); + if (VBase) + VirtualOffset = GetVirtualBaseClassOffset(Value, Class, VBase); - llvm::Value *Offset = - GetCXXBaseClassOffset(*this, Value, ClassDecl, BaseClassDecl); + const llvm::Type *PtrDiffTy = ConvertType(getContext().getPointerDiffType()); + llvm::Value *NonVirtualOffset = 0; + if (Offset) + NonVirtualOffset = llvm::ConstantInt::get(PtrDiffTy, Offset); - if (Offset) { - // Apply the offset. - Value = Builder.CreateBitCast(Value, Int8PtrTy); - Value = Builder.CreateGEP(Value, Offset, "add.ptr"); - } + llvm::Value *BaseOffset; + if (VBase) { + if (NonVirtualOffset) + BaseOffset = Builder.CreateAdd(VirtualOffset, NonVirtualOffset); + else + BaseOffset = VirtualOffset; + } else + BaseOffset = NonVirtualOffset; + + // Apply the base offset. + const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(getLLVMContext()); + Value = Builder.CreateBitCast(Value, Int8PtrTy); + Value = Builder.CreateGEP(Value, BaseOffset, "add.ptr"); // Cast back. Value = Builder.CreateBitCast(Value, BasePtrTy); @@ -212,19 +245,27 @@ CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value, llvm::Value * CodeGenFunction::GetAddressOfDerivedClass(llvm::Value *Value, - const CXXRecordDecl *ClassDecl, - const CXXRecordDecl *DerivedClassDecl, + const CXXRecordDecl *Class, + const CXXRecordDecl *DerivedClass, bool NullCheckValue) { QualType DerivedTy = getContext().getCanonicalType( - getContext().getTypeDeclType(const_cast(DerivedClassDecl))); + getContext().getTypeDeclType(const_cast(DerivedClass))); const llvm::Type *DerivedPtrTy = ConvertType(DerivedTy)->getPointerTo(); - if (ClassDecl == DerivedClassDecl) { + if (Class == DerivedClass) { // Just cast back. return Builder.CreateBitCast(Value, DerivedPtrTy); } + llvm::Value *NonVirtualOffset = + CGM.GetNonVirtualBaseClassOffset(DerivedClass, Class); + + if (!NonVirtualOffset) { + // No offset, we can just cast back. + return Builder.CreateBitCast(Value, DerivedPtrTy); + } + llvm::BasicBlock *CastNull = 0; llvm::BasicBlock *CastNotNull = 0; llvm::BasicBlock *CastEnd = 0; @@ -241,17 +282,13 @@ CodeGenFunction::GetAddressOfDerivedClass(llvm::Value *Value, EmitBlock(CastNotNull); } - llvm::Value *Offset = GetCXXBaseClassOffset(*this, Value, DerivedClassDecl, - ClassDecl); - if (Offset) { - // Apply the offset. - Value = Builder.CreatePtrToInt(Value, Offset->getType()); - Value = Builder.CreateSub(Value, Offset); - Value = Builder.CreateIntToPtr(Value, DerivedPtrTy); - } else { - // Just cast. - Value = Builder.CreateBitCast(Value, DerivedPtrTy); - } + // Apply the offset. + Value = Builder.CreatePtrToInt(Value, NonVirtualOffset->getType()); + Value = Builder.CreateSub(Value, NonVirtualOffset); + Value = Builder.CreateIntToPtr(Value, DerivedPtrTy); + + // Just cast. + Value = Builder.CreateBitCast(Value, DerivedPtrTy); if (NullCheckValue) { Builder.CreateBr(CastEnd); @@ -327,9 +364,9 @@ void CodeGenFunction::EmitClassAggrMemberwiseCopy(llvm::Value *Dest, // Push the Src ptr. CallArgs.push_back(std::make_pair(RValue::get(Src), BaseCopyCtor->getParamDecl(0)->getType())); - QualType ResultType = - BaseCopyCtor->getType()->getAs()->getResultType(); - EmitCall(CGM.getTypes().getFunctionInfo(ResultType, CallArgs), + const FunctionProtoType *FPT + = BaseCopyCtor->getType()->getAs(); + EmitCall(CGM.getTypes().getFunctionInfo(CallArgs, FPT), Callee, ReturnValueSlot(), CallArgs, BaseCopyCtor); } EmitBlock(ContinueBlock); @@ -412,8 +449,7 @@ void CodeGenFunction::EmitClassAggrCopyAssignment(llvm::Value *Dest, RValue SrcValue = SrcTy->isReferenceType() ? RValue::get(Src) : RValue::getAggregate(Src); CallArgs.push_back(std::make_pair(SrcValue, SrcTy)); - QualType ResultType = MD->getType()->getAs()->getResultType(); - EmitCall(CGM.getTypes().getFunctionInfo(ResultType, CallArgs), + EmitCall(CGM.getTypes().getFunctionInfo(CallArgs, FPT), Callee, ReturnValueSlot(), CallArgs, MD); } EmitBlock(ContinueBlock); @@ -503,9 +539,9 @@ void CodeGenFunction::EmitClassMemberwiseCopy( // Push the Src ptr. CallArgs.push_back(std::make_pair(RValue::get(Src), BaseCopyCtor->getParamDecl(0)->getType())); - QualType ResultType = - BaseCopyCtor->getType()->getAs()->getResultType(); - EmitCall(CGM.getTypes().getFunctionInfo(ResultType, CallArgs), + const FunctionProtoType *FPT = + BaseCopyCtor->getType()->getAs(); + EmitCall(CGM.getTypes().getFunctionInfo(CallArgs, FPT), Callee, ReturnValueSlot(), CallArgs, BaseCopyCtor); } } @@ -550,9 +586,7 @@ void CodeGenFunction::EmitClassCopyAssignment( RValue SrcValue = SrcTy->isReferenceType() ? RValue::get(Src) : RValue::getAggregate(Src); CallArgs.push_back(std::make_pair(SrcValue, SrcTy)); - QualType ResultType = - MD->getType()->getAs()->getResultType(); - EmitCall(CGM.getTypes().getFunctionInfo(ResultType, CallArgs), + EmitCall(CGM.getTypes().getFunctionInfo(CallArgs, FPT), Callee, ReturnValueSlot(), CallArgs, MD); } @@ -629,8 +663,8 @@ CodeGenFunction::SynthesizeCXXCopyConstructor(const CXXConstructorDecl *Ctor, if (const RecordType *FieldClassType = FieldType->getAs()) { CXXRecordDecl *FieldClassDecl = cast(FieldClassType->getDecl()); - LValue LHS = EmitLValueForField(LoadOfThis, Field, false, 0); - LValue RHS = EmitLValueForField(LoadOfSrc, Field, false, 0); + LValue LHS = EmitLValueForField(LoadOfThis, Field, 0); + LValue RHS = EmitLValueForField(LoadOfSrc, Field, 0); if (Array) { const llvm::Type *BasePtr = ConvertType(FieldType); BasePtr = llvm::PointerType::getUnqual(BasePtr); @@ -647,26 +681,9 @@ CodeGenFunction::SynthesizeCXXCopyConstructor(const CXXConstructorDecl *Ctor, continue; } - if (Field->getType()->isReferenceType()) { - unsigned FieldIndex = CGM.getTypes().getLLVMFieldNo(Field); - - llvm::Value *LHS = Builder.CreateStructGEP(LoadOfThis, FieldIndex, - "lhs.ref"); - - llvm::Value *RHS = Builder.CreateStructGEP(LoadOfThis, FieldIndex, - "rhs.ref"); - - // Load the value in RHS. - RHS = Builder.CreateLoad(RHS); - - // And store it in the LHS - Builder.CreateStore(RHS, LHS); - - continue; - } // Do a built-in assignment of scalar data members. - LValue LHS = EmitLValueForField(LoadOfThis, Field, false, 0); - LValue RHS = EmitLValueForField(LoadOfSrc, Field, false, 0); + LValue LHS = EmitLValueForFieldInitialization(LoadOfThis, Field, 0); + LValue RHS = EmitLValueForFieldInitialization(LoadOfSrc, Field, 0); if (!hasAggregateLLVMType(Field->getType())) { RValue RVRHS = EmitLoadOfLValue(RHS, Field->getType()); @@ -745,8 +762,8 @@ void CodeGenFunction::SynthesizeCXXCopyAssignment(const CXXMethodDecl *CD, if (const RecordType *FieldClassType = FieldType->getAs()) { CXXRecordDecl *FieldClassDecl = cast(FieldClassType->getDecl()); - LValue LHS = EmitLValueForField(LoadOfThis, *Field, false, 0); - LValue RHS = EmitLValueForField(LoadOfSrc, *Field, false, 0); + LValue LHS = EmitLValueForField(LoadOfThis, *Field, 0); + LValue RHS = EmitLValueForField(LoadOfSrc, *Field, 0); if (Array) { const llvm::Type *BasePtr = ConvertType(FieldType); BasePtr = llvm::PointerType::getUnqual(BasePtr); @@ -763,8 +780,8 @@ void CodeGenFunction::SynthesizeCXXCopyAssignment(const CXXMethodDecl *CD, continue; } // Do a built-in assignment of scalar data members. - LValue LHS = EmitLValueForField(LoadOfThis, *Field, false, 0); - LValue RHS = EmitLValueForField(LoadOfSrc, *Field, false, 0); + LValue LHS = EmitLValueForField(LoadOfThis, *Field, 0); + LValue RHS = EmitLValueForField(LoadOfSrc, *Field, 0); if (!hasAggregateLLVMType(Field->getType())) { RValue RVRHS = EmitLoadOfLValue(RHS, Field->getType()); EmitStoreThroughLValue(RVRHS, LHS, Field->getType()); @@ -810,29 +827,21 @@ static void EmitBaseInitializer(CodeGenFunction &CGF, if (CtorType == Ctor_Base && isBaseVirtual) return; - // Compute the offset to the base; we do this directly instead of using - // GetAddressOfBaseClass because the class doesn't have a vtable pointer - // at this point. - // FIXME: This could be refactored back into GetAddressOfBaseClass if it took - // an extra parameter for whether the derived class is the complete object - // class. - const ASTRecordLayout &Layout = - CGF.getContext().getASTRecordLayout(ClassDecl); - uint64_t Offset; - if (isBaseVirtual) - Offset = Layout.getVBaseClassOffset(BaseClassDecl); - else - Offset = Layout.getBaseClassOffset(BaseClassDecl); - const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); - const llvm::Type *BaseClassType = CGF.ConvertType(QualType(BaseType, 0)); - llvm::Value *V = CGF.Builder.CreateBitCast(ThisPtr, Int8PtrTy); - V = CGF.Builder.CreateConstInBoundsGEP1_64(V, Offset/8); - V = CGF.Builder.CreateBitCast(V, BaseClassType->getPointerTo()); - - CGF.EmitCXXConstructorCall(BaseInit->getConstructor(), - Ctor_Base, V, - BaseInit->const_arg_begin(), - BaseInit->const_arg_end()); + // We can pretend to be a complete class because it only matters for + // virtual bases, and we only do virtual bases for complete ctors. + llvm::Value *V = ThisPtr; + V = CGF.GetAddressOfBaseOfCompleteClass(V, isBaseVirtual, + ClassDecl, BaseClassDecl); + + CGF.EmitAggExpr(BaseInit->getInit(), V, false, false, true); + + if (CGF.Exceptions && !BaseClassDecl->hasTrivialDestructor()) { + // FIXME: Is this OK for C++0x delegating constructors? + CodeGenFunction::EHCleanupBlock Cleanup(CGF); + + CXXDestructorDecl *DD = BaseClassDecl->getDestructor(CGF.getContext()); + CGF.EmitCXXDestructorCall(DD, Dtor_Base, V); + } } static void EmitMemberInitializer(CodeGenFunction &CGF, @@ -846,91 +855,62 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, QualType FieldType = CGF.getContext().getCanonicalType(Field->getType()); llvm::Value *ThisPtr = CGF.LoadCXXThis(); - LValue LHS; - if (FieldType->isReferenceType()) { - // FIXME: This is really ugly; should be refactored somehow - unsigned idx = CGF.CGM.getTypes().getLLVMFieldNo(Field); - llvm::Value *V = CGF.Builder.CreateStructGEP(ThisPtr, idx, "tmp"); - assert(!FieldType.getObjCGCAttr() && "fields cannot have GC attrs"); - LHS = LValue::MakeAddr(V, CGF.MakeQualifiers(FieldType)); - } else { - LHS = CGF.EmitLValueForField(ThisPtr, Field, ClassDecl->isUnion(), 0); - } - + LValue LHS = CGF.EmitLValueForFieldInitialization(ThisPtr, Field, 0); + // If we are initializing an anonymous union field, drill down to the field. if (MemberInit->getAnonUnionMember()) { Field = MemberInit->getAnonUnionMember(); - LHS = CGF.EmitLValueForField(LHS.getAddress(), Field, - /*IsUnion=*/true, 0); + LHS = CGF.EmitLValueForField(LHS.getAddress(), Field, 0); FieldType = Field->getType(); } - // If the field is an array, branch based on the element type. - const ConstantArrayType *Array = - CGF.getContext().getAsConstantArrayType(FieldType); - if (Array) - FieldType = CGF.getContext().getBaseElementType(FieldType); - - // We lose the constructor for anonymous union members, so handle them - // explicitly. - // FIXME: This is somwhat ugly. - if (MemberInit->getAnonUnionMember() && FieldType->getAs()) { - if (MemberInit->getNumArgs()) - CGF.EmitAggExpr(*MemberInit->arg_begin(), LHS.getAddress(), - LHS.isVolatileQualified()); - else - CGF.EmitAggregateClear(LHS.getAddress(), Field->getType()); - return; - } - - if (FieldType->getAs()) { - assert(MemberInit->getConstructor() && - "EmitCtorPrologue - no constructor to initialize member"); - if (Array) { - const llvm::Type *BasePtr = CGF.ConvertType(FieldType); - BasePtr = llvm::PointerType::getUnqual(BasePtr); - llvm::Value *BaseAddrPtr = - CGF.Builder.CreateBitCast(LHS.getAddress(), BasePtr); - CGF.EmitCXXAggrConstructorCall(MemberInit->getConstructor(), - Array, BaseAddrPtr, - MemberInit->const_arg_begin(), - MemberInit->const_arg_end()); - } - else - CGF.EmitCXXConstructorCall(MemberInit->getConstructor(), - Ctor_Complete, LHS.getAddress(), - MemberInit->const_arg_begin(), - MemberInit->const_arg_end()); - return; - } - - assert(MemberInit->getNumArgs() == 1 && "Initializer count must be 1 only"); - Expr *RhsExpr = *MemberInit->arg_begin(); + // FIXME: If there's no initializer and the CXXBaseOrMemberInitializer + // was implicitly generated, we shouldn't be zeroing memory. RValue RHS; if (FieldType->isReferenceType()) { - RHS = CGF.EmitReferenceBindingToExpr(RhsExpr, FieldType, - /*IsInitializer=*/true); + RHS = CGF.EmitReferenceBindingToExpr(MemberInit->getInit(), + /*IsInitializer=*/true); CGF.EmitStoreThroughLValue(RHS, LHS, FieldType); - } else if (Array) { + } else if (FieldType->isArrayType() && !MemberInit->getInit()) { CGF.EmitMemSetToZero(LHS.getAddress(), Field->getType()); - } else if (!CGF.hasAggregateLLVMType(RhsExpr->getType())) { - RHS = RValue::get(CGF.EmitScalarExpr(RhsExpr, true)); + } else if (!CGF.hasAggregateLLVMType(Field->getType())) { + RHS = RValue::get(CGF.EmitScalarExpr(MemberInit->getInit(), true)); CGF.EmitStoreThroughLValue(RHS, LHS, FieldType); - } else if (RhsExpr->getType()->isAnyComplexType()) { - CGF.EmitComplexExprIntoAddr(RhsExpr, LHS.getAddress(), + } else if (MemberInit->getInit()->getType()->isAnyComplexType()) { + CGF.EmitComplexExprIntoAddr(MemberInit->getInit(), LHS.getAddress(), LHS.isVolatileQualified()); } else { - // Handle member function pointers; other aggregates shouldn't get this far. - CGF.EmitAggExpr(RhsExpr, LHS.getAddress(), LHS.isVolatileQualified()); + CGF.EmitAggExpr(MemberInit->getInit(), LHS.getAddress(), + LHS.isVolatileQualified(), false, true); + + if (!CGF.Exceptions) + return; + + const RecordType *RT = FieldType->getAs(); + if (!RT) + return; + + CXXRecordDecl *RD = cast(RT->getDecl()); + if (!RD->hasTrivialDestructor()) { + // FIXME: Is this OK for C++0x delegating constructors? + CodeGenFunction::EHCleanupBlock Cleanup(CGF); + + llvm::Value *ThisPtr = CGF.LoadCXXThis(); + LValue LHS = CGF.EmitLValueForField(ThisPtr, Field, 0); + + CXXDestructorDecl *DD = RD->getDestructor(CGF.getContext()); + CGF.EmitCXXDestructorCall(DD, Dtor_Complete, LHS.getAddress()); + } } } /// EmitCtorPrologue - This routine generates necessary code to initialize /// base classes and non-static data members belonging to this constructor. -/// FIXME: This needs to take a CXXCtorType. void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD, CXXCtorType CtorType) { const CXXRecordDecl *ClassDecl = CD->getParent(); + + llvm::SmallVector MemberInitializers; // FIXME: Add vbase initialization @@ -945,14 +925,17 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD, if (Member->isBaseInitializer()) EmitBaseInitializer(*this, ClassDecl, Member, CtorType); else - EmitMemberInitializer(*this, ClassDecl, Member); - - // Pop any live temporaries that the initializers might have pushed. - while (!LiveTemporaries.empty()) - PopCXXTemporary(); + MemberInitializers.push_back(Member); } InitializeVtablePtrs(ClassDecl); + + for (unsigned I = 0, E = MemberInitializers.size(); I != E; ++I) { + assert(LiveTemporaries.empty() && + "Should not have any live temporaries at initializer start!"); + + EmitMemberInitializer(*this, ClassDecl, MemberInitializers[I]); + } } /// EmitDtorEpilogue - Emit all code that comes at the end of class's @@ -1002,7 +985,6 @@ void CodeGenFunction::EmitDtorEpilogue(const CXXDestructorDecl *DD, llvm::Value *ThisPtr = LoadCXXThis(); LValue LHS = EmitLValueForField(ThisPtr, Field, - /*isUnion=*/false, // FIXME: Qualifiers? /*CVRQualifiers=*/0); if (Array) { @@ -1050,15 +1032,16 @@ void CodeGenFunction::EmitDtorEpilogue(const CXXDestructorDecl *DD, ClassDecl->vbases_rbegin(), E = ClassDecl->vbases_rend(); I != E; ++I) { const CXXBaseSpecifier &Base = *I; CXXRecordDecl *BaseClassDecl - = cast(Base.getType()->getAs()->getDecl()); + = cast(Base.getType()->getAs()->getDecl()); // Ignore trivial destructors. if (BaseClassDecl->hasTrivialDestructor()) continue; const CXXDestructorDecl *D = BaseClassDecl->getDestructor(getContext()); - llvm::Value *V = GetAddressOfBaseClass(LoadCXXThis(), - ClassDecl, BaseClassDecl, - /*NullCheckValue=*/false); + llvm::Value *V = GetAddressOfBaseOfCompleteClass(LoadCXXThis(), + true, + ClassDecl, + BaseClassDecl); EmitCXXDestructorCall(D, Dtor_Base, V); } @@ -1080,7 +1063,7 @@ void CodeGenFunction::SynthesizeDefaultDestructor(const CXXDestructorDecl *Dtor, StartFunction(GlobalDecl(Dtor, DtorType), Dtor->getResultType(), Fn, Args, SourceLocation()); - + InitializeVtablePtrs(Dtor->getParent()); EmitDtorEpilogue(Dtor, DtorType); FinishFunction(); } @@ -1263,7 +1246,8 @@ CodeGenFunction::GenerateCXXAggrDestructorHelper(const CXXDestructorDecl *D, llvm::SmallString<16> Name; llvm::raw_svector_ostream(Name) << "__tcf_" << (++UniqueAggrDestructorCount); QualType R = getContext().VoidTy; - const CGFunctionInfo &FI = CGM.getTypes().getFunctionInfo(R, Args); + const CGFunctionInfo &FI + = CGM.getTypes().getFunctionInfo(R, Args, CC_Default, false); const llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI, false); llvm::Function *Fn = llvm::Function::Create(FTy, llvm::GlobalValue::InternalLinkage, @@ -1295,20 +1279,21 @@ CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, llvm::Value *This, CallExpr::const_arg_iterator ArgBeg, CallExpr::const_arg_iterator ArgEnd) { - if (D->isCopyConstructor()) { - const CXXRecordDecl *ClassDecl = cast(D->getDeclContext()); - if (ClassDecl->hasTrivialCopyConstructor()) { - assert(!ClassDecl->hasUserDeclaredCopyConstructor() && - "EmitCXXConstructorCall - user declared copy constructor"); - const Expr *E = (*ArgBeg); - QualType Ty = E->getType(); - llvm::Value *Src = EmitLValue(E).getAddress(); - EmitAggregateCopy(This, Src, Ty); + if (D->isTrivial()) { + if (ArgBeg == ArgEnd) { + // Trivial default constructor, no codegen required. + assert(D->isDefaultConstructor() && + "trivial 0-arg ctor not a default ctor"); return; } - } else if (D->isTrivial()) { - // FIXME: Track down why we're trying to generate calls to the trivial - // default constructor! + + assert(ArgBeg + 1 == ArgEnd && "unexpected argcount for trivial ctor"); + assert(D->isCopyConstructor() && "trivial 1-arg ctor not a copy ctor"); + + const Expr *E = (*ArgBeg); + QualType Ty = E->getType(); + llvm::Value *Src = EmitLValue(E).getAddress(); + EmitAggregateCopy(This, Src, Ty); return; } @@ -1328,8 +1313,8 @@ void CodeGenFunction::EmitCXXDestructorCall(const CXXDestructorDecl *DD, } llvm::Value * -CodeGenFunction::GetVirtualCXXBaseClassOffset(llvm::Value *This, - const CXXRecordDecl *ClassDecl, +CodeGenFunction::GetVirtualBaseClassOffset(llvm::Value *This, + const CXXRecordDecl *ClassDecl, const CXXRecordDecl *BaseClassDecl) { const llvm::Type *Int8PtrTy = llvm::Type::getInt8Ty(VMContext)->getPointerTo(); diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 1ffad3edcac7..5b9c6b055e0e 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -49,19 +49,21 @@ void CGDebugInfo::setLocation(SourceLocation Loc) { CurLoc = CGM.getContext().getSourceManager().getInstantiationLoc(Loc); } -/// getContext - Get context info for the decl. -llvm::DIDescriptor CGDebugInfo::getContext(const VarDecl *Decl, - llvm::DIDescriptor &CompileUnit) { - if (Decl->isFileVarDecl()) +/// getContextDescriptor - Get context info for the decl. +llvm::DIDescriptor CGDebugInfo::getContextDescriptor(const Decl *Context, + llvm::DIDescriptor &CompileUnit) { + if (!Context) return CompileUnit; - if (Decl->getDeclContext()->isFunctionOrMethod()) { - // Find the last subprogram in region stack. - for (unsigned RI = RegionStack.size(), RE = 0; RI != RE; --RI) { - llvm::DIDescriptor R(RegionStack[RI - 1]); - if (R.isSubprogram()) - return R; - } - } + + llvm::DenseMap::iterator + I = RegionMap.find(Context); + if (I != RegionMap.end()) + return llvm::DIDescriptor(dyn_cast_or_null(I->second)); + + // Check namespace. + if (const NamespaceDecl *NSDecl = dyn_cast(Context)) + return llvm::DIDescriptor(getOrCreateNameSpace(NSDecl, CompileUnit)); + return CompileUnit; } @@ -78,10 +80,9 @@ llvm::StringRef CGDebugInfo::getFunctionName(const FunctionDecl *FD) { std::string NS = FD->getNameAsString(); // Copy this name on the side and use its reference. - unsigned Length = NS.length() + 1; - char *StrPtr = FunctionNames.Allocate(Length); - strncpy(StrPtr, NS.c_str(), Length); - return llvm::StringRef(StrPtr); + char *StrPtr = DebugInfoNames.Allocate(NS.length()); + memcpy(StrPtr, NS.data(), NS.length()); + return llvm::StringRef(StrPtr, NS.length()); } /// getOrCreateCompileUnit - Get the compile unit from the cache or create a new @@ -90,16 +91,15 @@ llvm::DICompileUnit CGDebugInfo::getOrCreateCompileUnit(SourceLocation Loc) { // Get source file information. const char *FileName = ""; SourceManager &SM = CGM.getContext().getSourceManager(); - unsigned FID = 0; if (Loc.isValid()) { PresumedLoc PLoc = SM.getPresumedLoc(Loc); FileName = PLoc.getFilename(); - FID = PLoc.getIncludeLoc().getRawEncoding(); - } + unsigned FID = PLoc.getIncludeLoc().getRawEncoding(); - // See if this compile unit has been used before. - llvm::DICompileUnit &Unit = CompileUnitCache[FID]; - if (!Unit.isNull()) return Unit; + // See if this compile unit has been used before for this valid location. + llvm::DICompileUnit &Unit = CompileUnitCache[FID]; + if (!Unit.isNull()) return Unit; + } // Get absolute path name. llvm::sys::Path AbsFileName(FileName); @@ -149,9 +149,16 @@ llvm::DICompileUnit CGDebugInfo::getOrCreateCompileUnit(SourceLocation Loc) { RuntimeVers = LO.ObjCNonFragileABI ? 2 : 1; // Create new compile unit. - return Unit = DebugFactory.CreateCompileUnit( + llvm::DICompileUnit Unit = DebugFactory.CreateCompileUnit( LangTag, AbsFileName.getLast(), AbsFileName.getDirname(), Producer, isMain, LO.Optimize, CGM.getCodeGenOpts().DwarfDebugFlags, RuntimeVers); + + if (Loc.isValid()) { + PresumedLoc PLoc = SM.getPresumedLoc(Loc); + unsigned FID = PLoc.getIncludeLoc().getRawEncoding(); + CompileUnitCache[FID] = Unit; + } + return Unit; } /// CreateType - Get the Basic type from the cache or create a new @@ -419,17 +426,18 @@ llvm::DIType CGDebugInfo::CreateType(const TypedefType *Ty, // We don't set size information, but do specify where the typedef was // declared. - SourceLocation DefLoc = Ty->getDecl()->getLocation(); - llvm::DICompileUnit DefUnit = getOrCreateCompileUnit(DefLoc); - SourceManager &SM = CGM.getContext().getSourceManager(); - PresumedLoc PLoc = SM.getPresumedLoc(DefLoc); + PresumedLoc PLoc = SM.getPresumedLoc(Ty->getDecl()->getLocation()); unsigned Line = PLoc.isInvalid() ? 0 : PLoc.getLine(); + llvm::DIDescriptor TyContext + = getContextDescriptor(dyn_cast(Ty->getDecl()->getDeclContext()), + Unit); llvm::DIType DbgTy = - DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_typedef, Unit, - Ty->getDecl()->getName(), - DefUnit, Line, 0, 0, 0, 0, Src); + DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_typedef, + TyContext, + Ty->getDecl()->getName(), Unit, + Line, 0, 0, 0, 0, Src); return DbgTy; } @@ -463,22 +471,21 @@ llvm::DIType CGDebugInfo::CreateType(const FunctionType *Ty, /// CollectRecordFields - A helper function to collect debug info for /// record fields. This is used while creating debug info entry for a Record. void CGDebugInfo:: -CollectRecordFields(const RecordDecl *Decl, - llvm::DICompileUnit Unit, +CollectRecordFields(const RecordDecl *RD, llvm::DICompileUnit Unit, llvm::SmallVectorImpl &EltTys) { unsigned FieldNo = 0; SourceManager &SM = CGM.getContext().getSourceManager(); - const ASTRecordLayout &RL = CGM.getContext().getASTRecordLayout(Decl); - for (RecordDecl::field_iterator I = Decl->field_begin(), - E = Decl->field_end(); + const ASTRecordLayout &RL = CGM.getContext().getASTRecordLayout(RD); + for (RecordDecl::field_iterator I = RD->field_begin(), + E = RD->field_end(); I != E; ++I, ++FieldNo) { FieldDecl *Field = *I; llvm::DIType FieldTy = getOrCreateType(Field->getType(), Unit); llvm::StringRef FieldName = Field->getName(); - // Ignore unnamed fields. - if (FieldName.empty()) + // Ignore unnamed fields. Do not ignore unnamed records. + if (FieldName.empty() && !isa(Field->getType())) continue; // Get the location for the field. @@ -519,87 +526,264 @@ CollectRecordFields(const RecordDecl *Decl, } } +/// getOrCreateMethodType - CXXMethodDecl's type is a FunctionType. This +/// function type is not updated to include implicit "this" pointer. Use this +/// routine to get a method type which includes "this" pointer. +llvm::DIType +CGDebugInfo::getOrCreateMethodType(const CXXMethodDecl *Method, + llvm::DICompileUnit Unit) { + llvm::DIType FnTy = getOrCreateType(Method->getType(), Unit); + + // Static methods do not need "this" pointer argument. + if (Method->isStatic()) + return FnTy; + + // Add "this" pointer. + + llvm::DIArray Args = llvm::DICompositeType(FnTy.getNode()).getTypeArray(); + assert (Args.getNumElements() && "Invalid number of arguments!"); + + llvm::SmallVector Elts; + + // First element is always return type. For 'void' functions it is NULL. + Elts.push_back(Args.getElement(0)); + + // "this" pointer is always first argument. + ASTContext &Context = CGM.getContext(); + QualType ThisPtr = + Context.getPointerType(Context.getTagDeclType(Method->getParent())); + llvm::DIType ThisPtrType = + DebugFactory.CreateArtificialType(getOrCreateType(ThisPtr, Unit)); + TypeCache[ThisPtr.getAsOpaquePtr()] = ThisPtrType.getNode(); + Elts.push_back(ThisPtrType); + + // Copy rest of the arguments. + for (unsigned i = 1, e = Args.getNumElements(); i != e; ++i) + Elts.push_back(Args.getElement(i)); + + llvm::DIArray EltTypeArray = + DebugFactory.GetOrCreateArray(Elts.data(), Elts.size()); + + return + DebugFactory.CreateCompositeType(llvm::dwarf::DW_TAG_subroutine_type, + Unit, "", llvm::DICompileUnit(), + 0, 0, 0, 0, 0, + llvm::DIType(), EltTypeArray); +} + +/// CreateCXXMemberFunction - A helper function to create a DISubprogram for +/// a single member function GlobalDecl. +llvm::DISubprogram +CGDebugInfo::CreateCXXMemberFunction(const CXXMethodDecl *Method, + llvm::DICompileUnit Unit, + llvm::DICompositeType &RecordTy) { + bool IsCtorOrDtor = + isa(Method) || isa(Method); + + llvm::StringRef MethodName = getFunctionName(Method); + llvm::StringRef MethodLinkageName; + llvm::DIType MethodTy = getOrCreateMethodType(Method, Unit); + + // Since a single ctor/dtor corresponds to multiple functions, it doesn't + // make sense to give a single ctor/dtor a linkage name. + if (!IsCtorOrDtor) + MethodLinkageName = CGM.getMangledName(Method); + + SourceManager &SM = CGM.getContext().getSourceManager(); + + // Get the location for the method. + SourceLocation MethodDefLoc = Method->getLocation(); + PresumedLoc PLoc = SM.getPresumedLoc(MethodDefLoc); + llvm::DICompileUnit MethodDefUnit; + unsigned MethodLine = 0; + + if (!PLoc.isInvalid()) { + MethodDefUnit = getOrCreateCompileUnit(MethodDefLoc); + MethodLine = PLoc.getLine(); + } + + // Collect virtual method info. + llvm::DIType ContainingType; + unsigned Virtuality = 0; + unsigned VIndex = 0; + + if (Method->isVirtual()) { + if (Method->isPure()) + Virtuality = llvm::dwarf::DW_VIRTUALITY_pure_virtual; + else + Virtuality = llvm::dwarf::DW_VIRTUALITY_virtual; + + // It doesn't make sense to give a virtual destructor a vtable index, + // since a single destructor has two entries in the vtable. + if (!isa(Method)) + VIndex = CGM.getVtableInfo().getMethodVtableIndex(Method); + ContainingType = RecordTy; + } + + llvm::DISubprogram SP = + DebugFactory.CreateSubprogram(RecordTy , MethodName, MethodName, + MethodLinkageName, + MethodDefUnit, MethodLine, + MethodTy, /*isLocalToUnit=*/false, + Method->isThisDeclarationADefinition(), + Virtuality, VIndex, ContainingType); + + // Don't cache ctors or dtors since we have to emit multiple functions for + // a single ctor or dtor. + if (!IsCtorOrDtor && Method->isThisDeclarationADefinition()) + SPCache[Method] = llvm::WeakVH(SP.getNode()); + + return SP; +} + /// CollectCXXMemberFunctions - A helper function to collect debug info for /// C++ member functions.This is used while creating debug info entry for /// a Record. void CGDebugInfo:: -CollectCXXMemberFunctions(const CXXRecordDecl *Decl, - llvm::DICompileUnit Unit, +CollectCXXMemberFunctions(const CXXRecordDecl *RD, llvm::DICompileUnit Unit, llvm::SmallVectorImpl &EltTys, llvm::DICompositeType &RecordTy) { - SourceManager &SM = CGM.getContext().getSourceManager(); - for(CXXRecordDecl::method_iterator I = Decl->method_begin(), - E = Decl->method_end(); I != E; ++I) { - CXXMethodDecl *Method = *I; - llvm::StringRef MethodName; - llvm::StringRef MethodLinkageName; - llvm::DIType MethodTy = getOrCreateType(Method->getType(), Unit); - if (CXXConstructorDecl *CDecl = dyn_cast(Method)) { - if (CDecl->isImplicit()) - continue; - MethodName = Decl->getName(); - // FIXME : Find linkage name. - } else if (CXXDestructorDecl *DDecl = dyn_cast(Method)) { - if (DDecl->isImplicit()) - continue; - MethodName = getFunctionName(Method); - // FIXME : Find linkage name. - } else { - if (Method->isImplicit()) - continue; - // regular method - MethodName = getFunctionName(Method); - MethodLinkageName = CGM.getMangledName(Method); - } - - // Get the location for the method. - SourceLocation MethodDefLoc = Method->getLocation(); - PresumedLoc PLoc = SM.getPresumedLoc(MethodDefLoc); - llvm::DICompileUnit MethodDefUnit; - unsigned MethodLine = 0; - - if (!PLoc.isInvalid()) { - MethodDefUnit = getOrCreateCompileUnit(MethodDefLoc); - MethodLine = PLoc.getLine(); - } + for(CXXRecordDecl::method_iterator I = RD->method_begin(), + E = RD->method_end(); I != E; ++I) { + const CXXMethodDecl *Method = *I; + + if (Method->isImplicit() && !Method->isUsed()) + continue; - llvm::DISubprogram SP = - DebugFactory.CreateSubprogram(RecordTy , MethodName, MethodName, - MethodLinkageName, - MethodDefUnit, MethodLine, - MethodTy, false, - Method->isThisDeclarationADefinition(), - 0 /*Virtuality*/, 0 /*VIndex*/, - llvm::DIType() /*ContainingType*/); - if (Method->isThisDeclarationADefinition()) - SPCache[cast(Method)] = llvm::WeakVH(SP.getNode()); - EltTys.push_back(SP); + EltTys.push_back(CreateCXXMemberFunction(Method, Unit, RecordTy)); } } +/// CollectCXXBases - A helper function to collect debug info for +/// C++ base classes. This is used while creating debug info entry for +/// a Record. +void CGDebugInfo:: +CollectCXXBases(const CXXRecordDecl *RD, llvm::DICompileUnit Unit, + llvm::SmallVectorImpl &EltTys, + llvm::DICompositeType &RecordTy) { + + const ASTRecordLayout &RL = CGM.getContext().getASTRecordLayout(RD); + for (CXXRecordDecl::base_class_const_iterator BI = RD->bases_begin(), + BE = RD->bases_end(); BI != BE; ++BI) { + unsigned BFlags = 0; + uint64_t BaseOffset; + + const CXXRecordDecl *Base = + cast(BI->getType()->getAs()->getDecl()); + + if (BI->isVirtual()) { + // virtual base offset index is -ve. The code generator emits dwarf + // expression where it expects +ve number. + BaseOffset = 0 - CGM.getVtableInfo().getVirtualBaseOffsetIndex(RD, Base); + BFlags = llvm::DIType::FlagVirtual; + } else + BaseOffset = RL.getBaseClassOffset(Base); + + AccessSpecifier Access = BI->getAccessSpecifier(); + if (Access == clang::AS_private) + BFlags |= llvm::DIType::FlagPrivate; + else if (Access == clang::AS_protected) + BFlags |= llvm::DIType::FlagProtected; + + llvm::DIType DTy = + DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_inheritance, + RecordTy, llvm::StringRef(), + llvm::DICompileUnit(), 0, 0, 0, + BaseOffset, BFlags, + getOrCreateType(BI->getType(), + Unit)); + EltTys.push_back(DTy); + } +} + +/// getOrCreateVTablePtrType - Return debug info descriptor for vtable. +llvm::DIType CGDebugInfo::getOrCreateVTablePtrType(llvm::DICompileUnit Unit) { + if (!VTablePtrType.isNull()) + return VTablePtrType; + + ASTContext &Context = CGM.getContext(); + + /* Function type */ + llvm::SmallVector STys; + STys.push_back(getOrCreateType(Context.IntTy, Unit)); + llvm::DIArray SElements = + DebugFactory.GetOrCreateArray(STys.data(), STys.size()); + llvm::DIType SubTy = + DebugFactory.CreateCompositeType(llvm::dwarf::DW_TAG_subroutine_type, + Unit, "", llvm::DICompileUnit(), + 0, 0, 0, 0, 0, llvm::DIType(), SElements); + + unsigned Size = Context.getTypeSize(Context.VoidPtrTy); + llvm::DIType vtbl_ptr_type + = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_pointer_type, + Unit, "__vtbl_ptr_type", llvm::DICompileUnit(), + 0, Size, 0, 0, 0, SubTy); + + VTablePtrType = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_pointer_type, + Unit, "", llvm::DICompileUnit(), + 0, Size, 0, 0, 0, vtbl_ptr_type); + return VTablePtrType; +} + +/// getVtableName - Get vtable name for the given Class. +llvm::StringRef CGDebugInfo::getVtableName(const CXXRecordDecl *RD) { + // Otherwise construct gdb compatible name name. + std::string Name = "_vptr$" + RD->getNameAsString(); + + // Copy this name on the side and use its reference. + char *StrPtr = DebugInfoNames.Allocate(Name.length()); + memcpy(StrPtr, Name.data(), Name.length()); + return llvm::StringRef(StrPtr, Name.length()); +} + + +/// CollectVtableInfo - If the C++ class has vtable info then insert appropriate +/// debug info entry in EltTys vector. +void CGDebugInfo:: +CollectVtableInfo(const CXXRecordDecl *RD, llvm::DICompileUnit Unit, + llvm::SmallVectorImpl &EltTys) { + const ASTRecordLayout &RL = CGM.getContext().getASTRecordLayout(RD); + + // If there is a primary base then it will hold vtable info. + if (RL.getPrimaryBase()) + return; + + // If this class is not dynamic then there is not any vtable info to collect. + if (!RD->isDynamicClass()) + return; + + unsigned Size = CGM.getContext().getTypeSize(CGM.getContext().VoidPtrTy); + llvm::DIType VPTR + = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, + getVtableName(RD), llvm::DICompileUnit(), + 0, Size, 0, 0, 0, + getOrCreateVTablePtrType(Unit)); + EltTys.push_back(VPTR); +} + /// CreateType - get structure or union type. llvm::DIType CGDebugInfo::CreateType(const RecordType *Ty, llvm::DICompileUnit Unit) { - RecordDecl *Decl = Ty->getDecl(); + RecordDecl *RD = Ty->getDecl(); unsigned Tag; - if (Decl->isStruct()) + if (RD->isStruct()) Tag = llvm::dwarf::DW_TAG_structure_type; - else if (Decl->isUnion()) + else if (RD->isUnion()) Tag = llvm::dwarf::DW_TAG_union_type; else { - assert(Decl->isClass() && "Unknown RecordType!"); + assert(RD->isClass() && "Unknown RecordType!"); Tag = llvm::dwarf::DW_TAG_class_type; } SourceManager &SM = CGM.getContext().getSourceManager(); // Get overall information about the record type for the debug info. - PresumedLoc PLoc = SM.getPresumedLoc(Decl->getLocation()); + PresumedLoc PLoc = SM.getPresumedLoc(RD->getLocation()); llvm::DICompileUnit DefUnit; unsigned Line = 0; if (!PLoc.isInvalid()) { - DefUnit = getOrCreateCompileUnit(Decl->getLocation()); + DefUnit = getOrCreateCompileUnit(RD->getLocation()); Line = PLoc.getLine(); } @@ -610,19 +794,19 @@ llvm::DIType CGDebugInfo::CreateType(const RecordType *Ty, // may refer to the forward decl if the struct is recursive) and replace all // uses of the forward declaration with the final definition. - // A Decl->getName() is not unique. However, the debug info descriptors - // are uniqued. The debug info descriptor describing record's context is - // necessary to keep two Decl's descriptor unique if their name match. - // FIXME : Use RecordDecl's DeclContext's descriptor. As a temp. step - // use type's name in FwdDecl. + // A RD->getName() is not unique. However, the debug info descriptors + // are uniqued so use type name to ensure uniquness. std::string STy = QualType(Ty, 0).getAsString(); + llvm::DIDescriptor FDContext = + getContextDescriptor(dyn_cast(RD->getDeclContext()), Unit); llvm::DICompositeType FwdDecl = - DebugFactory.CreateCompositeType(Tag, Unit, STy.c_str(), + DebugFactory.CreateCompositeType(Tag, FDContext, + STy.c_str(), DefUnit, Line, 0, 0, 0, 0, llvm::DIType(), llvm::DIArray()); // If this is just a forward declaration, return it. - if (!Decl->getDefinition(CGM.getContext())) + if (!RD->getDefinition()) return FwdDecl; llvm::TrackingVH FwdDeclNode = FwdDecl.getNode(); @@ -633,10 +817,25 @@ llvm::DIType CGDebugInfo::CreateType(const RecordType *Ty, // Convert all the elements. llvm::SmallVector EltTys; - CollectRecordFields(Decl, Unit, EltTys); - if (CXXRecordDecl *CXXDecl = dyn_cast(Decl)) + const CXXRecordDecl *CXXDecl = dyn_cast(RD); + if (CXXDecl) { + CollectCXXBases(CXXDecl, Unit, EltTys, FwdDecl); + CollectVtableInfo(CXXDecl, Unit, EltTys); + } + CollectRecordFields(RD, Unit, EltTys); + llvm::MDNode *ContainingType = NULL; + if (CXXDecl) { CollectCXXMemberFunctions(CXXDecl, Unit, EltTys, FwdDecl); + // A class's primary base or the class itself contains the vtable. + const ASTRecordLayout &RL = CGM.getContext().getASTRecordLayout(RD); + if (const CXXRecordDecl *PBase = RL.getPrimaryBase()) + ContainingType = + getOrCreateType(QualType(PBase->getTypeForDecl(), 0), Unit).getNode(); + else if (CXXDecl->isDynamicClass()) + ContainingType = FwdDecl.getNode(); + } + llvm::DIArray Elements = DebugFactory.GetOrCreateArray(EltTys.data(), EltTys.size()); @@ -644,10 +843,14 @@ llvm::DIType CGDebugInfo::CreateType(const RecordType *Ty, uint64_t Size = CGM.getContext().getTypeSize(Ty); uint64_t Align = CGM.getContext().getTypeAlign(Ty); + llvm::DIDescriptor RDContext = + getContextDescriptor(dyn_cast(RD->getDeclContext()), Unit); llvm::DICompositeType RealDecl = - DebugFactory.CreateCompositeType(Tag, Unit, Decl->getName(), + DebugFactory.CreateCompositeType(Tag, RDContext, + RD->getName(), DefUnit, Line, Size, Align, 0, 0, - llvm::DIType(), Elements); + llvm::DIType(), Elements, + 0, ContainingType); // Now that we have a real decl for the struct, replace anything using the // old decl with the new one. This will recursively update the debug info. @@ -659,14 +862,14 @@ llvm::DIType CGDebugInfo::CreateType(const RecordType *Ty, /// CreateType - get objective-c interface type. llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, llvm::DICompileUnit Unit) { - ObjCInterfaceDecl *Decl = Ty->getDecl(); + ObjCInterfaceDecl *ID = Ty->getDecl(); unsigned Tag = llvm::dwarf::DW_TAG_structure_type; SourceManager &SM = CGM.getContext().getSourceManager(); // Get overall information about the record type for the debug info. - llvm::DICompileUnit DefUnit = getOrCreateCompileUnit(Decl->getLocation()); - PresumedLoc PLoc = SM.getPresumedLoc(Decl->getLocation()); + llvm::DICompileUnit DefUnit = getOrCreateCompileUnit(ID->getLocation()); + PresumedLoc PLoc = SM.getPresumedLoc(ID->getLocation()); unsigned Line = PLoc.isInvalid() ? 0 : PLoc.getLine(); @@ -679,13 +882,13 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, // may refer to the forward decl if the struct is recursive) and replace all // uses of the forward declaration with the final definition. llvm::DICompositeType FwdDecl = - DebugFactory.CreateCompositeType(Tag, Unit, Decl->getName(), + DebugFactory.CreateCompositeType(Tag, Unit, ID->getName(), DefUnit, Line, 0, 0, 0, 0, llvm::DIType(), llvm::DIArray(), RuntimeLang); // If this is just a forward declaration, return it. - if (Decl->isForwardDecl()) + if (ID->isForwardDecl()) return FwdDecl; llvm::TrackingVH FwdDeclNode = FwdDecl.getNode(); @@ -696,7 +899,7 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, // Convert all the elements. llvm::SmallVector EltTys; - ObjCInterfaceDecl *SClass = Decl->getSuperClass(); + ObjCInterfaceDecl *SClass = ID->getSuperClass(); if (SClass) { llvm::DIType SClassTy = getOrCreateType(CGM.getContext().getObjCInterfaceType(SClass), Unit); @@ -707,11 +910,11 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, EltTys.push_back(InhTag); } - const ASTRecordLayout &RL = CGM.getContext().getASTObjCInterfaceLayout(Decl); + const ASTRecordLayout &RL = CGM.getContext().getASTObjCInterfaceLayout(ID); unsigned FieldNo = 0; - for (ObjCInterfaceDecl::ivar_iterator I = Decl->ivar_begin(), - E = Decl->ivar_end(); I != E; ++I, ++FieldNo) { + for (ObjCInterfaceDecl::ivar_iterator I = ID->ivar_begin(), + E = ID->ivar_end(); I != E; ++I, ++FieldNo) { ObjCIvarDecl *Field = *I; llvm::DIType FieldTy = getOrCreateType(Field->getType(), Unit); @@ -769,7 +972,7 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, uint64_t Align = CGM.getContext().getTypeAlign(Ty); llvm::DICompositeType RealDecl = - DebugFactory.CreateCompositeType(Tag, Unit, Decl->getName(), DefUnit, + DebugFactory.CreateCompositeType(Tag, Unit, ID->getName(), DefUnit, Line, Size, Align, 0, 0, llvm::DIType(), Elements, RuntimeLang); @@ -782,13 +985,13 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, llvm::DIType CGDebugInfo::CreateType(const EnumType *Ty, llvm::DICompileUnit Unit) { - EnumDecl *Decl = Ty->getDecl(); + EnumDecl *ED = Ty->getDecl(); llvm::SmallVector Enumerators; // Create DIEnumerator elements for each enumerator. for (EnumDecl::enumerator_iterator - Enum = Decl->enumerator_begin(), EnumEnd = Decl->enumerator_end(); + Enum = ED->enumerator_begin(), EnumEnd = ED->enumerator_end(); Enum != EnumEnd; ++Enum) { Enumerators.push_back(DebugFactory.CreateEnumerator(Enum->getName(), Enum->getInitVal().getZExtValue())); @@ -798,7 +1001,7 @@ llvm::DIType CGDebugInfo::CreateType(const EnumType *Ty, llvm::DIArray EltArray = DebugFactory.GetOrCreateArray(Enumerators.data(), Enumerators.size()); - SourceLocation DefLoc = Decl->getLocation(); + SourceLocation DefLoc = ED->getLocation(); llvm::DICompileUnit DefUnit = getOrCreateCompileUnit(DefLoc); SourceManager &SM = CGM.getContext().getSourceManager(); PresumedLoc PLoc = SM.getPresumedLoc(DefLoc); @@ -815,7 +1018,7 @@ llvm::DIType CGDebugInfo::CreateType(const EnumType *Ty, llvm::DIType DbgTy = DebugFactory.CreateCompositeType(llvm::dwarf::DW_TAG_enumeration_type, - Unit, Decl->getName(), DefUnit, Line, + Unit, ED->getName(), DefUnit, Line, Size, Align, 0, 0, llvm::DIType(), EltArray); return DbgTy; @@ -1083,6 +1286,7 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, QualType FnType, llvm::DISubprogram SP(dyn_cast_or_null(FI->second)); if (!SP.isNull() && SP.isSubprogram() && SP.isDefinition()) { RegionStack.push_back(SP.getNode()); + RegionMap[D] = llvm::WeakVH(SP.getNode()); return; } } @@ -1113,6 +1317,7 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, QualType FnType, // Push function on region stack. RegionStack.push_back(SP.getNode()); + RegionMap[D] = llvm::WeakVH(SP.getNode()); } @@ -1164,160 +1369,172 @@ void CGDebugInfo::EmitRegionEnd(llvm::Function *Fn, CGBuilderTy &Builder) { RegionStack.pop_back(); } -/// EmitDeclare - Emit local variable declaration debug info. -void CGDebugInfo::EmitDeclare(const VarDecl *Decl, unsigned Tag, - llvm::Value *Storage, CGBuilderTy &Builder) { - assert(!RegionStack.empty() && "Region stack mismatch, stack empty!"); - - // Do not emit variable debug information while generating optimized code. - // The llvm optimizer and code generator are not yet ready to support - // optimized code debugging. - const CodeGenOptions &CGO = CGM.getCodeGenOpts(); - if (CGO.OptimizationLevel) - return; - - llvm::DICompileUnit Unit = getOrCreateCompileUnit(Decl->getLocation()); - QualType Type = Decl->getType(); - llvm::DIType Ty = getOrCreateType(Type, Unit); - if (Decl->hasAttr()) { - llvm::DICompileUnit DefUnit; - unsigned Tag = llvm::dwarf::DW_TAG_structure_type; +// EmitTypeForVarWithBlocksAttr - Build up structure info for the byref. +// See BuildByRefType. +llvm::DIType CGDebugInfo::EmitTypeForVarWithBlocksAttr(const ValueDecl *VD, + uint64_t *XOffset) { - llvm::SmallVector EltTys; - - llvm::DIType FieldTy; + llvm::SmallVector EltTys; - QualType FType; - uint64_t FieldSize, FieldOffset; - unsigned FieldAlign; + QualType FType; + uint64_t FieldSize, FieldOffset; + unsigned FieldAlign; + + llvm::DICompileUnit Unit = getOrCreateCompileUnit(VD->getLocation()); + QualType Type = VD->getType(); - llvm::DIArray Elements; - llvm::DIType EltTy; - - // Build up structure for the byref. See BuildByRefType. - FieldOffset = 0; + FieldOffset = 0; + FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy); + llvm::DIType FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); + FieldSize = CGM.getContext().getTypeSize(FType); + FieldAlign = CGM.getContext().getTypeAlign(FType); + FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, + "__isa", llvm::DICompileUnit(), + 0, FieldSize, FieldAlign, + FieldOffset, 0, FieldTy); + EltTys.push_back(FieldTy); + FieldOffset += FieldSize; + + FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy); + FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); + FieldSize = CGM.getContext().getTypeSize(FType); + FieldAlign = CGM.getContext().getTypeAlign(FType); + FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, + "__forwarding", llvm::DICompileUnit(), + 0, FieldSize, FieldAlign, + FieldOffset, 0, FieldTy); + EltTys.push_back(FieldTy); + FieldOffset += FieldSize; + + FType = CGM.getContext().IntTy; + FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); + FieldSize = CGM.getContext().getTypeSize(FType); + FieldAlign = CGM.getContext().getTypeAlign(FType); + FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, + "__flags", llvm::DICompileUnit(), + 0, FieldSize, FieldAlign, + FieldOffset, 0, FieldTy); + EltTys.push_back(FieldTy); + FieldOffset += FieldSize; + + FType = CGM.getContext().IntTy; + FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); + FieldSize = CGM.getContext().getTypeSize(FType); + FieldAlign = CGM.getContext().getTypeAlign(FType); + FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, + "__size", llvm::DICompileUnit(), + 0, FieldSize, FieldAlign, + FieldOffset, 0, FieldTy); + EltTys.push_back(FieldTy); + FieldOffset += FieldSize; + + bool HasCopyAndDispose = CGM.BlockRequiresCopying(Type); + if (HasCopyAndDispose) { FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy); FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); FieldSize = CGM.getContext().getTypeSize(FType); FieldAlign = CGM.getContext().getTypeAlign(FType); FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__isa", DefUnit, + "__copy_helper", + llvm::DICompileUnit(), 0, FieldSize, FieldAlign, FieldOffset, 0, FieldTy); EltTys.push_back(FieldTy); FieldOffset += FieldSize; - + FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy); FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); FieldSize = CGM.getContext().getTypeSize(FType); FieldAlign = CGM.getContext().getTypeAlign(FType); FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__forwarding", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - - FType = CGM.getContext().IntTy; - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__flags", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - - FType = CGM.getContext().IntTy; - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__size", DefUnit, + "__destroy_helper", + llvm::DICompileUnit(), 0, FieldSize, FieldAlign, FieldOffset, 0, FieldTy); EltTys.push_back(FieldTy); FieldOffset += FieldSize; + } + + CharUnits Align = CGM.getContext().getDeclAlign(VD); + if (Align > CharUnits::fromQuantity( + CGM.getContext().Target.getPointerAlign(0) / 8)) { + unsigned AlignedOffsetInBytes + = llvm::RoundUpToAlignment(FieldOffset/8, Align.getQuantity()); + unsigned NumPaddingBytes + = AlignedOffsetInBytes - FieldOffset/8; - bool HasCopyAndDispose = CGM.BlockRequiresCopying(Type); - if (HasCopyAndDispose) { - FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy); - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__copy_helper", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - - FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy); + if (NumPaddingBytes > 0) { + llvm::APInt pad(32, NumPaddingBytes); + FType = CGM.getContext().getConstantArrayType(CGM.getContext().CharTy, + pad, ArrayType::Normal, 0); FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); FieldSize = CGM.getContext().getTypeSize(FType); FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__destroy_helper", DefUnit, + FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, + Unit, "", llvm::DICompileUnit(), 0, FieldSize, FieldAlign, FieldOffset, 0, FieldTy); EltTys.push_back(FieldTy); FieldOffset += FieldSize; } - - unsigned Align = CGM.getContext().getDeclAlignInBytes(Decl); - if (Align > CGM.getContext().Target.getPointerAlign(0) / 8) { - unsigned AlignedOffsetInBytes - = llvm::RoundUpToAlignment(FieldOffset/8, Align); - unsigned NumPaddingBytes - = AlignedOffsetInBytes - FieldOffset/8; - - if (NumPaddingBytes > 0) { - llvm::APInt pad(32, NumPaddingBytes); - FType = CGM.getContext().getConstantArrayType(CGM.getContext().CharTy, - pad, ArrayType::Normal, 0); - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, - Unit, "", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - } - } - - FType = Type; - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = Align*8; - - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - Decl->getName(), DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - - Elements = DebugFactory.GetOrCreateArray(EltTys.data(), EltTys.size()); - - unsigned Flags = llvm::DIType::FlagBlockByrefStruct; + } + + FType = Type; + FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); + FieldSize = CGM.getContext().getTypeSize(FType); + FieldAlign = Align.getQuantity()*8; - Ty = DebugFactory.CreateCompositeType(Tag, Unit, "", + *XOffset = FieldOffset; + FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, + VD->getName(), llvm::DICompileUnit(), + 0, FieldSize, FieldAlign, + FieldOffset, 0, FieldTy); + EltTys.push_back(FieldTy); + FieldOffset += FieldSize; + + llvm::DIArray Elements = + DebugFactory.GetOrCreateArray(EltTys.data(), EltTys.size()); + + unsigned Flags = llvm::DIType::FlagBlockByrefStruct; + + return DebugFactory.CreateCompositeType(llvm::dwarf::DW_TAG_structure_type, + Unit, "", llvm::DICompileUnit(), 0, FieldOffset, 0, 0, Flags, llvm::DIType(), Elements); - } + +} +/// EmitDeclare - Emit local variable declaration debug info. +void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag, + llvm::Value *Storage, CGBuilderTy &Builder) { + assert(!RegionStack.empty() && "Region stack mismatch, stack empty!"); + + // Do not emit variable debug information while generating optimized code. + // The llvm optimizer and code generator are not yet ready to support + // optimized code debugging. + const CodeGenOptions &CGO = CGM.getCodeGenOpts(); + if (CGO.OptimizationLevel) + return; + + llvm::DICompileUnit Unit = getOrCreateCompileUnit(VD->getLocation()); + llvm::DIType Ty; + uint64_t XOffset = 0; + if (VD->hasAttr()) + Ty = EmitTypeForVarWithBlocksAttr(VD, &XOffset); + else + Ty = getOrCreateType(VD->getType(), Unit); // Get location information. SourceManager &SM = CGM.getContext().getSourceManager(); - PresumedLoc PLoc = SM.getPresumedLoc(Decl->getLocation()); + PresumedLoc PLoc = SM.getPresumedLoc(VD->getLocation()); unsigned Line = 0; unsigned Column = 0; - if (!PLoc.isInvalid()) { + if (PLoc.isInvalid()) + PLoc = SM.getPresumedLoc(CurLoc); + if (PLoc.isValid()) { Line = PLoc.getLine(); Column = PLoc.getColumn(); + Unit = getOrCreateCompileUnit(CurLoc); } else { Unit = llvm::DICompileUnit(); } @@ -1325,7 +1542,7 @@ void CGDebugInfo::EmitDeclare(const VarDecl *Decl, unsigned Tag, // Create the descriptor for the variable. llvm::DIVariable D = DebugFactory.CreateVariable(Tag, llvm::DIDescriptor(RegionStack.back()), - Decl->getName(), + VD->getName(), Unit, Line, Ty); // Insert an llvm.dbg.declare into the current block. llvm::Instruction *Call = @@ -1342,7 +1559,7 @@ void CGDebugInfo::EmitDeclare(const VarDecl *Decl, unsigned Tag, void CGDebugInfo::EmitDeclare(const BlockDeclRefExpr *BDRE, unsigned Tag, llvm::Value *Storage, CGBuilderTy &Builder, CodeGenFunction *CGF) { - const ValueDecl *Decl = BDRE->getDecl(); + const ValueDecl *VD = BDRE->getDecl(); assert(!RegionStack.empty() && "Region stack mismatch, stack empty!"); // Do not emit variable debug information while generating optimized code. @@ -1353,186 +1570,50 @@ void CGDebugInfo::EmitDeclare(const BlockDeclRefExpr *BDRE, unsigned Tag, return; uint64_t XOffset = 0; - llvm::DICompileUnit Unit = getOrCreateCompileUnit(Decl->getLocation()); - QualType Type = Decl->getType(); - llvm::DIType Ty = getOrCreateType(Type, Unit); - if (Decl->hasAttr()) { - llvm::DICompileUnit DefUnit; - unsigned Tag = llvm::dwarf::DW_TAG_structure_type; - - llvm::SmallVector EltTys; - - llvm::DIType FieldTy; - - QualType FType; - uint64_t FieldSize, FieldOffset; - unsigned FieldAlign; - - llvm::DIArray Elements; - llvm::DIType EltTy; - - // Build up structure for the byref. See BuildByRefType. - FieldOffset = 0; - FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy); - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__isa", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - - FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy); - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__forwarding", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - - FType = CGM.getContext().IntTy; - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__flags", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - - FType = CGM.getContext().IntTy; - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__size", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - - bool HasCopyAndDispose = CGM.BlockRequiresCopying(Type); - if (HasCopyAndDispose) { - FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy); - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__copy_helper", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - - FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy); - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - "__destroy_helper", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - } - - unsigned Align = CGM.getContext().getDeclAlignInBytes(Decl); - if (Align > CGM.getContext().Target.getPointerAlign(0) / 8) { - unsigned AlignedOffsetInBytes - = llvm::RoundUpToAlignment(FieldOffset/8, Align); - unsigned NumPaddingBytes - = AlignedOffsetInBytes - FieldOffset/8; - - if (NumPaddingBytes > 0) { - llvm::APInt pad(32, NumPaddingBytes); - FType = CGM.getContext().getConstantArrayType(CGM.getContext().CharTy, - pad, ArrayType::Normal, 0); - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = CGM.getContext().getTypeAlign(FType); - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, - Unit, "", DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - } - } - - FType = Type; - FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); - FieldSize = CGM.getContext().getTypeSize(FType); - FieldAlign = Align*8; - - XOffset = FieldOffset; - FieldTy = DebugFactory.CreateDerivedType(llvm::dwarf::DW_TAG_member, Unit, - Decl->getName(), DefUnit, - 0, FieldSize, FieldAlign, - FieldOffset, 0, FieldTy); - EltTys.push_back(FieldTy); - FieldOffset += FieldSize; - - Elements = DebugFactory.GetOrCreateArray(EltTys.data(), EltTys.size()); - - unsigned Flags = llvm::DIType::FlagBlockByrefStruct; - - Ty = DebugFactory.CreateCompositeType(Tag, Unit, "", - llvm::DICompileUnit(), - 0, FieldOffset, 0, 0, Flags, - llvm::DIType(), Elements); - } + llvm::DICompileUnit Unit = getOrCreateCompileUnit(VD->getLocation()); + llvm::DIType Ty; + if (VD->hasAttr()) + Ty = EmitTypeForVarWithBlocksAttr(VD, &XOffset); + else + Ty = getOrCreateType(VD->getType(), Unit); // Get location information. SourceManager &SM = CGM.getContext().getSourceManager(); - PresumedLoc PLoc = SM.getPresumedLoc(Decl->getLocation()); + PresumedLoc PLoc = SM.getPresumedLoc(VD->getLocation()); unsigned Line = 0; if (!PLoc.isInvalid()) Line = PLoc.getLine(); else Unit = llvm::DICompileUnit(); - CharUnits offset = CGF->BlockDecls[Decl]; + CharUnits offset = CGF->BlockDecls[VD]; llvm::SmallVector addr; - llvm::LLVMContext &VMContext = CGM.getLLVMContext(); - addr.push_back(llvm::ConstantInt::get(llvm::Type::getInt64Ty(VMContext), - llvm::DIFactory::OpDeref)); - addr.push_back(llvm::ConstantInt::get(llvm::Type::getInt64Ty(VMContext), - llvm::DIFactory::OpPlus)); - addr.push_back(llvm::ConstantInt::get(llvm::Type::getInt64Ty(VMContext), - offset.getQuantity())); + const llvm::Type *Int64Ty = llvm::Type::getInt64Ty(CGM.getLLVMContext()); + addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpDeref)); + addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpPlus)); + addr.push_back(llvm::ConstantInt::get(Int64Ty, offset.getQuantity())); if (BDRE->isByRef()) { - addr.push_back(llvm::ConstantInt::get(llvm::Type::getInt64Ty(VMContext), - llvm::DIFactory::OpDeref)); - addr.push_back(llvm::ConstantInt::get(llvm::Type::getInt64Ty(VMContext), - llvm::DIFactory::OpPlus)); + addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpDeref)); + addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpPlus)); // offset of __forwarding field offset = CharUnits::fromQuantity(CGF->LLVMPointerWidth/8); - addr.push_back(llvm::ConstantInt::get(llvm::Type::getInt64Ty(VMContext), - offset.getQuantity())); - addr.push_back(llvm::ConstantInt::get(llvm::Type::getInt64Ty(VMContext), - llvm::DIFactory::OpDeref)); - addr.push_back(llvm::ConstantInt::get(llvm::Type::getInt64Ty(VMContext), - llvm::DIFactory::OpPlus)); + addr.push_back(llvm::ConstantInt::get(Int64Ty, offset.getQuantity())); + addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpDeref)); + addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpPlus)); // offset of x field offset = CharUnits::fromQuantity(XOffset/8); - addr.push_back(llvm::ConstantInt::get(llvm::Type::getInt64Ty(VMContext), - offset.getQuantity())); + addr.push_back(llvm::ConstantInt::get(Int64Ty, offset.getQuantity())); } // Create the descriptor for the variable. llvm::DIVariable D = - DebugFactory.CreateComplexVariable(Tag, llvm::DIDescriptor(RegionStack.back()), - Decl->getName(), Unit, Line, Ty, + DebugFactory.CreateComplexVariable(Tag, + llvm::DIDescriptor(RegionStack.back()), + VD->getName(), Unit, Line, Ty, addr); // Insert an llvm.dbg.declare into the current block. llvm::Instruction *Call = - DebugFactory.InsertDeclare(Storage, D, Builder.GetInsertPoint()); + DebugFactory.InsertDeclare(Storage, D, Builder.GetInsertBlock()); llvm::DIScope DS(RegionStack.back()); llvm::DILocation DO(NULL); @@ -1542,10 +1623,10 @@ void CGDebugInfo::EmitDeclare(const BlockDeclRefExpr *BDRE, unsigned Tag, Call->setMetadata("dbg", DL.getNode()); } -void CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *Decl, +void CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage, CGBuilderTy &Builder) { - EmitDeclare(Decl, llvm::dwarf::DW_TAG_auto_variable, Storage, Builder); + EmitDeclare(VD, llvm::dwarf::DW_TAG_auto_variable, Storage, Builder); } void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable( @@ -1556,24 +1637,24 @@ void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable( /// EmitDeclareOfArgVariable - Emit call to llvm.dbg.declare for an argument /// variable declaration. -void CGDebugInfo::EmitDeclareOfArgVariable(const VarDecl *Decl, llvm::Value *AI, +void CGDebugInfo::EmitDeclareOfArgVariable(const VarDecl *VD, llvm::Value *AI, CGBuilderTy &Builder) { - EmitDeclare(Decl, llvm::dwarf::DW_TAG_arg_variable, AI, Builder); + EmitDeclare(VD, llvm::dwarf::DW_TAG_arg_variable, AI, Builder); } /// EmitGlobalVariable - Emit information about a global variable. void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, - const VarDecl *Decl) { - + const VarDecl *D) { + // Create global variable debug descriptor. - llvm::DICompileUnit Unit = getOrCreateCompileUnit(Decl->getLocation()); + llvm::DICompileUnit Unit = getOrCreateCompileUnit(D->getLocation()); SourceManager &SM = CGM.getContext().getSourceManager(); - PresumedLoc PLoc = SM.getPresumedLoc(Decl->getLocation()); + PresumedLoc PLoc = SM.getPresumedLoc(D->getLocation()); unsigned LineNo = PLoc.isInvalid() ? 0 : PLoc.getLine(); - QualType T = Decl->getType(); + QualType T = D->getType(); if (T->isIncompleteArrayType()) { // CodeGen turns int[] into int[1] so we'll do the same here. @@ -1585,9 +1666,11 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, T = CGM.getContext().getConstantArrayType(ET, ConstVal, ArrayType::Normal, 0); } - llvm::StringRef DeclName = Decl->getName(); - DebugFactory.CreateGlobalVariable(getContext(Decl, Unit), DeclName, DeclName, - llvm::StringRef(), Unit, LineNo, + llvm::StringRef DeclName = D->getName(); + llvm::DIDescriptor DContext = + getContextDescriptor(dyn_cast(D->getDeclContext()), Unit); + DebugFactory.CreateGlobalVariable(DContext, DeclName, + DeclName, llvm::StringRef(), Unit, LineNo, getOrCreateType(T, Unit), Var->hasInternalLinkage(), true/*definition*/, Var); @@ -1595,16 +1678,16 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, /// EmitGlobalVariable - Emit information about an objective-c interface. void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, - ObjCInterfaceDecl *Decl) { + ObjCInterfaceDecl *ID) { // Create global variable debug descriptor. - llvm::DICompileUnit Unit = getOrCreateCompileUnit(Decl->getLocation()); + llvm::DICompileUnit Unit = getOrCreateCompileUnit(ID->getLocation()); SourceManager &SM = CGM.getContext().getSourceManager(); - PresumedLoc PLoc = SM.getPresumedLoc(Decl->getLocation()); + PresumedLoc PLoc = SM.getPresumedLoc(ID->getLocation()); unsigned LineNo = PLoc.isInvalid() ? 0 : PLoc.getLine(); - llvm::StringRef Name = Decl->getName(); + llvm::StringRef Name = ID->getName(); - QualType T = CGM.getContext().getObjCInterfaceType(Decl); + QualType T = CGM.getContext().getObjCInterfaceType(ID); if (T->isIncompleteArrayType()) { // CodeGen turns int[] into int[1] so we'll do the same here. @@ -1622,3 +1705,26 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, Var->hasInternalLinkage(), true/*definition*/, Var); } + +/// getOrCreateNamesSpace - Return namespace descriptor for the given +/// namespace decl. +llvm::DINameSpace +CGDebugInfo::getOrCreateNameSpace(const NamespaceDecl *NSDecl, + llvm::DIDescriptor Unit) { + llvm::DenseMap::iterator I = + NameSpaceCache.find(NSDecl); + if (I != NameSpaceCache.end()) + return llvm::DINameSpace(cast(I->second)); + + SourceManager &SM = CGM.getContext().getSourceManager(); + PresumedLoc PLoc = SM.getPresumedLoc(NSDecl->getLocation()); + unsigned LineNo = PLoc.isInvalid() ? 0 : PLoc.getLine(); + + llvm::DIDescriptor Context = + getContextDescriptor(dyn_cast(NSDecl->getDeclContext()), Unit); + llvm::DINameSpace NS = + DebugFactory.CreateNameSpace(Context, NSDecl->getName(), + llvm::DICompileUnit(Unit.getNode()), LineNo); + NameSpaceCache[NSDecl] = llvm::WeakVH(NS.getNode()); + return NS; +} diff --git a/lib/CodeGen/CGDebugInfo.h b/lib/CodeGen/CGDebugInfo.h index fddd23b4e9c3..b2d3a1f1fa53 100644 --- a/lib/CodeGen/CGDebugInfo.h +++ b/lib/CodeGen/CGDebugInfo.h @@ -47,6 +47,8 @@ class CGDebugInfo { llvm::DIFactory DebugFactory; SourceLocation CurLoc, PrevLoc; + + llvm::DIType VTablePtrType; /// CompileUnitCache - Cache of previously constructed CompileUnits. llvm::DenseMap CompileUnitCache; @@ -59,12 +61,14 @@ class CGDebugInfo { llvm::DIType BlockLiteralGeneric; std::vector > RegionStack; + llvm::DenseMap RegionMap; - /// FunctionNames - This is a storage for function names that are + /// DebugInfoNames - This is a storage for names that are /// constructed on demand. For example, C++ destructors, C++ operators etc.. - llvm::BumpPtrAllocator FunctionNames; + llvm::BumpPtrAllocator DebugInfoNames; llvm::DenseMap SPCache; + llvm::DenseMap NameSpaceCache; /// Helper functions for getOrCreateType. llvm::DIType CreateType(const BuiltinType *Ty, llvm::DICompileUnit U); @@ -83,16 +87,37 @@ class CGDebugInfo { llvm::DIType CreateType(const ArrayType *Ty, llvm::DICompileUnit U); llvm::DIType CreateType(const LValueReferenceType *Ty, llvm::DICompileUnit U); llvm::DIType CreateType(const MemberPointerType *Ty, llvm::DICompileUnit U); - + llvm::DIType getOrCreateMethodType(const CXXMethodDecl *Method, + llvm::DICompileUnit Unit); + llvm::DIType getOrCreateVTablePtrType(llvm::DICompileUnit Unit); + llvm::DINameSpace getOrCreateNameSpace(const NamespaceDecl *N, + llvm::DIDescriptor Unit); + llvm::DIType CreatePointerLikeType(unsigned Tag, const Type *Ty, QualType PointeeTy, llvm::DICompileUnit U); + + llvm::DISubprogram CreateCXXMemberFunction(const CXXMethodDecl *Method, + llvm::DICompileUnit Unit, + llvm::DICompositeType &RecordTy); + void CollectCXXMemberFunctions(const CXXRecordDecl *Decl, llvm::DICompileUnit U, llvm::SmallVectorImpl &E, llvm::DICompositeType &T); + void CollectCXXBases(const CXXRecordDecl *Decl, + llvm::DICompileUnit Unit, + llvm::SmallVectorImpl &EltTys, + llvm::DICompositeType &RecordTy); + + void CollectRecordFields(const RecordDecl *Decl, llvm::DICompileUnit U, llvm::SmallVectorImpl &E); + + void CollectVtableInfo(const CXXRecordDecl *Decl, + llvm::DICompileUnit Unit, + llvm::SmallVectorImpl &EltTys); + public: CGDebugInfo(CodeGenModule &CGM); ~CGDebugInfo(); @@ -150,8 +175,14 @@ private: void EmitDeclare(const BlockDeclRefExpr *BDRE, unsigned Tag, llvm::Value *AI, CGBuilderTy &Builder, CodeGenFunction *CGF); - /// getContext - Get context info for the decl. - llvm::DIDescriptor getContext(const VarDecl *Decl,llvm::DIDescriptor &CU); + // EmitTypeForVarWithBlocksAttr - Build up structure info for the byref. + // See BuildByRefType. + llvm::DIType EmitTypeForVarWithBlocksAttr(const ValueDecl *VD, + uint64_t *OffSet); + + /// getContextDescriptor - Get context info for the decl. + llvm::DIDescriptor getContextDescriptor(const Decl *Decl, + llvm::DIDescriptor &CU); /// getOrCreateCompileUnit - Get the compile unit from the cache or create a /// new one if necessary. @@ -168,6 +199,10 @@ private: /// name is constructred on demand (e.g. C++ destructor) then the name /// is stored on the side. llvm::StringRef getFunctionName(const FunctionDecl *FD); + + /// getVtableName - Get vtable name for the given Class. + llvm::StringRef getVtableName(const CXXRecordDecl *Decl); + }; } // namespace CodeGen } // namespace clang diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 9606a71527a4..793a22050677 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -76,8 +76,21 @@ void CodeGenFunction::EmitBlockVarDecl(const VarDecl &D) { case VarDecl::Auto: case VarDecl::Register: return EmitLocalBlockVarDecl(D); - case VarDecl::Static: - return EmitStaticBlockVarDecl(D); + case VarDecl::Static: { + llvm::GlobalValue::LinkageTypes Linkage = + llvm::GlobalValue::InternalLinkage; + + // If this is a static declaration inside an inline function, it must have + // weak linkage so that the linker will merge multiple definitions of it. + if (getContext().getLangOptions().CPlusPlus) { + if (const FunctionDecl *FD = dyn_cast(CurFuncDecl)) { + if (FD->isInlined()) + Linkage = llvm::GlobalValue::WeakAnyLinkage; + } + } + + return EmitStaticBlockVarDecl(D, Linkage); + } case VarDecl::Extern: case VarDecl::PrivateExtern: // Don't emit it now, allow it to be emitted lazily on its first use. @@ -120,7 +133,7 @@ CodeGenFunction::CreateStaticBlockVarDecl(const VarDecl &D, Ty.isConstant(getContext()), Linkage, CGM.EmitNullConstant(D.getType()), Name, 0, D.isThreadSpecified(), Ty.getAddressSpace()); - GV->setAlignment(getContext().getDeclAlignInBytes(&D)); + GV->setAlignment(getContext().getDeclAlign(&D).getQuantity()); return GV; } @@ -138,8 +151,13 @@ CodeGenFunction::AddInitializerToGlobalBlockVarDecl(const VarDecl &D, if (!Init) { if (!getContext().getLangOptions().CPlusPlus) CGM.ErrorUnsupported(D.getInit(), "constant l-value expression"); - else + else { + // Since we have a static initializer, this global variable can't + // be constant. + GV->setConstant(false); + EmitStaticCXXBlockVarDeclInit(D, GV); + } return GV; } @@ -172,12 +190,12 @@ CodeGenFunction::AddInitializerToGlobalBlockVarDecl(const VarDecl &D, return GV; } -void CodeGenFunction::EmitStaticBlockVarDecl(const VarDecl &D) { +void CodeGenFunction::EmitStaticBlockVarDecl(const VarDecl &D, + llvm::GlobalValue::LinkageTypes Linkage) { llvm::Value *&DMEntry = LocalDeclMap[&D]; assert(DMEntry == 0 && "Decl already exists in localdeclmap!"); - llvm::GlobalVariable *GV = - CreateStaticBlockVarDecl(D, ".", llvm::GlobalValue::InternalLinkage); + llvm::GlobalVariable *GV = CreateStaticBlockVarDecl(D, ".", Linkage); // Store into LocalDeclMap before generating initializer to handle // circular references. @@ -281,8 +299,8 @@ const llvm::Type *CodeGenFunction::BuildByRefType(const ValueDecl *D) { } bool Packed = false; - unsigned Align = getContext().getDeclAlignInBytes(D); - if (Align > Target.getPointerAlign(0) / 8) { + CharUnits Align = getContext().getDeclAlign(D); + if (Align > CharUnits::fromQuantity(Target.getPointerAlign(0) / 8)) { // We have to insert padding. // The struct above has 2 32-bit integers. @@ -294,7 +312,7 @@ const llvm::Type *CodeGenFunction::BuildByRefType(const ValueDecl *D) { // Align the offset. unsigned AlignedOffsetInBytes = - llvm::RoundUpToAlignment(CurrentOffsetInBytes, Align); + llvm::RoundUpToAlignment(CurrentOffsetInBytes, Align.getQuantity()); unsigned NumPaddingBytes = AlignedOffsetInBytes - CurrentOffsetInBytes; if (NumPaddingBytes > 0) { @@ -334,7 +352,7 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) { QualType Ty = D.getType(); bool isByRef = D.hasAttr(); bool needsDispose = false; - unsigned Align = 0; + CharUnits Align = CharUnits::Zero(); bool IsSimpleConstantInitializer = false; llvm::Value *DeclPtr; @@ -350,7 +368,7 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) { // If this variable is marked 'const', emit the value as a global. if (CGM.getCodeGenOpts().MergeAllConstants && Ty.isConstant(getContext())) { - EmitStaticBlockVarDecl(D); + EmitStaticBlockVarDecl(D, llvm::GlobalValue::InternalLinkage); return; } @@ -364,10 +382,11 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) { llvm::AllocaInst *Alloc = CreateTempAlloca(LTy); Alloc->setName(D.getNameAsString()); - Align = getContext().getDeclAlignInBytes(&D); + Align = getContext().getDeclAlign(&D); if (isByRef) - Align = std::max(Align, unsigned(Target.getPointerAlign(0) / 8)); - Alloc->setAlignment(Align); + Align = std::max(Align, + CharUnits::fromQuantity(Target.getPointerAlign(0) / 8)); + Alloc->setAlignment(Align.getQuantity()); DeclPtr = Alloc; } else { // Targets that don't support recursion emit locals as globals. @@ -420,7 +439,7 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) { // Allocate memory for the array. llvm::AllocaInst *VLA = Builder.CreateAlloca(llvm::Type::getInt8Ty(VMContext), VLASize, "vla"); - VLA->setAlignment(getContext().getDeclAlignInBytes(&D)); + VLA->setAlignment(getContext().getDeclAlign(&D).getQuantity()); DeclPtr = Builder.CreateBitCast(VLA, LElemPtrTy, "tmp"); } @@ -468,7 +487,8 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) { assert(Init != 0 && "Wasn't a simple constant init?"); llvm::Value *AlignVal = - llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), Align); + llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), + Align.getQuantity()); const llvm::Type *IntPtr = llvm::IntegerType::get(VMContext, LLVMPointerWidth); llvm::Value *SizeVal = @@ -492,7 +512,7 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) { new llvm::GlobalVariable(CGM.getModule(), Init->getType(), true, llvm::GlobalValue::InternalLinkage, Init, Name, 0, false, 0); - GV->setAlignment(Align); + GV->setAlignment(Align.getQuantity()); llvm::Value *SrcPtr = GV; if (SrcPtr->getType() != BP) @@ -501,7 +521,7 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) { Builder.CreateCall4(CGM.getMemCpyFn(), Loc, SrcPtr, SizeVal, AlignVal); } } else if (Ty->isReferenceType()) { - RValue RV = EmitReferenceBindingToExpr(Init, Ty, /*IsInitializer=*/true); + RValue RV = EmitReferenceBindingToExpr(Init, /*IsInitializer=*/true); EmitStoreOfScalar(RV.getScalarVal(), Loc, false, Ty); } else if (!hasAggregateLLVMType(Init->getType())) { llvm::Value *V = EmitScalarExpr(Init); @@ -554,19 +574,19 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) { const llvm::Type *V1; V1 = cast(DeclPtr->getType())->getElementType(); V = llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), - (CGM.getTargetData().getTypeStoreSizeInBits(V1) - / 8)); + CGM.GetTargetTypeStoreSize(V1).getQuantity()); Builder.CreateStore(V, size_field); if (flags & BLOCK_HAS_COPY_DISPOSE) { BlockHasCopyDispose = true; llvm::Value *copy_helper = Builder.CreateStructGEP(DeclPtr, 4); - Builder.CreateStore(BuildbyrefCopyHelper(DeclPtr->getType(), flag, Align), + Builder.CreateStore(BuildbyrefCopyHelper(DeclPtr->getType(), flag, + Align.getQuantity()), copy_helper); llvm::Value *destroy_helper = Builder.CreateStructGEP(DeclPtr, 5); Builder.CreateStore(BuildbyrefDestroyHelper(DeclPtr->getType(), flag, - Align), + Align.getQuantity()), destroy_helper); } } @@ -683,25 +703,18 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg) { CanQualType CTy = getContext().getCanonicalType(Ty); llvm::Value *DeclPtr; - if (!Ty->isConstantSizeType()) { - // Variable sized values always are passed by-reference. + // If this is an aggregate or variable sized value, reuse the input pointer. + if (!Ty->isConstantSizeType() || + CodeGenFunction::hasAggregateLLVMType(Ty)) { DeclPtr = Arg; } else { - // A fixed sized single-value variable becomes an alloca in the entry block. - const llvm::Type *LTy = ConvertTypeForMem(Ty); - if (LTy->isSingleValueType()) { - // TODO: Alignment - DeclPtr = CreateTempAlloca(LTy); - DeclPtr->setName(D.getNameAsString() + llvm::StringRef(".addr")); - - // Store the initial value into the alloca. - EmitStoreOfScalar(Arg, DeclPtr, CTy.isVolatileQualified(), Ty); - } else { - // Otherwise, if this is an aggregate, just use the input pointer. - DeclPtr = Arg; - } - Arg->setName(D.getNameAsString()); + // Otherwise, create a temporary to hold the value. + DeclPtr = CreateMemTemp(Ty, D.getName() + ".addr"); + + // Store the initial value into the alloca. + EmitStoreOfScalar(Arg, DeclPtr, CTy.isVolatileQualified(), Ty); } + Arg->setName(D.getName()); llvm::Value *&DMEntry = LocalDeclMap[&D]; assert(DMEntry == 0 && "Decl already exists in localdeclmap!"); diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index 47773a0d69e3..0de3b0be4b1a 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -80,8 +80,13 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D, EmitDeclInit(*this, D, DeclPtr); return; } - - ErrorUnsupported(Init, "global variable that binds to a reference"); + if (Init->isLvalue(getContext()) == Expr::LV_Valid) { + RValue RV = EmitReferenceBindingToExpr(Init, /*IsInitializer=*/true); + EmitStoreOfScalar(RV.getScalarVal(), DeclPtr, false, T); + return; + } + ErrorUnsupported(Init, + "global variable that binds reference to a non-lvalue"); } void @@ -179,57 +184,120 @@ void CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn, FinishFunction(); } +static llvm::Constant *getGuardAcquireFn(CodeGenFunction &CGF) { + // int __cxa_guard_acquire(__int64_t *guard_object); + + const llvm::Type *Int64PtrTy = + llvm::Type::getInt64PtrTy(CGF.getLLVMContext()); + + std::vector Args(1, Int64PtrTy); + + const llvm::FunctionType *FTy = + llvm::FunctionType::get(CGF.ConvertType(CGF.getContext().IntTy), + Args, /*isVarArg=*/false); + + return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_guard_acquire"); +} + +static llvm::Constant *getGuardReleaseFn(CodeGenFunction &CGF) { + // void __cxa_guard_release(__int64_t *guard_object); + + const llvm::Type *Int64PtrTy = + llvm::Type::getInt64PtrTy(CGF.getLLVMContext()); + + std::vector Args(1, Int64PtrTy); + + const llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), + Args, /*isVarArg=*/false); + + return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_guard_release"); +} + +static llvm::Constant *getGuardAbortFn(CodeGenFunction &CGF) { + // void __cxa_guard_abort(__int64_t *guard_object); + + const llvm::Type *Int64PtrTy = + llvm::Type::getInt64PtrTy(CGF.getLLVMContext()); + + std::vector Args(1, Int64PtrTy); + + const llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), + Args, /*isVarArg=*/false); + + return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_guard_abort"); +} + void CodeGenFunction::EmitStaticCXXBlockVarDeclInit(const VarDecl &D, llvm::GlobalVariable *GV) { - // FIXME: This should use __cxa_guard_{acquire,release}? - - assert(!getContext().getLangOptions().ThreadsafeStatics && - "thread safe statics are currently not supported!"); - + bool ThreadsafeStatics = getContext().getLangOptions().ThreadsafeStatics; + llvm::SmallString<256> GuardVName; CGM.getMangleContext().mangleGuardVariable(&D, GuardVName); // Create the guard variable. - llvm::GlobalValue *GuardV = - new llvm::GlobalVariable(CGM.getModule(), llvm::Type::getInt64Ty(VMContext), + const llvm::Type *Int64Ty = llvm::Type::getInt64Ty(VMContext); + llvm::GlobalValue *GuardVariable = + new llvm::GlobalVariable(CGM.getModule(), Int64Ty, false, GV->getLinkage(), - llvm::Constant::getNullValue(llvm::Type::getInt64Ty(VMContext)), + llvm::Constant::getNullValue(Int64Ty), GuardVName.str()); // Load the first byte of the guard variable. const llvm::Type *PtrTy = llvm::PointerType::get(llvm::Type::getInt8Ty(VMContext), 0); - llvm::Value *V = Builder.CreateLoad(Builder.CreateBitCast(GuardV, PtrTy), - "tmp"); - - // Compare it against 0. - llvm::Value *nullValue - = llvm::Constant::getNullValue(llvm::Type::getInt8Ty(VMContext)); - llvm::Value *ICmp = Builder.CreateICmpEQ(V, nullValue , "tobool"); + llvm::Value *V = + Builder.CreateLoad(Builder.CreateBitCast(GuardVariable, PtrTy), "tmp"); - llvm::BasicBlock *InitBlock = createBasicBlock("init"); + llvm::BasicBlock *InitCheckBlock = createBasicBlock("init.check"); llvm::BasicBlock *EndBlock = createBasicBlock("init.end"); - // If the guard variable is 0, jump to the initializer code. - Builder.CreateCondBr(ICmp, InitBlock, EndBlock); + // Check if the first byte of the guard variable is zero. + Builder.CreateCondBr(Builder.CreateIsNull(V, "tobool"), + InitCheckBlock, EndBlock); - EmitBlock(InitBlock); + EmitBlock(InitCheckBlock); + + if (ThreadsafeStatics) { + // Call __cxa_guard_acquire. + V = Builder.CreateCall(getGuardAcquireFn(*this), GuardVariable); + + llvm::BasicBlock *InitBlock = createBasicBlock("init"); + + Builder.CreateCondBr(Builder.CreateIsNotNull(V, "tobool"), + InitBlock, EndBlock); + + EmitBlock(InitBlock); + + if (Exceptions) { + EHCleanupBlock Cleanup(*this); + + // Call __cxa_guard_abort. + Builder.CreateCall(getGuardAbortFn(*this), GuardVariable); + } + } if (D.getType()->isReferenceType()) { QualType T = D.getType(); // We don't want to pass true for IsInitializer here, because a static // reference to a temporary does not extend its lifetime. - RValue RV = EmitReferenceBindingToExpr(D.getInit(), T, + RValue RV = EmitReferenceBindingToExpr(D.getInit(), /*IsInitializer=*/false); EmitStoreOfScalar(RV.getScalarVal(), GV, /*Volatile=*/false, T); } else EmitDeclInit(*this, D, GV); - Builder.CreateStore(llvm::ConstantInt::get(llvm::Type::getInt8Ty(VMContext), - 1), - Builder.CreateBitCast(GuardV, PtrTy)); + if (ThreadsafeStatics) { + // Call __cxa_guard_release. + Builder.CreateCall(getGuardReleaseFn(*this), GuardVariable); + } else { + llvm::Value *One = + llvm::ConstantInt::get(llvm::Type::getInt8Ty(VMContext), 1); + Builder.CreateStore(One, Builder.CreateBitCast(GuardVariable, PtrTy)); + } EmitBlock(EndBlock); } diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index bd0461fd2808..d956c1c3cd85 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -100,10 +100,6 @@ static llvm::Constant *getUnexpectedFn(CodeGenFunction &CGF) { return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_call_unexpected"); } -// FIXME: Eventually this will all go into the backend. Set from the target for -// now. -static int using_sjlj_exceptions = 0; - static llvm::Constant *getUnwindResumeOrRethrowFn(CodeGenFunction &CGF) { const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); std::vector Args(1, Int8PtrTy); @@ -112,7 +108,7 @@ static llvm::Constant *getUnwindResumeOrRethrowFn(CodeGenFunction &CGF) { llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), Args, false); - if (using_sjlj_exceptions) + if (CGF.CGM.getLangOptions().SjLjExceptions) return CGF.CGM.CreateRuntimeFunction(FTy, "_Unwind_SjLj_Resume"); return CGF.CGM.CreateRuntimeFunction(FTy, "_Unwind_Resume_or_Rethrow"); } @@ -194,9 +190,9 @@ static void CopyObject(CodeGenFunction &CGF, const Expr *E, // Push the Src ptr. CallArgs.push_back(std::make_pair(RValue::get(Src), CopyCtor->getParamDecl(0)->getType())); - QualType ResultType = - CopyCtor->getType()->getAs()->getResultType(); - CGF.EmitCall(CGF.CGM.getTypes().getFunctionInfo(ResultType, CallArgs), + const FunctionProtoType *FPT + = CopyCtor->getType()->getAs(); + CGF.EmitCall(CGF.CGM.getTypes().getFunctionInfo(CallArgs, FPT), Callee, ReturnValueSlot(), CallArgs, CopyCtor); CGF.setInvokeDest(PrevLandingPad); } else @@ -244,9 +240,10 @@ static void CopyObject(CodeGenFunction &CGF, QualType ObjectType, // Push the Src ptr. CallArgs.push_back(std::make_pair(RValue::get(Src), CopyCtor->getParamDecl(0)->getType())); - QualType ResultType = - CopyCtor->getType()->getAs()->getResultType(); - CGF.EmitCall(CGF.CGM.getTypes().getFunctionInfo(ResultType, CallArgs), + + const FunctionProtoType *FPT + = CopyCtor->getType()->getAs(); + CGF.EmitCall(CGF.CGM.getTypes().getFunctionInfo(CallArgs, FPT), Callee, ReturnValueSlot(), CallArgs, CopyCtor); } else llvm_unreachable("uncopyable object"); @@ -316,6 +313,9 @@ void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E) { } void CodeGenFunction::EmitStartEHSpec(const Decl *D) { + if (!Exceptions) + return; + const FunctionDecl* FD = dyn_cast_or_null(D); if (FD == 0) return; @@ -410,6 +410,9 @@ void CodeGenFunction::EmitStartEHSpec(const Decl *D) { } void CodeGenFunction::EmitEndEHSpec(const Decl *D) { + if (!Exceptions) + return; + const FunctionDecl* FD = dyn_cast_or_null(D); if (FD == 0) return; @@ -466,6 +469,7 @@ void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) { llvm::BasicBlock *DtorEpilogue = createBasicBlock("dtor.epilogue"); PushCleanupBlock(DtorEpilogue); + InitializeVtablePtrs(DD->getParent()); EmitStmt(S.getTryBlock()); CleanupBlockInfo Info = PopCleanupBlock(); diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 2358bb35923a..830954fd10cc 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -36,16 +36,24 @@ llvm::AllocaInst *CodeGenFunction::CreateTempAlloca(const llvm::Type *Ty, return new llvm::AllocaInst(Ty, 0, Name, AllocaInsertPt); } +llvm::Value *CodeGenFunction::CreateMemTemp(QualType Ty, const llvm::Twine &Name) { + llvm::AllocaInst *Alloc = CreateTempAlloca(ConvertTypeForMem(Ty), Name); + // FIXME: Should we prefer the preferred type alignment here? + CharUnits Align = getContext().getTypeAlignInChars(Ty); + Alloc->setAlignment(Align.getQuantity()); + return Alloc; +} + /// EvaluateExprAsBool - Perform the usual unary conversions on the specified /// expression and compare the result against zero, returning an Int1Ty value. llvm::Value *CodeGenFunction::EvaluateExprAsBool(const Expr *E) { QualType BoolTy = getContext().BoolTy; if (E->getType()->isMemberFunctionPointerType()) { - llvm::Value *Ptr = CreateTempAlloca(ConvertType(E->getType())); - EmitAggExpr(E, Ptr, /*VolatileDest=*/false); + LValue LV = EmitAggExprToLValue(E); // Get the pointer. - llvm::Value *FuncPtr = Builder.CreateStructGEP(Ptr, 0, "src.ptr"); + llvm::Value *FuncPtr = Builder.CreateStructGEP(LV.getAddress(), 0, + "src.ptr"); FuncPtr = Builder.CreateLoad(FuncPtr); llvm::Value *IsNotNull = @@ -87,13 +95,12 @@ RValue CodeGenFunction::EmitAnyExprToTemp(const Expr *E, if (hasAggregateLLVMType(E->getType()) && !E->getType()->isAnyComplexType()) - AggLoc = CreateTempAlloca(ConvertType(E->getType()), "agg.tmp"); + AggLoc = CreateMemTemp(E->getType(), "agg.tmp"); return EmitAnyExpr(E, AggLoc, IsAggLocVolatile, /*IgnoreResult=*/false, IsInitializer); } RValue CodeGenFunction::EmitReferenceBindingToExpr(const Expr* E, - QualType DestType, bool IsInitializer) { bool ShouldDestroyTemporaries = false; unsigned OldNumLiveTemporaries = 0; @@ -114,8 +121,16 @@ RValue CodeGenFunction::EmitReferenceBindingToExpr(const Expr* E, if (E->isLvalue(getContext()) == Expr::LV_Valid) { // Emit the expr as an lvalue. LValue LV = EmitLValue(E); - if (LV.isSimple()) + if (LV.isSimple()) { + if (ShouldDestroyTemporaries) { + // Pop temporaries. + while (LiveTemporaries.size() > OldNumLiveTemporaries) + PopCXXTemporary(); + } + return RValue::get(LV.getAddress()); + } + Val = EmitLoadOfLValue(LV, E->getType()); if (ShouldDestroyTemporaries) { @@ -188,8 +203,7 @@ RValue CodeGenFunction::EmitReferenceBindingToExpr(const Expr* E, Val = RValue::get(Val.getAggregateAddr()); } else { // Create a temporary variable that we can bind the reference to. - llvm::Value *Temp = CreateTempAlloca(ConvertTypeForMem(E->getType()), - "reftmp"); + llvm::Value *Temp = CreateMemTemp(E->getType(), "reftmp"); if (Val.isScalar()) EmitStoreOfScalar(Val.getScalarVal(), Temp, false, E->getType()); else @@ -546,6 +560,8 @@ RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, QualType ExprType) { cast(Ptr->getType())->getElementType(); // Simple scalar l-value. + // + // FIXME: We shouldn't have to use isSingleValueType here. if (EltTy->isSingleValueType()) return RValue::get(EmitLoadOfScalar(Ptr, LV.isVolatileQualified(), ExprType)); @@ -1069,9 +1085,12 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { if (const FunctionDecl *FD = dyn_cast(ND)) return EmitFunctionDeclLValue(*this, E, FD); + // FIXME: the qualifier check does not seem sufficient here if (E->getQualifier()) { - // FIXME: the qualifier check does not seem sufficient here - return EmitPointerToDataMemberLValue(cast(ND)); + const FieldDecl *FD = cast(ND); + llvm::Value *V = CGM.EmitPointerToDataMember(FD); + + return LValue::MakeAddr(V, MakeQualifiers(FD->getType())); } assert(false && "Unhandled DeclRefExpr"); @@ -1166,8 +1185,7 @@ LValue CodeGenFunction::EmitPredefinedFunctionName(unsigned Type) { GlobalVarName += FnName; std::string FunctionName = - PredefinedExpr::ComputeName(getContext(), (PredefinedExpr::IdentType)Type, - CurCodeDecl); + PredefinedExpr::ComputeName((PredefinedExpr::IdentType)Type, CurCodeDecl); llvm::Constant *C = CGM.GetAddrOfConstantCString(FunctionName, GlobalVarName.c_str()); @@ -1350,7 +1368,7 @@ EmitExtVectorElementExpr(const ExtVectorElementExpr *E) { llvm::Value *Vec = EmitScalarExpr(E->getBase()); // Store the vector to memory (because LValue wants an address). - llvm::Value *VecMem =CreateTempAlloca(ConvertType(E->getBase()->getType())); + llvm::Value *VecMem = CreateMemTemp(E->getBase()->getType()); Builder.CreateStore(Vec, VecMem); Base = LValue::MakeAddr(VecMem, Qualifiers()); } @@ -1381,7 +1399,6 @@ EmitExtVectorElementExpr(const ExtVectorElementExpr *E) { } LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { - bool isUnion = false; bool isNonGC = false; Expr *BaseExpr = E->getBase(); llvm::Value *BaseValue = NULL; @@ -1392,16 +1409,12 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { BaseValue = EmitScalarExpr(BaseExpr); const PointerType *PTy = BaseExpr->getType()->getAs(); - if (PTy->getPointeeType()->isUnionType()) - isUnion = true; BaseQuals = PTy->getPointeeType().getQualifiers(); } else if (isa(BaseExpr->IgnoreParens()) || isa( BaseExpr->IgnoreParens())) { RValue RV = EmitObjCPropertyGet(BaseExpr); BaseValue = RV.getAggregateAddr(); - if (BaseExpr->getType()->isUnionType()) - isUnion = true; BaseQuals = BaseExpr->getType().getQualifiers(); } else { LValue BaseLV = EmitLValue(BaseExpr); @@ -1410,14 +1423,12 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { // FIXME: this isn't right for bitfields. BaseValue = BaseLV.getAddress(); QualType BaseTy = BaseExpr->getType(); - if (BaseTy->isUnionType()) - isUnion = true; BaseQuals = BaseTy.getQualifiers(); } NamedDecl *ND = E->getMemberDecl(); if (FieldDecl *Field = dyn_cast(ND)) { - LValue LV = EmitLValueForField(BaseValue, Field, isUnion, + LValue LV = EmitLValueForField(BaseValue, Field, BaseQuals.getCVRQualifiers()); LValue::SetObjCNonGC(LV, isNonGC); setObjCGCLValueClass(getContext(), E, LV); @@ -1461,7 +1472,6 @@ LValue CodeGenFunction::EmitLValueForBitfield(llvm::Value* BaseValue, LValue CodeGenFunction::EmitLValueForField(llvm::Value* BaseValue, const FieldDecl* Field, - bool isUnion, unsigned CVRQualifiers) { if (Field->isBitField()) return EmitLValueForBitfield(BaseValue, Field, CVRQualifiers); @@ -1470,7 +1480,7 @@ LValue CodeGenFunction::EmitLValueForField(llvm::Value* BaseValue, llvm::Value *V = Builder.CreateStructGEP(BaseValue, idx, "tmp"); // Match union field type. - if (isUnion) { + if (Field->getParent()->isUnion()) { const llvm::Type *FieldTy = CGM.getTypes().ConvertTypeForMem(Field->getType()); const llvm::PointerType * BaseTy = @@ -1492,9 +1502,26 @@ LValue CodeGenFunction::EmitLValueForField(llvm::Value* BaseValue, return LValue::MakeAddr(V, Quals); } +LValue +CodeGenFunction::EmitLValueForFieldInitialization(llvm::Value* BaseValue, + const FieldDecl* Field, + unsigned CVRQualifiers) { + QualType FieldType = Field->getType(); + + if (!FieldType->isReferenceType()) + return EmitLValueForField(BaseValue, Field, CVRQualifiers); + + unsigned idx = CGM.getTypes().getLLVMFieldNo(Field); + llvm::Value *V = Builder.CreateStructGEP(BaseValue, idx, "tmp"); + + assert(!FieldType.getObjCGCAttr() && "fields cannot have GC attrs"); + + return LValue::MakeAddr(V, MakeQualifiers(FieldType)); +} + LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr* E){ - const llvm::Type *LTy = ConvertType(E->getType()); - llvm::Value *DeclPtr = CreateTempAlloca(LTy, ".compoundliteral"); + llvm::Value *DeclPtr = CreateTempAlloca(ConvertTypeForMem(E->getType()), + ".compoundliteral"); const Expr* InitExpr = E->getInitializer(); LValue Result = LValue::MakeAddr(DeclPtr, MakeQualifiers(E->getType())); @@ -1527,18 +1554,25 @@ CodeGenFunction::EmitConditionalOperatorLValue(const ConditionalOperator* E) { EmitBranchOnBoolExpr(E->getCond(), LHSBlock, RHSBlock); + // Any temporaries created here are conditional. + BeginConditionalBranch(); EmitBlock(LHSBlock); - LValue LHS = EmitLValue(E->getLHS()); + EndConditionalBranch(); + if (!LHS.isSimple()) return EmitUnsupportedLValue(E, "conditional operator"); + // FIXME: We shouldn't need an alloca for this. llvm::Value *Temp = CreateTempAlloca(LHS.getAddress()->getType(),"condtmp"); Builder.CreateStore(LHS.getAddress(), Temp); EmitBranch(ContBlock); + // Any temporaries created here are conditional. + BeginConditionalBranch(); EmitBlock(RHSBlock); LValue RHS = EmitLValue(E->getRHS()); + EndConditionalBranch(); if (!RHS.isSimple()) return EmitUnsupportedLValue(E, "conditional operator"); @@ -1556,10 +1590,7 @@ CodeGenFunction::EmitConditionalOperatorLValue(const ConditionalOperator* E) { !E->getType()->isAnyComplexType()) && "Unexpected conditional operator!"); - llvm::Value *Temp = CreateTempAlloca(ConvertType(E->getType())); - EmitAggExpr(E, Temp, false); - - return LValue::MakeAddr(Temp, MakeQualifiers(E->getType())); + return EmitAggExprToLValue(E); } /// EmitCastLValue - Casts are never lvalues unless that cast is a dynamic_cast. @@ -1606,12 +1637,8 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { return LValue::MakeAddr(Base, MakeQualifiers(E->getType())); } - case CastExpr::CK_ToUnion: { - llvm::Value *Temp = CreateTempAlloca(ConvertType(E->getType())); - EmitAnyExpr(E->getSubExpr(), Temp, false); - - return LValue::MakeAddr(Temp, MakeQualifiers(E->getType())); - } + case CastExpr::CK_ToUnion: + return EmitAggExprToLValue(E); case CastExpr::CK_BaseToDerived: { const RecordType *BaseClassTy = E->getSubExpr()->getType()->getAs(); @@ -1646,13 +1673,9 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { LValue CodeGenFunction::EmitNullInitializationLValue( const CXXZeroInitValueExpr *E) { QualType Ty = E->getType(); - const llvm::Type *LTy = ConvertTypeForMem(Ty); - llvm::AllocaInst *Alloc = CreateTempAlloca(LTy); - unsigned Align = getContext().getTypeAlign(Ty)/8; - Alloc->setAlignment(Align); - LValue lvalue = LValue::MakeAddr(Alloc, Qualifiers()); - EmitMemSetToZero(lvalue.getAddress(), Ty); - return lvalue; + LValue LV = LValue::MakeAddr(CreateMemTemp(Ty), MakeQualifiers(Ty)); + EmitMemSetToZero(LV.getAddress(), Ty); + return LV; } //===--------------------------------------------------------------------===// @@ -1725,10 +1748,7 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) { return LV; } - llvm::Value *Temp = CreateTempAlloca(ConvertType(E->getType())); - EmitAggExpr(E, Temp, false); - // FIXME: Are these qualifiers correct? - return LValue::MakeAddr(Temp, MakeQualifiers(E->getType())); + return EmitAggExprToLValue(E); } LValue CodeGenFunction::EmitCallExprLValue(const CallExpr *E) { @@ -1746,13 +1766,11 @@ LValue CodeGenFunction::EmitCallExprLValue(const CallExpr *E) { LValue CodeGenFunction::EmitVAArgExprLValue(const VAArgExpr *E) { // FIXME: This shouldn't require another copy. - llvm::Value *Temp = CreateTempAlloca(ConvertType(E->getType())); - EmitAggExpr(E, Temp, false); - return LValue::MakeAddr(Temp, MakeQualifiers(E->getType())); + return EmitAggExprToLValue(E); } LValue CodeGenFunction::EmitCXXConstructLValue(const CXXConstructExpr *E) { - llvm::Value *Temp = CreateTempAlloca(ConvertTypeForMem(E->getType()), "tmp"); + llvm::Value *Temp = CreateMemTemp(E->getType(), "tmp"); EmitCXXConstructExpr(Temp, E); return LValue::MakeAddr(Temp, MakeQualifiers(E->getType())); } @@ -1840,21 +1858,6 @@ LValue CodeGenFunction::EmitStmtExprLValue(const StmtExpr *E) { return LValue::MakeAddr(RV.getAggregateAddr(), MakeQualifiers(E->getType())); } - -LValue CodeGenFunction::EmitPointerToDataMemberLValue(const FieldDecl *Field) { - const CXXRecordDecl *ClassDecl = cast(Field->getDeclContext()); - QualType NNSpecTy = - getContext().getCanonicalType( - getContext().getTypeDeclType(const_cast(ClassDecl))); - NNSpecTy = getContext().getPointerType(NNSpecTy); - llvm::Value *V = llvm::Constant::getNullValue(ConvertType(NNSpecTy)); - LValue MemExpLV = EmitLValueForField(V, Field, /*isUnion=*/false, - /*Qualifiers=*/0); - const llvm::Type *ResultType = ConvertType(getContext().getPointerDiffType()); - V = Builder.CreatePtrToInt(MemExpLV.getAddress(), ResultType, "datamember"); - return LValue::MakeAddr(V, MakeQualifiers(Field->getType())); -} - RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee, ReturnValueSlot ReturnValue, CallExpr::const_arg_iterator ArgBeg, @@ -1867,20 +1870,14 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee, CalleeType = getContext().getCanonicalType(CalleeType); - QualType FnType = cast(CalleeType)->getPointeeType(); - QualType ResultType = cast(FnType)->getResultType(); + const FunctionType *FnType + = cast(cast(CalleeType)->getPointeeType()); + QualType ResultType = FnType->getResultType(); CallArgList Args; EmitCallArgs(Args, dyn_cast(FnType), ArgBeg, ArgEnd); - // FIXME: We should not need to do this, it should be part of the function - // type. - unsigned CallingConvention = 0; - if (const llvm::Function *F = - dyn_cast(Callee->stripPointerCasts())) - CallingConvention = F->getCallingConv(); - return EmitCall(CGM.getTypes().getFunctionInfo(ResultType, Args, - CallingConvention), + return EmitCall(CGM.getTypes().getFunctionInfo(Args, FnType), Callee, ReturnValue, Args, TargetDecl); } diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index c852d65b859f..97455c7b13cf 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -118,7 +118,7 @@ public: void VisitVAArgExpr(VAArgExpr *E); - void EmitInitializationToLValue(Expr *E, LValue Address); + void EmitInitializationToLValue(Expr *E, LValue Address, QualType T); void EmitNullInitializationToLValue(LValue Address, QualType T); // case Expr::ChooseExprClass: void VisitCXXThrowExpr(const CXXThrowExpr *E) { CGF.EmitCXXThrowExpr(E); } @@ -147,7 +147,7 @@ void AggExprEmitter::EmitFinalDestCopy(const Expr *E, RValue Src, bool Ignore) { return; // If the source is volatile, we must read from it; to do that, we need // some place to put it. - DestPtr = CGF.CreateTempAlloca(CGF.ConvertType(E->getType()), "agg.tmp"); + DestPtr = CGF.CreateMemTemp(E->getType(), "agg.tmp"); } if (RequiresGCollection) { @@ -188,7 +188,8 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { llvm::Value *CastPtr = Builder.CreateBitCast(DestPtr, CGF.ConvertType(PtrTy)); EmitInitializationToLValue(E->getSubExpr(), - LValue::MakeAddr(CastPtr, Qualifiers())); + LValue::MakeAddr(CastPtr, Qualifiers()), + E->getType()); break; } @@ -227,8 +228,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CastExpr::CK_BaseToDerivedMemberPointer: { QualType SrcType = E->getSubExpr()->getType(); - llvm::Value *Src = CGF.CreateTempAlloca(CGF.ConvertTypeForMem(SrcType), - "tmp"); + llvm::Value *Src = CGF.CreateMemTemp(SrcType, "tmp"); CGF.EmitAggExpr(E->getSubExpr(), Src, SrcType.isVolatileQualified()); llvm::Value *SrcPtr = Builder.CreateStructGEP(Src, 0, "src.ptr"); @@ -252,8 +252,8 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { if (E->getCastKind() == CastExpr::CK_DerivedToBaseMemberPointer) std::swap(DerivedDecl, BaseDecl); - llvm::Constant *Adj = CGF.CGM.GetCXXBaseClassOffset(DerivedDecl, BaseDecl); - if (Adj) { + if (llvm::Constant *Adj = + CGF.CGM.GetNonVirtualBaseClassOffset(DerivedDecl, BaseDecl)) { if (E->getCastKind() == CastExpr::CK_DerivedToBaseMemberPointer) SrcAdj = Builder.CreateSub(SrcAdj, Adj, "adj"); else @@ -326,10 +326,18 @@ void AggExprEmitter::VisitUnaryAddrOf(const UnaryOperator *E) { int64_t Index = CGF.CGM.getVtableInfo().getMethodVtableIndex(MD); - FuncPtr = llvm::ConstantInt::get(PtrDiffTy, Index + 1); + // Itanium C++ ABI 2.3: + // For a non-virtual function, this field is a simple function pointer. + // For a virtual function, it is 1 plus the virtual table offset + // (in bytes) of the function, represented as a ptrdiff_t. + FuncPtr = llvm::ConstantInt::get(PtrDiffTy, (Index * 8) + 1); } else { - FuncPtr = llvm::ConstantExpr::getPtrToInt(CGF.CGM.GetAddrOfFunction(MD), - PtrDiffTy); + const FunctionProtoType *FPT = MD->getType()->getAs(); + const llvm::Type *Ty = + CGF.CGM.getTypes().GetFunctionType(CGF.CGM.getTypes().getFunctionInfo(MD), + FPT->isVariadic()); + llvm::Constant *Fn = CGF.CGM.GetAddrOfFunction(MD, Ty); + FuncPtr = llvm::ConstantExpr::getPtrToInt(Fn, PtrDiffTy); } Builder.CreateStore(FuncPtr, DstPtr, VolatileDest); @@ -371,14 +379,14 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { if (LHS.isPropertyRef()) { llvm::Value *AggLoc = DestPtr; if (!AggLoc) - AggLoc = CGF.CreateTempAlloca(CGF.ConvertType(E->getRHS()->getType())); + AggLoc = CGF.CreateMemTemp(E->getRHS()->getType()); CGF.EmitAggExpr(E->getRHS(), AggLoc, VolatileDest); CGF.EmitObjCPropertySet(LHS.getPropertyRefExpr(), RValue::getAggregate(AggLoc, VolatileDest)); } else if (LHS.isKVCRef()) { llvm::Value *AggLoc = DestPtr; if (!AggLoc) - AggLoc = CGF.CreateTempAlloca(CGF.ConvertType(E->getRHS()->getType())); + AggLoc = CGF.CreateMemTemp(E->getRHS()->getType()); CGF.EmitAggExpr(E->getRHS(), AggLoc, VolatileDest); CGF.EmitObjCPropertySet(LHS.getKVCRefExpr(), RValue::getAggregate(AggLoc, VolatileDest)); @@ -408,21 +416,21 @@ void AggExprEmitter::VisitConditionalOperator(const ConditionalOperator *E) { CGF.EmitBranchOnBoolExpr(E->getCond(), LHSBlock, RHSBlock); - CGF.StartConditionalBranch(); + CGF.BeginConditionalBranch(); CGF.EmitBlock(LHSBlock); // Handle the GNU extension for missing LHS. assert(E->getLHS() && "Must have LHS for aggregate value"); Visit(E->getLHS()); - CGF.FinishConditionalBranch(); + CGF.EndConditionalBranch(); CGF.EmitBranch(ContBlock); - CGF.StartConditionalBranch(); + CGF.BeginConditionalBranch(); CGF.EmitBlock(RHSBlock); Visit(E->getRHS()); - CGF.FinishConditionalBranch(); + CGF.EndConditionalBranch(); CGF.EmitBranch(ContBlock); CGF.EmitBlock(ContBlock); @@ -449,7 +457,7 @@ void AggExprEmitter::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) { if (!Val) { // Create a temporary variable. - Val = CGF.CreateTempAlloca(CGF.ConvertTypeForMem(E->getType()), "tmp"); + Val = CGF.CreateMemTemp(E->getType(), "tmp"); // FIXME: volatile CGF.EmitAggExpr(E->getSubExpr(), Val, false); @@ -467,7 +475,7 @@ AggExprEmitter::VisitCXXConstructExpr(const CXXConstructExpr *E) { if (!Val) { // Create a temporary variable. - Val = CGF.CreateTempAlloca(CGF.ConvertTypeForMem(E->getType()), "tmp"); + Val = CGF.CreateMemTemp(E->getType(), "tmp"); } if (E->requiresZeroInitialization()) @@ -484,7 +492,7 @@ void AggExprEmitter::VisitCXXExprWithTemporaries(CXXExprWithTemporaries *E) { if (!Val) { // Create a temporary variable. - Val = CGF.CreateTempAlloca(CGF.ConvertTypeForMem(E->getType()), "tmp"); + Val = CGF.CreateMemTemp(E->getType(), "tmp"); } CGF.EmitCXXExprWithTemporaries(E, Val, VolatileDest, IsInitializer); } @@ -494,7 +502,7 @@ void AggExprEmitter::VisitCXXZeroInitValueExpr(CXXZeroInitValueExpr *E) { if (!Val) { // Create a temporary variable. - Val = CGF.CreateTempAlloca(CGF.ConvertTypeForMem(E->getType()), "tmp"); + Val = CGF.CreateMemTemp(E->getType(), "tmp"); } LValue LV = LValue::MakeAddr(Val, Qualifiers()); EmitNullInitializationToLValue(LV, E->getType()); @@ -505,23 +513,27 @@ void AggExprEmitter::VisitImplicitValueInitExpr(ImplicitValueInitExpr *E) { if (!Val) { // Create a temporary variable. - Val = CGF.CreateTempAlloca(CGF.ConvertTypeForMem(E->getType()), "tmp"); + Val = CGF.CreateMemTemp(E->getType(), "tmp"); } LValue LV = LValue::MakeAddr(Val, Qualifiers()); EmitNullInitializationToLValue(LV, E->getType()); } -void AggExprEmitter::EmitInitializationToLValue(Expr* E, LValue LV) { +void +AggExprEmitter::EmitInitializationToLValue(Expr* E, LValue LV, QualType T) { // FIXME: Ignore result? // FIXME: Are initializers affected by volatile? if (isa(E)) { - EmitNullInitializationToLValue(LV, E->getType()); - } else if (E->getType()->isComplexType()) { + EmitNullInitializationToLValue(LV, T); + } else if (T->isReferenceType()) { + RValue RV = CGF.EmitReferenceBindingToExpr(E, /*IsInitializer=*/false); + CGF.EmitStoreThroughLValue(RV, LV, T); + } else if (T->isAnyComplexType()) { CGF.EmitComplexExprIntoAddr(E, LV.getAddress(), false); - } else if (CGF.hasAggregateLLVMType(E->getType())) { + } else if (CGF.hasAggregateLLVMType(T)) { CGF.EmitAnyExpr(E, LV.getAddress(), false); } else { - CGF.EmitStoreThroughLValue(CGF.EmitAnyExpr(E), LV, E->getType()); + CGF.EmitStoreThroughLValue(CGF.EmitAnyExpr(E), LV, T); } } @@ -590,7 +602,8 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { llvm::Value *NextVal = Builder.CreateStructGEP(DestPtr, i, ".array"); if (i < NumInitElements) EmitInitializationToLValue(E->getInit(i), - LValue::MakeAddr(NextVal, Quals)); + LValue::MakeAddr(NextVal, Quals), + ElementType); else EmitNullInitializationToLValue(LValue::MakeAddr(NextVal, Quals), ElementType); @@ -627,11 +640,11 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { // FIXME: volatility FieldDecl *Field = E->getInitializedFieldInUnion(); - LValue FieldLoc = CGF.EmitLValueForField(DestPtr, Field, true, 0); + LValue FieldLoc = CGF.EmitLValueForFieldInitialization(DestPtr, Field, 0); if (NumInitElements) { // Store the initializer into the field - EmitInitializationToLValue(E->getInit(0), FieldLoc); + EmitInitializationToLValue(E->getInit(0), FieldLoc, Field->getType()); } else { // Default-initialize to null EmitNullInitializationToLValue(FieldLoc, Field->getType()); @@ -653,12 +666,13 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { continue; // FIXME: volatility - LValue FieldLoc = CGF.EmitLValueForField(DestPtr, *Field, false, 0); + LValue FieldLoc = CGF.EmitLValueForFieldInitialization(DestPtr, *Field, 0); // We never generate write-barries for initialized fields. LValue::SetObjCNonGC(FieldLoc, true); if (CurInitVal < NumInitElements) { // Store the initializer into the field - EmitInitializationToLValue(E->getInit(CurInitVal++), FieldLoc); + EmitInitializationToLValue(E->getInit(CurInitVal++), FieldLoc, + Field->getType()); } else { // We're out of initalizers; default-initialize to null EmitNullInitializationToLValue(FieldLoc, Field->getType()); @@ -674,6 +688,8 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { /// type. The result is computed into DestPtr. Note that if DestPtr is null, /// the value of the aggregate expression is not needed. If VolatileDest is /// true, DestPtr cannot be 0. +// +// FIXME: Take Qualifiers object. void CodeGenFunction::EmitAggExpr(const Expr *E, llvm::Value *DestPtr, bool VolatileDest, bool IgnoreResult, bool IsInitializer, @@ -688,6 +704,14 @@ void CodeGenFunction::EmitAggExpr(const Expr *E, llvm::Value *DestPtr, .Visit(const_cast(E)); } +LValue CodeGenFunction::EmitAggExprToLValue(const Expr *E) { + assert(hasAggregateLLVMType(E->getType()) && "Invalid argument!"); + Qualifiers Q = MakeQualifiers(E->getType()); + llvm::Value *Temp = CreateMemTemp(E->getType()); + EmitAggExpr(E, Temp, Q.hasVolatile()); + return LValue::MakeAddr(Temp, Q); +} + void CodeGenFunction::EmitAggregateClear(llvm::Value *DestPtr, QualType Ty) { assert(!Ty->isAnyComplexType() && "Shouldn't happen for complex"); diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index e264109f02f0..032862160464 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -42,8 +42,10 @@ RValue CodeGenFunction::EmitCXXMemberCall(const CXXMethodDecl *MD, // And the rest of the call args EmitCallArgs(Args, FPT, ArgBeg, ArgEnd); - QualType ResultType = MD->getType()->getAs()->getResultType(); - return EmitCall(CGM.getTypes().getFunctionInfo(ResultType, Args), Callee, + QualType ResultType = FPT->getResultType(); + return EmitCall(CGM.getTypes().getFunctionInfo(ResultType, Args, + FPT->getCallConv(), + FPT->getNoReturnAttr()), Callee, ReturnValue, Args, MD); } @@ -60,7 +62,7 @@ static bool canDevirtualizeMemberFunctionCalls(const Expr *Base) { } // We can always devirtualize calls on temporary object expressions. - if (isa(Base)) + if (isa(Base)) return true; // And calls on bound temporaries. @@ -159,12 +161,10 @@ CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E, CGM.getTypes().GetFunctionType(CGM.getTypes().getFunctionInfo(RD, FPT), FPT->isVariadic()); - const llvm::Type *Int8PtrTy = - llvm::Type::getInt8Ty(VMContext)->getPointerTo(); + const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(VMContext); // Get the member function pointer. - llvm::Value *MemFnPtr = - CreateTempAlloca(ConvertType(MemFnExpr->getType()), "mem.fn"); + llvm::Value *MemFnPtr = CreateMemTemp(MemFnExpr->getType(), "mem.fn"); EmitAggExpr(MemFnExpr, MemFnPtr, /*VolatileDest=*/false); // Emit the 'this' pointer. @@ -206,19 +206,20 @@ CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E, Builder.CreateCondBr(IsVirtual, FnVirtual, FnNonVirtual); EmitBlock(FnVirtual); - const llvm::Type *VTableTy = - FTy->getPointerTo()->getPointerTo()->getPointerTo(); + const llvm::Type *VtableTy = + FTy->getPointerTo()->getPointerTo(); - llvm::Value *VTable = Builder.CreateBitCast(This, VTableTy); - VTable = Builder.CreateLoad(VTable); + llvm::Value *Vtable = Builder.CreateBitCast(This, VtableTy->getPointerTo()); + Vtable = Builder.CreateLoad(Vtable); - VTable = Builder.CreateGEP(VTable, FnAsInt, "fn"); + Vtable = Builder.CreateBitCast(Vtable, Int8PtrTy); + llvm::Value *VtableOffset = + Builder.CreateSub(FnAsInt, llvm::ConstantInt::get(PtrDiffTy, 1)); - // Since the function pointer is 1 plus the virtual table offset, we - // subtract 1 by using a GEP. - VTable = Builder.CreateConstGEP1_64(VTable, (uint64_t)-1); + Vtable = Builder.CreateGEP(Vtable, VtableOffset, "fn"); + Vtable = Builder.CreateBitCast(Vtable, VtableTy); - llvm::Value *VirtualFn = Builder.CreateLoad(VTable, "virtualfn"); + llvm::Value *VirtualFn = Builder.CreateLoad(Vtable, "virtualfn"); EmitBranch(FnEnd); EmitBlock(FnNonVirtual); @@ -244,8 +245,8 @@ CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E, // And the rest of the call args EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end()); - QualType ResultType = BO->getType()->getAs()->getResultType(); - return EmitCall(CGM.getTypes().getFunctionInfo(ResultType, Args), Callee, + const FunctionType *BO_FPT = BO->getType()->getAs(); + return EmitCall(CGM.getTypes().getFunctionInfo(Args, BO_FPT), Callee, ReturnValue, Args); } @@ -339,18 +340,20 @@ CodeGenFunction::EmitCXXConstructExpr(llvm::Value *Dest, } else // Call the constructor. - EmitCXXConstructorCall(CD, Ctor_Complete, Dest, + EmitCXXConstructorCall(CD, + E->isBaseInitialization()? Ctor_Base : Ctor_Complete, + Dest, E->arg_begin(), E->arg_end()); } -static uint64_t CalculateCookiePadding(ASTContext &Ctx, QualType ElementType) { +static CharUnits CalculateCookiePadding(ASTContext &Ctx, QualType ElementType) { const RecordType *RT = ElementType->getAs(); if (!RT) - return 0; + return CharUnits::Zero(); const CXXRecordDecl *RD = dyn_cast(RT->getDecl()); if (!RD) - return 0; + return CharUnits::Zero(); // Check if the class has a trivial destructor. if (RD->hasTrivialDestructor()) { @@ -372,25 +375,25 @@ static uint64_t CalculateCookiePadding(ASTContext &Ctx, QualType ElementType) { // No usual deallocation function, we don't need a cookie. if (!UsualDeallocationFunction) - return 0; + return CharUnits::Zero(); // The usual deallocation function doesn't take a size_t argument, so we // don't need a cookie. if (UsualDeallocationFunction->getNumParams() == 1) - return 0; + return CharUnits::Zero(); assert(UsualDeallocationFunction->getNumParams() == 2 && "Unexpected deallocation function type!"); } // Padding is the maximum of sizeof(size_t) and alignof(ElementType) - return std::max(Ctx.getTypeSize(Ctx.getSizeType()), - static_cast(Ctx.getTypeAlign(ElementType))) / 8; + return std::max(Ctx.getTypeSizeInChars(Ctx.getSizeType()), + Ctx.getTypeAlignInChars(ElementType)); } -static uint64_t CalculateCookiePadding(ASTContext &Ctx, const CXXNewExpr *E) { +static CharUnits CalculateCookiePadding(ASTContext &Ctx, const CXXNewExpr *E) { if (!E->isArray()) - return 0; + return CharUnits::Zero(); // No cookie is required if the new operator being used is // ::operator new[](size_t, void*). @@ -401,7 +404,7 @@ static uint64_t CalculateCookiePadding(ASTContext &Ctx, const CXXNewExpr *E) { Ctx.getCanonicalType(OperatorNew->getParamDecl(1)->getType()); if (ParamType == Ctx.VoidPtrTy) - return 0; + return CharUnits::Zero(); } } @@ -412,25 +415,25 @@ static llvm::Value *EmitCXXNewAllocSize(CodeGenFunction &CGF, const CXXNewExpr *E, llvm::Value *& NumElements) { QualType Type = E->getAllocatedType(); - uint64_t TypeSizeInBytes = CGF.getContext().getTypeSize(Type) / 8; + CharUnits TypeSize = CGF.getContext().getTypeSizeInChars(Type); const llvm::Type *SizeTy = CGF.ConvertType(CGF.getContext().getSizeType()); if (!E->isArray()) - return llvm::ConstantInt::get(SizeTy, TypeSizeInBytes); + return llvm::ConstantInt::get(SizeTy, TypeSize.getQuantity()); - uint64_t CookiePadding = CalculateCookiePadding(CGF.getContext(), E); + CharUnits CookiePadding = CalculateCookiePadding(CGF.getContext(), E); Expr::EvalResult Result; if (E->getArraySize()->Evaluate(Result, CGF.getContext()) && !Result.HasSideEffects && Result.Val.isInt()) { - uint64_t AllocSize = - Result.Val.getInt().getZExtValue() * TypeSizeInBytes + CookiePadding; + CharUnits AllocSize = + Result.Val.getInt().getZExtValue() * TypeSize + CookiePadding; NumElements = llvm::ConstantInt::get(SizeTy, Result.Val.getInt().getZExtValue()); - return llvm::ConstantInt::get(SizeTy, AllocSize); + return llvm::ConstantInt::get(SizeTy, AllocSize.getQuantity()); } // Emit the array size expression. @@ -439,11 +442,13 @@ static llvm::Value *EmitCXXNewAllocSize(CodeGenFunction &CGF, // Multiply with the type size. llvm::Value *V = CGF.Builder.CreateMul(NumElements, - llvm::ConstantInt::get(SizeTy, TypeSizeInBytes)); + llvm::ConstantInt::get(SizeTy, + TypeSize.getQuantity())); // And add the cookie padding if necessary. - if (CookiePadding) - V = CGF.Builder.CreateAdd(V, llvm::ConstantInt::get(SizeTy, CookiePadding)); + if (!CookiePadding.isZero()) + V = CGF.Builder.CreateAdd(V, + llvm::ConstantInt::get(SizeTy, CookiePadding.getQuantity())); return V; } @@ -538,7 +543,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // Emit the call to new. RValue RV = - EmitCall(CGM.getTypes().getFunctionInfo(NewFTy->getResultType(), NewArgs), + EmitCall(CGM.getTypes().getFunctionInfo(NewArgs, NewFTy), CGM.GetAddrOfFunction(NewFD), ReturnValueSlot(), NewArgs, NewFD); // If an allocation function is declared with an empty exception specification @@ -567,20 +572,22 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { Builder.CreateCondBr(IsNull, NewNull, NewNotNull); EmitBlock(NewNotNull); } - - if (uint64_t CookiePadding = CalculateCookiePadding(getContext(), E)) { - uint64_t CookieOffset = - CookiePadding - getContext().getTypeSize(SizeTy) / 8; + + CharUnits CookiePadding = CalculateCookiePadding(getContext(), E); + if (!CookiePadding.isZero()) { + CharUnits CookieOffset = + CookiePadding - getContext().getTypeSizeInChars(SizeTy); llvm::Value *NumElementsPtr = - Builder.CreateConstInBoundsGEP1_64(NewPtr, CookieOffset); + Builder.CreateConstInBoundsGEP1_64(NewPtr, CookieOffset.getQuantity()); NumElementsPtr = Builder.CreateBitCast(NumElementsPtr, ConvertType(SizeTy)->getPointerTo()); Builder.CreateStore(NumElements, NumElementsPtr); // Now add the padding to the new ptr. - NewPtr = Builder.CreateConstInBoundsGEP1_64(NewPtr, CookiePadding); + NewPtr = Builder.CreateConstInBoundsGEP1_64(NewPtr, + CookiePadding.getQuantity()); } NewPtr = Builder.CreateBitCast(NewPtr, ConvertType(E->getType())); @@ -611,23 +618,24 @@ GetAllocatedObjectPtrAndNumElements(CodeGenFunction &CGF, QualType SizeTy = CGF.getContext().getSizeType(); const llvm::Type *SizeLTy = CGF.ConvertType(SizeTy); - uint64_t DeleteTypeAlign = CGF.getContext().getTypeAlign(DeleteTy); - uint64_t CookiePadding = std::max(CGF.getContext().getTypeSize(SizeTy), - DeleteTypeAlign) / 8; - assert(CookiePadding && "CookiePadding should not be 0."); + CharUnits DeleteTypeAlign = CGF.getContext().getTypeAlignInChars(DeleteTy); + CharUnits CookiePadding = + std::max(CGF.getContext().getTypeSizeInChars(SizeTy), + DeleteTypeAlign); + assert(!CookiePadding.isZero() && "CookiePadding should not be 0."); const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); - uint64_t CookieOffset = - CookiePadding - CGF.getContext().getTypeSize(SizeTy) / 8; + CharUnits CookieOffset = + CookiePadding - CGF.getContext().getTypeSizeInChars(SizeTy); llvm::Value *AllocatedObjectPtr = CGF.Builder.CreateBitCast(Ptr, Int8PtrTy); AllocatedObjectPtr = CGF.Builder.CreateConstInBoundsGEP1_64(AllocatedObjectPtr, - -CookiePadding); + -CookiePadding.getQuantity()); llvm::Value *NumElementsPtr = CGF.Builder.CreateConstInBoundsGEP1_64(AllocatedObjectPtr, - CookieOffset); + CookieOffset.getQuantity()); NumElementsPtr = CGF.Builder.CreateBitCast(NumElementsPtr, SizeLTy->getPointerTo()); @@ -651,13 +659,13 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD, QualType SizeTy; if (DeleteFTy->getNumArgs() == 2) { SizeTy = DeleteFTy->getArgType(1); - uint64_t DeleteTypeSize = getContext().getTypeSize(DeleteTy) / 8; - Size = llvm::ConstantInt::get(ConvertType(SizeTy), DeleteTypeSize); + CharUnits DeleteTypeSize = getContext().getTypeSizeInChars(DeleteTy); + Size = llvm::ConstantInt::get(ConvertType(SizeTy), + DeleteTypeSize.getQuantity()); } if (DeleteFD->getOverloadedOperator() == OO_Array_Delete && - - CalculateCookiePadding(getContext(), DeleteTy)) { + !CalculateCookiePadding(getContext(), DeleteTy).isZero()) { // We need to get the number of elements in the array from the cookie. llvm::Value *AllocatedObjectPtr; llvm::Value *NumElements; @@ -679,8 +687,7 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD, DeleteArgs.push_back(std::make_pair(RValue::get(Size), SizeTy)); // Emit the call to delete. - EmitCall(CGM.getTypes().getFunctionInfo(DeleteFTy->getResultType(), - DeleteArgs), + EmitCall(CGM.getTypes().getFunctionInfo(DeleteArgs, DeleteFTy), CGM.GetAddrOfFunction(DeleteFD), ReturnValueSlot(), DeleteArgs, DeleteFD); } diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp index 5ec336ce79e9..591534042f51 100644 --- a/lib/CodeGen/CGExprComplex.cpp +++ b/lib/CodeGen/CGExprComplex.cpp @@ -366,7 +366,7 @@ ComplexPairTy ComplexExprEmitter::VisitUnaryMinus(const UnaryOperator *E) { ComplexPairTy Op = Visit(E->getSubExpr()); llvm::Value *ResR, *ResI; - if (Op.first->getType()->isFloatingPoint()) { + if (Op.first->getType()->isFloatingPointTy()) { ResR = Builder.CreateFNeg(Op.first, "neg.r"); ResI = Builder.CreateFNeg(Op.second, "neg.i"); } else { @@ -384,7 +384,7 @@ ComplexPairTy ComplexExprEmitter::VisitUnaryNot(const UnaryOperator *E) { // ~(a+ib) = a + i*-b ComplexPairTy Op = Visit(E->getSubExpr()); llvm::Value *ResI; - if (Op.second->getType()->isFloatingPoint()) + if (Op.second->getType()->isFloatingPointTy()) ResI = Builder.CreateFNeg(Op.second, "conj.i"); else ResI = Builder.CreateNeg(Op.second, "conj.i"); @@ -395,7 +395,7 @@ ComplexPairTy ComplexExprEmitter::VisitUnaryNot(const UnaryOperator *E) { ComplexPairTy ComplexExprEmitter::EmitBinAdd(const BinOpInfo &Op) { llvm::Value *ResR, *ResI; - if (Op.LHS.first->getType()->isFloatingPoint()) { + if (Op.LHS.first->getType()->isFloatingPointTy()) { ResR = Builder.CreateFAdd(Op.LHS.first, Op.RHS.first, "add.r"); ResI = Builder.CreateFAdd(Op.LHS.second, Op.RHS.second, "add.i"); } else { @@ -407,7 +407,7 @@ ComplexPairTy ComplexExprEmitter::EmitBinAdd(const BinOpInfo &Op) { ComplexPairTy ComplexExprEmitter::EmitBinSub(const BinOpInfo &Op) { llvm::Value *ResR, *ResI; - if (Op.LHS.first->getType()->isFloatingPoint()) { + if (Op.LHS.first->getType()->isFloatingPointTy()) { ResR = Builder.CreateFSub(Op.LHS.first, Op.RHS.first, "sub.r"); ResI = Builder.CreateFSub(Op.LHS.second, Op.RHS.second, "sub.i"); } else { @@ -422,7 +422,7 @@ ComplexPairTy ComplexExprEmitter::EmitBinMul(const BinOpInfo &Op) { using llvm::Value; Value *ResR, *ResI; - if (Op.LHS.first->getType()->isFloatingPoint()) { + if (Op.LHS.first->getType()->isFloatingPointTy()) { Value *ResRl = Builder.CreateFMul(Op.LHS.first, Op.RHS.first, "mul.rl"); Value *ResRr = Builder.CreateFMul(Op.LHS.second, Op.RHS.second,"mul.rr"); ResR = Builder.CreateFSub(ResRl, ResRr, "mul.r"); @@ -448,7 +448,7 @@ ComplexPairTy ComplexExprEmitter::EmitBinDiv(const BinOpInfo &Op) { llvm::Value *DSTr, *DSTi; - if (Op.LHS.first->getType()->isFloatingPoint()) { + if (Op.LHS.first->getType()->isFloatingPointTy()) { // (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) llvm::Value *Tmp1 = Builder.CreateFMul(LHSr, RHSr, "tmp"); // a*c llvm::Value *Tmp2 = Builder.CreateFMul(LHSi, RHSi, "tmp"); // b*d diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index 7d5b3da0b6c6..3df552d75bb6 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -418,12 +418,19 @@ public: // Get the function pointer (or index if this is a virtual function). if (MD->isVirtual()) { uint64_t Index = CGM.getVtableInfo().getMethodVtableIndex(MD); - - // The pointer is 1 + the virtual table offset in bytes. + + // Itanium C++ ABI 2.3: + // For a non-virtual function, this field is a simple function pointer. + // For a virtual function, it is 1 plus the virtual table offset + // (in bytes) of the function, represented as a ptrdiff_t. Values[0] = llvm::ConstantInt::get(PtrDiffTy, (Index * 8) + 1); } else { - llvm::Constant *FuncPtr = CGM.GetAddrOfFunction(MD); + const FunctionProtoType *FPT = MD->getType()->getAs(); + const llvm::Type *Ty = + CGM.getTypes().GetFunctionType(CGM.getTypes().getFunctionInfo(MD), + FPT->isVariadic()); + llvm::Constant *FuncPtr = CGM.GetAddrOfFunction(MD, Ty); Values[0] = llvm::ConstantExpr::getPtrToInt(FuncPtr, PtrDiffTy); } @@ -438,16 +445,16 @@ public: if (const MemberPointerType *MPT = E->getType()->getAs()) { QualType T = MPT->getPointeeType(); - if (T->isFunctionProtoType()) { - DeclRefExpr *DRE = cast(E->getSubExpr()); - - return EmitMemberFunctionPointer(cast(DRE->getDecl())); - } + DeclRefExpr *DRE = cast(E->getSubExpr()); + + NamedDecl *ND = DRE->getDecl(); + if (T->isFunctionProtoType()) + return EmitMemberFunctionPointer(cast(ND)); - // FIXME: Should we handle other member pointer types here too, - // or should they be handled by Expr::Evaluate? + // We have a pointer to data member. + return CGM.EmitPointerToDataMember(cast(ND)); } - + return 0; } @@ -534,8 +541,8 @@ public: llvm::ConstantStruct *CS = cast(C); // Check if we need to update the adjustment. - if (llvm::Constant *Offset = CGM.GetCXXBaseClassOffset(DerivedClass, - BaseClass)) { + if (llvm::Constant *Offset = + CGM.GetNonVirtualBaseClassOffset(DerivedClass, BaseClass)) { llvm::Constant *Values[2]; Values[0] = CS->getOperand(0); @@ -668,6 +675,40 @@ public: return 0; } + llvm::Constant *VisitCXXConstructExpr(CXXConstructExpr *E) { + if (!E->getConstructor()->isTrivial()) + return 0; + + QualType Ty = E->getType(); + + // FIXME: We should not have to call getBaseElementType here. + const RecordType *RT = + CGM.getContext().getBaseElementType(Ty)->getAs(); + const CXXRecordDecl *RD = cast(RT->getDecl()); + + // If the class doesn't have a trivial destructor, we can't emit it as a + // constant expr. + if (!RD->hasTrivialDestructor()) + return 0; + + // Only copy and default constructors can be trivial. + + + if (E->getNumArgs()) { + assert(E->getNumArgs() == 1 && "trivial ctor with > 1 argument"); + assert(E->getConstructor()->isCopyConstructor() && + "trivial ctor has argument but isn't a copy ctor"); + + Expr *Arg = E->getArg(0); + assert(CGM.getContext().hasSameUnqualifiedType(Ty, Arg->getType()) && + "argument to copy ctor is of wrong type"); + + return Visit(Arg); + } + + return CGM.EmitNullConstant(Ty); + } + llvm::Constant *VisitStringLiteral(StringLiteral *E) { assert(!E->getType()->isPointerType() && "Strings are always arrays"); @@ -911,7 +952,24 @@ llvm::Constant *CodeGenModule::EmitConstantExpr(const Expr *E, return C; } -static inline bool isDataMemberPointerType(QualType T) { +static bool containsPointerToDataMember(CodeGenTypes &Types, QualType T) { + // No need to check for member pointers when not compiling C++. + if (!Types.getContext().getLangOptions().CPlusPlus) + return false; + + T = Types.getContext().getBaseElementType(T); + + if (const RecordType *RT = T->getAs()) { + const CXXRecordDecl *RD = cast(RT->getDecl()); + + // FIXME: It would be better if there was a way to explicitly compute the + // record layout instead of converting to a type. + Types.ConvertTagDeclType(RD); + + const CGRecordLayout &Layout = Types.getCGRecordLayout(RD); + return Layout.containsPointerToDataMember(); + } + if (const MemberPointerType *MPT = T->getAs()) return !MPT->getPointeeType()->isFunctionType(); @@ -919,43 +977,80 @@ static inline bool isDataMemberPointerType(QualType T) { } llvm::Constant *CodeGenModule::EmitNullConstant(QualType T) { - // No need to check for member pointers when not compiling C++. - if (!getContext().getLangOptions().CPlusPlus) + if (!containsPointerToDataMember(getTypes(), T)) return llvm::Constant::getNullValue(getTypes().ConvertTypeForMem(T)); - + if (const ConstantArrayType *CAT = Context.getAsConstantArrayType(T)) { QualType ElementTy = CAT->getElementType(); - // FIXME: Handle arrays of structs that contain member pointers. - if (isDataMemberPointerType(Context.getBaseElementType(ElementTy))) { - llvm::Constant *Element = EmitNullConstant(ElementTy); - uint64_t NumElements = CAT->getSize().getZExtValue(); - std::vector Array(NumElements); - for (uint64_t i = 0; i != NumElements; ++i) - Array[i] = Element; + llvm::Constant *Element = EmitNullConstant(ElementTy); + unsigned NumElements = CAT->getSize().getZExtValue(); + std::vector Array(NumElements); + for (unsigned i = 0; i != NumElements; ++i) + Array[i] = Element; - const llvm::ArrayType *ATy = - cast(getTypes().ConvertTypeForMem(T)); - return llvm::ConstantArray::get(ATy, Array); - } + const llvm::ArrayType *ATy = + cast(getTypes().ConvertTypeForMem(T)); + return llvm::ConstantArray::get(ATy, Array); } if (const RecordType *RT = T->getAs()) { - const RecordDecl *RD = RT->getDecl(); - // FIXME: It would be better if there was a way to explicitly compute the - // record layout instead of converting to a type. - Types.ConvertTagDeclType(RD); - - const CGRecordLayout &Layout = Types.getCGRecordLayout(RD); - if (Layout.containsMemberPointer()) { - assert(0 && "FIXME: No support for structs with member pointers yet!"); + const CXXRecordDecl *RD = cast(RT->getDecl()); + assert(!RD->getNumBases() && + "FIXME: Handle zero-initializing structs with bases and " + "pointers to data members."); + const llvm::StructType *STy = + cast(getTypes().ConvertTypeForMem(T)); + unsigned NumElements = STy->getNumElements(); + std::vector Elements(NumElements); + + for (RecordDecl::field_iterator I = RD->field_begin(), + E = RD->field_end(); I != E; ++I) { + const FieldDecl *FD = *I; + + unsigned FieldNo = getTypes().getLLVMFieldNo(FD); + Elements[FieldNo] = EmitNullConstant(FD->getType()); + } + + // Now go through all other fields and zero them out. + for (unsigned i = 0; i != NumElements; ++i) { + if (!Elements[i]) + Elements[i] = llvm::Constant::getNullValue(STy->getElementType(i)); } + + return llvm::ConstantStruct::get(STy, Elements); } - // FIXME: Handle structs that contain member pointers. - if (isDataMemberPointerType(T)) - return llvm::Constant::getAllOnesValue(getTypes().ConvertTypeForMem(T)); + assert(!T->getAs()->getPointeeType()->isFunctionType() && + "Should only see pointers to data members here!"); + + // Itanium C++ ABI 2.3: + // A NULL pointer is represented as -1. + return llvm::ConstantInt::get(getTypes().ConvertTypeForMem(T), -1ULL, + /*isSigned=*/true); +} + +llvm::Constant * +CodeGenModule::EmitPointerToDataMember(const FieldDecl *FD) { + + // Itanium C++ ABI 2.3: + // A pointer to data member is an offset from the base address of the class + // object containing it, represented as a ptrdiff_t + + const CXXRecordDecl *ClassDecl = cast(FD->getParent()); + QualType ClassType = + getContext().getTypeDeclType(const_cast(ClassDecl)); + + const llvm::StructType *ClassLTy = + cast(getTypes().ConvertType(ClassType)); + + unsigned FieldNo = getTypes().getLLVMFieldNo(FD); + uint64_t Offset = + getTargetData().getStructLayout(ClassLTy)->getElementOffset(FieldNo); + + const llvm::Type *PtrDiffTy = + getTypes().ConvertType(getContext().getPointerDiffType()); - return llvm::Constant::getNullValue(getTypes().ConvertTypeForMem(T)); + return llvm::ConstantInt::get(PtrDiffTy, Offset); } diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 690a7dc2fded..db0998b4dedc 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -290,7 +290,7 @@ public: if (CGF.getContext().getLangOptions().OverflowChecking && Ops.Ty->isSignedIntegerType()) return EmitOverflowCheckedBinOp(Ops); - if (Ops.LHS->getType()->isFPOrFPVector()) + if (Ops.LHS->getType()->isFPOrFPVectorTy()) return Builder.CreateFMul(Ops.LHS, Ops.RHS, "mul"); return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); } @@ -388,8 +388,6 @@ Value *ScalarExprEmitter::EmitConversionToBool(Value *Src, QualType SrcType) { } if (SrcType->isMemberPointerType()) { - // FIXME: This is ABI specific. - // Compare against -1. llvm::Value *NegativeOne = llvm::Constant::getAllOnesValue(Src->getType()); return Builder.CreateICmpNE(Src, NegativeOne, "tobool"); @@ -507,7 +505,7 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, return Builder.CreateUIToFP(Src, DstTy, "conv"); } - assert(Src->getType()->isFloatingPoint() && "Unknown real conversion"); + assert(Src->getType()->isFloatingPointTy() && "Unknown real conversion"); if (isa(DstTy)) { if (DstType->isSignedIntegerType()) return Builder.CreateFPToSI(Src, DstTy, "conv"); @@ -515,7 +513,7 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, return Builder.CreateFPToUI(Src, DstTy, "conv"); } - assert(DstTy->isFloatingPoint() && "Unknown real conversion"); + assert(DstTy->isFloatingPointTy() && "Unknown real conversion"); if (DstTy->getTypeID() < Src->getType()->getTypeID()) return Builder.CreateFPTrunc(Src, DstTy, "conv"); else @@ -891,8 +889,8 @@ Value *ScalarExprEmitter::EmitCastExpr(CastExpr *CE) { if (CE->getCastKind() == CastExpr::CK_DerivedToBaseMemberPointer) std::swap(DerivedDecl, BaseDecl); - llvm::Constant *Adj = CGF.CGM.GetCXXBaseClassOffset(DerivedDecl, BaseDecl); - if (Adj) { + if (llvm::Constant *Adj = + CGF.CGM.GetNonVirtualBaseClassOffset(DerivedDecl, BaseDecl)) { if (CE->getCastKind() == CastExpr::CK_DerivedToBaseMemberPointer) Src = Builder.CreateSub(Src, Adj, "adj"); else @@ -1009,7 +1007,7 @@ Value *ScalarExprEmitter::VisitBlockDeclRefExpr(const BlockDeclRefExpr *E) { Value *ScalarExprEmitter::VisitUnaryMinus(const UnaryOperator *E) { TestAndClearIgnoreResultAssign(); Value *Op = Visit(E->getSubExpr()); - if (Op->getType()->isFPOrFPVector()) + if (Op->getType()->isFPOrFPVectorTy()) return Builder.CreateFNeg(Op, "neg"); return Builder.CreateNeg(Op, "neg"); } @@ -1154,7 +1152,7 @@ Value *ScalarExprEmitter::EmitCompoundAssign(const CompoundAssignOperator *E, Value *ScalarExprEmitter::EmitDiv(const BinOpInfo &Ops) { - if (Ops.LHS->getType()->isFPOrFPVector()) + if (Ops.LHS->getType()->isFPOrFPVectorTy()) return Builder.CreateFDiv(Ops.LHS, Ops.RHS, "div"); else if (Ops.Ty->isUnsignedIntegerType()) return Builder.CreateUDiv(Ops.LHS, Ops.RHS, "div"); @@ -1261,7 +1259,7 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &Ops) { Ops.Ty->isSignedIntegerType()) return EmitOverflowCheckedBinOp(Ops); - if (Ops.LHS->getType()->isFPOrFPVector()) + if (Ops.LHS->getType()->isFPOrFPVectorTy()) return Builder.CreateFAdd(Ops.LHS, Ops.RHS, "add"); // Signed integer overflow is undefined behavior. @@ -1337,7 +1335,7 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &Ops) { && Ops.Ty->isSignedIntegerType()) return EmitOverflowCheckedBinOp(Ops); - if (Ops.LHS->getType()->isFPOrFPVector()) + if (Ops.LHS->getType()->isFPOrFPVectorTy()) return Builder.CreateFSub(Ops.LHS, Ops.RHS, "sub"); return Builder.CreateSub(Ops.LHS, Ops.RHS, "sub"); } @@ -1505,7 +1503,7 @@ Value *ScalarExprEmitter::EmitCompare(const BinaryOperator *E,unsigned UICmpOpc, Value *LHS = Visit(E->getLHS()); Value *RHS = Visit(E->getRHS()); - if (LHS->getType()->isFPOrFPVector()) { + if (LHS->getType()->isFPOrFPVectorTy()) { Result = Builder.CreateFCmp((llvm::CmpInst::Predicate)FCmpOpc, LHS, RHS, "cmp"); } else if (LHSTy->isSignedIntegerType()) { @@ -1615,10 +1613,10 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) { PI != PE; ++PI) PN->addIncoming(llvm::ConstantInt::getFalse(VMContext), *PI); - CGF.StartConditionalBranch(); + CGF.BeginConditionalBranch(); CGF.EmitBlock(RHSBlock); Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS()); - CGF.FinishConditionalBranch(); + CGF.EndConditionalBranch(); // Reaquire the RHS block, as there may be subblocks inserted. RHSBlock = Builder.GetInsertBlock(); @@ -1665,13 +1663,13 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) { PI != PE; ++PI) PN->addIncoming(llvm::ConstantInt::getTrue(VMContext), *PI); - CGF.StartConditionalBranch(); + CGF.BeginConditionalBranch(); // Emit the RHS condition as a bool value. CGF.EmitBlock(RHSBlock); Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS()); - CGF.FinishConditionalBranch(); + CGF.EndConditionalBranch(); // Reaquire the RHS block, as there may be subblocks inserted. RHSBlock = Builder.GetInsertBlock(); @@ -1785,7 +1783,7 @@ VisitConditionalOperator(const ConditionalOperator *E) { Builder.CreateCondBr(CondBoolVal, LHSBlock, RHSBlock); } - CGF.StartConditionalBranch(); + CGF.BeginConditionalBranch(); CGF.EmitBlock(LHSBlock); // Handle the GNU extension for missing LHS. @@ -1795,15 +1793,15 @@ VisitConditionalOperator(const ConditionalOperator *E) { else // Perform promotions, to handle cases like "short ?: int" LHS = EmitScalarConversion(CondVal, E->getCond()->getType(), E->getType()); - CGF.FinishConditionalBranch(); + CGF.EndConditionalBranch(); LHSBlock = Builder.GetInsertBlock(); CGF.EmitBranch(ContBlock); - CGF.StartConditionalBranch(); + CGF.BeginConditionalBranch(); CGF.EmitBlock(RHSBlock); Value *RHS = Visit(E->getRHS()); - CGF.FinishConditionalBranch(); + CGF.EndConditionalBranch(); RHSBlock = Builder.GetInsertBlock(); CGF.EmitBranch(ContBlock); @@ -1882,14 +1880,23 @@ LValue CodeGenFunction::EmitObjCIsaExpr(const ObjCIsaExpr *E) { llvm::Value *V; // object->isa or (*object).isa // Generate code as for: *(Class*)object + // build Class* type + const llvm::Type *ClassPtrTy = ConvertType(E->getType()); + Expr *BaseExpr = E->getBase(); - if (E->isArrow()) - V = ScalarExprEmitter(*this).EmitLoadOfLValue(BaseExpr); - else - V = EmitLValue(BaseExpr).getAddress(); + if (BaseExpr->isLvalue(getContext()) != Expr::LV_Valid) { + V = CreateTempAlloca(ClassPtrTy, "resval"); + llvm::Value *Src = EmitScalarExpr(BaseExpr); + Builder.CreateStore(Src, V); + } + else { + if (E->isArrow()) + V = ScalarExprEmitter(*this).EmitLoadOfLValue(BaseExpr); + else + V = EmitLValue(BaseExpr).getAddress(); + } // build Class* type - const llvm::Type *ClassPtrTy = ConvertType(E->getType()); ClassPtrTy = ClassPtrTy->getPointerTo(); V = Builder.CreateBitCast(V, ClassPtrTy); LValue LV = LValue::MakeAddr(V, MakeQualifiers(E->getType())); diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 896d2207ea4b..b62e6ed44366 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -122,7 +122,7 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl *OMD, E = OMD->param_end(); PI != E; ++PI) Args.push_back(std::make_pair(*PI, (*PI)->getType())); - StartFunction(OMD, OMD->getResultType(), Fn, Args, OMD->getLocEnd()); + StartFunction(OMD, OMD->getResultType(), Fn, Args, OMD->getLocStart()); } /// Generate an Objective-C method. An Objective-C method is a C function with @@ -190,7 +190,8 @@ void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP, Args.push_back(std::make_pair(RValue::get(True), getContext().BoolTy)); // FIXME: We shouldn't need to get the function info here, the // runtime already should have computed it to build the function. - RValue RV = EmitCall(Types.getFunctionInfo(PD->getType(), Args), + RValue RV = EmitCall(Types.getFunctionInfo(PD->getType(), Args, + CC_Default, false), GetPropertyFn, ReturnValueSlot(), Args); // We need to fix the type here. Ivars with copy & retain are // always objects so we don't need to worry about complex or @@ -278,7 +279,8 @@ void CodeGenFunction::GenerateObjCSetter(ObjCImplementationDecl *IMP, getContext().BoolTy)); // FIXME: We shouldn't need to get the function info here, the runtime // already should have computed it to build the function. - EmitCall(Types.getFunctionInfo(getContext().VoidTy, Args), SetPropertyFn, + EmitCall(Types.getFunctionInfo(getContext().VoidTy, Args, + CC_Default, false), SetPropertyFn, ReturnValueSlot(), Args); } else { // FIXME: Find a clean way to avoid AST node creation. @@ -450,9 +452,7 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ // Fast enumeration state. QualType StateTy = getContext().getObjCFastEnumerationStateType(); - llvm::AllocaInst *StatePtr = CreateTempAlloca(ConvertType(StateTy), - "state.ptr"); - StatePtr->setAlignment(getContext().getTypeAlign(StateTy) >> 3); + llvm::Value *StatePtr = CreateMemTemp(StateTy, "state.ptr"); EmitMemSetToZero(StatePtr, StateTy); // Number of elements in the items array. @@ -470,7 +470,7 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ getContext().getConstantArrayType(getContext().getObjCIdType(), llvm::APInt(32, NumItems), ArrayType::Normal, 0); - llvm::Value *ItemsPtr = CreateTempAlloca(ConvertType(ItemsTy), "items.ptr"); + llvm::Value *ItemsPtr = CreateMemTemp(ItemsTy, "items.ptr"); llvm::Value *Collection = EmitScalarExpr(S.getCollection()); @@ -492,7 +492,8 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ FastEnumSel, Collection, false, Args); - llvm::Value *LimitPtr = CreateTempAlloca(UnsignedLongLTy, "limit.ptr"); + llvm::Value *LimitPtr = CreateMemTemp(getContext().UnsignedLongTy, + "limit.ptr"); Builder.CreateStore(CountRV.getScalarVal(), LimitPtr); llvm::BasicBlock *NoElements = createBasicBlock("noelements"); @@ -506,8 +507,7 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ EmitBlock(SetStartMutations); - llvm::Value *StartMutationsPtr = - CreateTempAlloca(UnsignedLongLTy); + llvm::Value *StartMutationsPtr = CreateMemTemp(getContext().UnsignedLongTy); llvm::Value *StateMutationsPtrPtr = Builder.CreateStructGEP(StatePtr, 2, "mutationsptr.ptr"); @@ -522,7 +522,8 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ llvm::BasicBlock *LoopStart = createBasicBlock("loopstart"); EmitBlock(LoopStart); - llvm::Value *CounterPtr = CreateTempAlloca(UnsignedLongLTy, "counter.ptr"); + llvm::Value *CounterPtr = CreateMemTemp(getContext().UnsignedLongTy, + "counter.ptr"); Builder.CreateStore(Zero, CounterPtr); llvm::BasicBlock *LoopBody = createBasicBlock("loopbody"); @@ -553,7 +554,8 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ getContext().getObjCIdType())); // FIXME: We shouldn't need to get the function info here, the runtime already // should have computed it to build the function. - EmitCall(CGM.getTypes().getFunctionInfo(getContext().VoidTy, Args2), + EmitCall(CGM.getTypes().getFunctionInfo(getContext().VoidTy, Args2, + CC_Default, false), EnumerationMutationFn, ReturnValueSlot(), Args2); EmitBlock(WasNotMutated); diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index 77be9fb58231..1d38ef9e2d2f 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/lib/CodeGen/CGObjCGNU.cpp @@ -55,6 +55,7 @@ private: const llvm::PointerType *PtrToInt8Ty; const llvm::FunctionType *IMPTy; const llvm::PointerType *IdTy; + const llvm::PointerType *PtrToIdTy; QualType ASTIdTy; const llvm::IntegerType *IntTy; const llvm::PointerType *PtrTy; @@ -65,11 +66,17 @@ private: std::vector Classes; std::vector Categories; std::vector ConstantStrings; + llvm::StringMap ObjCStrings; llvm::Function *LoadFunction; llvm::StringMap ExistingProtocols; typedef std::pair TypedSelector; std::map TypedSelectors; llvm::StringMap UntypedSelectors; + // Selectors that we don't emit in GC mode + Selector RetainSel, ReleaseSel, AutoreleaseSel; + // Functions used for GC. + llvm::Constant *IvarAssignFn, *StrongCastAssignFn, *MemMoveFn, *WeakReadFn, + *WeakAssignFn, *GlobalAssignFn; // Some zeros used for GEPs in lots of places. llvm::Constant *Zeros[2]; llvm::Constant *NULLPtr; @@ -114,14 +121,18 @@ private: llvm::Constant *ExportUniqueString(const std::string &Str, const std::string prefix); llvm::Constant *MakeGlobal(const llvm::StructType *Ty, - std::vector &V, const std::string &Name="", + std::vector &V, llvm::StringRef Name="", llvm::GlobalValue::LinkageTypes linkage=llvm::GlobalValue::InternalLinkage); llvm::Constant *MakeGlobal(const llvm::ArrayType *Ty, - std::vector &V, const std::string &Name="", + std::vector &V, llvm::StringRef Name="", llvm::GlobalValue::LinkageTypes linkage=llvm::GlobalValue::InternalLinkage); llvm::GlobalVariable *ObjCIvarOffsetVariable(const ObjCInterfaceDecl *ID, const ObjCIvarDecl *Ivar); void EmitClassRef(const std::string &className); + llvm::Value* EnforceType(CGBuilderTy B, llvm::Value *V, const llvm::Type *Ty){ + if (V->getType() == Ty) return V; + return B.CreateBitCast(V, Ty); + } public: CGObjCGNU(CodeGen::CodeGenModule &cgm); virtual llvm::Constant *GenerateConstantString(const StringLiteral *); @@ -257,12 +268,54 @@ CGObjCGNU::CGObjCGNU(CodeGen::CodeGenModule &cgm) } else { IdTy = cast(CGM.getTypes().ConvertType(ASTIdTy)); } + PtrToIdTy = llvm::PointerType::getUnqual(IdTy); // IMP type std::vector IMPArgs; IMPArgs.push_back(IdTy); IMPArgs.push_back(SelectorTy); IMPTy = llvm::FunctionType::get(IdTy, IMPArgs, true); + + if (CGM.getLangOptions().getGCMode() != LangOptions::NonGC) { + // Get selectors needed in GC mode + RetainSel = GetNullarySelector("retain", CGM.getContext()); + ReleaseSel = GetNullarySelector("release", CGM.getContext()); + AutoreleaseSel = GetNullarySelector("autorelease", CGM.getContext()); + + // Get functions needed in GC mode + + // id objc_assign_ivar(id, id, ptrdiff_t); + std::vector Args(1, IdTy); + Args.push_back(PtrToIdTy); + // FIXME: ptrdiff_t + Args.push_back(LongTy); + llvm::FunctionType *FTy = llvm::FunctionType::get(IdTy, Args, false); + IvarAssignFn = CGM.CreateRuntimeFunction(FTy, "objc_assign_ivar"); + // id objc_assign_strongCast (id, id*) + Args.pop_back(); + FTy = llvm::FunctionType::get(IdTy, Args, false); + StrongCastAssignFn = + CGM.CreateRuntimeFunction(FTy, "objc_assign_strongCast"); + // id objc_assign_global(id, id*); + FTy = llvm::FunctionType::get(IdTy, Args, false); + GlobalAssignFn = CGM.CreateRuntimeFunction(FTy, "objc_assign_global"); + // id objc_assign_weak(id, id*); + FTy = llvm::FunctionType::get(IdTy, Args, false); + WeakAssignFn = CGM.CreateRuntimeFunction(FTy, "objc_assign_weak"); + // id objc_read_weak(id*); + Args.clear(); + Args.push_back(PtrToIdTy); + FTy = llvm::FunctionType::get(IdTy, Args, false); + WeakReadFn = CGM.CreateRuntimeFunction(FTy, "objc_read_weak"); + // void *objc_memmove_collectable(void*, void *, size_t); + Args.clear(); + Args.push_back(PtrToInt8Ty); + Args.push_back(PtrToInt8Ty); + // FIXME: size_t + Args.push_back(LongTy); + FTy = llvm::FunctionType::get(IdTy, Args, false); + MemMoveFn = CGM.CreateRuntimeFunction(FTy, "objc_memmove_collectable"); + } } // This has to perform the lookup every time, since posing and related @@ -340,7 +393,7 @@ llvm::Constant *CGObjCGNU::ExportUniqueString(const std::string &Str, } llvm::Constant *CGObjCGNU::MakeGlobal(const llvm::StructType *Ty, - std::vector &V, const std::string &Name, + std::vector &V, llvm::StringRef Name, llvm::GlobalValue::LinkageTypes linkage) { llvm::Constant *C = llvm::ConstantStruct::get(Ty, V); return new llvm::GlobalVariable(TheModule, Ty, false, @@ -348,7 +401,7 @@ llvm::Constant *CGObjCGNU::MakeGlobal(const llvm::StructType *Ty, } llvm::Constant *CGObjCGNU::MakeGlobal(const llvm::ArrayType *Ty, - std::vector &V, const std::string &Name, + std::vector &V, llvm::StringRef Name, llvm::GlobalValue::LinkageTypes linkage) { llvm::Constant *C = llvm::ConstantArray::get(Ty, V); return new llvm::GlobalVariable(TheModule, Ty, false, @@ -357,8 +410,14 @@ llvm::Constant *CGObjCGNU::MakeGlobal(const llvm::ArrayType *Ty, /// Generate an NSConstantString object. llvm::Constant *CGObjCGNU::GenerateConstantString(const StringLiteral *SL) { + std::string Str(SL->getStrData(), SL->getByteLength()); + // Look for an existing one + llvm::StringMap::iterator old = ObjCStrings.find(Str); + if (old != ObjCStrings.end()) + return old->getValue(); + std::vector Ivars; Ivars.push_back(NULLPtr); Ivars.push_back(MakeConstantString(Str)); @@ -366,8 +425,9 @@ llvm::Constant *CGObjCGNU::GenerateConstantString(const StringLiteral *SL) { llvm::Constant *ObjCStr = MakeGlobal( llvm::StructType::get(VMContext, PtrToInt8Ty, PtrToInt8Ty, IntTy, NULL), Ivars, ".objc_str"); - ConstantStrings.push_back( - llvm::ConstantExpr::getBitCast(ObjCStr, PtrToInt8Ty)); + ObjCStr = llvm::ConstantExpr::getBitCast(ObjCStr, PtrToInt8Ty); + ObjCStrings[Str] = ObjCStr; + ConstantStrings.push_back(ObjCStr); return ObjCStr; } @@ -384,6 +444,14 @@ CGObjCGNU::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, bool IsClassMessage, const CallArgList &CallArgs, const ObjCMethodDecl *Method) { + if (CGM.getLangOptions().getGCMode() != LangOptions::NonGC) { + if (Sel == RetainSel || Sel == AutoreleaseSel) { + return RValue::get(Receiver); + } + if (Sel == ReleaseSel) { + return RValue::get(0); + } + } llvm::Value *cmd = GetSelector(CGF.Builder, Sel); CallArgList ActualArgs; @@ -396,7 +464,8 @@ CGObjCGNU::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); CodeGenTypes &Types = CGM.getTypes(); - const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs); + const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs, + CC_Default, false); const llvm::FunctionType *impType = Types.GetFunctionType(FnInfo, Method ? Method->isVariadic() : false); @@ -478,6 +547,14 @@ CGObjCGNU::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, bool IsClassMessage, const CallArgList &CallArgs, const ObjCMethodDecl *Method) { + if (CGM.getLangOptions().getGCMode() != LangOptions::NonGC) { + if (Sel == RetainSel || Sel == AutoreleaseSel) { + return RValue::get(Receiver); + } + if (Sel == ReleaseSel) { + return RValue::get(0); + } + } CGBuilderTy &Builder = CGF.Builder; IdTy = cast(CGM.getTypes().ConvertType(ASTIdTy)); llvm::Value *cmd; @@ -495,7 +572,8 @@ CGObjCGNU::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); CodeGenTypes &Types = CGM.getTypes(); - const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs); + const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs, + CC_Default, false); const llvm::FunctionType *impType = Types.GetFunctionType(FnInfo, Method ? Method->isVariadic() : false); @@ -1517,7 +1595,7 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() { Elements.push_back(llvm::ConstantInt::get(LongTy, RuntimeVersion)); } // sizeof(ModuleTy) - llvm::TargetData td = llvm::TargetData::TargetData(&TheModule); + llvm::TargetData td(&TheModule); Elements.push_back(llvm::ConstantInt::get(LongTy, td.getTypeSizeInBits(ModuleTy)/8)); //FIXME: Should be the path to the file where this module was declared @@ -1610,7 +1688,8 @@ llvm::Constant *CGObjCGNU::EnumerationMutationFunction() { llvm::SmallVector Params; Params.push_back(ASTIdTy); const llvm::FunctionType *FTy = - Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params), false); + Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params, + CC_Default, false), false); return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); } @@ -1668,7 +1747,6 @@ void CGObjCGNU::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, CGF.EmitBlock(TryHandler); // Get the correct versions of the exception handling intrinsics - llvm::TargetData td = llvm::TargetData::TargetData(&TheModule); llvm::Value *llvm_eh_exception = CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_exception); llvm::Value *llvm_eh_selector = @@ -1899,35 +1977,58 @@ void CGObjCGNU::EmitThrowStmt(CodeGen::CodeGenFunction &CGF, llvm::Value * CGObjCGNU::EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF, llvm::Value *AddrWeakObj) { - return 0; + CGBuilderTy B = CGF.Builder; + AddrWeakObj = EnforceType(B, AddrWeakObj, IdTy); + return B.CreateCall(WeakReadFn, AddrWeakObj); } void CGObjCGNU::EmitObjCWeakAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst) { - return; + CGBuilderTy B = CGF.Builder; + src = EnforceType(B, src, IdTy); + dst = EnforceType(B, dst, PtrToIdTy); + B.CreateCall2(WeakAssignFn, src, dst); } void CGObjCGNU::EmitObjCGlobalAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst) { - return; + CGBuilderTy B = CGF.Builder; + src = EnforceType(B, src, IdTy); + dst = EnforceType(B, dst, PtrToIdTy); + B.CreateCall2(GlobalAssignFn, src, dst); } void CGObjCGNU::EmitObjCIvarAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst, llvm::Value *ivarOffset) { - return; + CGBuilderTy B = CGF.Builder; + src = EnforceType(B, src, IdTy); + dst = EnforceType(B, dst, PtrToIdTy); + B.CreateCall3(IvarAssignFn, src, dst, ivarOffset); } void CGObjCGNU::EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst) { - return; + CGBuilderTy B = CGF.Builder; + src = EnforceType(B, src, IdTy); + dst = EnforceType(B, dst, PtrToIdTy); + B.CreateCall2(StrongCastAssignFn, src, dst); } void CGObjCGNU::EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF, llvm::Value *DestPtr, llvm::Value *SrcPtr, QualType Ty) { - return; + CGBuilderTy B = CGF.Builder; + DestPtr = EnforceType(B, DestPtr, IdTy); + SrcPtr = EnforceType(B, SrcPtr, PtrToIdTy); + + std::pair TypeInfo = CGM.getContext().getTypeInfo(Ty); + unsigned long size = TypeInfo.first/8; + // FIXME: size_t + llvm::Value *N = llvm::ConstantInt::get(LongTy, size); + + B.CreateCall3(MemMoveFn, DestPtr, SrcPtr, N); } llvm::GlobalVariable *CGObjCGNU::ObjCIvarOffsetVariable( diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 137ea51721c7..b16a510f98f6 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -21,6 +21,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtObjC.h" #include "clang/Basic/LangOptions.h" +#include "clang/CodeGen/CodeGenOptions.h" #include "llvm/Intrinsics.h" #include "llvm/LLVMContext.h" @@ -304,7 +305,8 @@ public: Params.push_back(Ctx.LongTy); Params.push_back(Ctx.BoolTy); const llvm::FunctionType *FTy = - Types.GetFunctionType(Types.getFunctionInfo(IdType, Params), false); + Types.GetFunctionType(Types.getFunctionInfo(IdType, Params, + CC_Default, false), false); return CGM.CreateRuntimeFunction(FTy, "objc_getProperty"); } @@ -322,7 +324,8 @@ public: Params.push_back(Ctx.BoolTy); Params.push_back(Ctx.BoolTy); const llvm::FunctionType *FTy = - Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params), false); + Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params, + CC_Default, false), false); return CGM.CreateRuntimeFunction(FTy, "objc_setProperty"); } @@ -333,7 +336,8 @@ public: llvm::SmallVector Params; Params.push_back(Ctx.getObjCIdType()); const llvm::FunctionType *FTy = - Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params), false); + Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params, + CC_Default, false), false); return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); } @@ -734,7 +738,8 @@ public: return CGM.CreateRuntimeFunction( llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Params, false), - "_Unwind_Resume_or_Rethrow"); + (CGM.getLangOptions().SjLjExceptions ? "_Unwind_SjLj_Resume" : + "_Unwind_Resume_or_Rethrow")); } llvm::Constant *getObjCEndCatchFn() { @@ -1553,7 +1558,8 @@ CGObjCCommonMac::EmitLegacyMessageSend(CodeGen::CodeGenFunction &CGF, ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); CodeGenTypes &Types = CGM.getTypes(); - const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs); + const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs, + CC_Default, false); const llvm::FunctionType *FTy = Types.GetFunctionType(FnInfo, Method ? Method->isVariadic() : false); @@ -3625,7 +3631,7 @@ ObjCCommonTypesHelper::ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm) Ctx.getObjCIdType(), 0, 0, false)); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), 0, Ctx.getObjCClassType(), 0, 0, false)); - RD->completeDefinition(Ctx); + RD->completeDefinition(); SuperCTy = Ctx.getTagDeclType(RD); SuperPtrCTy = Ctx.getPointerType(SuperCTy); @@ -4086,7 +4092,7 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul Ctx.VoidPtrTy, 0, 0, false)); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), 0, Ctx.getObjCSelType(), 0, 0, false)); - RD->completeDefinition(Ctx); + RD->completeDefinition(); MessageRefCTy = Ctx.getTagDeclType(RD); MessageRefCPtrTy = Ctx.getPointerType(MessageRefCTy); @@ -4224,6 +4230,9 @@ void CGObjCNonFragileABIMac::FinishNonFragileABIModule() { /// message dispatch call for all the rest. /// bool CGObjCNonFragileABIMac::LegacyDispatchedSelector(Selector Sel) { + if (CGM.getCodeGenOpts().ObjCLegacyDispatch) + return true; + if (NonLegacyDispatchMethods.empty()) { NonLegacyDispatchMethods.insert(GetNullarySelector("alloc")); NonLegacyDispatchMethods.insert(GetNullarySelector("class")); @@ -5085,7 +5094,8 @@ CodeGen::RValue CGObjCNonFragileABIMac::EmitMessageSend( // FIXME. This is too much work to get the ABI-specific result type needed to // find the message name. const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, - llvm::SmallVector()); + llvm::SmallVector(), + CC_Default, false); llvm::Constant *Fn = 0; std::string Name("\01l_"); if (CGM.ReturnTypeUsesSret(FnInfo)) { @@ -5159,7 +5169,8 @@ CodeGen::RValue CGObjCNonFragileABIMac::EmitMessageSend( ActualArgs.push_back(std::make_pair(RValue::get(Arg1), ObjCTypes.MessageRefCPtrTy)); ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); - const CGFunctionInfo &FnInfo1 = Types.getFunctionInfo(ResultType, ActualArgs); + const CGFunctionInfo &FnInfo1 = Types.getFunctionInfo(ResultType, ActualArgs, + CC_Default, false); llvm::Value *Callee = CGF.Builder.CreateStructGEP(Arg1, 0); Callee = CGF.Builder.CreateLoad(Callee); const llvm::FunctionType *FTy = Types.GetFunctionType(FnInfo1, true); diff --git a/lib/CodeGen/CGRTTI.cpp b/lib/CodeGen/CGRTTI.cpp index 29552ce4441c..5236d2063489 100644 --- a/lib/CodeGen/CGRTTI.cpp +++ b/lib/CodeGen/CGRTTI.cpp @@ -256,6 +256,9 @@ bool ShouldUseExternalRTTIDescriptor(QualType Ty) { if (const RecordType *RecordTy = dyn_cast(Ty)) { const CXXRecordDecl *RD = cast(RecordTy->getDecl()); + if (!RD->hasDefinition()) + return false; + if (!RD->isDynamicClass()) return false; @@ -469,7 +472,7 @@ void RTTIBuilder::BuildVtablePointer(const Type *Ty) { const CXXRecordDecl *RD = cast(cast(Ty)->getDecl()); - if (!RD->getNumBases()) { + if (!RD->hasDefinition() || !RD->getNumBases()) { // abi::__class_type_info. VtableName = "_ZTVN10__cxxabiv117__class_type_infoE"; } else if (CanUseSingleInheritance(RD)) { @@ -566,7 +569,7 @@ llvm::Constant *RTTIBuilder::BuildTypeInfo(QualType Ty) { case Type::Record: { const CXXRecordDecl *RD = cast(cast(Ty)->getDecl()); - if (!RD->getNumBases()) { + if (!RD->hasDefinition() || !RD->getNumBases()) { // We don't need to emit any fields. break; } diff --git a/lib/CodeGen/CGRecordLayoutBuilder.cpp b/lib/CodeGen/CGRecordLayoutBuilder.cpp index 9f90ec5ff6e0..baafd6836c63 100644 --- a/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -108,6 +108,9 @@ bool CGRecordLayoutBuilder::LayoutField(const FieldDecl *D, return true; } + // Check if we have a pointer to data member in this field. + CheckForPointerToDataMember(D->getType()); + assert(FieldOffset % 8 == 0 && "FieldOffset is not on a byte boundary!"); uint64_t FieldOffsetInBytes = FieldOffset / 8; @@ -162,6 +165,8 @@ void CGRecordLayoutBuilder::LayoutUnion(const RecordDecl *D) { uint64_t Size = 0; unsigned Align = 0; + bool HasOnlyZeroSizedBitFields = true; + unsigned FieldNo = 0; for (RecordDecl::field_iterator Field = D->field_begin(), FieldEnd = D->field_end(); Field != FieldEnd; ++Field, ++FieldNo) { @@ -181,6 +186,8 @@ void CGRecordLayoutBuilder::LayoutUnion(const RecordDecl *D) { } else Types.addFieldInfo(*Field, 0); + HasOnlyZeroSizedBitFields = false; + const llvm::Type *FieldTy = Types.ConvertTypeForMemRecursive(Field->getType()); unsigned FieldAlign = Types.getTargetData().getABITypeAlignment(FieldTy); @@ -207,7 +214,8 @@ void CGRecordLayoutBuilder::LayoutUnion(const RecordDecl *D) { } } if (!Align) { - assert((D->field_begin() == D->field_end()) && "LayoutUnion - Align 0"); + assert(HasOnlyZeroSizedBitFields && + "0-align record did not have all zero-sized bit-fields!"); Align = 1; } @@ -333,23 +341,34 @@ uint64_t CGRecordLayoutBuilder::getTypeSizeInBytes(const llvm::Type *Ty) const { return Types.getTargetData().getTypeAllocSize(Ty); } -void CGRecordLayoutBuilder::CheckForMemberPointer(const FieldDecl *FD) { +void CGRecordLayoutBuilder::CheckForPointerToDataMember(QualType T) { // This record already contains a member pointer. - if (ContainsMemberPointer) + if (ContainsPointerToDataMember) return; // Can only have member pointers if we're compiling C++. if (!Types.getContext().getLangOptions().CPlusPlus) return; - QualType Ty = FD->getType(); - - if (Ty->isMemberPointerType()) { - // We have a member pointer! - ContainsMemberPointer = true; - return; - } + T = Types.getContext().getBaseElementType(T); + if (const MemberPointerType *MPT = T->getAs()) { + if (!MPT->getPointeeType()->isFunctionType()) { + // We have a pointer to data member. + ContainsPointerToDataMember = true; + } + } else if (const RecordType *RT = T->getAs()) { + const CXXRecordDecl *RD = cast(RT->getDecl()); + + // FIXME: It would be better if there was a way to explicitly compute the + // record layout instead of converting to a type. + Types.ConvertTagDeclType(RD); + + const CGRecordLayout &Layout = Types.getCGRecordLayout(RD); + + if (Layout.containsPointerToDataMember()) + ContainsPointerToDataMember = true; + } } CGRecordLayout * @@ -381,5 +400,5 @@ CGRecordLayoutBuilder::ComputeLayout(CodeGenTypes &Types, Types.addBitFieldInfo(Info.FD, Info.FieldNo, Info.Start, Info.Size); } - return new CGRecordLayout(Ty, Builder.ContainsMemberPointer); + return new CGRecordLayout(Ty, Builder.ContainsPointerToDataMember); } diff --git a/lib/CodeGen/CGRecordLayoutBuilder.h b/lib/CodeGen/CGRecordLayoutBuilder.h index cf84053e1907..eb60ed7b5b1d 100644 --- a/lib/CodeGen/CGRecordLayoutBuilder.h +++ b/lib/CodeGen/CGRecordLayoutBuilder.h @@ -27,6 +27,7 @@ namespace clang { class CXXRecordDecl; class FieldDecl; class RecordDecl; + class QualType; namespace CodeGen { class CGRecordLayout; @@ -38,9 +39,10 @@ class CGRecordLayoutBuilder { /// Packed - Whether the resulting LLVM struct will be packed or not. bool Packed; - /// ContainsMemberPointer - Whether one of the fields is a member pointer - /// or is a struct that contains a member pointer. - bool ContainsMemberPointer; + /// ContainsPointerToDataMember - Whether one of the fields in this record + /// layout is a pointer to data member, or a struct that contains pointer to + /// data member. + bool ContainsPointerToDataMember; /// Alignment - Contains the alignment of the RecordDecl. unsigned Alignment; @@ -78,7 +80,7 @@ class CGRecordLayoutBuilder { llvm::SmallVector LLVMBitFields; CGRecordLayoutBuilder(CodeGenTypes &Types) - : Types(Types), Packed(false), ContainsMemberPointer(false) + : Types(Types), Packed(false), ContainsPointerToDataMember(false) , Alignment(0), AlignmentAsLLVMStruct(1) , BitsAvailableInLastField(0), NextFieldOffsetInBytes(0) { } @@ -123,8 +125,9 @@ class CGRecordLayoutBuilder { unsigned getTypeAlignment(const llvm::Type *Ty) const; uint64_t getTypeSizeInBytes(const llvm::Type *Ty) const; - /// CheckForMemberPointer - Check if the field contains a member pointer. - void CheckForMemberPointer(const FieldDecl *FD); + /// CheckForPointerToDataMember - Check if the given type contains a pointer + /// to data member. + void CheckForPointerToDataMember(QualType T); public: /// ComputeLayout - Return the right record layout for a given record decl. diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index bbd546261800..008a480b9c12 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -861,14 +861,13 @@ llvm::Value* CodeGenFunction::EmitAsmInput(const AsmStmt &S, std::string &ConstraintStr) { llvm::Value *Arg; if (Info.allowsRegister() || !Info.allowsMemory()) { - const llvm::Type *Ty = ConvertType(InputExpr->getType()); - - if (Ty->isSingleValueType()) { + if (!CodeGenFunction::hasAggregateLLVMType(InputExpr->getType())) { Arg = EmitScalarExpr(InputExpr); } else { InputExpr = InputExpr->IgnoreParenNoopCasts(getContext()); LValue Dest = EmitLValue(InputExpr); + const llvm::Type *Ty = ConvertType(InputExpr->getType()); uint64_t Size = CGM.getTargetData().getTypeSizeInBits(Ty); if (Size <= 64 && llvm::isPowerOf2_64(Size)) { Ty = llvm::IntegerType::get(VMContext, Size); @@ -916,18 +915,18 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { for (unsigned i = 0, e = S.getNumOutputs(); i != e; i++) { TargetInfo::ConstraintInfo Info(S.getOutputConstraint(i), S.getOutputName(i)); - bool result = Target.validateOutputConstraint(Info); - assert(result && "Failed to parse output constraint"); result=result; + assert(Target.validateOutputConstraint(Info) && + "Failed to parse output constraint"); OutputConstraintInfos.push_back(Info); } for (unsigned i = 0, e = S.getNumInputs(); i != e; i++) { TargetInfo::ConstraintInfo Info(S.getInputConstraint(i), S.getInputName(i)); - bool result = Target.validateInputConstraint(OutputConstraintInfos.data(), - S.getNumOutputs(), - Info); result=result; - assert(result && "Failed to parse input constraint"); + assert(Target.validateInputConstraint(OutputConstraintInfos.data(), + S.getNumOutputs(), + Info) && + "Failed to parse input constraint"); InputConstraintInfos.push_back(Info); } @@ -1066,10 +1065,9 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { // Clobbers for (unsigned i = 0, e = S.getNumClobbers(); i != e; i++) { - std::string Clobber(S.getClobber(i)->getStrData(), - S.getClobber(i)->getByteLength()); + llvm::StringRef Clobber = S.getClobber(i)->getString(); - Clobber = Target.getNormalizedGCCRegisterName(Clobber.c_str()); + Clobber = Target.getNormalizedGCCRegisterName(Clobber); if (i != 0 || NumConstraints != 0) Constraints += ','; diff --git a/lib/CodeGen/CGVtable.cpp b/lib/CodeGen/CGVtable.cpp index e5abfc6f8d57..970bbd777f14 100644 --- a/lib/CodeGen/CGVtable.cpp +++ b/lib/CodeGen/CGVtable.cpp @@ -16,13 +16,1244 @@ #include "clang/AST/CXXInheritance.h" #include "clang/AST/RecordLayout.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Format.h" #include using namespace clang; using namespace CodeGen; namespace { + +/// FinalOverriders - Contains the final overrider member functions for all +/// member functions in the base subobjects of a class. +class FinalOverriders { +public: + /// BaseOffset - Represents an offset from a derived class to a direct or + /// indirect base class. + struct BaseOffset { + /// DerivedClass - The derived class. + const CXXRecordDecl *DerivedClass; + + /// VirtualBase - If the path from the derived class to the base class + /// involves a virtual base class, this holds its declaration. + const CXXRecordDecl *VirtualBase; + + /// NonVirtualOffset - The offset from the derived class to the base class. + /// Or the offset from the virtual base class to the base class, if the path + /// from the derived class to the base class involves a virtual base class. + int64_t NonVirtualOffset; + + BaseOffset() : DerivedClass(0), VirtualBase(0), NonVirtualOffset(0) { } + BaseOffset(const CXXRecordDecl *DerivedClass, + const CXXRecordDecl *VirtualBase, int64_t NonVirtualOffset) + : DerivedClass(DerivedClass), VirtualBase(VirtualBase), + NonVirtualOffset(NonVirtualOffset) { } + + bool isEmpty() const { return !NonVirtualOffset && !VirtualBase; } + }; + + /// OverriderInfo - Information about a final overrider. + struct OverriderInfo { + /// Method - The method decl of the overrider. + const CXXMethodDecl *Method; + + OverriderInfo() : Method(0) { } + }; + +private: + /// MostDerivedClass - The most derived class for which the final overriders + /// are stored. + const CXXRecordDecl *MostDerivedClass; + + ASTContext &Context; + + /// MostDerivedClassLayout - the AST record layout of the most derived class. + const ASTRecordLayout &MostDerivedClassLayout; + + /// BaseSubobjectMethodPairTy - Uniquely identifies a member function + /// in a base subobject. + typedef std::pair + BaseSubobjectMethodPairTy; + + typedef llvm::DenseMap OverridersMapTy; + + /// OverridersMap - The final overriders for all virtual member functions of + /// all the base subobjects of the most derived class. + OverridersMapTy OverridersMap; + + /// VisitedVirtualBases - A set of all the visited virtual bases, used to + /// avoid visiting virtual bases more than once. + llvm::SmallPtrSet VisitedVirtualBases; + + typedef llvm::DenseMap + AdjustmentOffsetsMapTy; + + /// ReturnAdjustments - Holds return adjustments for all the overriders that + /// need to perform return value adjustments. + AdjustmentOffsetsMapTy ReturnAdjustments; + + /// ThisAdjustments - Holds 'this' adjustments for all the overriders that + /// need them. + AdjustmentOffsetsMapTy ThisAdjustments; + + typedef llvm::SmallVector OffsetVectorTy; + + /// SubobjectOffsetsMapTy - This map is used for keeping track of all the + /// base subobject offsets that a single class declaration might refer to. + /// + /// For example, in: + /// + /// struct A { virtual void f(); }; + /// struct B1 : A { }; + /// struct B2 : A { }; + /// struct C : B1, B2 { virtual void f(); }; + /// + /// when we determine that C::f() overrides A::f(), we need to update the + /// overriders map for both A-in-B1 and A-in-B2 and the subobject offsets map + /// will have the subobject offsets for both A copies. + typedef llvm::DenseMap + SubobjectOffsetsMapTy; + + /// ComputeFinalOverriders - Compute the final overriders for a given base + /// subobject (and all its direct and indirect bases). + void ComputeFinalOverriders(BaseSubobject Base, + SubobjectOffsetsMapTy &Offsets); + + /// AddOverriders - Add the final overriders for this base subobject to the + /// map of final overriders. + void AddOverriders(BaseSubobject Base, SubobjectOffsetsMapTy &Offsets); + + /// PropagateOverrider - Propagate the NewMD overrider to all the functions + /// that OldMD overrides. For example, if we have: + /// + /// struct A { virtual void f(); }; + /// struct B : A { virtual void f(); }; + /// struct C : B { virtual void f(); }; + /// + /// and we want to override B::f with C::f, we also need to override A::f with + /// C::f. + void PropagateOverrider(const CXXMethodDecl *OldMD, + BaseSubobject NewBase, + const CXXMethodDecl *NewMD, + SubobjectOffsetsMapTy &Offsets); + + /// ComputeThisAdjustmentBaseOffset - Compute the base offset for adjusting + /// the 'this' pointer from the base subobject to the derived subobject. + BaseOffset ComputeThisAdjustmentBaseOffset(BaseSubobject Base, + BaseSubobject Derived); + + static void MergeSubobjectOffsets(const SubobjectOffsetsMapTy &NewOffsets, + SubobjectOffsetsMapTy &Offsets); + +public: + explicit FinalOverriders(const CXXRecordDecl *MostDerivedClass); + + /// getOverrider - Get the final overrider for the given method declaration in + /// the given base subobject. + OverriderInfo getOverrider(BaseSubobject Base, + const CXXMethodDecl *MD) const { + assert(OverridersMap.count(std::make_pair(Base, MD)) && + "Did not find overrider!"); + + return OverridersMap.lookup(std::make_pair(Base, MD)); + } + + /// getReturnAdjustmentOffset - Get the return adjustment offset for the + /// method decl in the given base subobject. Returns an empty base offset if + /// no adjustment is needed. + BaseOffset getReturnAdjustmentOffset(BaseSubobject Base, + const CXXMethodDecl *MD) const { + return ReturnAdjustments.lookup(std::make_pair(Base, MD)); + } + + /// getThisAdjustmentOffset - Get the 'this' pointer adjustment offset for the + /// method decl in the given base subobject. Returns an empty base offset if + /// no adjustment is needed. + BaseOffset getThisAdjustmentOffset(BaseSubobject Base, + const CXXMethodDecl *MD) const { + return ThisAdjustments.lookup(std::make_pair(Base, MD)); + } + + /// dump - dump the final overriders. + void dump() { + assert(VisitedVirtualBases.empty() && + "Visited virtual bases aren't empty!"); + dump(llvm::errs(), BaseSubobject(MostDerivedClass, 0)); + VisitedVirtualBases.clear(); + } + + /// dump - dump the final overriders for a base subobject, and all its direct + /// and indirect base subobjects. + void dump(llvm::raw_ostream &Out, BaseSubobject Base); +}; + +FinalOverriders::FinalOverriders(const CXXRecordDecl *MostDerivedClass) + : MostDerivedClass(MostDerivedClass), + Context(MostDerivedClass->getASTContext()), + MostDerivedClassLayout(Context.getASTRecordLayout(MostDerivedClass)) { + + // Compute the final overriders. + SubobjectOffsetsMapTy Offsets; + ComputeFinalOverriders(BaseSubobject(MostDerivedClass, 0), Offsets); + + // And dump them (for now). + dump(); + + // Also dump the base offsets (for now). + for (SubobjectOffsetsMapTy::const_iterator I = Offsets.begin(), + E = Offsets.end(); I != E; ++I) { + const OffsetVectorTy& OffsetVector = I->second; + + llvm::errs() << "Base offsets for "; + llvm::errs() << I->first->getQualifiedNameAsString() << '\n'; + + for (unsigned I = 0, E = OffsetVector.size(); I != E; ++I) + llvm::errs() << " " << I << " - " << OffsetVector[I] << '\n'; + } +} + +void FinalOverriders::AddOverriders(BaseSubobject Base, + SubobjectOffsetsMapTy &Offsets) { + const CXXRecordDecl *RD = Base.getBase(); + + for (CXXRecordDecl::method_iterator I = RD->method_begin(), + E = RD->method_end(); I != E; ++I) { + const CXXMethodDecl *MD = *I; + + if (!MD->isVirtual()) + continue; + + // First, propagate the overrider. + PropagateOverrider(MD, Base, MD, Offsets); + + // Add the overrider as the final overrider of itself. + OverriderInfo& Overrider = OverridersMap[std::make_pair(Base, MD)]; + assert(!Overrider.Method && "Overrider should not exist yet!"); + + Overrider.Method = MD; + } +} + +static FinalOverriders::BaseOffset +ComputeBaseOffset(ASTContext &Context, const CXXRecordDecl *DerivedRD, + const CXXBasePath &Path) { + int64_t NonVirtualOffset = 0; + + unsigned NonVirtualStart = 0; + const CXXRecordDecl *VirtualBase = 0; + + // First, look for the virtual base class. + for (unsigned I = 0, E = Path.size(); I != E; ++I) { + const CXXBasePathElement &Element = Path[I]; + + if (Element.Base->isVirtual()) { + // FIXME: Can we break when we find the first virtual base? + // (If we can't, can't we just iterate over the path in reverse order?) + NonVirtualStart = I + 1; + QualType VBaseType = Element.Base->getType(); + VirtualBase = + cast(VBaseType->getAs()->getDecl()); + } + } + + // Now compute the non-virtual offset. + for (unsigned I = NonVirtualStart, E = Path.size(); I != E; ++I) { + const CXXBasePathElement &Element = Path[I]; + + // Check the base class offset. + const ASTRecordLayout &Layout = Context.getASTRecordLayout(Element.Class); + + const RecordType *BaseType = Element.Base->getType()->getAs(); + const CXXRecordDecl *Base = cast(BaseType->getDecl()); + + NonVirtualOffset += Layout.getBaseClassOffset(Base); + } + + // FIXME: This should probably use CharUnits or something. Maybe we should + // even change the base offsets in ASTRecordLayout to be specified in + // CharUnits. + return FinalOverriders::BaseOffset(DerivedRD, VirtualBase, + NonVirtualOffset / 8); + +} + +static FinalOverriders::BaseOffset +ComputeBaseOffset(ASTContext &Context, const CXXRecordDecl *BaseRD, + const CXXRecordDecl *DerivedRD) { + CXXBasePaths Paths(/*FindAmbiguities=*/false, + /*RecordPaths=*/true, /*DetectVirtual=*/false); + + if (!const_cast(DerivedRD)-> + isDerivedFrom(const_cast(BaseRD), Paths)) { + assert(false && "Class must be derived from the passed in base class!"); + return FinalOverriders::BaseOffset(); + } + + return ComputeBaseOffset(Context, DerivedRD, Paths.front()); +} + +static FinalOverriders::BaseOffset +ComputeReturnAdjustmentBaseOffset(ASTContext &Context, + const CXXMethodDecl *DerivedMD, + const CXXMethodDecl *BaseMD) { + const FunctionType *BaseFT = BaseMD->getType()->getAs(); + const FunctionType *DerivedFT = DerivedMD->getType()->getAs(); + + // Canonicalize the return types. + CanQualType CanDerivedReturnType = + Context.getCanonicalType(DerivedFT->getResultType()); + CanQualType CanBaseReturnType = + Context.getCanonicalType(BaseFT->getResultType()); + + assert(CanDerivedReturnType->getTypeClass() == + CanBaseReturnType->getTypeClass() && + "Types must have same type class!"); + + if (CanDerivedReturnType == CanBaseReturnType) { + // No adjustment needed. + return FinalOverriders::BaseOffset(); + } + + if (isa(CanDerivedReturnType)) { + CanDerivedReturnType = + CanDerivedReturnType->getAs()->getPointeeType(); + CanBaseReturnType = + CanBaseReturnType->getAs()->getPointeeType(); + } else if (isa(CanDerivedReturnType)) { + CanDerivedReturnType = + CanDerivedReturnType->getAs()->getPointeeType(); + CanBaseReturnType = + CanBaseReturnType->getAs()->getPointeeType(); + } else { + assert(false && "Unexpected return type!"); + } + + // We need to compare unqualified types here; consider + // const T *Base::foo(); + // T *Derived::foo(); + if (CanDerivedReturnType.getUnqualifiedType() == + CanBaseReturnType.getUnqualifiedType()) { + // No adjustment needed. + return FinalOverriders::BaseOffset(); + } + + const CXXRecordDecl *DerivedRD = + cast(cast(CanDerivedReturnType)->getDecl()); + + const CXXRecordDecl *BaseRD = + cast(cast(CanBaseReturnType)->getDecl()); + + return ComputeBaseOffset(Context, BaseRD, DerivedRD); +} + +FinalOverriders::BaseOffset +FinalOverriders::ComputeThisAdjustmentBaseOffset(BaseSubobject Base, + BaseSubobject Derived) { + const CXXRecordDecl *BaseRD = Base.getBase(); + const CXXRecordDecl *DerivedRD = Derived.getBase(); + + CXXBasePaths Paths(/*FindAmbiguities=*/true, + /*RecordPaths=*/true, /*DetectVirtual=*/true); + + if (!const_cast(DerivedRD)-> + isDerivedFrom(const_cast(BaseRD), Paths)) { + assert(false && "Class must be derived from the passed in base class!"); + return FinalOverriders::BaseOffset(); + } + + assert(!Paths.getDetectedVirtual() && "FIXME: Handle virtual bases!"); + + BaseOffset Offset; + + // FIXME: This is not going to be enough with virtual bases. + // FIXME: We should not use / 8 here. + int64_t DerivedToBaseOffset = + (Base.getBaseOffset() - Derived.getBaseOffset()) / 8; + + Offset.NonVirtualOffset = -DerivedToBaseOffset; + + return Offset; +} + +void FinalOverriders::PropagateOverrider(const CXXMethodDecl *OldMD, + BaseSubobject NewBase, + const CXXMethodDecl *NewMD, + SubobjectOffsetsMapTy &Offsets) { + for (CXXMethodDecl::method_iterator I = OldMD->begin_overridden_methods(), + E = OldMD->end_overridden_methods(); I != E; ++I) { + const CXXMethodDecl *OverriddenMD = *I; + const CXXRecordDecl *OverriddenRD = OverriddenMD->getParent(); + + // We want to override OverriddenMD in all subobjects, for example: + // + /// struct A { virtual void f(); }; + /// struct B1 : A { }; + /// struct B2 : A { }; + /// struct C : B1, B2 { virtual void f(); }; + /// + /// When overriding A::f with C::f we need to do so in both A subobjects. + const OffsetVectorTy &OffsetVector = Offsets[OverriddenRD]; + + // Go through all the subobjects. + for (unsigned I = 0, E = OffsetVector.size(); I != E; ++I) { + uint64_t Offset = OffsetVector[I]; + + BaseSubobject OverriddenSubobject = BaseSubobject(OverriddenRD, Offset); + BaseSubobjectMethodPairTy SubobjectAndMethod = + std::make_pair(OverriddenSubobject, OverriddenMD); + + OverriderInfo &Overrider = OverridersMap[SubobjectAndMethod]; + + assert(Overrider.Method && "Did not find existing overrider!"); + + // Check if we need return adjustments or base adjustments. + // (We don't want to do this for pure virtual member functions). + if (!NewMD->isPure()) { + // Get the return adjustment base offset. + BaseOffset ReturnBaseOffset = + ComputeReturnAdjustmentBaseOffset(Context, NewMD, OverriddenMD); + + if (!ReturnBaseOffset.isEmpty()) { + // Store the return adjustment base offset. + ReturnAdjustments[SubobjectAndMethod] = ReturnBaseOffset; + } + + // Check if we need a 'this' adjustment base offset as well. + if (Offset != NewBase.getBaseOffset()) { + BaseOffset ThisBaseOffset = + ComputeThisAdjustmentBaseOffset(OverriddenSubobject, + NewBase); + assert(!ThisBaseOffset.isEmpty() && + "Should not get an empty 'this' adjustment!"); + + ThisAdjustments[SubobjectAndMethod] = ThisBaseOffset; + } + } + + // Set the new overrider. + Overrider.Method = NewMD; + + // And propagate it further. + PropagateOverrider(OverriddenMD, NewBase, NewMD, Offsets); + } + } +} + +void +FinalOverriders::MergeSubobjectOffsets(const SubobjectOffsetsMapTy &NewOffsets, + SubobjectOffsetsMapTy &Offsets) { + // Iterate over the new offsets. + for (SubobjectOffsetsMapTy::const_iterator I = NewOffsets.begin(), + E = NewOffsets.end(); I != E; ++I) { + const CXXRecordDecl *NewRD = I->first; + const OffsetVectorTy& NewOffsetVector = I->second; + + OffsetVectorTy &OffsetVector = Offsets[NewRD]; + if (OffsetVector.empty()) { + // There were no previous offsets in this vector, just insert all entries + // from the new offset vector. + OffsetVector.append(NewOffsetVector.begin(), NewOffsetVector.end()); + continue; + } + + // We need to merge the new offsets vector into the old, but we don't want + // to have duplicate entries. Do this by inserting the old offsets in a set + // so they'll be unique. After this, we iterate over the new offset vector + // and only append elements that aren't in the set. + + // First, add the existing offsets to the set. + llvm::SmallSet OffsetSet; + for (unsigned I = 0, E = OffsetVector.size(); I != E; ++I) { + bool Inserted = OffsetSet.insert(OffsetVector[I]); + if (!Inserted) + assert(false && "Set of offsets should be unique!"); + } + + // Next, only add the new offsets if they are not already in the set. + for (unsigned I = 0, E = NewOffsetVector.size(); I != E; ++I) { + uint64_t Offset = NewOffsetVector[I]; + + if (OffsetSet.count(Offset)) { + // Ignore the offset. + continue; + } + + // Otherwise, add it to the offsets vector. + OffsetVector.push_back(Offset); + } + } +} + +void FinalOverriders::ComputeFinalOverriders(BaseSubobject Base, + SubobjectOffsetsMapTy &Offsets) { + const CXXRecordDecl *RD = Base.getBase(); + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); + + SubobjectOffsetsMapTy NewOffsets; + + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I) { + const CXXRecordDecl *BaseDecl = + cast(I->getType()->getAs()->getDecl()); + + // Ignore bases that don't have any virtual member functions. + if (!BaseDecl->isPolymorphic()) + continue; + + uint64_t BaseOffset; + if (I->isVirtual()) { + BaseOffset = MostDerivedClassLayout.getVBaseClassOffset(BaseDecl); + } else { + BaseOffset = Layout.getBaseClassOffset(BaseDecl) + Base.getBaseOffset(); + } + + // Compute the final overriders for this base. + ComputeFinalOverriders(BaseSubobject(BaseDecl, BaseOffset), NewOffsets); + } + + /// Now add the overriders for this particular subobject. + AddOverriders(Base, NewOffsets); + + // And merge the newly discovered subobject offsets. + MergeSubobjectOffsets(NewOffsets, Offsets); + + /// Finally, add the offset for our own subobject. + Offsets[RD].push_back(Base.getBaseOffset()); +} + +void FinalOverriders::dump(llvm::raw_ostream &Out, BaseSubobject Base) { + const CXXRecordDecl *RD = Base.getBase(); + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); + + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I) { + const CXXRecordDecl *BaseDecl = + cast(I->getType()->getAs()->getDecl()); + + // Ignore bases that don't have any virtual member functions. + if (!BaseDecl->isPolymorphic()) + continue; + + uint64_t BaseOffset; + if (I->isVirtual()) { + if (!VisitedVirtualBases.insert(BaseDecl)) { + // We've visited this base before. + continue; + } + + BaseOffset = MostDerivedClassLayout.getVBaseClassOffset(BaseDecl); + } else { + BaseOffset = Layout.getBaseClassOffset(BaseDecl) + + Base.getBaseOffset(); + } + + dump(Out, BaseSubobject(BaseDecl, BaseOffset)); + } + + Out << "Final overriders for (" << RD->getQualifiedNameAsString() << ", "; + Out << Base.getBaseOffset() << ")\n"; + + // Now dump the overriders for this base subobject. + for (CXXRecordDecl::method_iterator I = RD->method_begin(), + E = RD->method_end(); I != E; ++I) { + const CXXMethodDecl *MD = *I; + + if (!MD->isVirtual()) + continue; + + OverriderInfo Overrider = getOverrider(Base, MD); + + Out << " " << MD->getQualifiedNameAsString() << " - "; + Out << Overrider.Method->getQualifiedNameAsString(); + + AdjustmentOffsetsMapTy::const_iterator AI = + ReturnAdjustments.find(std::make_pair(Base, MD)); + if (AI != ReturnAdjustments.end()) { + const BaseOffset &Offset = AI->second; + + Out << " [ret-adj: "; + if (Offset.VirtualBase) + Out << Offset.VirtualBase->getQualifiedNameAsString() << " vbase, "; + + Out << Offset.NonVirtualOffset << " nv]"; + } + + AI = ThisAdjustments.find(std::make_pair(Base, MD)); + if (AI != ThisAdjustments.end()) { + const BaseOffset &Offset = AI->second; + + Out << " [this-adj: "; + if (Offset.VirtualBase) + Out << Offset.VirtualBase->getQualifiedNameAsString() << " vbase, "; + + Out << Offset.NonVirtualOffset << " nv]"; + } + + Out << "\n"; + } +} + +/// VtableComponent - Represents a single component in a vtable. +class VtableComponent { +public: + enum Kind { + CK_VCallOffset, + CK_VBaseOffset, + CK_OffsetToTop, + CK_RTTI, + CK_FunctionPointer, + + /// CK_CompleteDtorPointer - A pointer to the complete destructor. + CK_CompleteDtorPointer, + + /// CK_DeletingDtorPointer - A pointer to the deleting destructor. + CK_DeletingDtorPointer + }; + + static VtableComponent MakeVBaseOffset(int64_t Offset) { + return VtableComponent(CK_VBaseOffset, Offset); + } + + static VtableComponent MakeOffsetToTop(int64_t Offset) { + return VtableComponent(CK_OffsetToTop, Offset); + } + + static VtableComponent MakeRTTI(const CXXRecordDecl *RD) { + return VtableComponent(CK_RTTI, reinterpret_cast(RD)); + } + + static VtableComponent MakeFunction(const CXXMethodDecl *MD) { + assert(!isa(MD) && + "Don't use MakeFunction with destructors!"); + + return VtableComponent(CK_FunctionPointer, + reinterpret_cast(MD)); + } + + static VtableComponent MakeCompleteDtor(const CXXDestructorDecl *DD) { + return VtableComponent(CK_CompleteDtorPointer, + reinterpret_cast(DD)); + } + + static VtableComponent MakeDeletingDtor(const CXXDestructorDecl *DD) { + return VtableComponent(CK_DeletingDtorPointer, + reinterpret_cast(DD)); + } + + /// getKind - Get the kind of this vtable component. + Kind getKind() const { + return (Kind)(Value & 0x7); + } + + int64_t getVBaseOffset() const { + assert(getKind() == CK_VBaseOffset && "Invalid component kind!"); + + return getOffset(); + } + + int64_t getOffsetToTop() const { + assert(getKind() == CK_OffsetToTop && "Invalid component kind!"); + + return getOffset(); + } + + const CXXRecordDecl *getRTTIDecl() const { + assert(getKind() == CK_RTTI && "Invalid component kind!"); + + return reinterpret_cast(getPointer()); + } + + const CXXMethodDecl *getFunctionDecl() const { + assert(getKind() == CK_FunctionPointer); + + return reinterpret_cast(getPointer()); + } + + const CXXDestructorDecl *getDestructorDecl() const { + assert((getKind() == CK_CompleteDtorPointer || + getKind() == CK_DeletingDtorPointer) && "Invalid component kind!"); + + return reinterpret_cast(getPointer()); + } + +private: + VtableComponent(Kind ComponentKind, int64_t Offset) { + assert((ComponentKind == CK_VCallOffset || + ComponentKind == CK_VBaseOffset || + ComponentKind == CK_OffsetToTop) && "Invalid component kind!"); + assert(Offset <= ((1LL << 56) - 1) && "Offset is too big!"); + + Value = ((Offset << 3) | ComponentKind); + } + + VtableComponent(Kind ComponentKind, uintptr_t Ptr) { + assert((ComponentKind == CK_RTTI || + ComponentKind == CK_FunctionPointer || + ComponentKind == CK_CompleteDtorPointer || + ComponentKind == CK_DeletingDtorPointer) && + "Invalid component kind!"); + + assert((Ptr & 7) == 0 && "Pointer not sufficiently aligned!"); + + Value = Ptr | ComponentKind; + } + + int64_t getOffset() const { + assert((getKind() == CK_VCallOffset || getKind() == CK_VBaseOffset || + getKind() == CK_OffsetToTop) && "Invalid component kind!"); + + return Value >> 3; + } + + uintptr_t getPointer() const { + assert((getKind() == CK_RTTI || + getKind() == CK_FunctionPointer || + getKind() == CK_CompleteDtorPointer || + getKind() == CK_DeletingDtorPointer) && + "Invalid component kind!"); + + return static_cast(Value & ~7ULL); + } + + /// The kind is stored in the lower 3 bits of the value. For offsets, we + /// make use of the facts that classes can't be larger than 2^55 bytes, + /// so we store the offset in the lower part of the 61 bytes that remain. + /// (The reason that we're not simply using a PointerIntPair here is that we + /// need the offsets to be 64-bit, even when on a 32-bit machine). + int64_t Value; +}; + +/// VtableBuilder - Class for building vtable layout information. class VtableBuilder { +public: + /// PrimaryBasesSetTy - A set of direct and indirect primary bases. + typedef llvm::SmallPtrSet PrimaryBasesSetTy; + +private: + /// VtableInfo - Global vtable information. + CGVtableInfo &VtableInfo; + + /// MostDerivedClass - The most derived class for which we're building this + /// vtable. + const CXXRecordDecl *MostDerivedClass; + + /// Context - The ASTContext which we will use for layout information. + ASTContext &Context; + + /// FinalOverriders - The final overriders of the most derived class. + FinalOverriders Overriders; + + /// VCallAndVBaseOffsets - The vcall and vbase offset, of the vtable we're + // building (in reverse order). + llvm::SmallVector VCallAndVBaseOffsets; + + /// Components - The components of the vtable being built. + llvm::SmallVector Components; + + /// AddressPoints - Address points for the vtable being built. + CGVtableInfo::AddressPointsMapTy AddressPoints; + + /// ReturnAdjustment - A return adjustment. + struct ReturnAdjustment { + /// NonVirtual - The non-virtual adjustment from the derived object to its + /// nearest virtual base. + int64_t NonVirtual; + + /// VBaseOffsetOffset - The offset, in bytes, relative to the address point + /// of the virtual base class offset. + int64_t VBaseOffsetOffset; + + ReturnAdjustment() : NonVirtual(0), VBaseOffsetOffset(0) { } + + bool isEmpty() const { return !NonVirtual && !VBaseOffsetOffset; } + }; + + /// ReturnAdjustments - The return adjustments needed in this vtable. + llvm::SmallVector, 16> + ReturnAdjustments; + + /// ThisAdjustment - A 'this' pointer adjustment thunk. + struct ThisAdjustment { + /// NonVirtual - The non-virtual adjustment from the derived object to its + /// nearest virtual base. + int64_t NonVirtual; + + /// FIXME: Add VCallOffsetOffset here. + + ThisAdjustment() : NonVirtual(0) { } + + bool isEmpty() const { return !NonVirtual; } + }; + + /// ThisAdjustments - The 'this' pointer adjustments needed in this vtable. + llvm::SmallVector, 16> + ThisAdjustments; + + typedef llvm::SmallPtrSet VisitedVirtualBasesSetTy; + + /// AddVCallAndVBaseOffsets - Add vcall offsets and vbase offsets for the + /// given class. + void AddVCallAndVBaseOffsets(const CXXRecordDecl *RD, int64_t OffsetToTop, + VisitedVirtualBasesSetTy &VBases); + + /// AddVBaseOffsets - Add vbase offsets for the given class. + void AddVBaseOffsets(const CXXRecordDecl *RD, int64_t OffsetToTop, + VisitedVirtualBasesSetTy &VBases); + + /// ComputeReturnAdjustment - Compute the return adjustment given a return + /// adjustment base offset. + ReturnAdjustment ComputeReturnAdjustment(FinalOverriders::BaseOffset Offset); + + /// ComputeThisAdjustment - Compute the 'this' pointer adjustment given a + /// 'this' pointer adjustment base offset. + ThisAdjustment ComputeThisAdjustment(FinalOverriders::BaseOffset Offset); + + /// AddMethod - Add a single virtual member function to the vtable + /// components vector. + void AddMethod(const CXXMethodDecl *MD, ReturnAdjustment ReturnAdjustment, + ThisAdjustment ThisAdjustment); + + /// AddMethods - Add the methods of this base subobject and all its + /// primary bases to the vtable components vector. + void AddMethods(BaseSubobject Base, PrimaryBasesSetTy &PrimaryBases); + + /// LayoutVtable - Layout a vtable and all its secondary vtables. + void LayoutVtable(BaseSubobject Base); + +public: + VtableBuilder(CGVtableInfo &VtableInfo, const CXXRecordDecl *MostDerivedClass) + : VtableInfo(VtableInfo), MostDerivedClass(MostDerivedClass), + Context(MostDerivedClass->getASTContext()), Overriders(MostDerivedClass) { + + LayoutVtable(BaseSubobject(MostDerivedClass, 0)); + } + + /// dumpLayout - Dump the vtable layout. + void dumpLayout(llvm::raw_ostream&); +}; + +/// OverridesMethodInPrimaryBase - Checks whether whether this virtual member +/// function overrides a member function in a direct or indirect primary base. +/// Returns the overridden member function, or null if none was found. +static const CXXMethodDecl * +OverridesMethodInPrimaryBase(const CXXMethodDecl *MD, + VtableBuilder::PrimaryBasesSetTy &PrimaryBases) { + for (CXXMethodDecl::method_iterator I = MD->begin_overridden_methods(), + E = MD->end_overridden_methods(); I != E; ++I) { + const CXXMethodDecl *OverriddenMD = *I; + const CXXRecordDecl *OverriddenRD = OverriddenMD->getParent(); + assert(OverriddenMD->isCanonicalDecl() && + "Should have the canonical decl of the overridden RD!"); + + if (PrimaryBases.count(OverriddenRD)) + return OverriddenMD; + } + + return 0; +} + +VtableBuilder::ReturnAdjustment +VtableBuilder::ComputeReturnAdjustment(FinalOverriders::BaseOffset Offset) { + ReturnAdjustment Adjustment; + + if (!Offset.isEmpty()) { + if (Offset.VirtualBase) { + // Get the virtual base offset offset. + Adjustment.VBaseOffsetOffset = + VtableInfo.getVirtualBaseOffsetIndex(Offset.DerivedClass, + Offset.VirtualBase); + // FIXME: Once the assert in getVirtualBaseOffsetIndex is back again, + // we can get rid of this assert. + assert(Adjustment.VBaseOffsetOffset != 0 && + "Invalid base offset offset!"); + } + + Adjustment.NonVirtual = Offset.NonVirtualOffset; + } + + return Adjustment; +} + +VtableBuilder::ThisAdjustment +VtableBuilder::ComputeThisAdjustment(FinalOverriders::BaseOffset Offset) { + ThisAdjustment Adjustment; + + if (!Offset.isEmpty()) { + assert(!Offset.VirtualBase && "FIXME: Handle virtual bases!"); + Adjustment.NonVirtual = Offset.NonVirtualOffset; + } + + return Adjustment; +} + +void +VtableBuilder::AddVCallAndVBaseOffsets(const CXXRecordDecl *RD, + int64_t OffsetToTop, + VisitedVirtualBasesSetTy &VBases) { + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); + + // Itanium C++ ABI 2.5.2: + // ..in classes sharing a virtual table with a primary base class, the vcall + // and vbase offsets added by the derived class all come before the vcall + // and vbase offsets required by the base class, so that the latter may be + // laid out as required by the base class without regard to additions from + // the derived class(es). + + // (Since we're emitting the vcall and vbase offsets in reverse order, we'll + // emit them for the primary base first). + if (const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase()) + AddVCallAndVBaseOffsets(PrimaryBase, OffsetToTop, VBases); + + AddVBaseOffsets(RD, OffsetToTop, VBases); +} + +void VtableBuilder::AddVBaseOffsets(const CXXRecordDecl *RD, + int64_t OffsetToTop, + VisitedVirtualBasesSetTy &VBases) { + const ASTRecordLayout &MostDerivedClassLayout = + Context.getASTRecordLayout(MostDerivedClass); + + // Add vbase offsets. + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I) { + const CXXRecordDecl *BaseDecl = + cast(I->getType()->getAs()->getDecl()); + + // Check if this is a virtual base that we haven't visited before. + if (I->isVirtual() && VBases.insert(BaseDecl)) { + // FIXME: We shouldn't use / 8 here. + uint64_t Offset = + OffsetToTop + MostDerivedClassLayout.getVBaseClassOffset(BaseDecl) / 8; + + VCallAndVBaseOffsets.push_back(VtableComponent::MakeVBaseOffset(Offset)); + } + + // Check the base class looking for more vbase offsets. + AddVBaseOffsets(BaseDecl, OffsetToTop, VBases); + } +} + +void +VtableBuilder::AddMethod(const CXXMethodDecl *MD, + ReturnAdjustment ReturnAdjustment, + ThisAdjustment ThisAdjustment) { + if (const CXXDestructorDecl *DD = dyn_cast(MD)) { + assert(ReturnAdjustment.isEmpty() && + "Destructor can't have return adjustment!"); + // Add the 'this' pointer adjustments if necessary. + if (!ThisAdjustment.isEmpty()) { + ThisAdjustments.push_back(std::make_pair(Components.size(), + ThisAdjustment)); + ThisAdjustments.push_back(std::make_pair(Components.size() + 1, + ThisAdjustment)); + } + + // Add both the complete destructor and the deleting destructor. + Components.push_back(VtableComponent::MakeCompleteDtor(DD)); + Components.push_back(VtableComponent::MakeDeletingDtor(DD)); + } else { + // Add the return adjustment if necessary. + if (!ReturnAdjustment.isEmpty()) + ReturnAdjustments.push_back(std::make_pair(Components.size(), + ReturnAdjustment)); + + // Add the 'this' pointer adjustment if necessary. + if (!ThisAdjustment.isEmpty()) + ThisAdjustments.push_back(std::make_pair(Components.size(), + ThisAdjustment)); + + // Add the function. + Components.push_back(VtableComponent::MakeFunction(MD)); + } +} + +void +VtableBuilder::AddMethods(BaseSubobject Base, PrimaryBasesSetTy &PrimaryBases) { + const CXXRecordDecl *RD = Base.getBase(); + + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); + + if (const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase()) { + if (Layout.getPrimaryBaseWasVirtual()) + assert(false && "FIXME: Handle vbases here."); + else + assert(Layout.getBaseClassOffset(PrimaryBase) == 0 && + "Primary base should have a zero offset!"); + + AddMethods(BaseSubobject(PrimaryBase, Base.getBaseOffset()), PrimaryBases); + + if (!PrimaryBases.insert(PrimaryBase)) + assert(false && "Found a duplicate primary base!"); + } + + // Now go through all virtual member functions and add them. + for (CXXRecordDecl::method_iterator I = RD->method_begin(), + E = RD->method_end(); I != E; ++I) { + const CXXMethodDecl *MD = *I; + + if (!MD->isVirtual()) + continue; + + // Get the final overrider. + FinalOverriders::OverriderInfo Overrider = + Overriders.getOverrider(Base, MD); + + // Check if this virtual member function overrides a method in a primary + // base. If this is the case, and the return type doesn't require adjustment + // then we can just use the member function from the primary base. + if (const CXXMethodDecl *OverriddenMD = + OverridesMethodInPrimaryBase(MD, PrimaryBases)) { + if (ComputeReturnAdjustmentBaseOffset(Context, MD, + OverriddenMD).isEmpty()) + continue; + } + + // Check if this overrider needs a return adjustment. + FinalOverriders::BaseOffset ReturnAdjustmentOffset = + Overriders.getReturnAdjustmentOffset(Base, MD); + + ReturnAdjustment ReturnAdjustment = + ComputeReturnAdjustment(ReturnAdjustmentOffset); + + // Check if this overrider needs a 'this' pointer adjustment. + FinalOverriders::BaseOffset ThisAdjustmentOffset = + Overriders.getThisAdjustmentOffset(Base, MD); + + ThisAdjustment ThisAdjustment = ComputeThisAdjustment(ThisAdjustmentOffset); + + AddMethod(Overrider.Method, ReturnAdjustment, ThisAdjustment); + } +} + +void VtableBuilder::LayoutVtable(BaseSubobject Base) { + const CXXRecordDecl *RD = Base.getBase(); + assert(RD->isDynamicClass() && "class does not have a vtable!"); + + int64_t OffsetToTop = -(int64_t)Base.getBaseOffset() / 8; + + // Add vcall and vbase offsets for this vtable. + VisitedVirtualBasesSetTy VBases; + AddVCallAndVBaseOffsets(RD, OffsetToTop, VBases); + + // Reverse them and add them to the vtable components. + std::reverse(VCallAndVBaseOffsets.begin(), VCallAndVBaseOffsets.end()); + Components.append(VCallAndVBaseOffsets.begin(), VCallAndVBaseOffsets.end()); + VCallAndVBaseOffsets.clear(); + + // Add the offset to top. + // FIXME: This is not going to be right for construction vtables. + // FIXME: We should not use / 8 here. + Components.push_back(VtableComponent::MakeOffsetToTop(OffsetToTop)); + + // Next, add the RTTI. + Components.push_back(VtableComponent::MakeRTTI(MostDerivedClass)); + + uint64_t AddressPoint = Components.size(); + + // Now go through all virtual member functions and add them. + PrimaryBasesSetTy PrimaryBases; + AddMethods(Base, PrimaryBases); + + // Record the address point. + AddressPoints.insert(std::make_pair(Base, AddressPoint)); + + // Record the address points for all primary bases. + for (PrimaryBasesSetTy::const_iterator I = PrimaryBases.begin(), + E = PrimaryBases.end(); I != E; ++I) { + const CXXRecordDecl *BaseDecl = *I; + + // We know that all the primary bases have the same offset as the base + // subobject. + BaseSubobject PrimaryBase(BaseDecl, Base.getBaseOffset()); + AddressPoints.insert(std::make_pair(PrimaryBase, AddressPoint)); + } + + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); + const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase(); + + // Layout secondary vtables. + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I) { + const CXXRecordDecl *BaseDecl = + cast(I->getType()->getAs()->getDecl()); + + // Ignore bases that don't have a vtable. + if (!BaseDecl->isDynamicClass()) + continue; + + // Ignore the primary base. + if (BaseDecl == PrimaryBase) + continue; + + // Ignore virtual bases, we'll emit them later. + if (I->isVirtual()) + continue; + + // Get the base offset of this base. + uint64_t BaseOffset = Base.getBaseOffset() + + Layout.getBaseClassOffset(BaseDecl); + + // Layout this secondary vtable. + LayoutVtable(BaseSubobject(BaseDecl, BaseOffset)); + } + + // FIXME: Emit vtables for virtual bases here. +} + +/// dumpLayout - Dump the vtable layout. +void VtableBuilder::dumpLayout(llvm::raw_ostream& Out) { + + Out << "Vtable for '" << MostDerivedClass->getQualifiedNameAsString(); + Out << "' (" << Components.size() << " entries).\n"; + + // Iterate through the address points and insert them into a new map where + // they are keyed by the index and not the base object. + // Since an address point can be shared by multiple subobjects, we use an + // STL multimap. + std::multimap AddressPointsByIndex; + for (CGVtableInfo::AddressPointsMapTy::const_iterator I = + AddressPoints.begin(), E = AddressPoints.end(); I != E; ++I) { + const BaseSubobject& Base = I->first; + uint64_t Index = I->second; + + AddressPointsByIndex.insert(std::make_pair(Index, Base)); + } + + unsigned NextReturnAdjustmentIndex = 0; + unsigned NextThisAdjustmentIndex = 0; + for (unsigned I = 0, E = Components.size(); I != E; ++I) { + uint64_t Index = I; + + if (AddressPointsByIndex.count(I)) { + if (AddressPointsByIndex.count(Index) == 1) { + const BaseSubobject &Base = AddressPointsByIndex.find(Index)->second; + + // FIXME: Instead of dividing by 8, we should be using CharUnits. + Out << " -- (" << Base.getBase()->getQualifiedNameAsString(); + Out << ", " << Base.getBaseOffset() / 8 << ") vtable address --\n"; + } else { + uint64_t BaseOffset = + AddressPointsByIndex.lower_bound(Index)->second.getBaseOffset(); + + // We store the class names in a set to get a stable order. + std::set ClassNames; + for (std::multimap::const_iterator I = + AddressPointsByIndex.lower_bound(Index), E = + AddressPointsByIndex.upper_bound(Index); I != E; ++I) { + assert(I->second.getBaseOffset() == BaseOffset && + "Invalid base offset!"); + const CXXRecordDecl *RD = I->second.getBase(); + ClassNames.insert(RD->getQualifiedNameAsString()); + } + + for (std::set::const_iterator I = ClassNames.begin(), + E = ClassNames.end(); I != E; ++I) { + // FIXME: Instead of dividing by 8, we should be using CharUnits. + Out << " -- (" << *I; + Out << ", " << BaseOffset / 8 << ") vtable address --\n"; + } + } + } + + Out << llvm::format("%4d | ", I); + + const VtableComponent &Component = Components[I]; + + // Dump the component. + switch (Component.getKind()) { + // FIXME: Remove this default case. + default: + assert(false && "Unhandled component kind!"); + break; + + case VtableComponent::CK_VBaseOffset: + Out << "vbase_offset (" << Component.getVBaseOffset() << ")"; + break; + + case VtableComponent::CK_OffsetToTop: + Out << "offset_to_top (" << Component.getOffsetToTop() << ")"; + break; + + case VtableComponent::CK_RTTI: + Out << Component.getRTTIDecl()->getQualifiedNameAsString() << " RTTI"; + break; + + case VtableComponent::CK_FunctionPointer: { + const CXXMethodDecl *MD = Component.getFunctionDecl(); + + std::string Str = + PredefinedExpr::ComputeName(PredefinedExpr::PrettyFunctionNoVirtual, + MD); + Out << Str; + if (MD->isPure()) + Out << " [pure]"; + + // If this function pointer has a return adjustment, dump it. + if (NextReturnAdjustmentIndex < ReturnAdjustments.size() && + ReturnAdjustments[NextReturnAdjustmentIndex].first == I) { + const ReturnAdjustment Adjustment = + ReturnAdjustments[NextReturnAdjustmentIndex].second; + + Out << "\n [return adjustment: "; + Out << Adjustment.NonVirtual << " non-virtual"; + + if (Adjustment.VBaseOffsetOffset) + Out << ", " << Adjustment.VBaseOffsetOffset << " vbase offset offset"; + Out << ']'; + + NextReturnAdjustmentIndex++; + } + + // If this function pointer has a 'this' pointer adjustment, dump it. + if (NextThisAdjustmentIndex < ThisAdjustments.size() && + ThisAdjustments[NextThisAdjustmentIndex].first == I) { + const ThisAdjustment Adjustment = + ThisAdjustments[NextThisAdjustmentIndex].second; + + Out << "\n [this adjustment: "; + Out << Adjustment.NonVirtual << " non-virtual"; + + Out << ']'; + + NextThisAdjustmentIndex++; + } + + break; + } + + case VtableComponent::CK_CompleteDtorPointer: { + const CXXDestructorDecl *DD = Component.getDestructorDecl(); + + Out << DD->getQualifiedNameAsString() << "() [complete]"; + if (DD->isPure()) + Out << " [pure]"; + + break; + } + + case VtableComponent::CK_DeletingDtorPointer: { + const CXXDestructorDecl *DD = Component.getDestructorDecl(); + + Out << DD->getQualifiedNameAsString() << "() [deleting]"; + if (DD->isPure()) + Out << " [pure]"; + + break; + } + + } + + Out << '\n'; + } + +} + +} + +namespace { +class OldVtableBuilder { public: /// Index_t - Vtable index type. typedef uint64_t Index_t; @@ -57,10 +1288,11 @@ private: llvm::LLVMContext &VMContext; CodeGenModule &CGM; // Per-module state. - llvm::DenseMap VCall; + llvm::DenseMap VCall; llvm::DenseMap VCallOffset; + llvm::DenseMap VCallOffsetForVCall; // This is the offset to the nearest virtual base - llvm::DenseMap NonVirtualOffset; + llvm::DenseMap NonVirtualOffset; llvm::DenseMap VBIndex; /// PureVirtualFunction - Points to __cxa_pure_virtual. @@ -180,6 +1412,196 @@ private: return *ref; } + bool DclIsSame(const FunctionDecl *New, const FunctionDecl *Old) { + FunctionTemplateDecl *OldTemplate = Old->getDescribedFunctionTemplate(); + FunctionTemplateDecl *NewTemplate = New->getDescribedFunctionTemplate(); + + // C++ [temp.fct]p2: + // A function template can be overloaded with other function templates + // and with normal (non-template) functions. + if ((OldTemplate == 0) != (NewTemplate == 0)) + return false; + + // Is the function New an overload of the function Old? + QualType OldQType = CGM.getContext().getCanonicalType(Old->getType()); + QualType NewQType = CGM.getContext().getCanonicalType(New->getType()); + + // Compare the signatures (C++ 1.3.10) of the two functions to + // determine whether they are overloads. If we find any mismatch + // in the signature, they are overloads. + + // If either of these functions is a K&R-style function (no + // prototype), then we consider them to have matching signatures. + if (isa(OldQType.getTypePtr()) || + isa(NewQType.getTypePtr())) + return true; + + FunctionProtoType* OldType = cast(OldQType); + FunctionProtoType* NewType = cast(NewQType); + + // The signature of a function includes the types of its + // parameters (C++ 1.3.10), which includes the presence or absence + // of the ellipsis; see C++ DR 357). + if (OldQType != NewQType && + (OldType->getNumArgs() != NewType->getNumArgs() || + OldType->isVariadic() != NewType->isVariadic() || + !std::equal(OldType->arg_type_begin(), OldType->arg_type_end(), + NewType->arg_type_begin()))) + return false; + +#if 0 + // C++ [temp.over.link]p4: + // The signature of a function template consists of its function + // signature, its return type and its template parameter list. The names + // of the template parameters are significant only for establishing the + // relationship between the template parameters and the rest of the + // signature. + // + // We check the return type and template parameter lists for function + // templates first; the remaining checks follow. + if (NewTemplate && + (!TemplateParameterListsAreEqual(NewTemplate->getTemplateParameters(), + OldTemplate->getTemplateParameters(), + TPL_TemplateMatch) || + OldType->getResultType() != NewType->getResultType())) + return false; +#endif + + // If the function is a class member, its signature includes the + // cv-qualifiers (if any) on the function itself. + // + // As part of this, also check whether one of the member functions + // is static, in which case they are not overloads (C++ + // 13.1p2). While not part of the definition of the signature, + // this check is important to determine whether these functions + // can be overloaded. + const CXXMethodDecl* OldMethod = dyn_cast(Old); + const CXXMethodDecl* NewMethod = dyn_cast(New); + if (OldMethod && NewMethod && + !OldMethod->isStatic() && !NewMethod->isStatic() && + OldMethod->getTypeQualifiers() != NewMethod->getTypeQualifiers()) + return false; + + // The signatures match; this is not an overload. + return true; + } + + typedef llvm::DenseMap + ForwardUnique_t; + ForwardUnique_t ForwardUnique; + llvm::DenseMap UniqueOverrider; + + void BuildUniqueOverrider(const CXXMethodDecl *U, const CXXMethodDecl *MD) { + const CXXMethodDecl *PrevU = UniqueOverrider[MD]; + assert(U && "no unique overrider"); + if (PrevU == U) + return; + if (PrevU != U && PrevU != 0) { + // If already set, note the two sets as the same + if (0) + printf("%s::%s same as %s::%s\n", + PrevU->getParent()->getNameAsCString(), + PrevU->getNameAsCString(), + U->getParent()->getNameAsCString(), + U->getNameAsCString()); + ForwardUnique[PrevU] = U; + return; + } + + // Not set, set it now + if (0) + printf("marking %s::%s %p override as %s::%s\n", + MD->getParent()->getNameAsCString(), + MD->getNameAsCString(), + (void*)MD, + U->getParent()->getNameAsCString(), + U->getNameAsCString()); + UniqueOverrider[MD] = U; + + for (CXXMethodDecl::method_iterator mi = MD->begin_overridden_methods(), + me = MD->end_overridden_methods(); mi != me; ++mi) { + BuildUniqueOverrider(U, *mi); + } + } + + void BuildUniqueOverriders(const CXXRecordDecl *RD) { + if (0) printf("walking %s\n", RD->getNameAsCString()); + for (CXXRecordDecl::method_iterator i = RD->method_begin(), + e = RD->method_end(); i != e; ++i) { + const CXXMethodDecl *MD = *i; + if (!MD->isVirtual()) + continue; + + if (UniqueOverrider[MD] == 0) { + // Only set this, if it hasn't been set yet. + BuildUniqueOverrider(MD, MD); + if (0) + printf("top set is %s::%s %p\n", + MD->getParent()->getNameAsCString(), + MD->getNameAsCString(), + (void*)MD); + ForwardUnique[MD] = MD; + } + } + for (CXXRecordDecl::base_class_const_iterator i = RD->bases_begin(), + e = RD->bases_end(); i != e; ++i) { + const CXXRecordDecl *Base = + cast(i->getType()->getAs()->getDecl()); + BuildUniqueOverriders(Base); + } + } + + static int DclCmp(const void *p1, const void *p2) { + const CXXMethodDecl *MD1 = *(const CXXMethodDecl *const *)p1; + const CXXMethodDecl *MD2 = *(const CXXMethodDecl *const *)p2; + + return (DeclarationName::compare(MD1->getDeclName(), MD2->getDeclName())); + } + + void MergeForwarding() { + typedef llvm::SmallVector A_t; + A_t A; + for (ForwardUnique_t::iterator I = ForwardUnique.begin(), + E = ForwardUnique.end(); I != E; ++I) { + if (I->first == I->second) + // Only add the roots of all trees + A.push_back(I->first); + } + llvm::array_pod_sort(A.begin(), A.end(), DclCmp); + for (A_t::iterator I = A.begin(), + E = A.end(); I != E; ++I) { + A_t::iterator J = I; + while (++J != E && DclCmp(I, J) == 0) + if (DclIsSame(*I, *J)) { + if (0) printf("connecting %s\n", (*I)->getNameAsCString()); + ForwardUnique[*J] = *I; + } + } + } + + const CXXMethodDecl *getUnique(const CXXMethodDecl *MD) { + const CXXMethodDecl *U = UniqueOverrider[MD]; + assert(U && "unique overrider not found"); + while (ForwardUnique.count(U)) { + const CXXMethodDecl *NU = ForwardUnique[U]; + if (NU == U) break; + U = NU; + } + return U; + } + + GlobalDecl getUnique(GlobalDecl GD) { + const CXXMethodDecl *Unique = getUnique(cast(GD.getDecl())); + + if (const CXXConstructorDecl *CD = dyn_cast(Unique)) + return GlobalDecl(CD, GD.getCtorType()); + + if (const CXXDestructorDecl *DD = dyn_cast(Unique)) + return GlobalDecl(DD, GD.getDtorType()); + + return Unique; + } + /// getPureVirtualFn - Return the __cxa_pure_virtual function. llvm::Constant* getPureVirtualFn() { if (!PureVirtualFn) { @@ -193,7 +1615,7 @@ private: } public: - VtableBuilder(const CXXRecordDecl *MostDerivedClass, + OldVtableBuilder(const CXXRecordDecl *MostDerivedClass, const CXXRecordDecl *l, uint64_t lo, CodeGenModule &cgm, bool build, CGVtableInfo::AddressPointsMapTy& AddressPoints) : BuildVtable(build), MostDerivedClass(MostDerivedClass), LayoutClass(l), @@ -209,6 +1631,8 @@ public: QualType ClassType = CGM.getContext().getTagDeclType(MostDerivedClass); rtti = CGM.GetAddrOfRTTIDescriptor(ClassType); } + BuildUniqueOverriders(MostDerivedClass); + MergeForwarding(); } // getVtableComponents - Returns a reference to the vtable components. @@ -384,17 +1808,24 @@ public: // entry. Methods.AddMethod(GD); - VCallOffset[GD] = Offset/8; + VCallOffset[GD] = Offset/8 - CurrentVBaseOffset/8; + if (MorallyVirtual) { - Index_t &idx = VCall[GD]; + GlobalDecl UGD = getUnique(GD); + const CXXMethodDecl *UMD = cast(UGD.getDecl()); + + assert(UMD && "final overrider not found"); + + Index_t &idx = VCall[UMD]; // Allocate the first one, after that, we reuse the previous one. if (idx == 0) { - NonVirtualOffset[GD] = CurrentVBaseOffset/8 - Offset/8; + VCallOffsetForVCall[UGD] = Offset/8; + NonVirtualOffset[UMD] = Offset/8 - CurrentVBaseOffset/8; idx = VCalls.size()+1; - VCalls.push_back(0); + VCalls.push_back(Offset/8 - CurrentVBaseOffset/8); D1(printf(" vcall for %s at %d with delta %d\n", dyn_cast(GD.getDecl())->getNameAsCString(), - (int)-VCalls.size()-3, 0)); + (int)-VCalls.size()-3, (int)VCalls[idx-1])); } } } @@ -459,6 +1890,9 @@ public: } VCalls.clear(); VCall.clear(); + VCallOffsetForVCall.clear(); + VCallOffset.clear(); + NonVirtualOffset.clear(); } void AddAddressPoints(const CXXRecordDecl *RD, uint64_t Offset, @@ -699,84 +2133,7 @@ public: }; } // end anonymous namespace -/// TypeConversionRequiresAdjustment - Returns whether conversion from a -/// derived type to a base type requires adjustment. -static bool -TypeConversionRequiresAdjustment(ASTContext &Ctx, - const CXXRecordDecl *DerivedDecl, - const CXXRecordDecl *BaseDecl) { - CXXBasePaths Paths(/*FindAmbiguities=*/false, - /*RecordPaths=*/true, /*DetectVirtual=*/true); - if (!const_cast(DerivedDecl)-> - isDerivedFrom(const_cast(BaseDecl), Paths)) { - assert(false && "Class must be derived from the passed in base class!"); - return false; - } - - // If we found a virtual base we always want to require adjustment. - if (Paths.getDetectedVirtual()) - return true; - - const CXXBasePath &Path = Paths.front(); - - for (size_t Start = 0, End = Path.size(); Start != End; ++Start) { - const CXXBasePathElement &Element = Path[Start]; - - // Check the base class offset. - const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(Element.Class); - - const RecordType *BaseType = Element.Base->getType()->getAs(); - const CXXRecordDecl *Base = cast(BaseType->getDecl()); - - if (Layout.getBaseClassOffset(Base) != 0) { - // This requires an adjustment. - return true; - } - } - - return false; -} - -static bool -TypeConversionRequiresAdjustment(ASTContext &Ctx, - QualType DerivedType, QualType BaseType) { - // Canonicalize the types. - QualType CanDerivedType = Ctx.getCanonicalType(DerivedType); - QualType CanBaseType = Ctx.getCanonicalType(BaseType); - - assert(CanDerivedType->getTypeClass() == CanBaseType->getTypeClass() && - "Types must have same type class!"); - - if (CanDerivedType == CanBaseType) { - // No adjustment needed. - return false; - } - - if (const ReferenceType *RT = dyn_cast(CanDerivedType)) { - CanDerivedType = RT->getPointeeType(); - CanBaseType = cast(CanBaseType)->getPointeeType(); - } else if (const PointerType *PT = dyn_cast(CanDerivedType)) { - CanDerivedType = PT->getPointeeType(); - CanBaseType = cast(CanBaseType)->getPointeeType(); - } else { - assert(false && "Unexpected return type!"); - } - - if (CanDerivedType == CanBaseType) { - // No adjustment needed. - return false; - } - - const CXXRecordDecl *DerivedDecl = - cast(cast(CanDerivedType)->getDecl()); - - const CXXRecordDecl *BaseDecl = - cast(cast(CanBaseType)->getDecl()); - - return TypeConversionRequiresAdjustment(Ctx, DerivedDecl, BaseDecl); -} - -bool VtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, +bool OldVtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, Index_t OverrideOffset, Index_t Offset, int64_t CurrentVBaseOffset) { const CXXMethodDecl *MD = cast(GD.getDecl()); @@ -788,6 +2145,7 @@ bool VtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, for (CXXMethodDecl::method_iterator mi = MD->begin_overridden_methods(), e = MD->end_overridden_methods(); mi != e; ++mi) { GlobalDecl OGD; + GlobalDecl OGD2; const CXXMethodDecl *OMD = *mi; if (const CXXDestructorDecl *DD = dyn_cast(OMD)) @@ -801,6 +2159,8 @@ bool VtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, if (!Methods.getIndex(OGD, Index)) continue; + OGD2 = OGD; + // Get the original method, which we should be computing thunks, etc, // against. OGD = Methods.getOrigMethod(Index); @@ -812,8 +2172,8 @@ bool VtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, OMD->getType()->getAs()->getResultType(); // Check if we need a return type adjustment. - if (TypeConversionRequiresAdjustment(CGM.getContext(), ReturnType, - OverriddenReturnType)) { + if (!ComputeReturnAdjustmentBaseOffset(CGM.getContext(), MD, + OMD).isEmpty()) { CanQualType &BaseReturnType = BaseReturnTypes[Index]; // Insert the base return type. @@ -824,26 +2184,39 @@ bool VtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, Methods.OverrideMethod(OGD, GD); + GlobalDecl UGD = getUnique(GD); + const CXXMethodDecl *UMD = cast(UGD.getDecl()); + assert(UGD.getDecl() && "unique overrider not found"); + assert(UGD == getUnique(OGD) && "unique overrider not unique"); + ThisAdjustments.erase(Index); - if (MorallyVirtual || VCall.count(OGD)) { - Index_t &idx = VCall[OGD]; + if (MorallyVirtual || VCall.count(UMD)) { + + Index_t &idx = VCall[UMD]; if (idx == 0) { - NonVirtualOffset[GD] = -OverrideOffset/8 + CurrentVBaseOffset/8; - VCallOffset[GD] = OverrideOffset/8; + VCallOffset[GD] = VCallOffset[OGD]; + // NonVirtualOffset[UMD] = CurrentVBaseOffset/8 - OverrideOffset/8; + NonVirtualOffset[UMD] = VCallOffset[OGD]; + VCallOffsetForVCall[UMD] = OverrideOffset/8; idx = VCalls.size()+1; - VCalls.push_back(0); + VCalls.push_back(OverrideOffset/8 - CurrentVBaseOffset/8); D1(printf(" vcall for %s at %d with delta %d most derived %s\n", MD->getNameAsString().c_str(), (int)-idx-3, (int)VCalls[idx-1], MostDerivedClass->getNameAsCString())); } else { - NonVirtualOffset[GD] = NonVirtualOffset[OGD]; - VCallOffset[GD] = VCallOffset[OGD]; - VCalls[idx-1] = -VCallOffset[OGD] + OverrideOffset/8; + VCallOffset[GD] = NonVirtualOffset[UMD]; + VCalls[idx-1] = -VCallOffsetForVCall[UGD] + OverrideOffset/8; D1(printf(" vcall patch for %s at %d with delta %d most derived %s\n", MD->getNameAsString().c_str(), (int)-idx-3, (int)VCalls[idx-1], MostDerivedClass->getNameAsCString())); } - int64_t NonVirtualAdjustment = NonVirtualOffset[GD]; + int64_t NonVirtualAdjustment = -VCallOffset[OGD]; + QualType DerivedType = MD->getThisType(CGM.getContext()); + QualType BaseType = cast(OGD.getDecl())->getThisType(CGM.getContext()); + int64_t NonVirtualAdjustment2 = -(getNVOffset(BaseType, DerivedType)/8); + if (NonVirtualAdjustment2 != NonVirtualAdjustment) { + NonVirtualAdjustment = NonVirtualAdjustment2; + } int64_t VirtualAdjustment = -((idx + extra + 2) * LLVMPointerWidth / 8); @@ -859,12 +2232,19 @@ bool VtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, SavedAdjustments.push_back( std::make_pair(GD, std::make_pair(OGD, ThisAdjustment))); } - VCall[GD] = idx; return true; } - int64_t NonVirtualAdjustment = -VCallOffset[OGD] + OverrideOffset/8; + VCallOffset[GD] = VCallOffset[OGD2] - OverrideOffset/8; + int64_t NonVirtualAdjustment = -VCallOffset[GD]; + QualType DerivedType = MD->getThisType(CGM.getContext()); + QualType BaseType = cast(OGD.getDecl())->getThisType(CGM.getContext()); + int64_t NonVirtualAdjustment2 = -(getNVOffset(BaseType, DerivedType)/8); + if (NonVirtualAdjustment2 != NonVirtualAdjustment) { + NonVirtualAdjustment = NonVirtualAdjustment2; + } + if (NonVirtualAdjustment) { ThunkAdjustment ThisAdjustment(NonVirtualAdjustment, 0); @@ -880,7 +2260,7 @@ bool VtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, return false; } -void VtableBuilder::AppendMethodsToVtable() { +void OldVtableBuilder::AppendMethodsToVtable() { if (!BuildVtable) { VtableComponents.insert(VtableComponents.end(), Methods.size(), (llvm::Constant *)0); @@ -948,13 +2328,13 @@ void VtableBuilder::AppendMethodsToVtable() { void CGVtableInfo::ComputeMethodVtableIndices(const CXXRecordDecl *RD) { // Itanium C++ ABI 2.5.2: - // The order of the virtual function pointers in a virtual table is the - // order of declaration of the corresponding member functions in the class. + // The order of the virtual function pointers in a virtual table is the + // order of declaration of the corresponding member functions in the class. // - // There is an entry for any virtual function declared in a class, - // whether it is a new function or overrides a base class function, - // unless it overrides a function from the primary base, and conversion - // between their return types does not require an adjustment. + // There is an entry for any virtual function declared in a class, + // whether it is a new function or overrides a base class function, + // unless it overrides a function from the primary base, and conversion + // between their return types does not require an adjustment. int64_t CurrentIndex = 0; @@ -972,7 +2352,7 @@ void CGVtableInfo::ComputeMethodVtableIndices(const CXXRecordDecl *RD) { // Collect all the primary bases, so we can check whether methods override // a method from the base. - llvm::SmallPtrSet PrimaryBases; + VtableBuilder::PrimaryBasesSetTy PrimaryBases; for (ASTRecordLayout::primary_base_info_iterator I = Layout.primary_base_begin(), E = Layout.primary_base_end(); I != E; ++I) @@ -988,51 +2368,33 @@ void CGVtableInfo::ComputeMethodVtableIndices(const CXXRecordDecl *RD) { if (!MD->isVirtual()) continue; - bool ShouldAddEntryForMethod = true; - // Check if this method overrides a method in the primary base. - for (CXXMethodDecl::method_iterator i = MD->begin_overridden_methods(), - e = MD->end_overridden_methods(); i != e; ++i) { - const CXXMethodDecl *OverriddenMD = *i; - const CXXRecordDecl *OverriddenRD = OverriddenMD->getParent(); - assert(OverriddenMD->isCanonicalDecl() && - "Should have the canonical decl of the overridden RD!"); - - if (PrimaryBases.count(OverriddenRD)) { - // Check if converting from the return type of the method to the - // return type of the overridden method requires conversion. - QualType ReturnType = - MD->getType()->getAs()->getResultType(); - QualType OverriddenReturnType = - OverriddenMD->getType()->getAs()->getResultType(); - - if (!TypeConversionRequiresAdjustment(CGM.getContext(), - ReturnType, OverriddenReturnType)) { - // This index is shared between the index in the vtable of the primary - // base class. - if (const CXXDestructorDecl *DD = dyn_cast(MD)) { - const CXXDestructorDecl *OverriddenDD = - cast(OverriddenMD); - - // Add both the complete and deleting entries. - MethodVtableIndices[GlobalDecl(DD, Dtor_Complete)] = - getMethodVtableIndex(GlobalDecl(OverriddenDD, Dtor_Complete)); - MethodVtableIndices[GlobalDecl(DD, Dtor_Deleting)] = - getMethodVtableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting)); - } else { - MethodVtableIndices[MD] = getMethodVtableIndex(OverriddenMD); - } + if (const CXXMethodDecl *OverriddenMD = + OverridesMethodInPrimaryBase(MD, PrimaryBases)) { + // Check if converting from the return type of the method to the + // return type of the overridden method requires conversion. + if (ComputeReturnAdjustmentBaseOffset(CGM.getContext(), MD, + OverriddenMD).isEmpty()) { + // This index is shared between the index in the vtable of the primary + // base class. + if (const CXXDestructorDecl *DD = dyn_cast(MD)) { + const CXXDestructorDecl *OverriddenDD = + cast(OverriddenMD); - // We don't need to add an entry for this method. - ShouldAddEntryForMethod = false; - break; - } + // Add both the complete and deleting entries. + MethodVtableIndices[GlobalDecl(DD, Dtor_Complete)] = + getMethodVtableIndex(GlobalDecl(OverriddenDD, Dtor_Complete)); + MethodVtableIndices[GlobalDecl(DD, Dtor_Deleting)] = + getMethodVtableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting)); + } else { + MethodVtableIndices[MD] = getMethodVtableIndex(OverriddenMD); + } + + // We don't need to add an entry for this method. + continue; } } - if (!ShouldAddEntryForMethod) - continue; - if (const CXXDestructorDecl *DD = dyn_cast(MD)) { if (MD->isImplicit()) { assert(!ImplicitVirtualDtor && @@ -1054,8 +2416,8 @@ void CGVtableInfo::ComputeMethodVtableIndices(const CXXRecordDecl *RD) { if (ImplicitVirtualDtor) { // Itanium C++ ABI 2.5.2: - // If a class has an implicitly-defined virtual destructor, - // its entries come after the declared virtual function pointers. + // If a class has an implicitly-defined virtual destructor, + // its entries come after the declared virtual function pointers. // Add the complete dtor. MethodVtableIndices[GlobalDecl(ImplicitVirtualDtor, Dtor_Complete)] = @@ -1107,12 +2469,12 @@ CGVtableInfo::getAdjustments(GlobalDecl GD) { return 0; AddressPointsMapTy AddressPoints; - VtableBuilder b(RD, RD, 0, CGM, false, AddressPoints); + OldVtableBuilder b(RD, RD, 0, CGM, false, AddressPoints); D1(printf("vtable %s\n", RD->getNameAsCString())); b.GenerateVtableForBase(RD); b.GenerateVtableForVBases(RD); - for (VtableBuilder::SavedAdjustmentsVectorTy::iterator + for (OldVtableBuilder::SavedAdjustmentsVectorTy::iterator i = b.getSavedAdjustments().begin(), e = b.getSavedAdjustments().end(); i != e; i++) SavedAdjustments[i->first].push_back(i->second); @@ -1136,7 +2498,7 @@ int64_t CGVtableInfo::getVirtualBaseOffsetIndex(const CXXRecordDecl *RD, // FIXME: This seems expensive. Can we do a partial job to get // just this data. AddressPointsMapTy AddressPoints; - VtableBuilder b(RD, RD, 0, CGM, false, AddressPoints); + OldVtableBuilder b(RD, RD, 0, CGM, false, AddressPoints); D1(printf("vtable %s\n", RD->getNameAsCString())); b.GenerateVtableForBase(RD); b.GenerateVtableForVBases(RD); @@ -1150,6 +2512,12 @@ int64_t CGVtableInfo::getVirtualBaseOffsetIndex(const CXXRecordDecl *RD, } I = VirtualBaseClassIndicies.find(ClassPair); + // FIXME: The assertion below assertion currently fails with the old vtable + /// layout code if there is a non-virtual thunk adjustment in a vtable. + // Once the new layout is in place, this return should be removed. + if (I == VirtualBaseClassIndicies.end()) + return 0; + assert(I != VirtualBaseClassIndicies.end() && "Did not find index!"); return I->second; @@ -1168,6 +2536,13 @@ CGVtableInfo::GenerateVtable(llvm::GlobalVariable::LinkageTypes Linkage, const CXXRecordDecl *LayoutClass, const CXXRecordDecl *RD, uint64_t Offset, AddressPointsMapTy& AddressPoints) { + if (GenerateDefinition && CGM.getLangOptions().DumpVtableLayouts && + LayoutClass == RD) { + VtableBuilder Builder(*this, RD); + + Builder.dumpLayout(llvm::errs()); + } + llvm::SmallString<256> OutName; if (LayoutClass != RD) CGM.getMangleContext().mangleCXXCtorVtable(LayoutClass, Offset / 8, @@ -1179,8 +2554,8 @@ CGVtableInfo::GenerateVtable(llvm::GlobalVariable::LinkageTypes Linkage, llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name); if (GV == 0 || CGM.getVtableInfo().AddressPoints[LayoutClass] == 0 || GV->isDeclaration()) { - VtableBuilder b(RD, LayoutClass, Offset, CGM, GenerateDefinition, - AddressPoints); + OldVtableBuilder b(RD, LayoutClass, Offset, CGM, GenerateDefinition, + AddressPoints); D1(printf("vtable %s\n", RD->getNameAsCString())); // First comes the vtables for all the non-virtual bases... diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index f0a5c64d2fdf..5a4f94e3e092 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -171,6 +171,16 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, CurFn = Fn; assert(CurFn->isDeclaration() && "Function already has body?"); + // Pass inline keyword to optimizer if it appears explicitly on any + // declaration. + if (const FunctionDecl *FD = dyn_cast_or_null(D)) + for (FunctionDecl::redecl_iterator RI = FD->redecls_begin(), + RE = FD->redecls_end(); RI != RE; ++RI) + if (RI->isInlineSpecified()) { + Fn->addFnAttr(llvm::Attribute::InlineHint); + break; + } + llvm::BasicBlock *EntryBB = createBasicBlock("entry", CurFn); // Create a marker to make it easy to insert allocas into the entryblock @@ -196,7 +206,9 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, } // FIXME: Leaked. - CurFnInfo = &CGM.getTypes().getFunctionInfo(FnRetTy, Args); + // CC info is ignored, hopefully? + CurFnInfo = &CGM.getTypes().getFunctionInfo(FnRetTy, Args, + CC_Default, false); if (RetTy->isVoidType()) { // Void type; nothing to return. @@ -281,6 +293,8 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn) { llvm::BasicBlock *DtorEpilogue = createBasicBlock("dtor.epilogue"); PushCleanupBlock(DtorEpilogue); + InitializeVtablePtrs(DD->getParent()); + EmitStmt(S); CleanupBlockInfo Info = PopCleanupBlock(); @@ -431,7 +445,11 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond, EmitBranchOnBoolExpr(CondBOp->getLHS(), LHSTrue, FalseBlock); EmitBlock(LHSTrue); + // Any temporaries created here are conditional. + BeginConditionalBranch(); EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock); + EndConditionalBranch(); + return; } else if (CondBOp->getOpcode() == BinaryOperator::LOr) { // If we have "0 || X", simplify the code. "1 || X" would have constant @@ -454,7 +472,11 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond, EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, LHSFalse); EmitBlock(LHSFalse); + // Any temporaries created here are conditional. + BeginConditionalBranch(); EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock); + EndConditionalBranch(); + return; } } diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 30ad663771be..fb2e5fee7309 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -268,7 +268,7 @@ public: /// this behavior for branches? void EmitBranchThroughCleanup(llvm::BasicBlock *Dest); - /// StartConditionalBranch - Should be called before a conditional part of an + /// BeginConditionalBranch - Should be called before a conditional part of an /// expression is emitted. For example, before the RHS of the expression below /// is emitted: /// @@ -276,13 +276,16 @@ public: /// /// This is used to make sure that any temporaries created in the conditional /// branch are only destroyed if the branch is taken. - void StartConditionalBranch() { + void BeginConditionalBranch() { ++ConditionalBranchLevel; } - /// FinishConditionalBranch - Should be called after a conditional part of an + /// EndConditionalBranch - Should be called after a conditional part of an /// expression has been emitted. - void FinishConditionalBranch() { + void EndConditionalBranch() { + assert(ConditionalBranchLevel != 0 && + "Conditional branch mismatch!"); + --ConditionalBranchLevel; } @@ -472,7 +475,7 @@ public: const BlockInfo& Info, const Decl *OuterFuncDecl, llvm::DenseMap ldm, - CharUnits &Size, uint64_t &Align, + CharUnits &Size, CharUnits &Align, llvm::SmallVector &subBlockDeclRefDecls, bool &subBlockHasCopyDispose); @@ -569,6 +572,9 @@ public: const llvm::Type *ConvertTypeForMem(QualType T); const llvm::Type *ConvertType(QualType T); + const llvm::Type *ConvertType(const TypeDecl *T) { + return ConvertType(getContext().getTypeDeclType(T)); + } /// LoadObjCSelf - Load the value of self. This function is only valid while /// generating code for an Objective-C method. @@ -652,10 +658,15 @@ public: } /// CreateTempAlloca - This creates a alloca and inserts it into the entry - /// block. + /// block. The caller is responsible for setting an appropriate alignment on + /// the alloca. llvm::AllocaInst *CreateTempAlloca(const llvm::Type *Ty, const llvm::Twine &Name = "tmp"); + /// CreateMemTemp - Create a temporary memory object of the given type, with + /// appropriate alignment. + llvm::Value *CreateMemTemp(QualType T, const llvm::Twine &Name = "tmp"); + /// EvaluateExprAsBool - Perform the usual unary conversions on the specified /// expression and compare the result against zero, returning an Int1Ty value. llvm::Value *EvaluateExprAsBool(const Expr *E); @@ -732,11 +743,16 @@ public: /// LoadCXXVTT - Load the VTT parameter to base constructors/destructors have /// virtual bases. llvm::Value *LoadCXXVTT(); + + /// GetAddressOfBaseOfCompleteClass - Convert the given pointer to a + /// complete class down to one of its virtual bases. + llvm::Value *GetAddressOfBaseOfCompleteClass(llvm::Value *Value, + bool IsVirtual, + const CXXRecordDecl *Derived, + const CXXRecordDecl *Base); /// GetAddressOfBaseClass - This function will add the necessary delta to the /// load of 'this' and returns address of the base class. - // FIXME. This currently only does a derived to non-virtual base conversion. - // Other kinds of conversions will come later. llvm::Value *GetAddressOfBaseClass(llvm::Value *Value, const CXXRecordDecl *ClassDecl, const CXXRecordDecl *BaseClassDecl, @@ -747,10 +763,9 @@ public: const CXXRecordDecl *DerivedClassDecl, bool NullCheckValue); - llvm::Value * - GetVirtualCXXBaseClassOffset(llvm::Value *This, - const CXXRecordDecl *ClassDecl, - const CXXRecordDecl *BaseClassDecl); + llvm::Value *GetVirtualBaseClassOffset(llvm::Value *This, + const CXXRecordDecl *ClassDecl, + const CXXRecordDecl *BaseClassDecl); void EmitClassAggrMemberwiseCopy(llvm::Value *DestValue, llvm::Value *SrcValue, @@ -843,7 +858,8 @@ public: /// This function can be called with a null (unreachable) insert point. void EmitLocalBlockVarDecl(const VarDecl &D); - void EmitStaticBlockVarDecl(const VarDecl &D); + void EmitStaticBlockVarDecl(const VarDecl &D, + llvm::GlobalValue::LinkageTypes Linkage); /// EmitParmDecl - Emit a ParmVarDecl or an ImplicitParamDecl. void EmitParmDecl(const VarDecl &D, llvm::Value *Arg); @@ -1005,12 +1021,18 @@ public: LValue EmitCastLValue(const CastExpr *E); LValue EmitNullInitializationLValue(const CXXZeroInitValueExpr *E); - LValue EmitPointerToDataMemberLValue(const FieldDecl *Field); - llvm::Value *EmitIvarOffset(const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar); LValue EmitLValueForField(llvm::Value* Base, const FieldDecl* Field, - bool isUnion, unsigned CVRQualifiers); + unsigned CVRQualifiers); + + /// EmitLValueForFieldInitialization - Like EmitLValueForField, except that + /// if the Field is a reference, this will return the address of the reference + /// and not the address of the value stored in the reference. + LValue EmitLValueForFieldInitialization(llvm::Value* Base, + const FieldDecl* Field, + unsigned CVRQualifiers); + LValue EmitLValueForIvar(QualType ObjectTy, llvm::Value* Base, const ObjCIvarDecl *Ivar, unsigned CVRQualifiers); @@ -1103,8 +1125,7 @@ public: /// EmitReferenceBindingToExpr - Emits a reference binding to the passed in /// expression. Will emit a temporary variable if E is not an LValue. - RValue EmitReferenceBindingToExpr(const Expr* E, QualType DestType, - bool IsInitializer = false); + RValue EmitReferenceBindingToExpr(const Expr* E, bool IsInitializer = false); //===--------------------------------------------------------------------===// // Expression Emission @@ -1135,6 +1156,10 @@ public: bool IgnoreResult = false, bool IsInitializer = false, bool RequiresGCollection = false); + /// EmitAggExprToLValue - Emit the computation of the specified expression of + /// aggregate type into a temporary LValue. + LValue EmitAggExprToLValue(const Expr *E); + /// EmitGCMemmoveCollectable - Emit special API for structs with object /// pointers. void EmitGCMemmoveCollectable(llvm::Value *DestPtr, llvm::Value *SrcPtr, diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index cf504a7c2a0c..5a552c490ac6 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -20,6 +20,7 @@ #include "TargetInfo.h" #include "clang/CodeGen/CodeGenOptions.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/RecordLayout.h" @@ -131,6 +132,10 @@ CodeGenModule::getDeclVisibilityMode(const Decl *D) const { } } + // This decl should have the same visibility as its parent. + if (const DeclContext *DC = D->getDeclContext()) + return getDeclVisibilityMode(cast(DC)); + return getLangOptions().getVisibilityMode(); } @@ -254,34 +259,40 @@ void CodeGenModule::EmitAnnotations() { static CodeGenModule::GVALinkage GetLinkageForFunction(ASTContext &Context, const FunctionDecl *FD, const LangOptions &Features) { - // Everything located semantically within an anonymous namespace is - // always internal. - if (FD->isInAnonymousNamespace()) - return CodeGenModule::GVA_Internal; + CodeGenModule::GVALinkage External = CodeGenModule::GVA_StrongExternal; - // "static" functions get internal linkage. - if (FD->getStorageClass() == FunctionDecl::Static && !isa(FD)) - return CodeGenModule::GVA_Internal; + Linkage L = FD->getLinkage(); + if (L == ExternalLinkage && Context.getLangOptions().CPlusPlus && + FD->getType()->getLinkage() == UniqueExternalLinkage) + L = UniqueExternalLinkage; - // The kind of external linkage this function will have, if it is not - // inline or static. - CodeGenModule::GVALinkage External = CodeGenModule::GVA_StrongExternal; - if (Context.getLangOptions().CPlusPlus) { - TemplateSpecializationKind TSK = FD->getTemplateSpecializationKind(); + switch (L) { + case NoLinkage: + case InternalLinkage: + case UniqueExternalLinkage: + return CodeGenModule::GVA_Internal; - if (TSK == TSK_ExplicitInstantiationDefinition) { - // If a function has been explicitly instantiated, then it should - // always have strong external linkage. + case ExternalLinkage: + switch (FD->getTemplateSpecializationKind()) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + External = CodeGenModule::GVA_StrongExternal; + break; + + case TSK_ExplicitInstantiationDefinition: + // FIXME: explicit instantiation definitions should use weak linkage return CodeGenModule::GVA_StrongExternal; - } - - if (TSK == TSK_ImplicitInstantiation) + + case TSK_ExplicitInstantiationDeclaration: + case TSK_ImplicitInstantiation: External = CodeGenModule::GVA_TemplateInstantiation; + break; + } } if (!FD->isInlined()) return External; - + if (!Features.CPlusPlus || FD->hasAttr()) { // GNU or C99 inline semantics. Determine whether this symbol should be // externally visible. @@ -402,11 +413,13 @@ void CodeGenModule::SetInternalFunctionAttributes(const Decl *D, SetCommonAttributes(D, F); } -void CodeGenModule::SetFunctionAttributes(const FunctionDecl *FD, +void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F, bool IsIncompleteFunction) { + const FunctionDecl *FD = cast(GD.getDecl()); + if (!IsIncompleteFunction) - SetLLVMFunctionAttributes(FD, getTypes().getFunctionInfo(FD), F); + SetLLVMFunctionAttributes(FD, getTypes().getFunctionInfo(GD), F); // Only a few attributes are set on declarations; these may later be // overridden by a definition. @@ -580,14 +593,24 @@ bool CodeGenModule::MayDeferGeneration(const ValueDecl *Global) { // Static data may be deferred, but out-of-line static data members // cannot be. - if (VD->isInAnonymousNamespace()) - return true; - if (VD->getLinkage() == VarDecl::InternalLinkage) { + Linkage L = VD->getLinkage(); + if (L == ExternalLinkage && getContext().getLangOptions().CPlusPlus && + VD->getType()->getLinkage() == UniqueExternalLinkage) + L = UniqueExternalLinkage; + + switch (L) { + case NoLinkage: + case InternalLinkage: + case UniqueExternalLinkage: // Initializer has side effects? if (VD->getInit() && VD->getInit()->HasSideEffects(Context)) return false; return !(VD->isStaticDataMember() && VD->isOutOfLine()); + + case ExternalLinkage: + break; } + return false; } @@ -608,20 +631,7 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) { const VarDecl *VD = cast(Global); assert(VD->isFileVarDecl() && "Cannot emit local var decl as global."); - if (getLangOptions().CPlusPlus && !VD->getInit()) { - // In C++, if this is marked "extern", defer code generation. - if (VD->getStorageClass() == VarDecl::Extern || VD->isExternC()) - return; - - // If this is a declaration of an explicit specialization of a static - // data member in a class template, don't emit it. - if (VD->isStaticDataMember() && - VD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) - return; - } - - // In C, if this isn't a definition, defer code generation. - if (!getLangOptions().CPlusPlus && !VD->getInit()) + if (VD->isThisDeclarationADefinition() != VarDecl::Definition) return; } @@ -715,8 +725,7 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(const char *MangledName, "", &getModule()); F->setName(MangledName); if (D.getDecl()) - SetFunctionAttributes(cast(D.getDecl()), F, - IsIncompleteFunction); + SetFunctionAttributes(D, F, IsIncompleteFunction); Entry = F; // This is the first use or definition of a mangled name. If there is a @@ -774,7 +783,7 @@ CodeGenModule::CreateRuntimeFunction(const llvm::FunctionType *FTy, } static bool DeclIsConstantGlobal(ASTContext &Context, const VarDecl *D) { - if (!D->getType().isConstant(Context)) + if (!D->getType().isConstant(Context) && !D->getType()->isReferenceType()) return false; if (Context.getLangOptions().CPlusPlus && Context.getBaseElementType(D->getType())->getAs()) { @@ -941,16 +950,30 @@ CodeGenModule::getVtableLinkage(const CXXRecordDecl *RD) { static CodeGenModule::GVALinkage GetLinkageForVariable(ASTContext &Context, const VarDecl *VD) { - // Everything located semantically within an anonymous namespace is - // always internal. - if (VD->isInAnonymousNamespace()) + // If this is a static data member, compute the kind of template + // specialization. Otherwise, this variable is not part of a + // template. + TemplateSpecializationKind TSK = TSK_Undeclared; + if (VD->isStaticDataMember()) + TSK = VD->getTemplateSpecializationKind(); + + Linkage L = VD->getLinkage(); + if (L == ExternalLinkage && Context.getLangOptions().CPlusPlus && + VD->getType()->getLinkage() == UniqueExternalLinkage) + L = UniqueExternalLinkage; + + switch (L) { + case NoLinkage: + case InternalLinkage: + case UniqueExternalLinkage: return CodeGenModule::GVA_Internal; - // Handle linkage for static data members. - if (VD->isStaticDataMember()) { - switch (VD->getTemplateSpecializationKind()) { + case ExternalLinkage: + switch (TSK) { case TSK_Undeclared: case TSK_ExplicitSpecialization: + + // FIXME: ExplicitInstantiationDefinition should be weak! case TSK_ExplicitInstantiationDefinition: return CodeGenModule::GVA_StrongExternal; @@ -959,22 +982,26 @@ GetLinkageForVariable(ASTContext &Context, const VarDecl *VD) { // Fall through to treat this like any other instantiation. case TSK_ImplicitInstantiation: - return CodeGenModule::GVA_TemplateInstantiation; + return CodeGenModule::GVA_TemplateInstantiation; } } - if (VD->getLinkage() == VarDecl::InternalLinkage) - return CodeGenModule::GVA_Internal; - return CodeGenModule::GVA_StrongExternal; } +CharUnits CodeGenModule::GetTargetTypeStoreSize(const llvm::Type *Ty) const { + return CharUnits::fromQuantity( + TheTargetData.getTypeStoreSizeInBits(Ty) / Context.getCharWidth()); +} + void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { llvm::Constant *Init = 0; QualType ASTTy = D->getType(); bool NonConstInit = false; - if (D->getInit() == 0) { + const Expr *InitExpr = D->getAnyInitializer(); + + if (!InitExpr) { // This is a tentative definition; tentative definitions are // implicitly initialized with { 0 }. // @@ -987,10 +1014,10 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { assert(!ASTTy->isIncompleteType() && "Unexpected incomplete type"); Init = EmitNullConstant(D->getType()); } else { - Init = EmitConstantExpr(D->getInit(), D->getType()); + Init = EmitConstantExpr(InitExpr, D->getType()); if (!Init) { - QualType T = D->getInit()->getType(); + QualType T = InitExpr->getType(); if (getLangOptions().CPlusPlus) { EmitCXXGlobalVarDeclInitFunc(D); Init = EmitNullConstant(T); @@ -1058,7 +1085,7 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { if (!NonConstInit && DeclIsConstantGlobal(Context, D)) GV->setConstant(true); - GV->setAlignment(getContext().getDeclAlignInBytes(D)); + GV->setAlignment(getContext().getDeclAlign(D).getQuantity()); // Set the llvm linkage type as appropriate. GVALinkage Linkage = GetLinkageForVariable(getContext(), D); @@ -1256,8 +1283,8 @@ void CodeGenModule::EmitAliasDefinition(const ValueDecl *D) { const llvm::Type *DeclTy = getTypes().ConvertTypeForMem(D->getType()); // Unique the name through the identifier table. - const char *AliaseeName = AA->getAliasee().c_str(); - AliaseeName = getContext().Idents.get(AliaseeName).getNameStart(); + const char *AliaseeName = + getContext().Idents.get(AA->getAliasee()).getNameStart(); // Create a reference to the named value. This ensures that it is emitted // if a deferred decl. @@ -1473,11 +1500,9 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) { // String pointer. llvm::Constant *C = llvm::ConstantArray::get(VMContext, Entry.getKey().str()); - const char *Sect = 0; llvm::GlobalValue::LinkageTypes Linkage; bool isConstant; if (isUTF16) { - Sect = getContext().Target.getUnicodeStringSection(); // FIXME: why do utf strings get "_" labels instead of "L" labels? Linkage = llvm::GlobalValue::InternalLinkage; // Note: -fwritable-strings doesn't make unicode CFStrings writable, but @@ -1491,11 +1516,9 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) { llvm::GlobalVariable *GV = new llvm::GlobalVariable(getModule(), C->getType(), isConstant, Linkage, C, ".str"); - if (Sect) - GV->setSection(Sect); if (isUTF16) { - unsigned Align = getContext().getTypeAlign(getContext().ShortTy)/8; - GV->setAlignment(Align); + CharUnits Align = getContext().getTypeAlignInChars(getContext().ShortTy); + GV->setAlignment(Align.getQuantity()); } Fields[2] = llvm::ConstantExpr::getGetElementPtr(GV, Zeros, 2); diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 81f39791fcd0..8280766c7035 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -53,6 +53,7 @@ namespace clang { class ObjCProtocolDecl; class ObjCEncodeExpr; class BlockExpr; + class CharUnits; class Decl; class Expr; class Stmt; @@ -238,10 +239,11 @@ public: BuildCovariantThunk(const GlobalDecl &GD, bool Extern, const CovariantThunkAdjustment &Adjustment); - /// GetCXXBaseClassOffset - Returns the offset from a derived class to its - /// base class. Returns null if the offset is 0. - llvm::Constant *GetCXXBaseClassOffset(const CXXRecordDecl *ClassDecl, - const CXXRecordDecl *BaseClassDecl); + /// GetNonVirtualBaseClassOffset - Returns the offset from a derived class to + /// its base class. Returns null if the offset is 0. + llvm::Constant * + GetNonVirtualBaseClassOffset(const CXXRecordDecl *ClassDecl, + const CXXRecordDecl *BaseClassDecl); /// ComputeThunkAdjustment - Returns the two parts required to compute the /// offset for an object. @@ -346,6 +348,8 @@ public: llvm::Constant *EmitAnnotateAttr(llvm::GlobalValue *GV, const AnnotateAttr *AA, unsigned LineNo); + llvm::Constant *EmitPointerToDataMember(const FieldDecl *FD); + /// ErrorUnsupported - Print out an error that codegen doesn't support the /// specified stmt yet. /// \param OmitOnError - If true, then this error should only be emitted if no @@ -417,6 +421,10 @@ public: /// and type information of the given class. static llvm::GlobalVariable::LinkageTypes getVtableLinkage(const CXXRecordDecl *RD); + + /// GetTargetTypeStoreSize - Return the store size, in character units, of + /// the given LLVM type. + CharUnits GetTargetTypeStoreSize(const llvm::Type *Ty) const; private: /// UniqueMangledName - Unique a name by (if necessary) inserting it into the @@ -443,7 +451,7 @@ private: /// SetFunctionAttributes - Set function attributes for a function /// declaration. - void SetFunctionAttributes(const FunctionDecl *FD, + void SetFunctionAttributes(GlobalDecl GD, llvm::Function *F, bool IsIncompleteFunction); diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp index 838f62a5cb1d..3c20934baf25 100644 --- a/lib/CodeGen/CodeGenTypes.cpp +++ b/lib/CodeGen/CodeGenTypes.cpp @@ -84,7 +84,7 @@ const llvm::Type *CodeGenTypes::ConvertTypeRecursive(QualType T) { const llvm::Type *CodeGenTypes::ConvertTypeForMemRecursive(QualType T) { const llvm::Type *ResultType = ConvertTypeRecursive(T); - if (ResultType->isInteger(1)) + if (ResultType->isIntegerTy(1)) return llvm::IntegerType::get(getLLVMContext(), (unsigned)Context.getTypeSize(T)); // FIXME: Should assert that the llvm type and AST type has the same size. @@ -99,7 +99,7 @@ const llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T) { const llvm::Type *R = ConvertType(T); // If this is a non-bool type, don't map it. - if (!R->isInteger(1)) + if (!R->isIntegerTy(1)) return R; // Otherwise, return an integer of the target-specified size. @@ -399,18 +399,6 @@ const llvm::Type *CodeGenTypes::ConvertNewType(QualType T) { /// enum. const llvm::Type *CodeGenTypes::ConvertTagDeclType(const TagDecl *TD) { - // FIXME. This may have to move to a better place. - if (const CXXRecordDecl *RD = dyn_cast(TD)) { - for (CXXRecordDecl::base_class_const_iterator i = RD->bases_begin(), - e = RD->bases_end(); i != e; ++i) { - if (!i->isVirtual()) { - const CXXRecordDecl *Base = - cast(i->getType()->getAs()->getDecl()); - ConvertTagDeclType(Base); - } - } - } - // TagDecl's are not necessarily unique, instead use the (clang) // type connected to the decl. const Type *Key = @@ -422,8 +410,8 @@ const llvm::Type *CodeGenTypes::ConvertTagDeclType(const TagDecl *TD) { if (TDTI != TagDeclTypes.end()) return TDTI->second; - // If this is still a forward definition, just define an opaque type to use - // for this tagged decl. + // If this is still a forward declaration, just define an opaque + // type to use for this tagged decl. if (!TD->isDefinition()) { llvm::Type *ResultType = llvm::OpaqueType::get(getLLVMContext()); TagDeclTypes.insert(std::make_pair(Key, ResultType)); @@ -446,6 +434,18 @@ const llvm::Type *CodeGenTypes::ConvertTagDeclType(const TagDecl *TD) { const RecordDecl *RD = cast(TD); + // Force conversion of non-virtual base classes recursively. + if (const CXXRecordDecl *RD = dyn_cast(TD)) { + for (CXXRecordDecl::base_class_const_iterator i = RD->bases_begin(), + e = RD->bases_end(); i != e; ++i) { + if (!i->isVirtual()) { + const CXXRecordDecl *Base = + cast(i->getType()->getAs()->getDecl()); + ConvertTagDeclType(Base); + } + } + } + // Layout fields. CGRecordLayout *Layout = CGRecordLayoutBuilder::ComputeLayout(*this, RD); diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index 7e342526e84b..87ba0bcfba1d 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -20,7 +20,7 @@ #include #include "CGCall.h" -#include "CGCXX.h" +#include "GlobalDecl.h" namespace llvm { class FunctionType; @@ -60,21 +60,24 @@ namespace CodeGen { /// LLVMType - The LLVMType corresponding to this record layout. const llvm::Type *LLVMType; - /// ContainsMemberPointer - Whether one of the fields in this record layout - /// is a member pointer, or a struct that contains a member pointer. - bool ContainsMemberPointer; + /// ContainsPointerToDataMember - Whether one of the fields in this record + /// layout is a pointer to data member, or a struct that contains pointer to + /// data member. + bool ContainsPointerToDataMember; public: - CGRecordLayout(const llvm::Type *T, bool ContainsMemberPointer) - : LLVMType(T), ContainsMemberPointer(ContainsMemberPointer) { } + CGRecordLayout(const llvm::Type *T, bool ContainsPointerToDataMember) + : LLVMType(T), ContainsPointerToDataMember(ContainsPointerToDataMember) { } /// getLLVMType - Return llvm type associated with this record. const llvm::Type *getLLVMType() const { return LLVMType; } - bool containsMemberPointer() const { - return ContainsMemberPointer; + /// containsPointerToDataMember - Whether this struct contains pointers to + /// data members. + bool containsPointerToDataMember() const { + return ContainsPointerToDataMember; } }; @@ -187,6 +190,8 @@ private: public: /// getFunctionInfo - Get the function info for the specified function decl. + const CGFunctionInfo &getFunctionInfo(GlobalDecl GD); + const CGFunctionInfo &getFunctionInfo(const FunctionDecl *FD); const CGFunctionInfo &getFunctionInfo(const CXXMethodDecl *MD); const CGFunctionInfo &getFunctionInfo(const ObjCMethodDecl *MD); @@ -195,6 +200,12 @@ public: const CGFunctionInfo &getFunctionInfo(const CXXDestructorDecl *D, CXXDtorType Type); + const CGFunctionInfo &getFunctionInfo(const CallArgList &Args, + const FunctionType *Ty) { + return getFunctionInfo(Ty->getResultType(), Args, + Ty->getCallConv(), Ty->getNoReturnAttr()); + } + // getFunctionInfo - Get the function info for a member function. const CGFunctionInfo &getFunctionInfo(const CXXRecordDecl *RD, const FunctionProtoType *FTP); @@ -204,13 +215,16 @@ public: /// specified, the "C" calling convention will be used. const CGFunctionInfo &getFunctionInfo(QualType ResTy, const CallArgList &Args, - unsigned CallingConvention = 0); + CallingConv CC, + bool NoReturn); const CGFunctionInfo &getFunctionInfo(QualType ResTy, const FunctionArgList &Args, - unsigned CallingConvention = 0); + CallingConv CC, + bool NoReturn); const CGFunctionInfo &getFunctionInfo(QualType RetTy, const llvm::SmallVector &ArgTys, - unsigned CallingConvention = 0); + CallingConv CC, + bool NoReturn); public: // These are internal details of CGT that shouldn't be used externally. /// addFieldInfo - Assign field number to field FD. diff --git a/lib/CodeGen/GlobalDecl.h b/lib/CodeGen/GlobalDecl.h index b054312a0185..b8a98d7c03b6 100644 --- a/lib/CodeGen/GlobalDecl.h +++ b/lib/CodeGen/GlobalDecl.h @@ -15,6 +15,10 @@ #ifndef CLANG_CODEGEN_GLOBALDECL_H #define CLANG_CODEGEN_GLOBALDECL_H +#include "CGCXX.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" + namespace clang { namespace CodeGen { diff --git a/lib/CodeGen/Makefile b/lib/CodeGen/Makefile index 108490b71d48..83cb36738709 100644 --- a/lib/CodeGen/Makefile +++ b/lib/CodeGen/Makefile @@ -15,7 +15,6 @@ LEVEL = ../../../.. LIBRARYNAME := clangCodeGen BUILD_ARCHIVE = 1 -CXXFLAGS = -fno-rtti CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include ifdef CLANG_VENDOR diff --git a/lib/CodeGen/Mangle.cpp b/lib/CodeGen/Mangle.cpp index d873cfec1bd7..a302225c7f77 100644 --- a/lib/CodeGen/Mangle.cpp +++ b/lib/CodeGen/Mangle.cpp @@ -26,6 +26,13 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Support/ErrorHandling.h" #include "CGVtable.h" + +#define MANGLE_CHECKER 0 + +#if MANGLE_CHECKER +#include +#endif + using namespace clang; using namespace CodeGen; @@ -44,6 +51,8 @@ static const CXXMethodDecl *getStructor(const CXXMethodDecl *MD) { return MD; } + +static const unsigned UnknownArity = ~0U; /// CXXNameMangler - Manage the mangling of a single name. class CXXNameMangler { @@ -55,6 +64,8 @@ class CXXNameMangler { llvm::DenseMap Substitutions; + ASTContext &getASTContext() const { return Context.getASTContext(); } + public: CXXNameMangler(MangleContext &C, llvm::SmallVectorImpl &Res) : Context(C), Out(Res), Structor(0), StructorType(0) { } @@ -65,6 +76,17 @@ public: const CXXDestructorDecl *D, CXXDtorType Type) : Context(C), Out(Res), Structor(getStructor(D)), StructorType(Type) { } +#if MANGLE_CHECKER + ~CXXNameMangler() { + if (Out.str()[0] == '\01') + return; + + int status = 0; + char *result = abi::__cxa_demangle(Out.str().str().c_str(), 0, 0, &status); + assert(status == 0 && "Could not demangle mangled name!"); + free(result); + } +#endif llvm::raw_svector_ostream &getStream() { return Out; } void mangle(const NamedDecl *D, llvm::StringRef Prefix = "_Z"); @@ -89,10 +111,19 @@ private: void addSubstitution(QualType T); void addSubstitution(uintptr_t Ptr); + void mangleUnresolvedScope(NestedNameSpecifier *Qualifier); + void mangleUnresolvedName(NestedNameSpecifier *Qualifier, + DeclarationName Name, + unsigned KnownArity = UnknownArity); + void mangleName(const TemplateDecl *TD, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs); - void mangleUnqualifiedName(const NamedDecl *ND); + void mangleUnqualifiedName(const NamedDecl *ND) { + mangleUnqualifiedName(ND, ND->getDeclName(), UnknownArity); + } + void mangleUnqualifiedName(const NamedDecl *ND, DeclarationName Name, + unsigned KnownArity); void mangleUnscopedName(const NamedDecl *ND); void mangleUnscopedTemplateName(const TemplateDecl *ND); void mangleSourceName(const IdentifierInfo *II); @@ -119,6 +150,11 @@ private: bool MangleReturnType); void mangleIntegerLiteral(QualType T, const llvm::APSInt &Value); + void mangleMemberExpr(const Expr *Base, bool IsArrow, + NestedNameSpecifier *Qualifier, + DeclarationName Name, + unsigned KnownArity); + void mangleCalledExpression(const Expr *E, unsigned KnownArity); void mangleExpression(const Expr *E); void mangleCXXCtorType(CXXCtorType T); void mangleCXXDtorType(CXXDtorType T); @@ -171,14 +207,14 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) { isInExternCSystemHeader(D->getLocation())) return false; - // Variables at global scope are not mangled. + // Variables at global scope with non-internal linkage are not mangled if (!FD) { const DeclContext *DC = D->getDeclContext(); // Check for extern variable declared locally. if (isa(DC) && D->hasLinkage()) while (!DC->isNamespace() && !DC->isTranslationUnit()) DC = DC->getParent(); - if (DC->isTranslationUnit()) + if (DC->isTranslationUnit() && D->getLinkage() != InternalLinkage) return false; } @@ -199,13 +235,10 @@ void CXXNameMangler::mangle(const NamedDecl *D, llvm::StringRef Prefix) { return; } - // ::= _Z [L] + // ::= _Z // ::= // ::= Out << Prefix; - if (D->getLinkage() == NamedDecl::InternalLinkage) // match gcc behavior - Out << 'L'; - if (const FunctionDecl *FD = dyn_cast(D)) mangleFunctionEncoding(FD); else @@ -367,6 +400,13 @@ void CXXNameMangler::mangleUnscopedTemplateName(const TemplateDecl *ND) { if (mangleSubstitution(ND)) return; + // ::= + if (const TemplateTemplateParmDecl *TTP + = dyn_cast(ND)) { + mangleTemplateParameter(TTP->getIndex()); + return; + } + mangleUnscopedName(ND->getTemplatedDecl()); addSubstitution(ND); } @@ -401,28 +441,69 @@ void CXXNameMangler::mangleCallOffset(const ThunkAdjustment &Adjustment) { Out << '_'; } -void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND) { +void CXXNameMangler::mangleUnresolvedScope(NestedNameSpecifier *Qualifier) { + Qualifier = getASTContext().getCanonicalNestedNameSpecifier(Qualifier); + switch (Qualifier->getKind()) { + case NestedNameSpecifier::Global: + // nothing + break; + case NestedNameSpecifier::Namespace: + mangleName(Qualifier->getAsNamespace()); + break; + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + mangleType(QualType(Qualifier->getAsType(), 0)); + break; + case NestedNameSpecifier::Identifier: + mangleUnresolvedScope(Qualifier->getPrefix()); + mangleSourceName(Qualifier->getAsIdentifier()); + break; + } +} + +/// Mangles a name which was not resolved to a specific entity. +void CXXNameMangler::mangleUnresolvedName(NestedNameSpecifier *Qualifier, + DeclarationName Name, + unsigned KnownArity) { + if (Qualifier) + mangleUnresolvedScope(Qualifier); + // FIXME: ambiguity of unqualified lookup with :: + + mangleUnqualifiedName(0, Name, KnownArity); +} + +void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, + DeclarationName Name, + unsigned KnownArity) { // ::= // ::= // ::= - DeclarationName Name = ND->getDeclName(); switch (Name.getNameKind()) { case DeclarationName::Identifier: { + if (const IdentifierInfo *II = Name.getAsIdentifierInfo()) { + // We must avoid conflicts between internally- and externally- + // linked variable declaration names in the same TU. + // This naming convention is the same as that followed by GCC, though it + // shouldn't actually matter. + if (ND && isa(ND) && ND->getLinkage() == InternalLinkage && + ND->getDeclContext()->isFileContext()) + Out << 'L'; + + mangleSourceName(II); + break; + } + + // Otherwise, an anonymous entity. We must have a declaration. + assert(ND && "mangling empty name without declaration"); + if (const NamespaceDecl *NS = dyn_cast(ND)) { if (NS->isAnonymousNamespace()) { - // This is how gcc mangles these names. It's apparently - // always '1', no matter how many different anonymous - // namespaces appear in a context. + // This is how gcc mangles these names. Out << "12_GLOBAL__N_1"; break; } } - if (const IdentifierInfo *II = Name.getAsIdentifierInfo()) { - mangleSourceName(II); - break; - } - // We must have an anonymous struct. const TagDecl *TD = cast(ND); if (const TypedefDecl *D = TD->getTypedefForAnonDecl()) { @@ -484,13 +565,18 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND) { break; case DeclarationName::CXXOperatorName: { - unsigned Arity = cast(ND)->getNumParams(); + unsigned Arity; + if (ND) { + Arity = cast(ND)->getNumParams(); - // If we have a C++ member function, we need to include the 'this' pointer. - // FIXME: This does not make sense for operators that are static, but their - // names stay the same regardless of the arity (operator new for instance). - if (isa(ND)) - Arity++; + // If we have a C++ member function, we need to include the 'this' pointer. + // FIXME: This does not make sense for operators that are static, but their + // names stay the same regardless of the arity (operator new for instance). + if (isa(ND)) + Arity++; + } else + Arity = KnownArity; + mangleOperatorName(Name.getCXXOverloadedOperator(), Arity); break; } @@ -596,15 +682,21 @@ void CXXNameMangler::mangleTemplatePrefix(const TemplateDecl *ND) { // ::=