diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:01 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:01 +0000 |
commit | 486754660bb926339aefcf012a3f848592babb8b (patch) | |
tree | ecdbc446c9876f4f120f701c243373cd3cb43db3 /lib/Analysis/ConstructionContext.cpp | |
parent | 55e6d896ad333f07bb3b1ba487df214fc268a4ab (diff) |
Notes
Diffstat (limited to 'lib/Analysis/ConstructionContext.cpp')
-rw-r--r-- | lib/Analysis/ConstructionContext.cpp | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/lib/Analysis/ConstructionContext.cpp b/lib/Analysis/ConstructionContext.cpp new file mode 100644 index 0000000000000..ed1e632432172 --- /dev/null +++ b/lib/Analysis/ConstructionContext.cpp @@ -0,0 +1,184 @@ +//===- ConstructionContext.cpp - CFG constructor information --------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/ConstructionContext.h" + +using namespace clang; + +const ConstructionContextLayer * +ConstructionContextLayer::create(BumpVectorContext &C, TriggerTy Trigger, + const ConstructionContextLayer *Parent) { + ConstructionContextLayer *CC = + C.getAllocator().Allocate<ConstructionContextLayer>(); + return new (CC) ConstructionContextLayer(Trigger, Parent); +} + +bool ConstructionContextLayer::isStrictlyMoreSpecificThan( + const ConstructionContextLayer *Other) const { + const ConstructionContextLayer *Self = this; + while (true) { + if (!Other) + return Self; + if (!Self || !Self->isSameLayer(Other)) + return false; + Self = Self->getParent(); + Other = Other->getParent(); + } + llvm_unreachable("The above loop can only be terminated via return!"); +} + +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()) { + assert(TopLayer->isLast()); + return create<SimpleConstructorInitializerConstructionContext>(C, I); + } + llvm_unreachable("Unexpected construction context!"); +} |