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!");
|
||
|
}
|