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