| //===- 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<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 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. |
| 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!"); |
| } |