#include "clang/Analysis/ConstructionContext.h"
#include "clang/AST/ExprObjC.h"
using namespace clang;
const ConstructionContextLayer *
ConstructionContextLayer::create(BumpVectorContext &C,
const ConstructionContextItem &Item,
const ConstructionContextLayer *Parent) {
ConstructionContextLayer *CC =
C.getAllocator().Allocate<ConstructionContextLayer>();
return new (CC) ConstructionContextLayer(Item, Parent);
}
bool ConstructionContextLayer::isStrictlyMoreSpecificThan(
const ConstructionContextLayer *Other) const {
const ConstructionContextLayer *Self = this;
while (true) {
if (!Other)
return Self;
if (!Self || !(Self->Item == Other->Item))
return false;
Self = Self->getParent();
Other = Other->getParent();
}
llvm_unreachable("The above loop can only be terminated via return!");
}
const ConstructionContext *
ConstructionContext::createMaterializedTemporaryFromLayers(
BumpVectorContext &C, const MaterializeTemporaryExpr *MTE,
const CXXBindTemporaryExpr *BTE,
const ConstructionContextLayer *ParentLayer) {
assert(MTE);
if (!BTE && !(MTE->getType().getCanonicalType()->getAsCXXRecordDecl()
->hasTrivialDestructor() ||
MTE->getStorageDuration() != SD_FullExpression)) {
return nullptr;
}
if (MTE->getStorageDuration() != SD_FullExpression) {
BTE = nullptr;
}
const CXXConstructExpr *ElidedCE = nullptr;
const ConstructionContext *ElidedCC = nullptr;
if (ParentLayer) {
const ConstructionContextItem &ElidedItem = ParentLayer->getItem();
assert(ElidedItem.getKind() ==
ConstructionContextItem::ElidableConstructorKind);
ElidedCE = cast<CXXConstructExpr>(ElidedItem.getStmt());
assert(ElidedCE->isElidable());
ElidedCC = createFromLayers(C, ParentLayer->getParent());
if (!ElidedCC) {
return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE);
}
return create<ElidedTemporaryObjectConstructionContext>(
C, BTE, MTE, ElidedCE, ElidedCC);
}
assert(!ParentLayer);
return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE);
}
const ConstructionContext *ConstructionContext::createBoundTemporaryFromLayers(
BumpVectorContext &C, const CXXBindTemporaryExpr *BTE,
const ConstructionContextLayer *ParentLayer) {
if (!ParentLayer) {
return create<SimpleTemporaryObjectConstructionContext>(C, BTE,
nullptr);
}
const ConstructionContextItem &ParentItem = ParentLayer->getItem();
switch (ParentItem.getKind()) {
case ConstructionContextItem::VariableKind: {
const auto *DS = cast<DeclStmt>(ParentItem.getStmt());
assert(!cast<VarDecl>(DS->getSingleDecl())->getType().getCanonicalType()
->getAsCXXRecordDecl()->hasTrivialDestructor());
return create<CXX17ElidedCopyVariableConstructionContext>(C, DS, BTE);
}
case ConstructionContextItem::NewAllocatorKind: {
llvm_unreachable("This context does not accept a bound temporary!");
}
case ConstructionContextItem::ReturnKind: {
assert(ParentLayer->isLast());
const auto *RS = cast<ReturnStmt>(ParentItem.getStmt());
assert(!RS->getRetValue()->getType().getCanonicalType()
->getAsCXXRecordDecl()->hasTrivialDestructor());
return create<CXX17ElidedCopyReturnedValueConstructionContext>(C, RS,
BTE);
}
case ConstructionContextItem::MaterializationKind: {
const auto *MTE = cast<MaterializeTemporaryExpr>(ParentItem.getStmt());
return createMaterializedTemporaryFromLayers(C, MTE, BTE,
ParentLayer->getParent());
}
case ConstructionContextItem::TemporaryDestructorKind: {
llvm_unreachable("Duplicate CXXBindTemporaryExpr in the AST!");
}
case ConstructionContextItem::ElidedDestructorKind: {
llvm_unreachable("Elided destructor items are not produced by the CFG!");
}
case ConstructionContextItem::ElidableConstructorKind: {
llvm_unreachable("Materialization is necessary to put temporary into a "
"copy or move constructor!");
}
case ConstructionContextItem::ArgumentKind: {
assert(ParentLayer->isLast());
const auto *E = cast<Expr>(ParentItem.getStmt());
assert(isa<CallExpr>(E) || isa<CXXConstructExpr>(E) ||
isa<ObjCMessageExpr>(E));
return create<ArgumentConstructionContext>(C, E, ParentItem.getIndex(),
BTE);
}
case ConstructionContextItem::InitializerKind: {
assert(ParentLayer->isLast());
const auto *I = ParentItem.getCXXCtorInitializer();
assert(!I->getAnyMember()->getType().getCanonicalType()
->getAsCXXRecordDecl()->hasTrivialDestructor());
return create<CXX17ElidedCopyConstructorInitializerConstructionContext>(
C, I, BTE);
}
case ConstructionContextItem::LambdaCaptureKind: {
assert(ParentLayer->isLast());
const auto *E = cast<LambdaExpr>(ParentItem.getStmt());
return create<LambdaCaptureConstructionContext>(C, E,
ParentItem.getIndex());
}
}
llvm_unreachable("Unexpected construction context with destructor!");
}
const ConstructionContext *ConstructionContext::createFromLayers(
BumpVectorContext &C, const ConstructionContextLayer *TopLayer) {
const ConstructionContextItem &TopItem = TopLayer->getItem();
switch (TopItem.getKind()) {
case ConstructionContextItem::VariableKind: {
assert(TopLayer->isLast());
const auto *DS = cast<DeclStmt>(TopItem.getStmt());
return create<SimpleVariableConstructionContext>(C, DS);
}
case ConstructionContextItem::NewAllocatorKind: {
assert(TopLayer->isLast());
const auto *NE = cast<CXXNewExpr>(TopItem.getStmt());
return create<NewAllocatedObjectConstructionContext>(C, NE);
}
case ConstructionContextItem::ReturnKind: {
assert(TopLayer->isLast());
const auto *RS = cast<ReturnStmt>(TopItem.getStmt());
return create<SimpleReturnedValueConstructionContext>(C, RS);
}
case ConstructionContextItem::MaterializationKind: {
const auto *MTE = cast<MaterializeTemporaryExpr>(TopItem.getStmt());
return createMaterializedTemporaryFromLayers(C, MTE, nullptr,
TopLayer->getParent());
}
case ConstructionContextItem::TemporaryDestructorKind: {
const auto *BTE = cast<CXXBindTemporaryExpr>(TopItem.getStmt());
assert(BTE->getType().getCanonicalType()->getAsCXXRecordDecl()
->hasNonTrivialDestructor());
return createBoundTemporaryFromLayers(C, BTE, TopLayer->getParent());
}
case ConstructionContextItem::ElidedDestructorKind: {
llvm_unreachable("Elided destructor items are not produced by the CFG!");
}
case ConstructionContextItem::ElidableConstructorKind: {
llvm_unreachable("The argument needs to be materialized first!");
}
case ConstructionContextItem::LambdaCaptureKind: {
assert(TopLayer->isLast());
const auto *E = cast<LambdaExpr>(TopItem.getStmt());
return create<LambdaCaptureConstructionContext>(C, E, TopItem.getIndex());
}
case ConstructionContextItem::InitializerKind: {
assert(TopLayer->isLast());
const CXXCtorInitializer *I = TopItem.getCXXCtorInitializer();
return create<SimpleConstructorInitializerConstructionContext>(C, I);
}
case ConstructionContextItem::ArgumentKind: {
assert(TopLayer->isLast());
const auto *E = cast<Expr>(TopItem.getStmt());
return create<ArgumentConstructionContext>(C, E, TopItem.getIndex(),
nullptr);
}
} llvm_unreachable("Unexpected construction context!");
}