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 *
23 ConstructionContextLayer::create(BumpVectorContext &C, TriggerTy Trigger,
24                                  unsigned Index,
25                                  const ConstructionContextLayer *Parent) {
26   ConstructionContextLayer *CC =
27       C.getAllocator().Allocate<ConstructionContextLayer>();
28   return new (CC) ConstructionContextLayer(Trigger, Index, Parent);
29 }
30 
31 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->isSameLayer(Other))
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 *ConstructionContext::createFromLayers(
46     BumpVectorContext &C, const ConstructionContextLayer *TopLayer) {
47   // Before this point all we've had was a stockpile of arbitrary layers.
48   // Now validate that it is shaped as one of the finite amount of expected
49   // patterns.
50   if (const Stmt *S = TopLayer->getTriggerStmt()) {
51     if (const auto *DS = dyn_cast<DeclStmt>(S)) {
52       assert(TopLayer->isLast());
53       return create<SimpleVariableConstructionContext>(C, DS);
54     }
55     if (const auto *NE = dyn_cast<CXXNewExpr>(S)) {
56       assert(TopLayer->isLast());
57       return create<NewAllocatedObjectConstructionContext>(C, NE);
58     }
59     if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(S)) {
60       const MaterializeTemporaryExpr *MTE = nullptr;
61       assert(BTE->getType().getCanonicalType()
62                 ->getAsCXXRecordDecl()->hasNonTrivialDestructor());
63       // For temporaries with destructors, there may or may not be
64       // lifetime extension on the parent layer.
65       if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) {
66         // C++17 *requires* elision of the constructor at the return site
67         // and at variable/member initialization site, while previous standards
68         // were allowing an optional elidable constructor.
69         // This is the C++17 copy-elided construction into a ctor initializer.
70         if (const CXXCtorInitializer *I = ParentLayer->getTriggerInit()) {
71           return create<
72               CXX17ElidedCopyConstructorInitializerConstructionContext>(C,
73                                                                         I, BTE);
74         }
75         assert(ParentLayer->getTriggerStmt() &&
76                "Non-statement-based layers have been handled above!");
77         // This is the normal, non-C++17 case: a temporary object which has
78         // both destruction and materialization info attached to it in the AST.
79         if ((MTE = dyn_cast<MaterializeTemporaryExpr>(
80                  ParentLayer->getTriggerStmt()))) {
81           if (MTE->getStorageDuration() != SD_FullExpression) {
82             // If the temporary is lifetime-extended, don't save the BTE,
83             // because we don't need a temporary destructor, but an automatic
84             // destructor.
85             BTE = nullptr;
86           }
87 
88           // Handle pre-C++17 copy and move elision.
89           const CXXConstructExpr *ElidedCE = nullptr;
90           const ConstructionContext *ElidedCC = nullptr;
91           if (const ConstructionContextLayer *ElidedLayer =
92                   ParentLayer->getParent()) {
93             ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt());
94             assert(ElidedCE->isElidable());
95             // We're creating a construction context that might have already
96             // been created elsewhere. Maybe we should unique our construction
97             // contexts. That's what we often do, but in this case it's unlikely
98             // to bring any benefits.
99             ElidedCC = createFromLayers(C, ElidedLayer->getParent());
100             if (!ElidedCC) {
101               // We may fail to create the elided construction context.
102               // In this case, skip copy elision entirely.
103               return create<SimpleTemporaryObjectConstructionContext>(C, BTE,
104                                                                       MTE);
105             } else {
106               return create<ElidedTemporaryObjectConstructionContext>(
107                   C, BTE, MTE, ElidedCE, ElidedCC);
108             }
109           }
110           assert(ParentLayer->isLast());
111           return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE);
112         }
113         assert(ParentLayer->isLast());
114 
115         // This is a constructor into a function argument.
116         if (isa<CallExpr>(ParentLayer->getTriggerStmt()) ||
117             isa<CXXConstructExpr>(ParentLayer->getTriggerStmt()) ||
118             isa<ObjCMessageExpr>(ParentLayer->getTriggerStmt())) {
119           return create<ArgumentConstructionContext>(
120               C, cast<Expr>(ParentLayer->getTriggerStmt()),
121               ParentLayer->getIndex(), BTE);
122         }
123         // This is C++17 copy-elided construction into return statement.
124         if (auto *RS = dyn_cast<ReturnStmt>(ParentLayer->getTriggerStmt())) {
125           assert(!RS->getRetValue()->getType().getCanonicalType()
126                     ->getAsCXXRecordDecl()->hasTrivialDestructor());
127           return create<CXX17ElidedCopyReturnedValueConstructionContext>(C,
128                                                                        RS, BTE);
129         }
130         // This is C++17 copy-elided construction into a simple variable.
131         if (auto *DS = dyn_cast<DeclStmt>(ParentLayer->getTriggerStmt())) {
132           assert(!cast<VarDecl>(DS->getSingleDecl())->getType()
133                       .getCanonicalType()->getAsCXXRecordDecl()
134                       ->hasTrivialDestructor());
135           return create<CXX17ElidedCopyVariableConstructionContext>(C, DS, BTE);
136         }
137         llvm_unreachable("Unexpected construction context with destructor!");
138       }
139       // A temporary object that doesn't require materialization.
140       // In particular, it shouldn't require copy elision, because
141       // copy/move constructors take a reference, which requires
142       // materialization to obtain the glvalue.
143       return create<SimpleTemporaryObjectConstructionContext>(C, BTE,
144                                                               /*MTE=*/nullptr);
145     }
146     if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) {
147       // If the object requires destruction and is not lifetime-extended,
148       // then it must have a BTE within its MTE.
149       // FIXME: This should be an assertion.
150       if (!(MTE->getType().getCanonicalType()
151                 ->getAsCXXRecordDecl()->hasTrivialDestructor() ||
152              MTE->getStorageDuration() != SD_FullExpression))
153         return nullptr;
154 
155       // Handle pre-C++17 copy and move elision.
156       const CXXConstructExpr *ElidedCE = nullptr;
157       const ConstructionContext *ElidedCC = nullptr;
158       if (const ConstructionContextLayer *ElidedLayer = TopLayer->getParent()) {
159         ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt());
160         assert(ElidedCE->isElidable());
161         // We're creating a construction context that might have already
162         // been created elsewhere. Maybe we should unique our construction
163         // contexts. That's what we often do, but in this case it's unlikely
164         // to bring any benefits.
165         ElidedCC = createFromLayers(C, ElidedLayer->getParent());
166         if (!ElidedCC) {
167           // We may fail to create the elided construction context.
168           // In this case, skip copy elision entirely.
169           return create<SimpleTemporaryObjectConstructionContext>(C, nullptr,
170                                                                   MTE);
171         }
172         return create<ElidedTemporaryObjectConstructionContext>(
173             C, nullptr, MTE, ElidedCE, ElidedCC);
174       }
175       assert(TopLayer->isLast());
176       return create<SimpleTemporaryObjectConstructionContext>(C, nullptr, MTE);
177     }
178     if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
179       assert(TopLayer->isLast());
180       return create<SimpleReturnedValueConstructionContext>(C, RS);
181     }
182     // This is a constructor into a function argument.
183     if (isa<CallExpr>(TopLayer->getTriggerStmt()) ||
184         isa<CXXConstructExpr>(TopLayer->getTriggerStmt()) ||
185         isa<ObjCMessageExpr>(TopLayer->getTriggerStmt())) {
186       assert(TopLayer->isLast());
187       return create<ArgumentConstructionContext>(
188           C, cast<Expr>(TopLayer->getTriggerStmt()), TopLayer->getIndex(),
189           /*BTE=*/nullptr);
190     }
191     llvm_unreachable("Unexpected construction context with statement!");
192   } else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {
193     assert(TopLayer->isLast());
194     return create<SimpleConstructorInitializerConstructionContext>(C, I);
195   }
196   llvm_unreachable("Unexpected construction context!");
197 }
198