228 lines · cpp
1//===- ConstructionContext.cpp - CFG constructor information --------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines the ConstructionContext class and its sub-classes,10// which represent various different ways of constructing C++ objects11// with the additional information the users may want to know about12// the constructor.13//14//===----------------------------------------------------------------------===//15 16#include "clang/Analysis/ConstructionContext.h"17#include "clang/AST/ExprObjC.h"18 19using namespace clang;20 21const ConstructionContextLayer *22ConstructionContextLayer::create(BumpVectorContext &C,23 const ConstructionContextItem &Item,24 const ConstructionContextLayer *Parent) {25 ConstructionContextLayer *CC =26 C.getAllocator().Allocate<ConstructionContextLayer>();27 return new (CC) ConstructionContextLayer(Item, Parent);28}29 30bool ConstructionContextLayer::isStrictlyMoreSpecificThan(31 const ConstructionContextLayer *Other) const {32 const ConstructionContextLayer *Self = this;33 while (true) {34 if (!Other)35 return Self;36 if (!Self || !(Self->Item == Other->Item))37 return false;38 Self = Self->getParent();39 Other = Other->getParent();40 }41 llvm_unreachable("The above loop can only be terminated via return!");42}43 44const ConstructionContext *45ConstructionContext::createMaterializedTemporaryFromLayers(46 BumpVectorContext &C, const MaterializeTemporaryExpr *MTE,47 const CXXBindTemporaryExpr *BTE,48 const ConstructionContextLayer *ParentLayer) {49 assert(MTE);50 51 // If the object requires destruction and is not lifetime-extended,52 // then it must have a BTE within its MTE, otherwise it shouldn't.53 // FIXME: This should be an assertion.54 if (!BTE && !(MTE->getType().getCanonicalType()->getAsCXXRecordDecl()55 ->hasTrivialDestructor() ||56 MTE->getStorageDuration() != SD_FullExpression)) {57 return nullptr;58 }59 60 // If the temporary is lifetime-extended, don't save the BTE,61 // because we don't need a temporary destructor, but an automatic62 // destructor.63 if (MTE->getStorageDuration() != SD_FullExpression) {64 BTE = nullptr;65 }66 67 // Handle pre-C++17 copy and move elision.68 const CXXConstructExpr *ElidedCE = nullptr;69 const ConstructionContext *ElidedCC = nullptr;70 if (ParentLayer) {71 const ConstructionContextItem &ElidedItem = ParentLayer->getItem();72 assert(ElidedItem.getKind() ==73 ConstructionContextItem::ElidableConstructorKind);74 ElidedCE = cast<CXXConstructExpr>(ElidedItem.getStmt());75 assert(ElidedCE->isElidable());76 // We're creating a construction context that might have already77 // been created elsewhere. Maybe we should unique our construction78 // contexts. That's what we often do, but in this case it's unlikely79 // to bring any benefits.80 ElidedCC = createFromLayers(C, ParentLayer->getParent());81 if (!ElidedCC) {82 // We may fail to create the elided construction context.83 // In this case, skip copy elision entirely.84 return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE);85 }86 return create<ElidedTemporaryObjectConstructionContext>(87 C, BTE, MTE, ElidedCE, ElidedCC);88 }89 90 // This is a normal temporary.91 assert(!ParentLayer);92 return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE);93}94 95const ConstructionContext *ConstructionContext::createBoundTemporaryFromLayers(96 BumpVectorContext &C, const CXXBindTemporaryExpr *BTE,97 const ConstructionContextLayer *ParentLayer) {98 if (!ParentLayer) {99 // A temporary object that doesn't require materialization.100 // In particular, it shouldn't require copy elision, because101 // copy/move constructors take a reference, which requires102 // materialization to obtain the glvalue.103 return create<SimpleTemporaryObjectConstructionContext>(C, BTE,104 /*MTE=*/nullptr);105 }106 107 const ConstructionContextItem &ParentItem = ParentLayer->getItem();108 switch (ParentItem.getKind()) {109 case ConstructionContextItem::VariableKind: {110 const auto *DS = cast<DeclStmt>(ParentItem.getStmt());111 assert(!cast<VarDecl>(DS->getSingleDecl())->getType().getCanonicalType()112 ->getAsCXXRecordDecl()->hasTrivialDestructor());113 return create<CXX17ElidedCopyVariableConstructionContext>(C, DS, BTE);114 }115 case ConstructionContextItem::NewAllocatorKind: {116 llvm_unreachable("This context does not accept a bound temporary!");117 }118 case ConstructionContextItem::ReturnKind: {119 assert(ParentLayer->isLast());120 const auto *RS = cast<ReturnStmt>(ParentItem.getStmt());121 assert(!RS->getRetValue()->getType().getCanonicalType()122 ->getAsCXXRecordDecl()->hasTrivialDestructor());123 return create<CXX17ElidedCopyReturnedValueConstructionContext>(C, RS,124 BTE);125 }126 127 case ConstructionContextItem::MaterializationKind: {128 // No assert. We may have an elidable copy on the grandparent layer.129 const auto *MTE = cast<MaterializeTemporaryExpr>(ParentItem.getStmt());130 return createMaterializedTemporaryFromLayers(C, MTE, BTE,131 ParentLayer->getParent());132 }133 case ConstructionContextItem::TemporaryDestructorKind: {134 llvm_unreachable("Duplicate CXXBindTemporaryExpr in the AST!");135 }136 case ConstructionContextItem::ElidedDestructorKind: {137 llvm_unreachable("Elided destructor items are not produced by the CFG!");138 }139 case ConstructionContextItem::ElidableConstructorKind: {140 llvm_unreachable("Materialization is necessary to put temporary into a "141 "copy or move constructor!");142 }143 case ConstructionContextItem::ArgumentKind: {144 assert(ParentLayer->isLast());145 const auto *E = cast<Expr>(ParentItem.getStmt());146 assert(isa<CallExpr>(E) || isa<CXXConstructExpr>(E) ||147 isa<ObjCMessageExpr>(E));148 return create<ArgumentConstructionContext>(C, E, ParentItem.getIndex(),149 BTE);150 }151 case ConstructionContextItem::InitializerKind: {152 assert(ParentLayer->isLast());153 const auto *I = ParentItem.getCXXCtorInitializer();154 assert(!I->getAnyMember()->getType().getCanonicalType()155 ->getAsCXXRecordDecl()->hasTrivialDestructor());156 return create<CXX17ElidedCopyConstructorInitializerConstructionContext>(157 C, I, BTE);158 }159 case ConstructionContextItem::LambdaCaptureKind: {160 assert(ParentLayer->isLast());161 const auto *E = cast<LambdaExpr>(ParentItem.getStmt());162 return create<LambdaCaptureConstructionContext>(C, E,163 ParentItem.getIndex());164 }165 } // switch (ParentItem.getKind())166 167 llvm_unreachable("Unexpected construction context with destructor!");168}169 170const ConstructionContext *ConstructionContext::createFromLayers(171 BumpVectorContext &C, const ConstructionContextLayer *TopLayer) {172 // Before this point all we've had was a stockpile of arbitrary layers.173 // Now validate that it is shaped as one of the finite amount of expected174 // patterns.175 const ConstructionContextItem &TopItem = TopLayer->getItem();176 switch (TopItem.getKind()) {177 case ConstructionContextItem::VariableKind: {178 assert(TopLayer->isLast());179 const auto *DS = cast<DeclStmt>(TopItem.getStmt());180 return create<SimpleVariableConstructionContext>(C, DS);181 }182 case ConstructionContextItem::NewAllocatorKind: {183 assert(TopLayer->isLast());184 const auto *NE = cast<CXXNewExpr>(TopItem.getStmt());185 return create<NewAllocatedObjectConstructionContext>(C, NE);186 }187 case ConstructionContextItem::ReturnKind: {188 assert(TopLayer->isLast());189 const auto *RS = cast<ReturnStmt>(TopItem.getStmt());190 return create<SimpleReturnedValueConstructionContext>(C, RS);191 }192 case ConstructionContextItem::MaterializationKind: {193 const auto *MTE = cast<MaterializeTemporaryExpr>(TopItem.getStmt());194 return createMaterializedTemporaryFromLayers(C, MTE, /*BTE=*/nullptr,195 TopLayer->getParent());196 }197 case ConstructionContextItem::TemporaryDestructorKind: {198 const auto *BTE = cast<CXXBindTemporaryExpr>(TopItem.getStmt());199 assert(BTE->getType().getCanonicalType()->getAsCXXRecordDecl()200 ->hasNonTrivialDestructor());201 return createBoundTemporaryFromLayers(C, BTE, TopLayer->getParent());202 }203 case ConstructionContextItem::ElidedDestructorKind: {204 llvm_unreachable("Elided destructor items are not produced by the CFG!");205 }206 case ConstructionContextItem::ElidableConstructorKind: {207 llvm_unreachable("The argument needs to be materialized first!");208 }209 case ConstructionContextItem::LambdaCaptureKind: {210 assert(TopLayer->isLast());211 const auto *E = cast<LambdaExpr>(TopItem.getStmt());212 return create<LambdaCaptureConstructionContext>(C, E, TopItem.getIndex());213 }214 case ConstructionContextItem::InitializerKind: {215 assert(TopLayer->isLast());216 const CXXCtorInitializer *I = TopItem.getCXXCtorInitializer();217 return create<SimpleConstructorInitializerConstructionContext>(C, I);218 }219 case ConstructionContextItem::ArgumentKind: {220 assert(TopLayer->isLast());221 const auto *E = cast<Expr>(TopItem.getStmt());222 return create<ArgumentConstructionContext>(C, E, TopItem.getIndex(),223 /*BTE=*/nullptr);224 }225 } // switch (TopItem.getKind())226 llvm_unreachable("Unexpected construction context!");227}228