1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
//===- ConstructionContext.cpp - CFG constructor information --------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// 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"
using namespace clang;
const ConstructionContextLayer *
ConstructionContextLayer::create(BumpVectorContext &C, TriggerTy Trigger,
const ConstructionContextLayer *Parent) {
ConstructionContextLayer *CC =
C.getAllocator().Allocate<ConstructionContextLayer>();
return new (CC) ConstructionContextLayer(Trigger, Parent);
}
bool ConstructionContextLayer::isStrictlyMoreSpecificThan(
const ConstructionContextLayer *Other) const {
const ConstructionContextLayer *Self = this;
while (true) {
if (!Other)
return Self;
if (!Self || !Self->isSameLayer(Other))
return false;
Self = Self->getParent();
Other = Other->getParent();
}
llvm_unreachable("The above loop can only be terminated via return!");
}
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.
if (const Stmt *S = TopLayer->getTriggerStmt()) {
if (const auto *DS = dyn_cast<DeclStmt>(S)) {
assert(TopLayer->isLast());
return create<SimpleVariableConstructionContext>(C, DS);
}
if (const auto *NE = dyn_cast<CXXNewExpr>(S)) {
assert(TopLayer->isLast());
return create<NewAllocatedObjectConstructionContext>(C, NE);
}
if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(S)) {
const MaterializeTemporaryExpr *MTE = nullptr;
assert(BTE->getType().getCanonicalType()
->getAsCXXRecordDecl()->hasNonTrivialDestructor());
// For temporaries with destructors, there may or may not be
// lifetime extension on the parent layer.
if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) {
// C++17 *requires* elision of the constructor at the return site
// and at variable/member initialization site, while previous standards
// were allowing an optional elidable constructor.
// This is the C++17 copy-elided construction into a ctor initializer.
if (const CXXCtorInitializer *I = ParentLayer->getTriggerInit()) {
return create<
CXX17ElidedCopyConstructorInitializerConstructionContext>(C,
I, BTE);
}
assert(ParentLayer->getTriggerStmt() &&
"Non-statement-based layers have been handled above!");
// This is the normal, non-C++17 case: a temporary object which has
// both destruction and materialization info attached to it in the AST.
if ((MTE = dyn_cast<MaterializeTemporaryExpr>(
ParentLayer->getTriggerStmt()))) {
// Handle pre-C++17 copy and move elision.
const CXXConstructExpr *ElidedCE = nullptr;
const ConstructionContext *ElidedCC = nullptr;
if (const ConstructionContextLayer *ElidedLayer =
ParentLayer->getParent()) {
ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt());
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, ElidedLayer->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);
} else {
return create<ElidedTemporaryObjectConstructionContext>(
C, BTE, MTE, ElidedCE, ElidedCC);
}
}
assert(ParentLayer->isLast());
return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE);
}
assert(ParentLayer->isLast());
// This is a constructor into a function argument. Not implemented yet.
if (isa<CallExpr>(ParentLayer->getTriggerStmt()))
return nullptr;
// This is C++17 copy-elided construction into return statement.
if (auto *RS = dyn_cast<ReturnStmt>(ParentLayer->getTriggerStmt())) {
assert(!RS->getRetValue()->getType().getCanonicalType()
->getAsCXXRecordDecl()->hasTrivialDestructor());
return create<CXX17ElidedCopyReturnedValueConstructionContext>(C,
RS, BTE);
}
// This is C++17 copy-elided construction into a simple variable.
if (auto *DS = dyn_cast<DeclStmt>(ParentLayer->getTriggerStmt())) {
assert(!cast<VarDecl>(DS->getSingleDecl())->getType()
.getCanonicalType()->getAsCXXRecordDecl()
->hasTrivialDestructor());
return create<CXX17ElidedCopyVariableConstructionContext>(C, DS, BTE);
}
llvm_unreachable("Unexpected construction context with destructor!");
}
// 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);
}
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) {
// If the object requires destruction and is not lifetime-extended,
// then it must have a BTE within its MTE.
// FIXME: This should be an assertion.
if (!(MTE->getType().getCanonicalType()
->getAsCXXRecordDecl()->hasTrivialDestructor() ||
MTE->getStorageDuration() != SD_FullExpression))
return nullptr;
// Handle pre-C++17 copy and move elision.
const CXXConstructExpr *ElidedCE = nullptr;
const ConstructionContext *ElidedCC = nullptr;
if (const ConstructionContextLayer *ElidedLayer = TopLayer->getParent()) {
ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt());
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, ElidedLayer->getParent());
if (!ElidedCC) {
// We may fail to create the elided construction context.
// In this case, skip copy elision entirely.
return create<SimpleTemporaryObjectConstructionContext>(C, nullptr,
MTE);
}
return create<ElidedTemporaryObjectConstructionContext>(
C, nullptr, MTE, ElidedCE, ElidedCC);
}
assert(TopLayer->isLast());
return create<SimpleTemporaryObjectConstructionContext>(C, nullptr, MTE);
}
if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
assert(TopLayer->isLast());
return create<SimpleReturnedValueConstructionContext>(C, RS);
}
// This is a constructor into a function argument. Not implemented yet.
if (isa<CallExpr>(TopLayer->getTriggerStmt()))
return nullptr;
llvm_unreachable("Unexpected construction context with statement!");
} else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {
assert(TopLayer->isLast());
return create<SimpleConstructorInitializerConstructionContext>(C, I);
}
llvm_unreachable("Unexpected construction context!");
}
|