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