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