diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Analysis/AnalysisDeclContext.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Analysis/CFG.cpp | 77 | ||||
-rw-r--r-- | clang/lib/Analysis/ConstructionContext.cpp | 56 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp | 1 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp | 6 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 6 |
6 files changed, 119 insertions, 31 deletions
diff --git a/clang/lib/Analysis/AnalysisDeclContext.cpp b/clang/lib/Analysis/AnalysisDeclContext.cpp index e746e8dfa53..486fffbe129 100644 --- a/clang/lib/Analysis/AnalysisDeclContext.cpp +++ b/clang/lib/Analysis/AnalysisDeclContext.cpp @@ -71,7 +71,8 @@ AnalysisDeclContextManager::AnalysisDeclContextManager( bool addInitializers, bool addTemporaryDtors, bool addLifetime, bool addLoopExit, bool addScopes, bool synthesizeBodies, bool addStaticInitBranch, bool addCXXNewAllocator, - bool addRichCXXConstructors, CodeInjector *injector) + bool addRichCXXConstructors, bool markElidedCXXConstructors, + CodeInjector *injector) : Injector(injector), FunctionBodyFarm(ASTCtx, injector), SynthesizeBodies(synthesizeBodies) { cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG; @@ -84,6 +85,7 @@ AnalysisDeclContextManager::AnalysisDeclContextManager( cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator; cfgBuildOptions.AddRichCXXConstructors = addRichCXXConstructors; + cfgBuildOptions.MarkElidedCXXConstructors = markElidedCXXConstructors; } void AnalysisDeclContextManager::clear() { Contexts.clear(); } diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index b02fbe10712..62803f4ba56 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -1252,10 +1252,22 @@ void CFGBuilder::findConstructionContexts( if (!Child) return; + auto withExtraLayer = [this, Layer](Stmt *S) { + return ConstructionContextLayer::create(cfg->getBumpVectorContext(), S, + Layer); + }; + switch(Child->getStmtClass()) { case Stmt::CXXConstructExprClass: case Stmt::CXXTemporaryObjectExprClass: { - consumeConstructionContext(Layer, cast<CXXConstructExpr>(Child)); + // Support pre-C++17 copy elision AST. + auto *CE = cast<CXXConstructExpr>(Child); + if (BuildOpts.MarkElidedCXXConstructors && CE->isElidable()) { + assert(CE->getNumArgs() == 1); + findConstructionContexts(withExtraLayer(CE), CE->getArg(0)); + } + + consumeConstructionContext(Layer, CE); break; } // FIXME: This, like the main visit, doesn't support CUDAKernelCallExpr. @@ -1294,10 +1306,21 @@ void CFGBuilder::findConstructionContexts( } case Stmt::CXXBindTemporaryExprClass: { auto *BTE = cast<CXXBindTemporaryExpr>(Child); - findConstructionContexts( - ConstructionContextLayer::create(cfg->getBumpVectorContext(), - BTE, Layer), - BTE->getSubExpr()); + findConstructionContexts(withExtraLayer(BTE), BTE->getSubExpr()); + break; + } + case Stmt::MaterializeTemporaryExprClass: { + // Normally we don't want to search in MaterializeTemporaryExpr because + // it indicates the beginning of a temporary object construction context, + // so it shouldn't be found in the middle. However, if it is the beginning + // of an elidable copy or move construction context, we need to include it. + if (const auto *CE = + dyn_cast_or_null<CXXConstructExpr>(Layer->getTriggerStmt())) { + if (CE->isElidable()) { + auto *MTE = cast<MaterializeTemporaryExpr>(Child); + findConstructionContexts(withExtraLayer(MTE), MTE->GetTemporaryExpr()); + } + } break; } case Stmt::ConditionalOperatorClass: { @@ -4931,7 +4954,7 @@ static void print_initializer(raw_ostream &OS, StmtPrinterHelper &Helper, static void print_construction_context(raw_ostream &OS, StmtPrinterHelper &Helper, const ConstructionContext *CC) { - const Stmt *S1 = nullptr, *S2 = nullptr; + SmallVector<const Stmt *, 3> Stmts; switch (CC->getKind()) { case ConstructionContext::SimpleConstructorInitializerKind: { OS << ", "; @@ -4944,52 +4967,56 @@ static void print_construction_context(raw_ostream &OS, const auto *CICC = cast<CXX17ElidedCopyConstructorInitializerConstructionContext>(CC); print_initializer(OS, Helper, CICC->getCXXCtorInitializer()); - S2 = CICC->getCXXBindTemporaryExpr(); + Stmts.push_back(CICC->getCXXBindTemporaryExpr()); break; } case ConstructionContext::SimpleVariableKind: { const auto *SDSCC = cast<SimpleVariableConstructionContext>(CC); - S1 = SDSCC->getDeclStmt(); + Stmts.push_back(SDSCC->getDeclStmt()); break; } case ConstructionContext::CXX17ElidedCopyVariableKind: { const auto *CDSCC = cast<CXX17ElidedCopyVariableConstructionContext>(CC); - S1 = CDSCC->getDeclStmt(); - S2 = CDSCC->getCXXBindTemporaryExpr(); + Stmts.push_back(CDSCC->getDeclStmt()); + Stmts.push_back(CDSCC->getCXXBindTemporaryExpr()); break; } case ConstructionContext::NewAllocatedObjectKind: { const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC); - S1 = NECC->getCXXNewExpr(); + Stmts.push_back(NECC->getCXXNewExpr()); break; } case ConstructionContext::SimpleReturnedValueKind: { const auto *RSCC = cast<SimpleReturnedValueConstructionContext>(CC); - S1 = RSCC->getReturnStmt(); + Stmts.push_back(RSCC->getReturnStmt()); break; } case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { const auto *RSCC = cast<CXX17ElidedCopyReturnedValueConstructionContext>(CC); - S1 = RSCC->getReturnStmt(); - S2 = RSCC->getCXXBindTemporaryExpr(); + Stmts.push_back(RSCC->getReturnStmt()); + Stmts.push_back(RSCC->getCXXBindTemporaryExpr()); break; } - case ConstructionContext::TemporaryObjectKind: { - const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC); - S1 = TOCC->getCXXBindTemporaryExpr(); - S2 = TOCC->getMaterializedTemporaryExpr(); + case ConstructionContext::SimpleTemporaryObjectKind: { + const auto *TOCC = cast<SimpleTemporaryObjectConstructionContext>(CC); + Stmts.push_back(TOCC->getCXXBindTemporaryExpr()); + Stmts.push_back(TOCC->getMaterializedTemporaryExpr()); break; } + case ConstructionContext::ElidedTemporaryObjectKind: { + const auto *TOCC = cast<ElidedTemporaryObjectConstructionContext>(CC); + Stmts.push_back(TOCC->getCXXBindTemporaryExpr()); + Stmts.push_back(TOCC->getMaterializedTemporaryExpr()); + Stmts.push_back(TOCC->getConstructorAfterElision()); + break; } - if (S1) { - OS << ", "; - Helper.handledStmt(const_cast<Stmt *>(S1), OS); - } - if (S2) { - OS << ", "; - Helper.handledStmt(const_cast<Stmt *>(S2), OS); } + for (auto I: Stmts) + if (I) { + OS << ", "; + Helper.handledStmt(const_cast<Stmt *>(I), OS); + } } static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, diff --git a/clang/lib/Analysis/ConstructionContext.cpp b/clang/lib/Analysis/ConstructionContext.cpp index 9db6f214bee..de003753367 100644 --- a/clang/lib/Analysis/ConstructionContext.cpp +++ b/clang/lib/Analysis/ConstructionContext.cpp @@ -61,7 +61,6 @@ const ConstructionContext *ConstructionContext::createFromLayers( // For temporaries with destructors, there may or may not be // lifetime extension on the parent layer. if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) { - assert(ParentLayer->isLast()); // C++17 *requires* elision of the constructor at the return site // and at variable/member initialization site, while previous standards // were allowing an optional elidable constructor. @@ -77,8 +76,33 @@ const ConstructionContext *ConstructionContext::createFromLayers( // both destruction and materialization info attached to it in the AST. if ((MTE = dyn_cast<MaterializeTemporaryExpr>( ParentLayer->getTriggerStmt()))) { - return create<TemporaryObjectConstructionContext>(C, BTE, MTE); + // Handle pre-C++17 copy and move elision. + const CXXConstructExpr *ElidedCE = nullptr; + const ConstructionContext *ElidedCC = nullptr; + if (const ConstructionContextLayer *ElidedLayer = + ParentLayer->getParent()) { + ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt()); + assert(ElidedCE->isElidable()); + // We're creating a construction context that might have already + // been created elsewhere. Maybe we should unique our construction + // contexts. That's what we often do, but in this case it's unlikely + // to bring any benefits. + ElidedCC = createFromLayers(C, ElidedLayer->getParent()); + if (!ElidedCC) { + // We may fail to create the elided construction context. + // In this case, skip copy elision entirely. + return create<SimpleTemporaryObjectConstructionContext>(C, BTE, + MTE); + } else { + return create<ElidedTemporaryObjectConstructionContext>( + C, BTE, MTE, ElidedCE, ElidedCC); + } + } + assert(ParentLayer->isLast()); + return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE); } + assert(ParentLayer->isLast()); + // This is a constructor into a function argument. Not implemented yet. if (isa<CallExpr>(ParentLayer->getTriggerStmt())) return nullptr; @@ -99,7 +123,11 @@ const ConstructionContext *ConstructionContext::createFromLayers( llvm_unreachable("Unexpected construction context with destructor!"); } // A temporary object that doesn't require materialization. - return create<TemporaryObjectConstructionContext>(C, BTE, /*MTE=*/nullptr); + // In particular, it shouldn't require copy elision, because + // copy/move constructors take a reference, which requires + // materialization to obtain the glvalue. + return create<SimpleTemporaryObjectConstructionContext>(C, BTE, + /*MTE=*/nullptr); } if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) { // If the object requires destruction and is not lifetime-extended, @@ -110,8 +138,28 @@ const ConstructionContext *ConstructionContext::createFromLayers( MTE->getStorageDuration() != SD_FullExpression)) return nullptr; + // Handle pre-C++17 copy and move elision. + const CXXConstructExpr *ElidedCE = nullptr; + const ConstructionContext *ElidedCC = nullptr; + if (const ConstructionContextLayer *ElidedLayer = TopLayer->getParent()) { + ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt()); + assert(ElidedCE->isElidable()); + // We're creating a construction context that might have already + // been created elsewhere. Maybe we should unique our construction + // contexts. That's what we often do, but in this case it's unlikely + // to bring any benefits. + ElidedCC = createFromLayers(C, ElidedLayer->getParent()); + if (!ElidedCC) { + // We may fail to create the elided construction context. + // In this case, skip copy elision entirely. + return create<SimpleTemporaryObjectConstructionContext>(C, nullptr, + MTE); + } + return create<ElidedTemporaryObjectConstructionContext>( + C, nullptr, MTE, ElidedCE, ElidedCC); + } assert(TopLayer->isLast()); - return create<TemporaryObjectConstructionContext>(C, nullptr, MTE); + return create<SimpleTemporaryObjectConstructionContext>(C, nullptr, MTE); } if (const auto *RS = dyn_cast<ReturnStmt>(S)) { assert(TopLayer->isLast()); diff --git a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp index af107ab224f..dc0d3ec8493 100644 --- a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -31,6 +31,7 @@ AnalysisManager::AnalysisManager( Options.shouldConditionalizeStaticInitializers(), /*addCXXNewAllocator=*/true, Options.includeRichConstructorsInCFG(), + Options.shouldElideConstructors(), injector), Ctx(ASTCtx), Diags(diags), LangOpts(lang), PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index bddd4435a75..6fa5fec5288 100644 --- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -321,6 +321,12 @@ bool AnalyzerOptions::shouldSerializeStats() { /* Default = */ false); } +bool AnalyzerOptions::shouldElideConstructors() { + return getBooleanOption(ElideConstructors, + "elide-constructors", + /* Default = */ true); +} + int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal, const CheckerBase *C, bool SearchInParents) { diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index f85bfc6e492..497e1fa5560 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -209,7 +209,11 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( } llvm_unreachable("Unhandled return value construction context!"); } - case ConstructionContext::TemporaryObjectKind: { + case ConstructionContext::ElidedTemporaryObjectKind: + assert(AMgr.getAnalyzerOptions().shouldElideConstructors()); + // FALL-THROUGH + case ConstructionContext::SimpleTemporaryObjectKind: { + // TODO: Copy elision implementation goes here. const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); |