r326238 - [CFG] NFC: Refactor ConstructionContext into a finite set of cases.
Artem Dergachev via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 27 12:03:35 PST 2018
Author: dergachev
Date: Tue Feb 27 12:03:35 2018
New Revision: 326238
URL: http://llvm.org/viewvc/llvm-project?rev=326238&view=rev
Log:
[CFG] NFC: Refactor ConstructionContext into a finite set of cases.
ConstructionContext is moved into a separate translation unit and is separated
into multiple classes. The "old" "raw" ConstructionContext is renamed into
ConstructionContextLayer - which corresponds to the idea of building the context
gradually layer-by-layer, but it isn't easy to use in the clients. Once
CXXConstructExpr is reached, layers that we've gathered so far are transformed
into the actual, "new-style" "flat" ConstructionContext, which is put into the
CFGConstructor element and has no layers whatsoever (until it actually needs
them, eg. aggregate initialization). The new-style ConstructionContext is
instead presented as a variety of sub-classes that enumerate different ways of
constructing an object in C++. There are 5 of these supported for now,
which is around a half of what needs to be supported.
The layer-by-layer buildup process is still a little bit weird, but it hides
all the weirdness in one place, that sounds like a good thing.
Differential Revision: https://reviews.llvm.org/D43533
Added:
cfe/trunk/include/clang/Analysis/ConstructionContext.h
cfe/trunk/lib/Analysis/ConstructionContext.cpp
Modified:
cfe/trunk/include/clang/Analysis/CFG.h
cfe/trunk/lib/Analysis/CFG.cpp
cfe/trunk/lib/Analysis/CMakeLists.txt
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
Modified: cfe/trunk/include/clang/Analysis/CFG.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/CFG.h?rev=326238&r1=326237&r2=326238&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/CFG.h (original)
+++ cfe/trunk/include/clang/Analysis/CFG.h Tue Feb 27 12:03:35 2018
@@ -38,6 +38,7 @@ namespace clang {
class ASTContext;
class BinaryOperator;
class CFG;
+class ConstructionContext;
class CXXBaseSpecifier;
class CXXBindTemporaryExpr;
class CXXCtorInitializer;
@@ -140,94 +141,6 @@ protected:
CFGStmt() = default;
};
-// This is bulky data for CFGConstructor which would not fit into the
-// CFGElement's room (pair of pointers). Contains the information
-// necessary to express what memory is being initialized by
-// the construction.
-class ConstructionContext {
-public:
- typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
-
-private:
- // The construction site - the statement that triggered the construction
- // for one of its parts. For instance, stack variable declaration statement
- // triggers construction of itself or its elements if it's an array,
- // new-expression triggers construction of the newly allocated object(s).
- TriggerTy Trigger;
-
- // Sometimes a single trigger is not enough to describe the construction site.
- // In this case we'd have a chain of "partial" construction contexts.
- // Some examples:
- // - A constructor within in an aggregate initializer list within a variable
- // would have a construction context of the initializer list with the parent
- // construction context of a variable.
- // - A constructor for a temporary that needs to be both destroyed
- // and materialized into an elidable copy constructor would have a
- // construction context of a CXXBindTemporaryExpr with the parent
- // construction context of a MaterializeTemproraryExpr.
- // Not all of these are currently supported.
- const ConstructionContext *Parent = nullptr;
-
- ConstructionContext() = default;
- ConstructionContext(TriggerTy Trigger, const ConstructionContext *Parent)
- : Trigger(Trigger), Parent(Parent) {}
-
-public:
- static const ConstructionContext *
- create(BumpVectorContext &C, TriggerTy Trigger,
- const ConstructionContext *Parent = nullptr) {
- ConstructionContext *CC = C.getAllocator().Allocate<ConstructionContext>();
- return new (CC) ConstructionContext(Trigger, Parent);
- }
-
- bool isNull() const { return Trigger.isNull(); }
-
- TriggerTy getTrigger() const { return Trigger; }
- const ConstructionContext *getParent() const { return Parent; }
-
- const Stmt *getTriggerStmt() const {
- return Trigger.dyn_cast<Stmt *>();
- }
-
- const CXXCtorInitializer *getTriggerInit() const {
- return Trigger.dyn_cast<CXXCtorInitializer *>();
- }
-
- const MaterializeTemporaryExpr *getMaterializedTemporary() const {
- // TODO: Be more careful to ensure that there's only one MTE around.
- for (const ConstructionContext *CC = this; CC; CC = CC->getParent()) {
- if (const auto *MTE = dyn_cast_or_null<MaterializeTemporaryExpr>(
- CC->getTriggerStmt())) {
- return MTE;
- }
- }
- return nullptr;
- }
-
- bool isSameAsPartialContext(const ConstructionContext *Other) const {
- assert(Other);
- return (Trigger == Other->Trigger);
- }
-
- // See if Other is a proper initial segment of this construction context
- // in terms of the parent chain - i.e. a few first parents coincide and
- // then the other context terminates but our context goes further - i.e.,
- // we are providing the same context that the other context provides,
- // and a bit more above that.
- bool isStrictlyMoreSpecificThan(const ConstructionContext *Other) const {
- const ConstructionContext *Self = this;
- while (true) {
- if (!Other)
- return Self;
- if (!Self || !Self->isSameAsPartialContext(Other))
- return false;
- Self = Self->getParent();
- Other = Other->getParent();
- }
- llvm_unreachable("The above loop can only be terminated via return!");
- }
-};
-
/// CFGConstructor - Represents C++ constructor call. Maintains information
/// necessary to figure out what memory is being initialized by the
/// constructor expression. For now this is only used by the analyzer's CFG.
@@ -235,7 +148,7 @@ class CFGConstructor : public CFGStmt {
public:
explicit CFGConstructor(CXXConstructExpr *CE, const ConstructionContext *C)
: CFGStmt(CE, Constructor) {
- assert(!C->isNull());
+ assert(C);
Data2.setPointer(const_cast<ConstructionContext *>(C));
}
@@ -247,22 +160,6 @@ public:
return cast<CXXConstructExpr>(getStmt())->getType();
}
- ConstructionContext::TriggerTy getTrigger() const {
- return getConstructionContext()->getTrigger();
- }
-
- const Stmt *getTriggerStmt() const {
- return getConstructionContext()->getTriggerStmt();
- }
-
- const CXXCtorInitializer *getTriggerInit() const {
- return getConstructionContext()->getTriggerInit();
- }
-
- const MaterializeTemporaryExpr *getMaterializedTemporary() const {
- return getConstructionContext()->getMaterializedTemporary();
- }
-
private:
friend class CFGElement;
Added: cfe/trunk/include/clang/Analysis/ConstructionContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/ConstructionContext.h?rev=326238&view=auto
==============================================================================
--- cfe/trunk/include/clang/Analysis/ConstructionContext.h (added)
+++ cfe/trunk/include/clang/Analysis/ConstructionContext.h Tue Feb 27 12:03:35 2018
@@ -0,0 +1,245 @@
+//===- ConstructionContext.h - CFG constructor information ------*- 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 ConstructionContext class and its sub-classes,
+// which represent various different ways of constructing C++ objects
+// with the additional information the users may want to know about
+// the constructor.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
+#define LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
+
+#include "clang/Analysis/Support/BumpVector.h"
+#include "clang/AST/ExprCXX.h"
+
+namespace clang {
+
+/// Construction context is a linked list of multiple layers. Layers are
+/// created gradually while traversing the AST, and layers that represent
+/// the outmost AST nodes are built first, while the node that immediately
+/// contains the constructor would be built last and capture the previous
+/// layers as its parents. Construction context captures the last layer
+/// (which has links to the previous layers) and classifies the seemingly
+/// arbitrary chain of layers into one of the possible ways of constructing
+/// an object in C++ for user-friendly experience.
+class ConstructionContextLayer {
+public:
+ typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
+
+private:
+ /// The construction site - the statement that triggered the construction
+ /// for one of its parts. For instance, stack variable declaration statement
+ /// triggers construction of itself or its elements if it's an array,
+ /// new-expression triggers construction of the newly allocated object(s).
+ TriggerTy Trigger;
+
+ /// Sometimes a single trigger is not enough to describe the construction
+ /// site. In this case we'd have a chain of "partial" construction context
+ /// layers.
+ /// Some examples:
+ /// - A constructor within in an aggregate initializer list within a variable
+ /// would have a construction context of the initializer list with
+ /// the parent construction context of a variable.
+ /// - A constructor for a temporary that needs to be both destroyed
+ /// and materialized into an elidable copy constructor would have a
+ /// construction context of a CXXBindTemporaryExpr with the parent
+ /// construction context of a MaterializeTemproraryExpr.
+ /// Not all of these are currently supported.
+ const ConstructionContextLayer *Parent = nullptr;
+
+ ConstructionContextLayer(TriggerTy Trigger,
+ const ConstructionContextLayer *Parent)
+ : Trigger(Trigger), Parent(Parent) {}
+
+public:
+ static const ConstructionContextLayer *
+ create(BumpVectorContext &C, TriggerTy Trigger,
+ const ConstructionContextLayer *Parent = nullptr);
+
+ const ConstructionContextLayer *getParent() const { return Parent; }
+ bool isLast() const { return !Parent; }
+
+ const Stmt *getTriggerStmt() const {
+ return Trigger.dyn_cast<Stmt *>();
+ }
+
+ const CXXCtorInitializer *getTriggerInit() const {
+ return Trigger.dyn_cast<CXXCtorInitializer *>();
+ }
+
+ /// Returns true if these layers are equal as individual layers, even if
+ /// their parents are different.
+ bool isSameLayer(const ConstructionContextLayer *Other) const {
+ assert(Other);
+ return (Trigger == Other->Trigger);
+ }
+
+ /// See if Other is a proper initial segment of this construction context
+ /// in terms of the parent chain - i.e. a few first parents coincide and
+ /// then the other context terminates but our context goes further - i.e.,
+ /// we are providing the same context that the other context provides,
+ /// and a bit more above that.
+ bool isStrictlyMoreSpecificThan(const ConstructionContextLayer *Other) const;
+};
+
+
+/// ConstructionContext's subclasses describe different ways of constructing
+/// an object in C++. The context re-captures the essential parent AST nodes
+/// of the CXXConstructExpr it is assigned to and presents these nodes
+/// through easy-to-understand accessor methods.
+class ConstructionContext {
+public:
+ enum Kind {
+ SimpleVariableKind,
+ ConstructorInitializerKind,
+ NewAllocatedObjectKind,
+ TemporaryObjectKind,
+ ReturnedValueKind
+ };
+
+protected:
+ Kind K;
+
+protected:
+ // Do not make public! These need to only be constructed
+ // via createFromLayers().
+ explicit ConstructionContext(Kind K) : K(K) {}
+
+public:
+
+ /// Consume the construction context layer, together with its parent layers,
+ /// and wrap it up into a complete construction context.
+ static const ConstructionContext *
+ createFromLayers(BumpVectorContext &C,
+ const ConstructionContextLayer *TopLayer);
+
+ Kind getKind() const { return K; }
+};
+
+/// Represents construction into a simple local variable, eg. T var(123);.
+class SimpleVariableConstructionContext : public ConstructionContext {
+ const DeclStmt *DS;
+
+public:
+ explicit SimpleVariableConstructionContext(const DeclStmt *DS)
+ : ConstructionContext(ConstructionContext::SimpleVariableKind), DS(DS) {
+ assert(DS);
+ }
+
+ const DeclStmt *getDeclStmt() const { return DS; }
+
+ static bool classof(const ConstructionContext *CC) {
+ return CC->getKind() == SimpleVariableKind;
+ }
+};
+
+/// Represents construction into a field or a base class within a bigger object
+/// via a constructor initializer, eg. T(): field(123) { ... }.
+class ConstructorInitializerConstructionContext : public ConstructionContext {
+ const CXXCtorInitializer *I;
+
+public:
+ explicit ConstructorInitializerConstructionContext(
+ const CXXCtorInitializer *I)
+ : ConstructionContext(ConstructionContext::ConstructorInitializerKind),
+ I(I) {
+ assert(I);
+ }
+
+ const CXXCtorInitializer *getCXXCtorInitializer() const { return I; }
+
+ static bool classof(const ConstructionContext *CC) {
+ return CC->getKind() == ConstructorInitializerKind;
+ }
+};
+
+/// Represents immediate initialization of memory allocated by operator new,
+/// eg. new T(123);.
+class NewAllocatedObjectConstructionContext : public ConstructionContext {
+ const CXXNewExpr *NE;
+
+public:
+ explicit NewAllocatedObjectConstructionContext(const CXXNewExpr *NE)
+ : ConstructionContext(ConstructionContext::NewAllocatedObjectKind),
+ NE(NE) {
+ assert(NE);
+ }
+
+ const CXXNewExpr *getCXXNewExpr() const { return NE; }
+
+ static bool classof(const ConstructionContext *CC) {
+ return CC->getKind() == NewAllocatedObjectKind;
+ }
+};
+
+/// Represents a temporary object, eg. T(123), that does not immediately cross
+/// function boundaries "by value"; constructors that construct function
+/// value-type arguments or values that are immediately returned from the
+/// function that returns a value receive separate construction context kinds.
+class TemporaryObjectConstructionContext : public ConstructionContext {
+ const CXXBindTemporaryExpr *BTE;
+ const MaterializeTemporaryExpr *MTE;
+
+public:
+ explicit TemporaryObjectConstructionContext(
+ const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE)
+ : ConstructionContext(ConstructionContext::TemporaryObjectKind),
+ BTE(BTE), MTE(MTE) {
+ // Both BTE and MTE can be null here, all combinations possible.
+ // Even though for now at least one should be non-null, we simply haven't
+ // implemented this case yet (this would be a temporary in the middle of
+ // nowhere that doesn't have a non-trivial destructor).
+ }
+
+ /// CXXBindTemporaryExpr here is non-null as long as the temporary has
+ /// a non-trivial destructor.
+ const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const {
+ return BTE;
+ }
+
+ /// MaterializeTemporaryExpr is non-null as long as the temporary is actually
+ /// used after construction, eg. by binding to a reference (lifetime
+ /// extension), accessing a field, calling a method, or passing it into
+ /// a function (an elidable copy or move constructor would be a common
+ /// example) by reference.
+ const MaterializeTemporaryExpr *getMaterializedTemporaryExpr() const {
+ return MTE;
+ }
+
+ static bool classof(const ConstructionContext *CC) {
+ return CC->getKind() == TemporaryObjectKind;
+ }
+};
+
+/// Represents a temporary object that is being immediately returned from a
+/// function by value, eg. return t; or return T(123);. In this case there is
+/// always going to be a constructor at the return site. However, the usual
+/// temporary-related bureaucracy (CXXBindTemporaryExpr,
+/// MaterializeTemporaryExpr) is normally located in the caller function's AST.
+class ReturnedValueConstructionContext : public ConstructionContext {
+ const ReturnStmt *RS;
+
+public:
+ explicit ReturnedValueConstructionContext(const ReturnStmt *RS)
+ : ConstructionContext(ConstructionContext::ReturnedValueKind), RS(RS) {
+ assert(RS);
+ }
+
+ const ReturnStmt *getReturnStmt() const { return RS; }
+
+ static bool classof(const ConstructionContext *CC) {
+ return CC->getKind() == ReturnedValueKind;
+ }
+};
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
Modified: cfe/trunk/lib/Analysis/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=326238&r1=326237&r2=326238&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp (original)
+++ cfe/trunk/lib/Analysis/CFG.cpp Tue Feb 27 12:03:35 2018
@@ -29,6 +29,7 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/Support/BumpVector.h"
+#include "clang/Analysis/ConstructionContext.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/ExceptionSpecificationType.h"
#include "clang/Basic/LLVM.h"
@@ -475,7 +476,7 @@ class CFGBuilder {
// Information about the currently visited C++ object construction site.
// This is set in the construction trigger and read when the constructor
// itself is being visited.
- llvm::DenseMap<CXXConstructExpr *, const ConstructionContext *>
+ llvm::DenseMap<CXXConstructExpr *, const ConstructionContextLayer *>
ConstructionContextMap;
bool badCFG = false;
@@ -652,18 +653,19 @@ private:
return Block;
}
- // Remember to apply \p CC when constructing the CFG element for \p CE.
- void consumeConstructionContext(const ConstructionContext *CC,
+ // Remember to apply the construction context based on the current \p Layer
+ // when constructing the CFG element for \p CE.
+ void consumeConstructionContext(const ConstructionContextLayer *Layer,
CXXConstructExpr *CE);
- // Scan the child statement \p Child to find the constructor that might
- // have been directly triggered by the current node, \p Trigger. If such
- // constructor has been found, set current construction context to point
- // to the trigger statement. The construction context will be unset once
- // it is consumed when the CFG building procedure processes the
- // construct-expression and adds the respective CFGConstructor element.
- void findConstructionContexts(const ConstructionContext *ContextSoFar,
+ // Scan \p Child statement to find constructors in it, while keeping in mind
+ // that its parent statement is providing a partial construction context
+ // described by \p Layer. If a constructor is found, it would be assigned
+ // the context based on the layer. If an additional construction context layer
+ // is found, the function recurses into that.
+ void findConstructionContexts(const ConstructionContextLayer *Layer,
Stmt *Child);
+
// Unset the construction context after consuming it. This is done immediately
// after adding the CFGConstructor element, so there's no need to
// do this manually in every Visit... function.
@@ -710,7 +712,11 @@ private:
void appendConstructor(CFGBlock *B, CXXConstructExpr *CE) {
if (BuildOpts.AddRichCXXConstructors) {
- if (const ConstructionContext *CC = ConstructionContextMap.lookup(CE)) {
+ if (const ConstructionContextLayer *Layer =
+ ConstructionContextMap.lookup(CE)) {
+ const ConstructionContext *CC =
+ ConstructionContext::createFromLayers(cfg->getBumpVectorContext(),
+ Layer);
B->appendConstructor(CE, CC, cfg->getBumpVectorContext());
cleanupConstructionContext(CE);
return;
@@ -1155,20 +1161,21 @@ static const VariableArrayType *FindVA(c
return nullptr;
}
-void CFGBuilder::consumeConstructionContext(const ConstructionContext *CC, CXXConstructExpr *CE) {
- if (const ConstructionContext *PreviousContext =
+void CFGBuilder::consumeConstructionContext(
+ const ConstructionContextLayer *Layer, CXXConstructExpr *CE) {
+ if (const ConstructionContextLayer *PreviouslyStoredLayer =
ConstructionContextMap.lookup(CE)) {
// We might have visited this child when we were finding construction
// contexts within its parents.
- assert(PreviousContext->isStrictlyMoreSpecificThan(CC) &&
+ assert(PreviouslyStoredLayer->isStrictlyMoreSpecificThan(Layer) &&
"Already within a different construction context!");
} else {
- ConstructionContextMap[CE] = CC;
+ ConstructionContextMap[CE] = Layer;
}
}
void CFGBuilder::findConstructionContexts(
- const ConstructionContext *ContextSoFar, Stmt *Child) {
+ const ConstructionContextLayer *Layer, Stmt *Child) {
if (!BuildOpts.AddRichCXXConstructors)
return;
@@ -1178,36 +1185,36 @@ void CFGBuilder::findConstructionContext
switch(Child->getStmtClass()) {
case Stmt::CXXConstructExprClass:
case Stmt::CXXTemporaryObjectExprClass: {
- consumeConstructionContext(ContextSoFar, cast<CXXConstructExpr>(Child));
+ consumeConstructionContext(Layer, cast<CXXConstructExpr>(Child));
break;
}
case Stmt::ExprWithCleanupsClass: {
auto *Cleanups = cast<ExprWithCleanups>(Child);
- findConstructionContexts(ContextSoFar, Cleanups->getSubExpr());
+ findConstructionContexts(Layer, Cleanups->getSubExpr());
break;
}
case Stmt::CXXFunctionalCastExprClass: {
auto *Cast = cast<CXXFunctionalCastExpr>(Child);
- findConstructionContexts(ContextSoFar, Cast->getSubExpr());
+ findConstructionContexts(Layer, Cast->getSubExpr());
break;
}
case Stmt::ImplicitCastExprClass: {
auto *Cast = cast<ImplicitCastExpr>(Child);
- findConstructionContexts(ContextSoFar, Cast->getSubExpr());
+ findConstructionContexts(Layer, Cast->getSubExpr());
break;
}
case Stmt::CXXBindTemporaryExprClass: {
auto *BTE = cast<CXXBindTemporaryExpr>(Child);
findConstructionContexts(
- ConstructionContext::create(cfg->getBumpVectorContext(), BTE,
- ContextSoFar),
+ ConstructionContextLayer::create(cfg->getBumpVectorContext(),
+ BTE, Layer),
BTE->getSubExpr());
break;
}
case Stmt::ConditionalOperatorClass: {
auto *CO = cast<ConditionalOperator>(Child);
- findConstructionContexts(ContextSoFar, CO->getLHS());
- findConstructionContexts(ContextSoFar, CO->getRHS());
+ findConstructionContexts(Layer, CO->getLHS());
+ findConstructionContexts(Layer, CO->getRHS());
break;
}
default:
@@ -1356,7 +1363,7 @@ CFGBlock *CFGBuilder::addInitializer(CXX
if (Init) {
findConstructionContexts(
- ConstructionContext::create(cfg->getBumpVectorContext(), I),
+ ConstructionContextLayer::create(cfg->getBumpVectorContext(), I),
Init);
if (HasTemporaries) {
@@ -2448,7 +2455,7 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(D
appendStmt(Block, DS);
findConstructionContexts(
- ConstructionContext::create(cfg->getBumpVectorContext(), DS),
+ ConstructionContextLayer::create(cfg->getBumpVectorContext(), DS),
Init);
// Keep track of the last non-null block, as 'Block' can be nulled out
@@ -2642,7 +2649,7 @@ CFGBlock *CFGBuilder::VisitReturnStmt(Re
addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R);
findConstructionContexts(
- ConstructionContext::create(cfg->getBumpVectorContext(), R),
+ ConstructionContextLayer::create(cfg->getBumpVectorContext(), R),
R->getRetValue());
// If the one of the destructors does not return, we already have the Exit
@@ -3015,7 +3022,7 @@ CFGBlock *
CFGBuilder::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE,
AddStmtChoice asc) {
findConstructionContexts(
- ConstructionContext::create(cfg->getBumpVectorContext(), MTE),
+ ConstructionContextLayer::create(cfg->getBumpVectorContext(), MTE),
MTE->getTemporary());
return VisitStmt(MTE, asc);
@@ -4002,7 +4009,7 @@ CFGBlock *CFGBuilder::VisitCXXBindTempor
appendStmt(Block, E);
findConstructionContexts(
- ConstructionContext::create(cfg->getBumpVectorContext(), E),
+ ConstructionContextLayer::create(cfg->getBumpVectorContext(), E),
E->getSubExpr());
// We do not want to propagate the AlwaysAdd property.
@@ -4025,7 +4032,7 @@ CFGBlock *CFGBuilder::VisitCXXNewExpr(CX
appendStmt(Block, NE);
findConstructionContexts(
- ConstructionContext::create(cfg->getBumpVectorContext(), NE),
+ ConstructionContextLayer::create(cfg->getBumpVectorContext(), NE),
const_cast<CXXConstructExpr *>(NE->getConstructExpr()));
if (NE->getInitializer())
@@ -4752,19 +4759,44 @@ static void print_elem(raw_ostream &OS,
} else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) {
OS << " (CXXConstructExpr, ";
if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) {
- // TODO: Refactor into ConstructionContext::print().
- if (const Stmt *S = CE->getTriggerStmt())
- Helper.handledStmt(const_cast<Stmt *>(S), OS);
- else if (const CXXCtorInitializer *I = CE->getTriggerInit())
- print_initializer(OS, Helper, I);
- else
- llvm_unreachable("Unexpected trigger kind!");
- OS << ", ";
- if (const Stmt *S = CE->getMaterializedTemporary()) {
- if (S != CE->getTriggerStmt()) {
- Helper.handledStmt(const_cast<Stmt *>(S), OS);
- OS << ", ";
- }
+ const ConstructionContext *CC = CE->getConstructionContext();
+ const Stmt *S1 = nullptr, *S2 = nullptr;
+ switch (CC->getKind()) {
+ case ConstructionContext::ConstructorInitializerKind: {
+ const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+ print_initializer(OS, Helper, ICC->getCXXCtorInitializer());
+ OS << ", ";
+ break;
+ }
+ case ConstructionContext::SimpleVariableKind: {
+ const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
+ S1 = DSCC->getDeclStmt();
+ break;
+ }
+ case ConstructionContext::NewAllocatedObjectKind: {
+ const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
+ S1 = NECC->getCXXNewExpr();
+ break;
+ }
+ case ConstructionContext::ReturnedValueKind: {
+ const auto *RSCC = cast<ReturnedValueConstructionContext>(CC);
+ S1 = RSCC->getReturnStmt();
+ break;
+ }
+ case ConstructionContext::TemporaryObjectKind: {
+ const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
+ S1 = TOCC->getCXXBindTemporaryExpr();
+ S2 = TOCC->getMaterializedTemporaryExpr();
+ break;
+ }
+ }
+ if (S1) {
+ Helper.handledStmt(const_cast<Stmt *>(S1), OS);
+ OS << ", ";
+ }
+ if (S2) {
+ Helper.handledStmt(const_cast<Stmt *>(S2), OS);
+ OS << ", ";
}
}
OS << CCE->getType().getAsString() << ")";
Modified: cfe/trunk/lib/Analysis/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CMakeLists.txt?rev=326238&r1=326237&r2=326238&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CMakeLists.txt (original)
+++ cfe/trunk/lib/Analysis/CMakeLists.txt Tue Feb 27 12:03:35 2018
@@ -11,6 +11,7 @@ add_clang_library(clangAnalysis
CallGraph.cpp
CloneDetection.cpp
CocoaConventions.cpp
+ ConstructionContext.cpp
Consumed.cpp
CodeInjector.cpp
Dominators.cpp
Added: cfe/trunk/lib/Analysis/ConstructionContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ConstructionContext.cpp?rev=326238&view=auto
==============================================================================
--- cfe/trunk/lib/Analysis/ConstructionContext.cpp (added)
+++ cfe/trunk/lib/Analysis/ConstructionContext.cpp Tue Feb 27 12:03:35 2018
@@ -0,0 +1,92 @@
+//===- ConstructionContext.cpp - CFG constructor information --------------===//
+//
+// 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 ConstructionContext class and its sub-classes,
+// which represent various different ways of constructing C++ objects
+// with the additional information the users may want to know about
+// the constructor.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/ConstructionContext.h"
+
+using namespace clang;
+
+const ConstructionContextLayer *
+ConstructionContextLayer::create(BumpVectorContext &C, TriggerTy Trigger,
+ const ConstructionContextLayer *Parent) {
+ ConstructionContextLayer *CC =
+ C.getAllocator().Allocate<ConstructionContextLayer>();
+ return new (CC) ConstructionContextLayer(Trigger, Parent);
+}
+
+bool ConstructionContextLayer::isStrictlyMoreSpecificThan(
+ const ConstructionContextLayer *Other) const {
+ const ConstructionContextLayer *Self = this;
+ while (true) {
+ if (!Other)
+ return Self;
+ if (!Self || !Self->isSameLayer(Other))
+ return false;
+ Self = Self->getParent();
+ Other = Other->getParent();
+ }
+ llvm_unreachable("The above loop can only be terminated via return!");
+}
+
+const ConstructionContext *ConstructionContext::createFromLayers(
+ BumpVectorContext &C, const ConstructionContextLayer *TopLayer) {
+ // Before this point all we've had was a stockpile of arbitrary layers.
+ // Now validate that it is shaped as one of the finite amount of expected
+ // patterns.
+ if (const Stmt *S = TopLayer->getTriggerStmt()) {
+ if (const auto *DS = dyn_cast<DeclStmt>(S)) {
+ assert(TopLayer->isLast());
+ auto *CC =
+ C.getAllocator().Allocate<SimpleVariableConstructionContext>();
+ return new (CC) SimpleVariableConstructionContext(DS);
+ } else if (const auto *NE = dyn_cast<CXXNewExpr>(S)) {
+ assert(TopLayer->isLast());
+ auto *CC =
+ C.getAllocator().Allocate<NewAllocatedObjectConstructionContext>();
+ return new (CC) NewAllocatedObjectConstructionContext(NE);
+ } else if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(S)) {
+ const MaterializeTemporaryExpr *MTE = nullptr;
+ assert(BTE->getType().getCanonicalType()
+ ->getAsCXXRecordDecl()->hasNonTrivialDestructor());
+ // 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());
+ MTE = cast<MaterializeTemporaryExpr>(ParentLayer->getTriggerStmt());
+ }
+ auto *CC =
+ C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
+ return new (CC) TemporaryObjectConstructionContext(BTE, MTE);
+ } else if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) {
+ assert(MTE->getType().getCanonicalType()
+ ->getAsCXXRecordDecl()->hasTrivialDestructor());
+ assert(TopLayer->isLast());
+ auto *CC =
+ C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
+ return new (CC) TemporaryObjectConstructionContext(nullptr, MTE);
+ } else if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
+ assert(TopLayer->isLast());
+ auto *CC =
+ C.getAllocator().Allocate<ReturnedValueConstructionContext>();
+ return new (CC) ReturnedValueConstructionContext(RS);
+ }
+ } else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {
+ assert(TopLayer->isLast());
+ auto *CC =
+ C.getAllocator().Allocate<ConstructorInitializerConstructionContext>();
+ return new (CC) ConstructorInitializerConstructionContext(I);
+ }
+ llvm_unreachable("Unexpected construction context!");
+}
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp?rev=326238&r1=326237&r2=326238&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp Tue Feb 27 12:03:35 2018
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/Analysis/ConstructionContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/ParentMap.h"
@@ -111,47 +112,20 @@ ExprEngine::getRegionForConstructedObjec
// See if we're constructing an existing region by looking at the
// current construction context.
if (CC) {
- if (const Stmt *TriggerStmt = CC->getTriggerStmt()) {
- if (const CXXNewExpr *CNE = dyn_cast<CXXNewExpr>(TriggerStmt)) {
- if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
- // TODO: Detect when the allocator returns a null pointer.
- // Constructor shall not be called in this case.
- if (const SubRegion *MR = dyn_cast_or_null<SubRegion>(
- getCXXNewAllocatorValue(State, CNE, LCtx).getAsRegion())) {
- if (CNE->isArray()) {
- // TODO: In fact, we need to call the constructor for every
- // allocated element, not just the first one!
- CallOpts.IsArrayCtorOrDtor = true;
- return getStoreManager().GetElementZeroRegion(
- MR, CNE->getType()->getPointeeType());
- }
- return MR;
- }
- }
- } else if (auto *DS = dyn_cast<DeclStmt>(TriggerStmt)) {
- const auto *Var = cast<VarDecl>(DS->getSingleDecl());
- SVal LValue = State->getLValue(Var, LCtx);
- QualType Ty = Var->getType();
- LValue = makeZeroElementRegion(State, LValue, Ty,
- CallOpts.IsArrayCtorOrDtor);
- return LValue.getAsRegion();
- } else if (isa<ReturnStmt>(TriggerStmt)) {
- // TODO: We should construct into a CXXBindTemporaryExpr or a
- // MaterializeTemporaryExpr around the call-expression on the previous
- // stack frame. Currently we re-bind the temporary to the correct region
- // later, but that's not semantically correct. This of course does not
- // apply when we're in the top frame. But if we are in an inlined
- // function, we should be able to take the call-site CFG element,
- // and it should contain (but right now it wouldn't) some sort of
- // construction context that'd give us the right temporary expression.
- CallOpts.IsTemporaryCtorOrDtor = true;
- return MRMgr.getCXXTempObjectRegion(CE, LCtx);
- } else if (isa<CXXBindTemporaryExpr>(TriggerStmt)) {
- CallOpts.IsTemporaryCtorOrDtor = true;
- return MRMgr.getCXXTempObjectRegion(CE, LCtx);
- }
- // TODO: Consider other directly initialized elements.
- } else if (const CXXCtorInitializer *Init = CC->getTriggerInit()) {
+ switch (CC->getKind()) {
+ case ConstructionContext::SimpleVariableKind: {
+ const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
+ const auto *DS = DSCC->getDeclStmt();
+ const auto *Var = cast<VarDecl>(DS->getSingleDecl());
+ SVal LValue = State->getLValue(Var, LCtx);
+ QualType Ty = Var->getType();
+ LValue =
+ makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
+ return LValue.getAsRegion();
+ }
+ case ConstructionContext::ConstructorInitializerKind: {
+ const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+ const auto *Init = ICC->getCXXCtorInitializer();
assert(Init->isAnyMemberInitializer());
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
Loc ThisPtr =
@@ -173,10 +147,45 @@ ExprEngine::getRegionForConstructedObjec
CallOpts.IsArrayCtorOrDtor);
return FieldVal.getAsRegion();
}
-
- // FIXME: This will eventually need to handle new-expressions as well.
- // Don't forget to update the pre-constructor initialization code in
- // ExprEngine::VisitCXXConstructExpr.
+ case ConstructionContext::NewAllocatedObjectKind: {
+ if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
+ const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
+ const auto *NE = NECC->getCXXNewExpr();
+ // TODO: Detect when the allocator returns a null pointer.
+ // Constructor shall not be called in this case.
+ if (const SubRegion *MR = dyn_cast_or_null<SubRegion>(
+ getCXXNewAllocatorValue(State, NE, LCtx).getAsRegion())) {
+ if (NE->isArray()) {
+ // TODO: In fact, we need to call the constructor for every
+ // allocated element, not just the first one!
+ CallOpts.IsArrayCtorOrDtor = true;
+ return getStoreManager().GetElementZeroRegion(
+ MR, NE->getType()->getPointeeType());
+ }
+ return MR;
+ }
+ }
+ break;
+ }
+ case ConstructionContext::TemporaryObjectKind: {
+ // TODO: Support temporaries lifetime-extended via static references.
+ // They'd need a getCXXStaticTempObjectRegion().
+ CallOpts.IsTemporaryCtorOrDtor = true;
+ return MRMgr.getCXXTempObjectRegion(CE, LCtx);
+ }
+ case ConstructionContext::ReturnedValueKind: {
+ // TODO: We should construct into a CXXBindTemporaryExpr or a
+ // MaterializeTemporaryExpr around the call-expression on the previous
+ // stack frame. Currently we re-bind the temporary to the correct region
+ // later, but that's not semantically correct. This of course does not
+ // apply when we're in the top frame. But if we are in an inlined
+ // function, we should be able to take the call-site CFG element,
+ // and it should contain (but right now it wouldn't) some sort of
+ // construction context that'd give us the right temporary expression.
+ CallOpts.IsTemporaryCtorOrDtor = true;
+ return MRMgr.getCXXTempObjectRegion(CE, LCtx);
+ }
+ }
}
// If we couldn't find an existing region to construct into, assume we're
// constructing a temporary. Notify the caller of our failure.
@@ -227,6 +236,7 @@ void ExprEngine::VisitCXXConstructExpr(c
EvalCallOptions CallOpts;
auto C = getCurrentCFGElement().getAs<CFGConstructor>();
+ assert(C || getCurrentCFGElement().getAs<CFGStmt>());
const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
const CXXBindTemporaryExpr *BTE = nullptr;
@@ -235,15 +245,27 @@ void ExprEngine::VisitCXXConstructExpr(c
switch (CE->getConstructionKind()) {
case CXXConstructExpr::CK_Complete: {
Target = getRegionForConstructedObject(CE, Pred, CC, CallOpts);
- if (CC && AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG() &&
- !CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion &&
- CallOpts.IsTemporaryCtorOrDtor) {
- MTE = CC->getMaterializedTemporary();
- if (!MTE || MTE->getStorageDuration() == SD_FullExpression) {
- // If the temporary is lifetime-extended, don't save the BTE,
- // because we don't need a temporary destructor, but an automatic
- // destructor. The cast may fail because it may as well be a ReturnStmt.
- BTE = dyn_cast<CXXBindTemporaryExpr>(CC->getTriggerStmt());
+
+ // In case of temporary object construction, extract data necessary for
+ // destruction and lifetime extension.
+ if (const auto *TCC =
+ dyn_cast_or_null<TemporaryObjectConstructionContext>(CC)) {
+ assert(CallOpts.IsTemporaryCtorOrDtor);
+ assert(!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion);
+ if (AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) {
+ BTE = TCC->getCXXBindTemporaryExpr();
+ MTE = TCC->getMaterializedTemporaryExpr();
+ if (!BTE) {
+ // FIXME: lifetime extension for temporaries without destructors
+ // is not implemented yet.
+ MTE = nullptr;
+ }
+ if (MTE && MTE->getStorageDuration() != SD_FullExpression) {
+ // If the temporary is lifetime-extended, don't save the BTE,
+ // because we don't need a temporary destructor, but an automatic
+ // destructor.
+ BTE = nullptr;
+ }
}
}
break;
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp?rev=326238&r1=326237&r2=326238&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Tue Feb 27 12:03:35 2018
@@ -16,6 +16,7 @@
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
+#include "clang/Analysis/ConstructionContext.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "llvm/ADT/SmallSet.h"
@@ -635,10 +636,11 @@ ExprEngine::mayInlineCallKind(const Call
const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
- auto CC = getCurrentCFGElement().getAs<CFGConstructor>();
- const Stmt *ParentExpr = CC ? CC->getTriggerStmt() : nullptr;
+ auto CCE = getCurrentCFGElement().getAs<CFGConstructor>();
+ const ConstructionContext *CC = CCE ? CCE->getConstructionContext()
+ : nullptr;
- if (ParentExpr && isa<CXXNewExpr>(ParentExpr) &&
+ if (CC && isa<NewAllocatedObjectConstructionContext>(CC) &&
!Opts.mayInlineCXXAllocator())
return CIP_DisallowedOnce;
More information about the cfe-commits
mailing list