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