217 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- 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!");
 | |
| }
 |