summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Analysis/AnalysisDeclContext.cpp4
-rw-r--r--clang/lib/Analysis/CFG.cpp77
-rw-r--r--clang/lib/Analysis/ConstructionContext.cpp56
-rw-r--r--clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp1
-rw-r--r--clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp6
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();
OpenPOWER on IntegriCloud