aboutsummaryrefslogtreecommitdiff
path: root/include/clang/Analysis/ConstructionContext.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/clang/Analysis/ConstructionContext.h')
-rw-r--r--include/clang/Analysis/ConstructionContext.h265
1 files changed, 222 insertions, 43 deletions
diff --git a/include/clang/Analysis/ConstructionContext.h b/include/clang/Analysis/ConstructionContext.h
index 40cb0e7e5dda..aee67865df25 100644
--- a/include/clang/Analysis/ConstructionContext.h
+++ b/include/clang/Analysis/ConstructionContext.h
@@ -22,66 +22,201 @@
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 {
+/// Represents a single point (AST node) in the program that requires attention
+/// during construction of an object. ConstructionContext would be represented
+/// as a list of such items.
+class ConstructionContextItem {
public:
- typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
+ enum ItemKind {
+ VariableKind,
+ NewAllocatorKind,
+ ReturnKind,
+ MaterializationKind,
+ TemporaryDestructorKind,
+ ElidedDestructorKind,
+ ElidableConstructorKind,
+ ArgumentKind,
+ STATEMENT_WITH_INDEX_KIND_BEGIN=ArgumentKind,
+ STATEMENT_WITH_INDEX_KIND_END=ArgumentKind,
+ STATEMENT_KIND_BEGIN = VariableKind,
+ STATEMENT_KIND_END = ArgumentKind,
+ InitializerKind,
+ INITIALIZER_KIND_BEGIN=InitializerKind,
+ INITIALIZER_KIND_END=InitializerKind
+ };
+
+ LLVM_DUMP_METHOD static StringRef getKindAsString(ItemKind K) {
+ switch (K) {
+ case VariableKind: return "construct into local variable";
+ case NewAllocatorKind: return "construct into new-allocator";
+ case ReturnKind: return "construct into return address";
+ case MaterializationKind: return "materialize temporary";
+ case TemporaryDestructorKind: return "destroy temporary";
+ case ElidedDestructorKind: return "elide destructor";
+ case ElidableConstructorKind: return "elide constructor";
+ case ArgumentKind: return "construct into argument";
+ case InitializerKind: return "construct into member variable";
+ };
+ llvm_unreachable("Unknown ItemKind");
+ }
private:
+ const void *const Data;
+ const ItemKind Kind;
+ const unsigned Index = 0;
+
+ bool hasStatement() const {
+ return Kind >= STATEMENT_KIND_BEGIN &&
+ Kind <= STATEMENT_KIND_END;
+ }
+
+ bool hasIndex() const {
+ return Kind >= STATEMENT_WITH_INDEX_KIND_BEGIN &&
+ Kind >= STATEMENT_WITH_INDEX_KIND_END;
+ }
+
+ bool hasInitializer() const {
+ return Kind >= INITIALIZER_KIND_BEGIN &&
+ Kind <= INITIALIZER_KIND_END;
+ }
+
+public:
+ // ConstructionContextItem should be simple enough so that it was easy to
+ // re-construct it from the AST node it captures. For that reason we provide
+ // simple implicit conversions from all sorts of supported AST nodes.
+ ConstructionContextItem(const DeclStmt *DS)
+ : Data(DS), Kind(VariableKind) {}
+
+ ConstructionContextItem(const CXXNewExpr *NE)
+ : Data(NE), Kind(NewAllocatorKind) {}
+
+ ConstructionContextItem(const ReturnStmt *RS)
+ : Data(RS), Kind(ReturnKind) {}
+
+ ConstructionContextItem(const MaterializeTemporaryExpr *MTE)
+ : Data(MTE), Kind(MaterializationKind) {}
+
+ ConstructionContextItem(const CXXBindTemporaryExpr *BTE,
+ bool IsElided = false)
+ : Data(BTE),
+ Kind(IsElided ? ElidedDestructorKind : TemporaryDestructorKind) {}
+
+ ConstructionContextItem(const CXXConstructExpr *CE)
+ : Data(CE), Kind(ElidableConstructorKind) {}
+
+ ConstructionContextItem(const CallExpr *CE, unsigned Index)
+ : Data(CE), Kind(ArgumentKind), Index(Index) {}
+
+ ConstructionContextItem(const CXXConstructExpr *CE, unsigned Index)
+ : Data(CE), Kind(ArgumentKind), Index(Index) {}
+
+ ConstructionContextItem(const ObjCMessageExpr *ME, unsigned Index)
+ : Data(ME), Kind(ArgumentKind), Index(Index) {}
+
+ // A polymorphic version of the previous calls with dynamic type check.
+ ConstructionContextItem(const Expr *E, unsigned Index)
+ : Data(E), Kind(ArgumentKind), Index(Index) {
+ assert(isa<CallExpr>(E) || isa<CXXConstructExpr>(E) ||
+ isa<ObjCMessageExpr>(E));
+ }
+
+ ConstructionContextItem(const CXXCtorInitializer *Init)
+ : Data(Init), Kind(InitializerKind), Index(0) {}
+
+ ItemKind getKind() const { return Kind; }
+
+ LLVM_DUMP_METHOD StringRef getKindAsString() const {
+ return getKindAsString(getKind());
+ }
+
/// 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;
+ const Stmt *getStmt() const {
+ assert(hasStatement());
+ return static_cast<const Stmt *>(Data);
+ }
+
+ const Stmt *getStmtOrNull() const {
+ return hasStatement() ? getStmt() : nullptr;
+ }
+
+ /// The construction site is not necessarily a statement. It may also be a
+ /// CXXCtorInitializer, which means that a member variable is being
+ /// constructed during initialization of the object that contains it.
+ const CXXCtorInitializer *getCXXCtorInitializer() const {
+ assert(hasInitializer());
+ return static_cast<const CXXCtorInitializer *>(Data);
+ }
- /// 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.
+ /// If a single trigger statement triggers multiple constructors, they are
+ /// usually being enumerated. This covers function argument constructors
+ /// triggered by a call-expression and items in an initializer list triggered
+ /// by an init-list-expression.
+ unsigned getIndex() const {
+ // This is a fairly specific request. Let's make sure the user knows
+ // what he's doing.
+ assert(hasIndex());
+ return Index;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddPointer(Data);
+ ID.AddInteger(Kind);
+ ID.AddInteger(Index);
+ }
+
+ bool operator==(const ConstructionContextItem &Other) const {
+ // For most kinds the Index comparison is trivially true, but
+ // checking kind separately doesn't seem to be less expensive
+ // than checking Index. Same in operator<().
+ return std::make_tuple(Data, Kind, Index) ==
+ std::make_tuple(Other.Data, Other.Kind, Other.Index);
+ }
+
+ bool operator<(const ConstructionContextItem &Other) const {
+ return std::make_tuple(Data, Kind, Index) <
+ std::make_tuple(Other.Data, Other.Kind, Other.Index);
+ }
+};
+
+/// Construction context can be seen as a linked list of multiple layers.
+/// Sometimes a single trigger is not enough to describe the construction
+/// site. That's what causing us to 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.
+/// 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 {
const ConstructionContextLayer *Parent = nullptr;
+ ConstructionContextItem Item;
- ConstructionContextLayer(TriggerTy Trigger,
- const ConstructionContextLayer *Parent)
- : Trigger(Trigger), Parent(Parent) {}
+ ConstructionContextLayer(ConstructionContextItem Item,
+ const ConstructionContextLayer *Parent)
+ : Parent(Parent), Item(Item) {}
public:
static const ConstructionContextLayer *
- create(BumpVectorContext &C, TriggerTy Trigger,
+ create(BumpVectorContext &C, const ConstructionContextItem &Item,
const ConstructionContextLayer *Parent = nullptr);
+ const ConstructionContextItem &getItem() const { return Item; }
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.,
@@ -114,7 +249,8 @@ public:
SimpleReturnedValueKind,
CXX17ElidedCopyReturnedValueKind,
RETURNED_VALUE_BEGIN = SimpleReturnedValueKind,
- RETURNED_VALUE_END = CXX17ElidedCopyReturnedValueKind
+ RETURNED_VALUE_END = CXX17ElidedCopyReturnedValueKind,
+ ArgumentKind
};
protected:
@@ -132,6 +268,23 @@ private:
return new (CC) T(Args...);
}
+ // A sub-routine of createFromLayers() that deals with temporary objects
+ // that need to be materialized. The BTE argument is for the situation when
+ // the object also needs to be bound for destruction.
+ static const ConstructionContext *createMaterializedTemporaryFromLayers(
+ BumpVectorContext &C, const MaterializeTemporaryExpr *MTE,
+ const CXXBindTemporaryExpr *BTE,
+ const ConstructionContextLayer *ParentLayer);
+
+ // A sub-routine of createFromLayers() that deals with temporary objects
+ // that need to be bound for destruction. Automatically finds out if the
+ // object also needs to be materialized and delegates to
+ // createMaterializedTemporaryFromLayers() if necessary.
+ static const ConstructionContext *
+ createBoundTemporaryFromLayers(
+ BumpVectorContext &C, const CXXBindTemporaryExpr *BTE,
+ const ConstructionContextLayer *ParentLayer);
+
public:
/// Consume the construction context layer, together with its parent layers,
/// and wrap it up into a complete construction context. May return null
@@ -469,6 +622,32 @@ public:
}
};
+class ArgumentConstructionContext : public ConstructionContext {
+ const Expr *CE; // The call of which the context is an argument.
+ unsigned Index; // Which argument we're constructing.
+ const CXXBindTemporaryExpr *BTE; // Whether the object needs to be destroyed.
+
+ friend class ConstructionContext; // Allows to create<>() itself.
+
+ explicit ArgumentConstructionContext(const Expr *CE, unsigned Index,
+ const CXXBindTemporaryExpr *BTE)
+ : ConstructionContext(ArgumentKind), CE(CE),
+ Index(Index), BTE(BTE) {
+ assert(isa<CallExpr>(CE) || isa<CXXConstructExpr>(CE) ||
+ isa<ObjCMessageExpr>(CE));
+ // BTE is optional.
+ }
+
+public:
+ const Expr *getCallLikeExpr() const { return CE; }
+ unsigned getIndex() const { return Index; }
+ const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const { return BTE; }
+
+ static bool classof(const ConstructionContext *CC) {
+ return CC->getKind() == ArgumentKind;
+ }
+};
+
} // end namespace clang
#endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H