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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines the ConstructionContext class and its sub-classes, 10 // which represent various different ways of constructing C++ objects 11 // with the additional information the users may want to know about 12 // the constructor. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "clang/Analysis/ConstructionContext.h" 17 #include "clang/AST/ExprObjC.h" 18 19 using namespace clang; 20 21 const ConstructionContextLayer * 22 ConstructionContextLayer::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 30 bool 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 44 const ConstructionContext * 45 ConstructionContext::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 automatic 62 // 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 already 77 // been created elsewhere. Maybe we should unique our construction 78 // contexts. That's what we often do, but in this case it's unlikely 79 // 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 95 const 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, because 101 // copy/move constructors take a reference, which requires 102 // 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 } // switch (ParentItem.getKind()) 160 161 llvm_unreachable("Unexpected construction context with destructor!"); 162 } 163 164 const ConstructionContext *ConstructionContext::createFromLayers( 165 BumpVectorContext &C, const ConstructionContextLayer *TopLayer) { 166 // Before this point all we've had was a stockpile of arbitrary layers. 167 // Now validate that it is shaped as one of the finite amount of expected 168 // patterns. 169 const ConstructionContextItem &TopItem = TopLayer->getItem(); 170 switch (TopItem.getKind()) { 171 case ConstructionContextItem::VariableKind: { 172 assert(TopLayer->isLast()); 173 const auto *DS = cast<DeclStmt>(TopItem.getStmt()); 174 return create<SimpleVariableConstructionContext>(C, DS); 175 } 176 case ConstructionContextItem::NewAllocatorKind: { 177 assert(TopLayer->isLast()); 178 const auto *NE = cast<CXXNewExpr>(TopItem.getStmt()); 179 return create<NewAllocatedObjectConstructionContext>(C, NE); 180 } 181 case ConstructionContextItem::ReturnKind: { 182 assert(TopLayer->isLast()); 183 const auto *RS = cast<ReturnStmt>(TopItem.getStmt()); 184 return create<SimpleReturnedValueConstructionContext>(C, RS); 185 } 186 case ConstructionContextItem::MaterializationKind: { 187 const auto *MTE = cast<MaterializeTemporaryExpr>(TopItem.getStmt()); 188 return createMaterializedTemporaryFromLayers(C, MTE, /*BTE=*/nullptr, 189 TopLayer->getParent()); 190 } 191 case ConstructionContextItem::TemporaryDestructorKind: { 192 const auto *BTE = cast<CXXBindTemporaryExpr>(TopItem.getStmt()); 193 assert(BTE->getType().getCanonicalType()->getAsCXXRecordDecl() 194 ->hasNonTrivialDestructor()); 195 return createBoundTemporaryFromLayers(C, BTE, TopLayer->getParent()); 196 } 197 case ConstructionContextItem::ElidedDestructorKind: { 198 llvm_unreachable("Elided destructor items are not produced by the CFG!"); 199 } 200 case ConstructionContextItem::ElidableConstructorKind: { 201 llvm_unreachable("The argument needs to be materialized first!"); 202 } 203 case ConstructionContextItem::InitializerKind: { 204 assert(TopLayer->isLast()); 205 const CXXCtorInitializer *I = TopItem.getCXXCtorInitializer(); 206 return create<SimpleConstructorInitializerConstructionContext>(C, I); 207 } 208 case ConstructionContextItem::ArgumentKind: { 209 assert(TopLayer->isLast()); 210 const auto *E = cast<Expr>(TopItem.getStmt()); 211 return create<ArgumentConstructionContext>(C, E, TopItem.getIndex(), 212 /*BTE=*/nullptr); 213 } 214 } // switch (TopItem.getKind()) 215 llvm_unreachable("Unexpected construction context!"); 216 } 217