Home | History | Annotate | Line # | Download | only in Analysis
      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 
     19 using namespace clang;
     20 
     21 const ConstructionContextLayer *
     22 ConstructionContextLayer::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 
     30 bool 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 
     44 const ConstructionContext *
     45 ConstructionContext::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 
     95 const 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 
    164 const 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