diff options
Diffstat (limited to 'clang/lib/Analysis/ConstructionContext.cpp')
| -rw-r--r-- | clang/lib/Analysis/ConstructionContext.cpp | 216 | 
1 files changed, 216 insertions, 0 deletions
| diff --git a/clang/lib/Analysis/ConstructionContext.cpp b/clang/lib/Analysis/ConstructionContext.cpp new file mode 100644 index 000000000000..6ba1e2173d2c --- /dev/null +++ b/clang/lib/Analysis/ConstructionContext.cpp @@ -0,0 +1,216 @@ +//===- ConstructionContext.cpp - CFG constructor information --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the ConstructionContext class and its sub-classes, +// which represent various different ways of constructing C++ objects +// with the additional information the users may want to know about +// the constructor. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/ConstructionContext.h" +#include "clang/AST/ExprObjC.h" + +using namespace clang; + +const ConstructionContextLayer * +ConstructionContextLayer::create(BumpVectorContext &C, +                                 const ConstructionContextItem &Item, +                                 const ConstructionContextLayer *Parent) { +  ConstructionContextLayer *CC = +      C.getAllocator().Allocate<ConstructionContextLayer>(); +  return new (CC) ConstructionContextLayer(Item, Parent); +} + +bool ConstructionContextLayer::isStrictlyMoreSpecificThan( +    const ConstructionContextLayer *Other) const { +  const ConstructionContextLayer *Self = this; +  while (true) { +    if (!Other) +      return Self; +    if (!Self || !(Self->Item == Other->Item)) +      return false; +    Self = Self->getParent(); +    Other = Other->getParent(); +  } +  llvm_unreachable("The above loop can only be terminated via return!"); +} + +const ConstructionContext * +ConstructionContext::createMaterializedTemporaryFromLayers( +    BumpVectorContext &C, const MaterializeTemporaryExpr *MTE, +    const CXXBindTemporaryExpr *BTE, +    const ConstructionContextLayer *ParentLayer) { +  assert(MTE); + +  // If the object requires destruction and is not lifetime-extended, +  // then it must have a BTE within its MTE, otherwise it shouldn't. +  // FIXME: This should be an assertion. +  if (!BTE && !(MTE->getType().getCanonicalType()->getAsCXXRecordDecl() +                    ->hasTrivialDestructor() || +                MTE->getStorageDuration() != SD_FullExpression)) { +    return nullptr; +  } + +  // If the temporary is lifetime-extended, don't save the BTE, +  // because we don't need a temporary destructor, but an automatic +  // destructor. +  if (MTE->getStorageDuration() != SD_FullExpression) { +    BTE = nullptr; +  } + +  // Handle pre-C++17 copy and move elision. +  const CXXConstructExpr *ElidedCE = nullptr; +  const ConstructionContext *ElidedCC = nullptr; +  if (ParentLayer) { +    const ConstructionContextItem &ElidedItem = ParentLayer->getItem(); +    assert(ElidedItem.getKind() == +           ConstructionContextItem::ElidableConstructorKind); +    ElidedCE = cast<CXXConstructExpr>(ElidedItem.getStmt()); +    assert(ElidedCE->isElidable()); +    // We're creating a construction context that might have already +    // been created elsewhere. Maybe we should unique our construction +    // contexts. That's what we often do, but in this case it's unlikely +    // to bring any benefits. +    ElidedCC = createFromLayers(C, ParentLayer->getParent()); +    if (!ElidedCC) { +      // We may fail to create the elided construction context. +      // In this case, skip copy elision entirely. +      return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE); +    } +    return create<ElidedTemporaryObjectConstructionContext>( +        C, BTE, MTE, ElidedCE, ElidedCC); +  } + +  // This is a normal temporary. +  assert(!ParentLayer); +  return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE); +} + +const ConstructionContext *ConstructionContext::createBoundTemporaryFromLayers( +    BumpVectorContext &C, const CXXBindTemporaryExpr *BTE, +    const ConstructionContextLayer *ParentLayer) { +  if (!ParentLayer) { +    // A temporary object that doesn't require materialization. +    // In particular, it shouldn't require copy elision, because +    // copy/move constructors take a reference, which requires +    // materialization to obtain the glvalue. +    return create<SimpleTemporaryObjectConstructionContext>(C, BTE, +                                                            /*MTE=*/nullptr); +  } + +  const ConstructionContextItem &ParentItem = ParentLayer->getItem(); +  switch (ParentItem.getKind()) { +  case ConstructionContextItem::VariableKind: { +    const auto *DS = cast<DeclStmt>(ParentItem.getStmt()); +    assert(!cast<VarDecl>(DS->getSingleDecl())->getType().getCanonicalType() +                            ->getAsCXXRecordDecl()->hasTrivialDestructor()); +    return create<CXX17ElidedCopyVariableConstructionContext>(C, DS, BTE); +  } +  case ConstructionContextItem::NewAllocatorKind: { +    llvm_unreachable("This context does not accept a bound temporary!"); +  } +  case ConstructionContextItem::ReturnKind: { +    assert(ParentLayer->isLast()); +    const auto *RS = cast<ReturnStmt>(ParentItem.getStmt()); +    assert(!RS->getRetValue()->getType().getCanonicalType() +              ->getAsCXXRecordDecl()->hasTrivialDestructor()); +    return create<CXX17ElidedCopyReturnedValueConstructionContext>(C, RS, +                                                                   BTE); +  } + +  case ConstructionContextItem::MaterializationKind: { +    // No assert. We may have an elidable copy on the grandparent layer. +    const auto *MTE = cast<MaterializeTemporaryExpr>(ParentItem.getStmt()); +    return createMaterializedTemporaryFromLayers(C, MTE, BTE, +                                                 ParentLayer->getParent()); +  } +  case ConstructionContextItem::TemporaryDestructorKind: { +    llvm_unreachable("Duplicate CXXBindTemporaryExpr in the AST!"); +  } +  case ConstructionContextItem::ElidedDestructorKind: { +    llvm_unreachable("Elided destructor items are not produced by the CFG!"); +  } +  case ConstructionContextItem::ElidableConstructorKind: { +    llvm_unreachable("Materialization is necessary to put temporary into a " +                     "copy or move constructor!"); +  } +  case ConstructionContextItem::ArgumentKind: { +    assert(ParentLayer->isLast()); +    const auto *E = cast<Expr>(ParentItem.getStmt()); +    assert(isa<CallExpr>(E) || isa<CXXConstructExpr>(E) || +           isa<ObjCMessageExpr>(E)); +    return create<ArgumentConstructionContext>(C, E, ParentItem.getIndex(), +                                               BTE); +  } +  case ConstructionContextItem::InitializerKind: { +    assert(ParentLayer->isLast()); +    const auto *I = ParentItem.getCXXCtorInitializer(); +    assert(!I->getAnyMember()->getType().getCanonicalType() +             ->getAsCXXRecordDecl()->hasTrivialDestructor()); +    return create<CXX17ElidedCopyConstructorInitializerConstructionContext>( +        C, I, BTE); +  } +  } // switch (ParentItem.getKind()) + +  llvm_unreachable("Unexpected construction context with destructor!"); +} + +const ConstructionContext *ConstructionContext::createFromLayers( +    BumpVectorContext &C, const ConstructionContextLayer *TopLayer) { +  // Before this point all we've had was a stockpile of arbitrary layers. +  // Now validate that it is shaped as one of the finite amount of expected +  // patterns. +  const ConstructionContextItem &TopItem = TopLayer->getItem(); +  switch (TopItem.getKind()) { +  case ConstructionContextItem::VariableKind: { +    assert(TopLayer->isLast()); +    const auto *DS = cast<DeclStmt>(TopItem.getStmt()); +    return create<SimpleVariableConstructionContext>(C, DS); +  } +  case ConstructionContextItem::NewAllocatorKind: { +    assert(TopLayer->isLast()); +    const auto *NE = cast<CXXNewExpr>(TopItem.getStmt()); +    return create<NewAllocatedObjectConstructionContext>(C, NE); +  } +  case ConstructionContextItem::ReturnKind: { +    assert(TopLayer->isLast()); +    const auto *RS = cast<ReturnStmt>(TopItem.getStmt()); +    return create<SimpleReturnedValueConstructionContext>(C, RS); +  } +  case ConstructionContextItem::MaterializationKind: { +    const auto *MTE = cast<MaterializeTemporaryExpr>(TopItem.getStmt()); +    return createMaterializedTemporaryFromLayers(C, MTE, /*BTE=*/nullptr, +                                                 TopLayer->getParent()); +  } +  case ConstructionContextItem::TemporaryDestructorKind: { +    const auto *BTE = cast<CXXBindTemporaryExpr>(TopItem.getStmt()); +    assert(BTE->getType().getCanonicalType()->getAsCXXRecordDecl() +              ->hasNonTrivialDestructor()); +    return createBoundTemporaryFromLayers(C, BTE, TopLayer->getParent()); +  } +  case ConstructionContextItem::ElidedDestructorKind: { +    llvm_unreachable("Elided destructor items are not produced by the CFG!"); +  } +  case ConstructionContextItem::ElidableConstructorKind: { +    llvm_unreachable("The argument needs to be materialized first!"); +  } +  case ConstructionContextItem::InitializerKind: { +    assert(TopLayer->isLast()); +    const CXXCtorInitializer *I = TopItem.getCXXCtorInitializer(); +    return create<SimpleConstructorInitializerConstructionContext>(C, I); +  } +  case ConstructionContextItem::ArgumentKind: { +    assert(TopLayer->isLast()); +    const auto *E = cast<Expr>(TopItem.getStmt()); +    return create<ArgumentConstructionContext>(C, E, TopItem.getIndex(), +                                               /*BTE=*/nullptr); +  } +  } // switch (TopItem.getKind()) +  llvm_unreachable("Unexpected construction context!"); +} | 
