diff options
Diffstat (limited to 'include/clang/Analysis/ConstructionContext.h')
-rw-r--r-- | include/clang/Analysis/ConstructionContext.h | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/include/clang/Analysis/ConstructionContext.h b/include/clang/Analysis/ConstructionContext.h new file mode 100644 index 000000000000..40cb0e7e5dda --- /dev/null +++ b/include/clang/Analysis/ConstructionContext.h @@ -0,0 +1,474 @@ +//===- 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, + CXX17ElidedCopyVariableKind, + VARIABLE_BEGIN = SimpleVariableKind, + VARIABLE_END = CXX17ElidedCopyVariableKind, + SimpleConstructorInitializerKind, + CXX17ElidedCopyConstructorInitializerKind, + INITIALIZER_BEGIN = SimpleConstructorInitializerKind, + INITIALIZER_END = CXX17ElidedCopyConstructorInitializerKind, + NewAllocatedObjectKind, + SimpleTemporaryObjectKind, + ElidedTemporaryObjectKind, + TEMPORARY_BEGIN = SimpleTemporaryObjectKind, + TEMPORARY_END = ElidedTemporaryObjectKind, + SimpleReturnedValueKind, + CXX17ElidedCopyReturnedValueKind, + RETURNED_VALUE_BEGIN = SimpleReturnedValueKind, + RETURNED_VALUE_END = CXX17ElidedCopyReturnedValueKind + }; + +protected: + Kind K; + + // Do not make public! These need to only be constructed + // via createFromLayers(). + explicit ConstructionContext(Kind K) : K(K) {} + +private: + // A helper function for constructing an instance into a bump vector context. + template <typename T, typename... ArgTypes> + static T *create(BumpVectorContext &C, ArgTypes... Args) { + auto *CC = C.getAllocator().Allocate<T>(); + return new (CC) T(Args...); + } + +public: + /// Consume the construction context layer, together with its parent layers, + /// and wrap it up into a complete construction context. May return null + /// if layers do not form any supported construction context. + static const ConstructionContext * + createFromLayers(BumpVectorContext &C, + const ConstructionContextLayer *TopLayer); + + Kind getKind() const { return K; } +}; + +/// An abstract base class for local variable constructors. +class VariableConstructionContext : public ConstructionContext { + const DeclStmt *DS; + +protected: + VariableConstructionContext(ConstructionContext::Kind K, const DeclStmt *DS) + : ConstructionContext(K), DS(DS) { + assert(classof(this)); + assert(DS); + } + +public: + const DeclStmt *getDeclStmt() const { return DS; } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() >= VARIABLE_BEGIN && + CC->getKind() <= VARIABLE_END; + } +}; + +/// Represents construction into a simple local variable, eg. T var(123);. +/// If a variable has an initializer, eg. T var = makeT();, then the final +/// elidable copy-constructor from makeT() into var would also be a simple +/// variable constructor handled by this class. +class SimpleVariableConstructionContext : public VariableConstructionContext { + friend class ConstructionContext; // Allows to create<>() itself. + + explicit SimpleVariableConstructionContext(const DeclStmt *DS) + : VariableConstructionContext(ConstructionContext::SimpleVariableKind, + DS) {} + +public: + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == SimpleVariableKind; + } +}; + +/// Represents construction into a simple variable with an initializer syntax, +/// with a single constructor, eg. T var = makeT();. Such construction context +/// may only appear in C++17 because previously it was split into a temporary +/// object constructor and an elidable simple variable copy-constructor and +/// we were producing separate construction contexts for these constructors. +/// In C++17 we have a single construction context that combines both. +/// Note that if the object has trivial destructor, then this code is +/// indistinguishable from a simple variable constructor on the AST level; +/// in this case we provide a simple variable construction context. +class CXX17ElidedCopyVariableConstructionContext + : public VariableConstructionContext { + const CXXBindTemporaryExpr *BTE; + + friend class ConstructionContext; // Allows to create<>() itself. + + explicit CXX17ElidedCopyVariableConstructionContext( + const DeclStmt *DS, const CXXBindTemporaryExpr *BTE) + : VariableConstructionContext(CXX17ElidedCopyVariableKind, DS), BTE(BTE) { + assert(BTE); + } + +public: + const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const { return BTE; } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == CXX17ElidedCopyVariableKind; + } +}; + +// An abstract base class for constructor-initializer-based constructors. +class ConstructorInitializerConstructionContext : public ConstructionContext { + const CXXCtorInitializer *I; + +protected: + explicit ConstructorInitializerConstructionContext( + ConstructionContext::Kind K, const CXXCtorInitializer *I) + : ConstructionContext(K), I(I) { + assert(classof(this)); + assert(I); + } + +public: + const CXXCtorInitializer *getCXXCtorInitializer() const { return I; } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() >= INITIALIZER_BEGIN && + CC->getKind() <= INITIALIZER_END; + } +}; + +/// Represents construction into a field or a base class within a bigger object +/// via a constructor initializer, eg. T(): field(123) { ... }. +class SimpleConstructorInitializerConstructionContext + : public ConstructorInitializerConstructionContext { + friend class ConstructionContext; // Allows to create<>() itself. + + explicit SimpleConstructorInitializerConstructionContext( + const CXXCtorInitializer *I) + : ConstructorInitializerConstructionContext( + ConstructionContext::SimpleConstructorInitializerKind, I) {} + +public: + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == SimpleConstructorInitializerKind; + } +}; + +/// Represents construction into a field or a base class within a bigger object +/// via a constructor initializer, with a single constructor, eg. +/// T(): field(Field(123)) { ... }. Such construction context may only appear +/// in C++17 because previously it was split into a temporary object constructor +/// and an elidable simple constructor-initializer copy-constructor and we were +/// producing separate construction contexts for these constructors. In C++17 +/// we have a single construction context that combines both. Note that if the +/// object has trivial destructor, then this code is indistinguishable from +/// a simple constructor-initializer constructor on the AST level; in this case +/// we provide a simple constructor-initializer construction context. +class CXX17ElidedCopyConstructorInitializerConstructionContext + : public ConstructorInitializerConstructionContext { + const CXXBindTemporaryExpr *BTE; + + friend class ConstructionContext; // Allows to create<>() itself. + + explicit CXX17ElidedCopyConstructorInitializerConstructionContext( + const CXXCtorInitializer *I, const CXXBindTemporaryExpr *BTE) + : ConstructorInitializerConstructionContext( + CXX17ElidedCopyConstructorInitializerKind, I), + BTE(BTE) { + assert(BTE); + } + +public: + const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const { return BTE; } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == CXX17ElidedCopyConstructorInitializerKind; + } +}; + +/// Represents immediate initialization of memory allocated by operator new, +/// eg. new T(123);. +class NewAllocatedObjectConstructionContext : public ConstructionContext { + const CXXNewExpr *NE; + + friend class ConstructionContext; // Allows to create<>() itself. + + explicit NewAllocatedObjectConstructionContext(const CXXNewExpr *NE) + : ConstructionContext(ConstructionContext::NewAllocatedObjectKind), + NE(NE) { + assert(NE); + } + +public: + 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; + +protected: + explicit TemporaryObjectConstructionContext( + ConstructionContext::Kind K, const CXXBindTemporaryExpr *BTE, + const MaterializeTemporaryExpr *MTE) + : ConstructionContext(K), 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 the other case yet (this would be a temporary in the middle + // of nowhere that doesn't have a non-trivial destructor). + } + +public: + /// 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() >= TEMPORARY_BEGIN && CC->getKind() <= TEMPORARY_END; + } +}; + +/// Represents a temporary object that is not constructed for the purpose of +/// being immediately copied/moved by an elidable copy/move-constructor. +/// This includes temporary objects "in the middle of nowhere" like T(123) and +/// lifetime-extended temporaries. +class SimpleTemporaryObjectConstructionContext + : public TemporaryObjectConstructionContext { + friend class ConstructionContext; // Allows to create<>() itself. + + explicit SimpleTemporaryObjectConstructionContext( + const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE) + : TemporaryObjectConstructionContext( + ConstructionContext::SimpleTemporaryObjectKind, BTE, MTE) {} + +public: + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == SimpleTemporaryObjectKind; + } +}; + +/// Represents a temporary object that is constructed for the sole purpose +/// of being immediately copied by an elidable copy/move constructor. +/// For example, T t = T(123); includes a temporary T(123) that is immediately +/// copied to variable t. In such cases the elidable copy can (but not +/// necessarily should) be omitted ("elided") accodring to the rules of the +/// language; the constructor would then construct variable t directly. +/// This construction context contains information of the elidable constructor +/// and its respective construction context. +class ElidedTemporaryObjectConstructionContext + : public TemporaryObjectConstructionContext { + const CXXConstructExpr *ElidedCE; + const ConstructionContext *ElidedCC; + + friend class ConstructionContext; // Allows to create<>() itself. + + explicit ElidedTemporaryObjectConstructionContext( + const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE, + const CXXConstructExpr *ElidedCE, const ConstructionContext *ElidedCC) + : TemporaryObjectConstructionContext( + ConstructionContext::ElidedTemporaryObjectKind, BTE, MTE), + ElidedCE(ElidedCE), ElidedCC(ElidedCC) { + // Elided constructor and its context should be either both specified + // or both unspecified. In the former case, the constructor must be + // elidable. + assert(ElidedCE && ElidedCE->isElidable() && ElidedCC); + } + +public: + const CXXConstructExpr *getConstructorAfterElision() const { + return ElidedCE; + } + + const ConstructionContext *getConstructionContextAfterElision() const { + return ElidedCC; + } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == ElidedTemporaryObjectKind; + } +}; + +class ReturnedValueConstructionContext : public ConstructionContext { + const ReturnStmt *RS; + +protected: + explicit ReturnedValueConstructionContext(ConstructionContext::Kind K, + const ReturnStmt *RS) + : ConstructionContext(K), RS(RS) { + assert(classof(this)); + assert(RS); + } + +public: + const ReturnStmt *getReturnStmt() const { return RS; } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() >= RETURNED_VALUE_BEGIN && + CC->getKind() <= RETURNED_VALUE_END; + } +}; + +/// 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 SimpleReturnedValueConstructionContext + : public ReturnedValueConstructionContext { + friend class ConstructionContext; // Allows to create<>() itself. + + explicit SimpleReturnedValueConstructionContext(const ReturnStmt *RS) + : ReturnedValueConstructionContext( + ConstructionContext::SimpleReturnedValueKind, RS) {} + +public: + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == SimpleReturnedValueKind; + } +}; + +/// Represents a temporary object that is being immediately returned from a +/// function by value, eg. return t; or return T(123); in C++17. +/// In C++17 there is not going to be an elidable copy constructor at the +/// return site. However, the usual temporary-related bureaucracy (CXXBindTemporaryExpr, +/// MaterializeTemporaryExpr) is normally located in the caller function's AST. +/// Note that if the object has trivial destructor, then this code is +/// indistinguishable from a simple returned value constructor on the AST level; +/// in this case we provide a simple returned value construction context. +class CXX17ElidedCopyReturnedValueConstructionContext + : public ReturnedValueConstructionContext { + const CXXBindTemporaryExpr *BTE; + + friend class ConstructionContext; // Allows to create<>() itself. + + explicit CXX17ElidedCopyReturnedValueConstructionContext( + const ReturnStmt *RS, const CXXBindTemporaryExpr *BTE) + : ReturnedValueConstructionContext( + ConstructionContext::CXX17ElidedCopyReturnedValueKind, RS), + BTE(BTE) { + assert(BTE); + } + +public: + const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const { return BTE; } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == CXX17ElidedCopyReturnedValueKind; + } +}; + +} // end namespace clang + +#endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H |