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