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