xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/ConstructionContext.cpp (revision 972a253a57b6f144b0e4a3e2080a2a0076ec55a0)
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 *
create(BumpVectorContext & C,const ConstructionContextItem & Item,const ConstructionContextLayer * Parent)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 
isStrictlyMoreSpecificThan(const ConstructionContextLayer * Other) const30 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 *
createMaterializedTemporaryFromLayers(BumpVectorContext & C,const MaterializeTemporaryExpr * MTE,const CXXBindTemporaryExpr * BTE,const ConstructionContextLayer * ParentLayer)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 
createBoundTemporaryFromLayers(BumpVectorContext & C,const CXXBindTemporaryExpr * BTE,const ConstructionContextLayer * ParentLayer)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   case ConstructionContextItem::LambdaCaptureKind: {
160     assert(ParentLayer->isLast());
161     const auto *E = cast<LambdaExpr>(ParentItem.getStmt());
162     return create<LambdaCaptureConstructionContext>(C, E,
163                                                     ParentItem.getIndex());
164   }
165   } // switch (ParentItem.getKind())
166 
167   llvm_unreachable("Unexpected construction context with destructor!");
168 }
169 
createFromLayers(BumpVectorContext & C,const ConstructionContextLayer * TopLayer)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
174   // patterns.
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);
181   }
182   case ConstructionContextItem::NewAllocatorKind: {
183     assert(TopLayer->isLast());
184     const auto *NE = cast<CXXNewExpr>(TopItem.getStmt());
185     return create<NewAllocatedObjectConstructionContext>(C, NE);
186   }
187   case ConstructionContextItem::ReturnKind: {
188     assert(TopLayer->isLast());
189     const auto *RS = cast<ReturnStmt>(TopItem.getStmt());
190     return create<SimpleReturnedValueConstructionContext>(C, RS);
191   }
192   case ConstructionContextItem::MaterializationKind: {
193     const auto *MTE = cast<MaterializeTemporaryExpr>(TopItem.getStmt());
194     return createMaterializedTemporaryFromLayers(C, MTE, /*BTE=*/nullptr,
195                                                  TopLayer->getParent());
196   }
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());
202   }
203   case ConstructionContextItem::ElidedDestructorKind: {
204     llvm_unreachable("Elided destructor items are not produced by the CFG!");
205   }
206   case ConstructionContextItem::ElidableConstructorKind: {
207     llvm_unreachable("The argument needs to be materialized first!");
208   }
209   case ConstructionContextItem::LambdaCaptureKind: {
210     assert(TopLayer->isLast());
211     const auto *E = cast<LambdaExpr>(TopItem.getStmt());
212     return create<LambdaCaptureConstructionContext>(C, E, TopItem.getIndex());
213   }
214   case ConstructionContextItem::InitializerKind: {
215     assert(TopLayer->isLast());
216     const CXXCtorInitializer *I = TopItem.getCXXCtorInitializer();
217     return create<SimpleConstructorInitializerConstructionContext>(C, I);
218   }
219   case ConstructionContextItem::ArgumentKind: {
220     assert(TopLayer->isLast());
221     const auto *E = cast<Expr>(TopItem.getStmt());
222     return create<ArgumentConstructionContext>(C, E, TopItem.getIndex(),
223                                                /*BTE=*/nullptr);
224   }
225   } // switch (TopItem.getKind())
226   llvm_unreachable("Unexpected construction context!");
227 }
228