blob: 8df95a8ada20a6e0cfff6ba91ae6159327cef144 [file] [log] [blame]
//===- 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,
TemporaryObjectKind,
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;
friend class ConstructionContext; // Allows to create<>() itself.
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).
}
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() == TemporaryObjectKind;
}
};
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