diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-08-02 17:33:11 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-08-02 17:33:11 +0000 |
commit | c7e70c433efc6953dc3888b9fbf9f3512d7da2b0 (patch) | |
tree | 27425930fc0c91650a7f3527fcac8e0f92907b90 /lib/Analysis/ConstructionContext.cpp | |
parent | 486754660bb926339aefcf012a3f848592babb8b (diff) |
Notes
Diffstat (limited to 'lib/Analysis/ConstructionContext.cpp')
-rw-r--r-- | lib/Analysis/ConstructionContext.cpp | 303 |
1 files changed, 168 insertions, 135 deletions
diff --git a/lib/Analysis/ConstructionContext.cpp b/lib/Analysis/ConstructionContext.cpp index ed1e632432172..8169d4a93a6d9 100644 --- a/lib/Analysis/ConstructionContext.cpp +++ b/lib/Analysis/ConstructionContext.cpp @@ -15,15 +15,17 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/ConstructionContext.h" +#include "clang/AST/ExprObjC.h" using namespace clang; const ConstructionContextLayer * -ConstructionContextLayer::create(BumpVectorContext &C, TriggerTy Trigger, +ConstructionContextLayer::create(BumpVectorContext &C, + const ConstructionContextItem &Item, const ConstructionContextLayer *Parent) { ConstructionContextLayer *CC = C.getAllocator().Allocate<ConstructionContextLayer>(); - return new (CC) ConstructionContextLayer(Trigger, Parent); + return new (CC) ConstructionContextLayer(Item, Parent); } bool ConstructionContextLayer::isStrictlyMoreSpecificThan( @@ -32,7 +34,7 @@ bool ConstructionContextLayer::isStrictlyMoreSpecificThan( while (true) { if (!Other) return Self; - if (!Self || !Self->isSameLayer(Other)) + if (!Self || !(Self->Item == Other->Item)) return false; Self = Self->getParent(); Other = Other->getParent(); @@ -40,145 +42,176 @@ bool ConstructionContextLayer::isStrictlyMoreSpecificThan( 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 the object requires destruction and is not lifetime-extended, + // then it must have a BTE within its MTE, otherwise it shouldn't. + // FIXME: This should be an assertion. + if (!BTE && !(MTE->getType().getCanonicalType()->getAsCXXRecordDecl() + ->hasTrivialDestructor() || + MTE->getStorageDuration() != SD_FullExpression)) { + return nullptr; + } + + // If the temporary is lifetime-extended, don't save the BTE, + // because we don't need a temporary destructor, but an automatic + // destructor. + if (MTE->getStorageDuration() != SD_FullExpression) { + BTE = nullptr; + } + + // Handle pre-C++17 copy and move elision. + 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()); + // We're creating a construction context that might have already + // been created elsewhere. Maybe we should unique our construction + // contexts. That's what we often do, but in this case it's unlikely + // to bring any benefits. + ElidedCC = createFromLayers(C, ParentLayer->getParent()); + if (!ElidedCC) { + // We may fail to create the elided construction context. + // In this case, skip copy elision entirely. + return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE); + } + return create<ElidedTemporaryObjectConstructionContext>( + C, BTE, MTE, ElidedCE, ElidedCC); + } + + // This is a normal temporary. + assert(!ParentLayer); + return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE); +} + +const ConstructionContext *ConstructionContext::createBoundTemporaryFromLayers( + BumpVectorContext &C, const CXXBindTemporaryExpr *BTE, + const ConstructionContextLayer *ParentLayer) { + if (!ParentLayer) { + // A temporary object that doesn't require materialization. + // In particular, it shouldn't require copy elision, because + // copy/move constructors take a reference, which requires + // materialization to obtain the glvalue. + return create<SimpleTemporaryObjectConstructionContext>(C, BTE, + /*MTE=*/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: { + // No assert. We may have an elidable copy on the grandparent layer. + 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); + } + } // switch (ParentItem.getKind()) + + llvm_unreachable("Unexpected construction context with destructor!"); +} + const ConstructionContext *ConstructionContext::createFromLayers( BumpVectorContext &C, const ConstructionContextLayer *TopLayer) { // Before this point all we've had was a stockpile of arbitrary layers. // Now validate that it is shaped as one of the finite amount of expected // patterns. - if (const Stmt *S = TopLayer->getTriggerStmt()) { - if (const auto *DS = dyn_cast<DeclStmt>(S)) { - assert(TopLayer->isLast()); - return create<SimpleVariableConstructionContext>(C, DS); - } - if (const auto *NE = dyn_cast<CXXNewExpr>(S)) { - assert(TopLayer->isLast()); - return create<NewAllocatedObjectConstructionContext>(C, NE); - } - if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(S)) { - const MaterializeTemporaryExpr *MTE = nullptr; - assert(BTE->getType().getCanonicalType() - ->getAsCXXRecordDecl()->hasNonTrivialDestructor()); - // For temporaries with destructors, there may or may not be - // lifetime extension on the parent layer. - if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) { - // C++17 *requires* elision of the constructor at the return site - // and at variable/member initialization site, while previous standards - // were allowing an optional elidable constructor. - // This is the C++17 copy-elided construction into a ctor initializer. - if (const CXXCtorInitializer *I = ParentLayer->getTriggerInit()) { - return create< - CXX17ElidedCopyConstructorInitializerConstructionContext>(C, - I, BTE); - } - assert(ParentLayer->getTriggerStmt() && - "Non-statement-based layers have been handled above!"); - // This is the normal, non-C++17 case: a temporary object which has - // both destruction and materialization info attached to it in the AST. - if ((MTE = dyn_cast<MaterializeTemporaryExpr>( - ParentLayer->getTriggerStmt()))) { - if (MTE->getStorageDuration() != SD_FullExpression) { - // If the temporary is lifetime-extended, don't save the BTE, - // because we don't need a temporary destructor, but an automatic - // destructor. - BTE = nullptr; - } - - // Handle pre-C++17 copy and move elision. - const CXXConstructExpr *ElidedCE = nullptr; - const ConstructionContext *ElidedCC = nullptr; - if (const ConstructionContextLayer *ElidedLayer = - ParentLayer->getParent()) { - ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt()); - assert(ElidedCE->isElidable()); - // We're creating a construction context that might have already - // been created elsewhere. Maybe we should unique our construction - // contexts. That's what we often do, but in this case it's unlikely - // to bring any benefits. - ElidedCC = createFromLayers(C, ElidedLayer->getParent()); - if (!ElidedCC) { - // We may fail to create the elided construction context. - // In this case, skip copy elision entirely. - return create<SimpleTemporaryObjectConstructionContext>(C, BTE, - MTE); - } else { - return create<ElidedTemporaryObjectConstructionContext>( - C, BTE, MTE, ElidedCE, ElidedCC); - } - } - assert(ParentLayer->isLast()); - return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE); - } - assert(ParentLayer->isLast()); - - // This is a constructor into a function argument. Not implemented yet. - if (isa<CallExpr>(ParentLayer->getTriggerStmt())) - return nullptr; - // This is C++17 copy-elided construction into return statement. - if (auto *RS = dyn_cast<ReturnStmt>(ParentLayer->getTriggerStmt())) { - assert(!RS->getRetValue()->getType().getCanonicalType() - ->getAsCXXRecordDecl()->hasTrivialDestructor()); - return create<CXX17ElidedCopyReturnedValueConstructionContext>(C, - RS, BTE); - } - // This is C++17 copy-elided construction into a simple variable. - if (auto *DS = dyn_cast<DeclStmt>(ParentLayer->getTriggerStmt())) { - assert(!cast<VarDecl>(DS->getSingleDecl())->getType() - .getCanonicalType()->getAsCXXRecordDecl() - ->hasTrivialDestructor()); - return create<CXX17ElidedCopyVariableConstructionContext>(C, DS, BTE); - } - llvm_unreachable("Unexpected construction context with destructor!"); - } - // A temporary object that doesn't require materialization. - // In particular, it shouldn't require copy elision, because - // copy/move constructors take a reference, which requires - // materialization to obtain the glvalue. - return create<SimpleTemporaryObjectConstructionContext>(C, BTE, - /*MTE=*/nullptr); - } - if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) { - // If the object requires destruction and is not lifetime-extended, - // then it must have a BTE within its MTE. - // FIXME: This should be an assertion. - if (!(MTE->getType().getCanonicalType() - ->getAsCXXRecordDecl()->hasTrivialDestructor() || - MTE->getStorageDuration() != SD_FullExpression)) - return nullptr; - - // Handle pre-C++17 copy and move elision. - const CXXConstructExpr *ElidedCE = nullptr; - const ConstructionContext *ElidedCC = nullptr; - if (const ConstructionContextLayer *ElidedLayer = TopLayer->getParent()) { - ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt()); - assert(ElidedCE->isElidable()); - // We're creating a construction context that might have already - // been created elsewhere. Maybe we should unique our construction - // contexts. That's what we often do, but in this case it's unlikely - // to bring any benefits. - ElidedCC = createFromLayers(C, ElidedLayer->getParent()); - if (!ElidedCC) { - // We may fail to create the elided construction context. - // In this case, skip copy elision entirely. - return create<SimpleTemporaryObjectConstructionContext>(C, nullptr, - MTE); - } - return create<ElidedTemporaryObjectConstructionContext>( - C, nullptr, MTE, ElidedCE, ElidedCC); - } - assert(TopLayer->isLast()); - return create<SimpleTemporaryObjectConstructionContext>(C, nullptr, MTE); - } - if (const auto *RS = dyn_cast<ReturnStmt>(S)) { - assert(TopLayer->isLast()); - return create<SimpleReturnedValueConstructionContext>(C, RS); - } - // This is a constructor into a function argument. Not implemented yet. - if (isa<CallExpr>(TopLayer->getTriggerStmt())) - return nullptr; - llvm_unreachable("Unexpected construction context with statement!"); - } else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) { + 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, /*BTE=*/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::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(), + /*BTE=*/nullptr); + } + } // switch (TopItem.getKind()) llvm_unreachable("Unexpected construction context!"); } |