//===- ConstructionContext.cpp - CFG constructor information --------------===// // // 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. // //===----------------------------------------------------------------------===// #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(); 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 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(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(C, BTE, MTE); } return create( C, BTE, MTE, ElidedCE, ElidedCC); } // This is a normal temporary. assert(!ParentLayer); return create(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(C, BTE, /*MTE=*/nullptr); } const ConstructionContextItem &ParentItem = ParentLayer->getItem(); switch (ParentItem.getKind()) { case ConstructionContextItem::VariableKind: { const auto *DS = cast(ParentItem.getStmt()); assert(!cast(DS->getSingleDecl())->getType().getCanonicalType() ->getAsCXXRecordDecl()->hasTrivialDestructor()); return create(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(ParentItem.getStmt()); assert(!RS->getRetValue()->getType().getCanonicalType() ->getAsCXXRecordDecl()->hasTrivialDestructor()); return create(C, RS, BTE); } case ConstructionContextItem::MaterializationKind: { // No assert. We may have an elidable copy on the grandparent layer. const auto *MTE = cast(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(ParentItem.getStmt()); assert(isa(E) || isa(E) || isa(E)); return create(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( 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. const ConstructionContextItem &TopItem = TopLayer->getItem(); switch (TopItem.getKind()) { case ConstructionContextItem::VariableKind: { assert(TopLayer->isLast()); const auto *DS = cast(TopItem.getStmt()); return create(C, DS); } case ConstructionContextItem::NewAllocatorKind: { assert(TopLayer->isLast()); const auto *NE = cast(TopItem.getStmt()); return create(C, NE); } case ConstructionContextItem::ReturnKind: { assert(TopLayer->isLast()); const auto *RS = cast(TopItem.getStmt()); return create(C, RS); } case ConstructionContextItem::MaterializationKind: { const auto *MTE = cast(TopItem.getStmt()); return createMaterializedTemporaryFromLayers(C, MTE, /*BTE=*/nullptr, TopLayer->getParent()); } case ConstructionContextItem::TemporaryDestructorKind: { const auto *BTE = cast(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(C, I); } case ConstructionContextItem::ArgumentKind: { assert(TopLayer->isLast()); const auto *E = cast(TopItem.getStmt()); return create(C, E, TopItem.getIndex(), /*BTE=*/nullptr); } } // switch (TopItem.getKind()) llvm_unreachable("Unexpected construction context!"); }