clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
723 lines
27 KiB
C++
723 lines
27 KiB
C++
//===- ConstructionContext.h - CFG constructor information ------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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"
|
|
#include "clang/AST/ExprObjC.h"
|
|
|
|
namespace clang {
|
|
|
|
/// 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:
|
|
enum ItemKind {
|
|
VariableKind,
|
|
NewAllocatorKind,
|
|
ReturnKind,
|
|
MaterializationKind,
|
|
TemporaryDestructorKind,
|
|
ElidedDestructorKind,
|
|
ElidableConstructorKind,
|
|
ArgumentKind,
|
|
LambdaCaptureKind,
|
|
STATEMENT_WITH_INDEX_KIND_BEGIN = ArgumentKind,
|
|
STATEMENT_WITH_INDEX_KIND_END = LambdaCaptureKind,
|
|
STATEMENT_KIND_BEGIN = VariableKind,
|
|
STATEMENT_KIND_END = LambdaCaptureKind,
|
|
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 LambdaCaptureKind:
|
|
return "construct into lambda captured variable";
|
|
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 CXXInheritedCtorInitExpr *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<CXXDeleteExpr>(E) || isa<CXXInheritedCtorInitExpr>(E) ||
|
|
isa<ObjCMessageExpr>(E));
|
|
}
|
|
|
|
ConstructionContextItem(const CXXCtorInitializer *Init)
|
|
: Data(Init), Kind(InitializerKind), Index(0) {}
|
|
|
|
ConstructionContextItem(const LambdaExpr *LE, unsigned Index)
|
|
: Data(LE), Kind(LambdaCaptureKind), Index(Index) {}
|
|
|
|
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).
|
|
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);
|
|
}
|
|
|
|
/// 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(ConstructionContextItem Item,
|
|
const ConstructionContextLayer *Parent)
|
|
: Parent(Parent), Item(Item) {}
|
|
|
|
public:
|
|
static const ConstructionContextLayer *
|
|
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; }
|
|
|
|
/// 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,
|
|
ArgumentKind,
|
|
LambdaCaptureKind
|
|
};
|
|
|
|
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...);
|
|
}
|
|
|
|
// 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
|
|
/// if layers do not form any supported construction context.
|
|
static const ConstructionContext *
|
|
createFromLayers(BumpVectorContext &C,
|
|
const ConstructionContextLayer *TopLayer);
|
|
|
|
Kind getKind() const { return K; }
|
|
|
|
virtual const ArrayInitLoopExpr *getArrayInitLoop() const { return nullptr; }
|
|
|
|
// Only declared to silence -Wnon-virtual-dtor warnings.
|
|
virtual ~ConstructionContext() = default;
|
|
};
|
|
|
|
/// 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; }
|
|
|
|
const ArrayInitLoopExpr *getArrayInitLoop() const override {
|
|
const auto *Var = cast<VarDecl>(DS->getSingleDecl());
|
|
|
|
return dyn_cast<ArrayInitLoopExpr>(Var->getInit());
|
|
}
|
|
|
|
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; }
|
|
|
|
const ArrayInitLoopExpr *getArrayInitLoop() const override {
|
|
return dyn_cast<ArrayInitLoopExpr>(I->getInit());
|
|
}
|
|
|
|
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") according 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;
|
|
}
|
|
};
|
|
|
|
class ArgumentConstructionContext : public ConstructionContext {
|
|
// The call of which the context is an argument.
|
|
const Expr *CE;
|
|
|
|
// Which argument we're constructing. Note that when numbering between
|
|
// arguments and parameters is inconsistent (eg., operator calls),
|
|
// this is the index of the argument, not of the parameter.
|
|
unsigned Index;
|
|
|
|
// Whether the object needs to be destroyed.
|
|
const CXXBindTemporaryExpr *BTE;
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
class LambdaCaptureConstructionContext : public ConstructionContext {
|
|
// The lambda of which the initializer we capture.
|
|
const LambdaExpr *LE;
|
|
|
|
// Index of the captured element in the captured list.
|
|
unsigned Index;
|
|
|
|
friend class ConstructionContext; // Allows to create<>() itself.
|
|
|
|
explicit LambdaCaptureConstructionContext(const LambdaExpr *LE,
|
|
unsigned Index)
|
|
: ConstructionContext(LambdaCaptureKind), LE(LE), Index(Index) {}
|
|
|
|
public:
|
|
const LambdaExpr *getLambdaExpr() const { return LE; }
|
|
unsigned getIndex() const { return Index; }
|
|
|
|
const Expr *getInitializer() const {
|
|
return *(LE->capture_init_begin() + Index);
|
|
}
|
|
|
|
const FieldDecl *getFieldDecl() const {
|
|
auto It = LE->getLambdaClass()->field_begin();
|
|
std::advance(It, Index);
|
|
return *It;
|
|
}
|
|
|
|
const ArrayInitLoopExpr *getArrayInitLoop() const override {
|
|
return dyn_cast_or_null<ArrayInitLoopExpr>(getInitializer());
|
|
}
|
|
|
|
static bool classof(const ConstructionContext *CC) {
|
|
return CC->getKind() == LambdaCaptureKind;
|
|
}
|
|
};
|
|
|
|
} // end namespace clang
|
|
|
|
#endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
|