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