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
19using namespace clang;
20
21const ConstructionContextLayer *
22ConstructionContextLayer::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
30bool 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
44const ConstructionContext *
45ConstructionContext::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
95const 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  } // switch (ParentItem.getKind())
160
161  llvm_unreachable("Unexpected construction context with destructor!");
162}
163
164const ConstructionContext *ConstructionContext::createFromLayers(
165    BumpVectorContext &C, const ConstructionContextLayer *TopLayer) {
166  // Before this point all we've had was a stockpile of arbitrary layers.
167  // Now validate that it is shaped as one of the finite amount of expected
168  // patterns.
169  const ConstructionContextItem &TopItem = TopLayer->getItem();
170  switch (TopItem.getKind()) {
171  case ConstructionContextItem::VariableKind: {
172    assert(TopLayer->isLast());
173    const auto *DS = cast<DeclStmt>(TopItem.getStmt());
174    return create<SimpleVariableConstructionContext>(C, DS);
175  }
176  case ConstructionContextItem::NewAllocatorKind: {
177    assert(TopLayer->isLast());
178    const auto *NE = cast<CXXNewExpr>(TopItem.getStmt());
179    return create<NewAllocatedObjectConstructionContext>(C, NE);
180  }
181  case ConstructionContextItem::ReturnKind: {
182    assert(TopLayer->isLast());
183    const auto *RS = cast<ReturnStmt>(TopItem.getStmt());
184    return create<SimpleReturnedValueConstructionContext>(C, RS);
185  }
186  case ConstructionContextItem::MaterializationKind: {
187    const auto *MTE = cast<MaterializeTemporaryExpr>(TopItem.getStmt());
188    return createMaterializedTemporaryFromLayers(C, MTE, /*BTE=*/nullptr,
189                                                 TopLayer->getParent());
190  }
191  case ConstructionContextItem::TemporaryDestructorKind: {
192    const auto *BTE = cast<CXXBindTemporaryExpr>(TopItem.getStmt());
193    assert(BTE->getType().getCanonicalType()->getAsCXXRecordDecl()
194              ->hasNonTrivialDestructor());
195    return createBoundTemporaryFromLayers(C, BTE, TopLayer->getParent());
196  }
197  case ConstructionContextItem::ElidedDestructorKind: {
198    llvm_unreachable("Elided destructor items are not produced by the CFG!");
199  }
200  case ConstructionContextItem::ElidableConstructorKind: {
201    llvm_unreachable("The argument needs to be materialized first!");
202  }
203  case ConstructionContextItem::InitializerKind: {
204    assert(TopLayer->isLast());
205    const CXXCtorInitializer *I = TopItem.getCXXCtorInitializer();
206    return create<SimpleConstructorInitializerConstructionContext>(C, I);
207  }
208  case ConstructionContextItem::ArgumentKind: {
209    assert(TopLayer->isLast());
210    const auto *E = cast<Expr>(TopItem.getStmt());
211    return create<ArgumentConstructionContext>(C, E, TopItem.getIndex(),
212                                               /*BTE=*/nullptr);
213  }
214  } // switch (TopItem.getKind())
215  llvm_unreachable("Unexpected construction context!");
216}
217