1 //===- ConstructionContext.cpp - CFG constructor information --------------===//
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
7 //===----------------------------------------------------------------------===//
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
14 //===----------------------------------------------------------------------===//
16 #include "clang/Analysis/ConstructionContext.h"
17 #include "clang/AST/ExprObjC.h"
19 using namespace clang
;
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
);
30 bool ConstructionContextLayer::isStrictlyMoreSpecificThan(
31 const ConstructionContextLayer
*Other
) const {
32 const ConstructionContextLayer
*Self
= this;
36 if (!Self
|| !(Self
->Item
== Other
->Item
))
38 Self
= Self
->getParent();
39 Other
= Other
->getParent();
41 llvm_unreachable("The above loop can only be terminated via return!");
44 const ConstructionContext
*
45 ConstructionContext::createMaterializedTemporaryFromLayers(
46 BumpVectorContext
&C
, const MaterializeTemporaryExpr
*MTE
,
47 const CXXBindTemporaryExpr
*BTE
,
48 const ConstructionContextLayer
*ParentLayer
) {
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
)) {
60 // If the temporary is lifetime-extended, don't save the BTE,
61 // because we don't need a temporary destructor, but an automatic
63 if (MTE
->getStorageDuration() != SD_FullExpression
) {
67 // Handle pre-C++17 copy and move elision.
68 const CXXConstructExpr
*ElidedCE
= nullptr;
69 const ConstructionContext
*ElidedCC
= nullptr;
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());
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
);
86 return create
<ElidedTemporaryObjectConstructionContext
>(
87 C
, BTE
, MTE
, ElidedCE
, ElidedCC
);
90 // This is a normal temporary.
92 return create
<SimpleTemporaryObjectConstructionContext
>(C
, BTE
, MTE
);
95 const ConstructionContext
*ConstructionContext::createBoundTemporaryFromLayers(
96 BumpVectorContext
&C
, const CXXBindTemporaryExpr
*BTE
,
97 const ConstructionContextLayer
*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
,
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
);
115 case ConstructionContextItem::NewAllocatorKind
: {
116 llvm_unreachable("This context does not accept a bound temporary!");
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
,
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());
133 case ConstructionContextItem::TemporaryDestructorKind
: {
134 llvm_unreachable("Duplicate CXXBindTemporaryExpr in the AST!");
136 case ConstructionContextItem::ElidedDestructorKind
: {
137 llvm_unreachable("Elided destructor items are not produced by the CFG!");
139 case ConstructionContextItem::ElidableConstructorKind
: {
140 llvm_unreachable("Materialization is necessary to put temporary into a "
141 "copy or move constructor!");
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(),
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
>(
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());
165 } // switch (ParentItem.getKind())
167 llvm_unreachable("Unexpected construction context with destructor!");
170 const 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 expected
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
);
182 case ConstructionContextItem::NewAllocatorKind
: {
183 assert(TopLayer
->isLast());
184 const auto *NE
= cast
<CXXNewExpr
>(TopItem
.getStmt());
185 return create
<NewAllocatedObjectConstructionContext
>(C
, NE
);
187 case ConstructionContextItem::ReturnKind
: {
188 assert(TopLayer
->isLast());
189 const auto *RS
= cast
<ReturnStmt
>(TopItem
.getStmt());
190 return create
<SimpleReturnedValueConstructionContext
>(C
, RS
);
192 case ConstructionContextItem::MaterializationKind
: {
193 const auto *MTE
= cast
<MaterializeTemporaryExpr
>(TopItem
.getStmt());
194 return createMaterializedTemporaryFromLayers(C
, MTE
, /*BTE=*/nullptr,
195 TopLayer
->getParent());
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());
203 case ConstructionContextItem::ElidedDestructorKind
: {
204 llvm_unreachable("Elided destructor items are not produced by the CFG!");
206 case ConstructionContextItem::ElidableConstructorKind
: {
207 llvm_unreachable("The argument needs to be materialized first!");
209 case ConstructionContextItem::LambdaCaptureKind
: {
210 assert(TopLayer
->isLast());
211 const auto *E
= cast
<LambdaExpr
>(TopItem
.getStmt());
212 return create
<LambdaCaptureConstructionContext
>(C
, E
, TopItem
.getIndex());
214 case ConstructionContextItem::InitializerKind
: {
215 assert(TopLayer
->isLast());
216 const CXXCtorInitializer
*I
= TopItem
.getCXXCtorInitializer();
217 return create
<SimpleConstructorInitializerConstructionContext
>(C
, I
);
219 case ConstructionContextItem::ArgumentKind
: {
220 assert(TopLayer
->isLast());
221 const auto *E
= cast
<Expr
>(TopItem
.getStmt());
222 return create
<ArgumentConstructionContext
>(C
, E
, TopItem
.getIndex(),
225 } // switch (TopItem.getKind())
226 llvm_unreachable("Unexpected construction context!");