150
|
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 }
|