Skip to content

Commit 4068481

Browse files
committed
[CFG] NFC: Refactor ConstructionContext into a finite set of cases.
ConstructionContext is moved into a separate translation unit and is separated into multiple classes. The "old" "raw" ConstructionContext is renamed into ConstructionContextLayer - which corresponds to the idea of building the context gradually layer-by-layer, but it isn't easy to use in the clients. Once CXXConstructExpr is reached, layers that we've gathered so far are transformed into the actual, "new-style" "flat" ConstructionContext, which is put into the CFGConstructor element and has no layers whatsoever (until it actually needs them, eg. aggregate initialization). The new-style ConstructionContext is instead presented as a variety of sub-classes that enumerate different ways of constructing an object in C++. There are 5 of these supported for now, which is around a half of what needs to be supported. The layer-by-layer buildup process is still a little bit weird, but it hides all the weirdness in one place, that sounds like a good thing. Differential Revision: https://reviews.llvm.org/D43533 llvm-svn: 326238
1 parent 3019910 commit 4068481

File tree

7 files changed

+496
-205
lines changed

7 files changed

+496
-205
lines changed

clang/include/clang/Analysis/CFG.h

Lines changed: 2 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ namespace clang {
3838
class ASTContext;
3939
class BinaryOperator;
4040
class CFG;
41+
class ConstructionContext;
4142
class CXXBaseSpecifier;
4243
class CXXBindTemporaryExpr;
4344
class CXXCtorInitializer;
@@ -140,102 +141,14 @@ class CFGStmt : public CFGElement {
140141
CFGStmt() = default;
141142
};
142143

143-
// This is bulky data for CFGConstructor which would not fit into the
144-
// CFGElement's room (pair of pointers). Contains the information
145-
// necessary to express what memory is being initialized by
146-
// the construction.
147-
class ConstructionContext {
148-
public:
149-
typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
150-
151-
private:
152-
// The construction site - the statement that triggered the construction
153-
// for one of its parts. For instance, stack variable declaration statement
154-
// triggers construction of itself or its elements if it's an array,
155-
// new-expression triggers construction of the newly allocated object(s).
156-
TriggerTy Trigger;
157-
158-
// Sometimes a single trigger is not enough to describe the construction site.
159-
// In this case we'd have a chain of "partial" construction contexts.
160-
// Some examples:
161-
// - A constructor within in an aggregate initializer list within a variable
162-
// would have a construction context of the initializer list with the parent
163-
// construction context of a variable.
164-
// - A constructor for a temporary that needs to be both destroyed
165-
// and materialized into an elidable copy constructor would have a
166-
// construction context of a CXXBindTemporaryExpr with the parent
167-
// construction context of a MaterializeTemproraryExpr.
168-
// Not all of these are currently supported.
169-
const ConstructionContext *Parent = nullptr;
170-
171-
ConstructionContext() = default;
172-
ConstructionContext(TriggerTy Trigger, const ConstructionContext *Parent)
173-
: Trigger(Trigger), Parent(Parent) {}
174-
175-
public:
176-
static const ConstructionContext *
177-
create(BumpVectorContext &C, TriggerTy Trigger,
178-
const ConstructionContext *Parent = nullptr) {
179-
ConstructionContext *CC = C.getAllocator().Allocate<ConstructionContext>();
180-
return new (CC) ConstructionContext(Trigger, Parent);
181-
}
182-
183-
bool isNull() const { return Trigger.isNull(); }
184-
185-
TriggerTy getTrigger() const { return Trigger; }
186-
const ConstructionContext *getParent() const { return Parent; }
187-
188-
const Stmt *getTriggerStmt() const {
189-
return Trigger.dyn_cast<Stmt *>();
190-
}
191-
192-
const CXXCtorInitializer *getTriggerInit() const {
193-
return Trigger.dyn_cast<CXXCtorInitializer *>();
194-
}
195-
196-
const MaterializeTemporaryExpr *getMaterializedTemporary() const {
197-
// TODO: Be more careful to ensure that there's only one MTE around.
198-
for (const ConstructionContext *CC = this; CC; CC = CC->getParent()) {
199-
if (const auto *MTE = dyn_cast_or_null<MaterializeTemporaryExpr>(
200-
CC->getTriggerStmt())) {
201-
return MTE;
202-
}
203-
}
204-
return nullptr;
205-
}
206-
207-
bool isSameAsPartialContext(const ConstructionContext *Other) const {
208-
assert(Other);
209-
return (Trigger == Other->Trigger);
210-
}
211-
212-
// See if Other is a proper initial segment of this construction context
213-
// in terms of the parent chain - i.e. a few first parents coincide and
214-
// then the other context terminates but our context goes further - i.e.,
215-
// we are providing the same context that the other context provides,
216-
// and a bit more above that.
217-
bool isStrictlyMoreSpecificThan(const ConstructionContext *Other) const {
218-
const ConstructionContext *Self = this;
219-
while (true) {
220-
if (!Other)
221-
return Self;
222-
if (!Self || !Self->isSameAsPartialContext(Other))
223-
return false;
224-
Self = Self->getParent();
225-
Other = Other->getParent();
226-
}
227-
llvm_unreachable("The above loop can only be terminated via return!");
228-
}
229-
};
230-
231144
/// CFGConstructor - Represents C++ constructor call. Maintains information
232145
/// necessary to figure out what memory is being initialized by the
233146
/// constructor expression. For now this is only used by the analyzer's CFG.
234147
class CFGConstructor : public CFGStmt {
235148
public:
236149
explicit CFGConstructor(CXXConstructExpr *CE, const ConstructionContext *C)
237150
: CFGStmt(CE, Constructor) {
238-
assert(!C->isNull());
151+
assert(C);
239152
Data2.setPointer(const_cast<ConstructionContext *>(C));
240153
}
241154

@@ -247,22 +160,6 @@ class CFGConstructor : public CFGStmt {
247160
return cast<CXXConstructExpr>(getStmt())->getType();
248161
}
249162

250-
ConstructionContext::TriggerTy getTrigger() const {
251-
return getConstructionContext()->getTrigger();
252-
}
253-
254-
const Stmt *getTriggerStmt() const {
255-
return getConstructionContext()->getTriggerStmt();
256-
}
257-
258-
const CXXCtorInitializer *getTriggerInit() const {
259-
return getConstructionContext()->getTriggerInit();
260-
}
261-
262-
const MaterializeTemporaryExpr *getMaterializedTemporary() const {
263-
return getConstructionContext()->getMaterializedTemporary();
264-
}
265-
266163
private:
267164
friend class CFGElement;
268165

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
//===- ConstructionContext.h - CFG constructor information ------*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This file defines the ConstructionContext class and its sub-classes,
11+
// which represent various different ways of constructing C++ objects
12+
// with the additional information the users may want to know about
13+
// the constructor.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
18+
#define LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
19+
20+
#include "clang/Analysis/Support/BumpVector.h"
21+
#include "clang/AST/ExprCXX.h"
22+
23+
namespace clang {
24+
25+
/// Construction context is a linked list of multiple layers. Layers are
26+
/// created gradually while traversing the AST, and layers that represent
27+
/// the outmost AST nodes are built first, while the node that immediately
28+
/// contains the constructor would be built last and capture the previous
29+
/// layers as its parents. Construction context captures the last layer
30+
/// (which has links to the previous layers) and classifies the seemingly
31+
/// arbitrary chain of layers into one of the possible ways of constructing
32+
/// an object in C++ for user-friendly experience.
33+
class ConstructionContextLayer {
34+
public:
35+
typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
36+
37+
private:
38+
/// The construction site - the statement that triggered the construction
39+
/// for one of its parts. For instance, stack variable declaration statement
40+
/// triggers construction of itself or its elements if it's an array,
41+
/// new-expression triggers construction of the newly allocated object(s).
42+
TriggerTy Trigger;
43+
44+
/// Sometimes a single trigger is not enough to describe the construction
45+
/// site. In this case we'd have a chain of "partial" construction context
46+
/// layers.
47+
/// Some examples:
48+
/// - A constructor within in an aggregate initializer list within a variable
49+
/// would have a construction context of the initializer list with
50+
/// the parent construction context of a variable.
51+
/// - A constructor for a temporary that needs to be both destroyed
52+
/// and materialized into an elidable copy constructor would have a
53+
/// construction context of a CXXBindTemporaryExpr with the parent
54+
/// construction context of a MaterializeTemproraryExpr.
55+
/// Not all of these are currently supported.
56+
const ConstructionContextLayer *Parent = nullptr;
57+
58+
ConstructionContextLayer(TriggerTy Trigger,
59+
const ConstructionContextLayer *Parent)
60+
: Trigger(Trigger), Parent(Parent) {}
61+
62+
public:
63+
static const ConstructionContextLayer *
64+
create(BumpVectorContext &C, TriggerTy Trigger,
65+
const ConstructionContextLayer *Parent = nullptr);
66+
67+
const ConstructionContextLayer *getParent() const { return Parent; }
68+
bool isLast() const { return !Parent; }
69+
70+
const Stmt *getTriggerStmt() const {
71+
return Trigger.dyn_cast<Stmt *>();
72+
}
73+
74+
const CXXCtorInitializer *getTriggerInit() const {
75+
return Trigger.dyn_cast<CXXCtorInitializer *>();
76+
}
77+
78+
/// Returns true if these layers are equal as individual layers, even if
79+
/// their parents are different.
80+
bool isSameLayer(const ConstructionContextLayer *Other) const {
81+
assert(Other);
82+
return (Trigger == Other->Trigger);
83+
}
84+
85+
/// See if Other is a proper initial segment of this construction context
86+
/// in terms of the parent chain - i.e. a few first parents coincide and
87+
/// then the other context terminates but our context goes further - i.e.,
88+
/// we are providing the same context that the other context provides,
89+
/// and a bit more above that.
90+
bool isStrictlyMoreSpecificThan(const ConstructionContextLayer *Other) const;
91+
};
92+
93+
94+
/// ConstructionContext's subclasses describe different ways of constructing
95+
/// an object in C++. The context re-captures the essential parent AST nodes
96+
/// of the CXXConstructExpr it is assigned to and presents these nodes
97+
/// through easy-to-understand accessor methods.
98+
class ConstructionContext {
99+
public:
100+
enum Kind {
101+
SimpleVariableKind,
102+
ConstructorInitializerKind,
103+
NewAllocatedObjectKind,
104+
TemporaryObjectKind,
105+
ReturnedValueKind
106+
};
107+
108+
protected:
109+
Kind K;
110+
111+
protected:
112+
// Do not make public! These need to only be constructed
113+
// via createFromLayers().
114+
explicit ConstructionContext(Kind K) : K(K) {}
115+
116+
public:
117+
118+
/// Consume the construction context layer, together with its parent layers,
119+
/// and wrap it up into a complete construction context.
120+
static const ConstructionContext *
121+
createFromLayers(BumpVectorContext &C,
122+
const ConstructionContextLayer *TopLayer);
123+
124+
Kind getKind() const { return K; }
125+
};
126+
127+
/// Represents construction into a simple local variable, eg. T var(123);.
128+
class SimpleVariableConstructionContext : public ConstructionContext {
129+
const DeclStmt *DS;
130+
131+
public:
132+
explicit SimpleVariableConstructionContext(const DeclStmt *DS)
133+
: ConstructionContext(ConstructionContext::SimpleVariableKind), DS(DS) {
134+
assert(DS);
135+
}
136+
137+
const DeclStmt *getDeclStmt() const { return DS; }
138+
139+
static bool classof(const ConstructionContext *CC) {
140+
return CC->getKind() == SimpleVariableKind;
141+
}
142+
};
143+
144+
/// Represents construction into a field or a base class within a bigger object
145+
/// via a constructor initializer, eg. T(): field(123) { ... }.
146+
class ConstructorInitializerConstructionContext : public ConstructionContext {
147+
const CXXCtorInitializer *I;
148+
149+
public:
150+
explicit ConstructorInitializerConstructionContext(
151+
const CXXCtorInitializer *I)
152+
: ConstructionContext(ConstructionContext::ConstructorInitializerKind),
153+
I(I) {
154+
assert(I);
155+
}
156+
157+
const CXXCtorInitializer *getCXXCtorInitializer() const { return I; }
158+
159+
static bool classof(const ConstructionContext *CC) {
160+
return CC->getKind() == ConstructorInitializerKind;
161+
}
162+
};
163+
164+
/// Represents immediate initialization of memory allocated by operator new,
165+
/// eg. new T(123);.
166+
class NewAllocatedObjectConstructionContext : public ConstructionContext {
167+
const CXXNewExpr *NE;
168+
169+
public:
170+
explicit NewAllocatedObjectConstructionContext(const CXXNewExpr *NE)
171+
: ConstructionContext(ConstructionContext::NewAllocatedObjectKind),
172+
NE(NE) {
173+
assert(NE);
174+
}
175+
176+
const CXXNewExpr *getCXXNewExpr() const { return NE; }
177+
178+
static bool classof(const ConstructionContext *CC) {
179+
return CC->getKind() == NewAllocatedObjectKind;
180+
}
181+
};
182+
183+
/// Represents a temporary object, eg. T(123), that does not immediately cross
184+
/// function boundaries "by value"; constructors that construct function
185+
/// value-type arguments or values that are immediately returned from the
186+
/// function that returns a value receive separate construction context kinds.
187+
class TemporaryObjectConstructionContext : public ConstructionContext {
188+
const CXXBindTemporaryExpr *BTE;
189+
const MaterializeTemporaryExpr *MTE;
190+
191+
public:
192+
explicit TemporaryObjectConstructionContext(
193+
const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE)
194+
: ConstructionContext(ConstructionContext::TemporaryObjectKind),
195+
BTE(BTE), MTE(MTE) {
196+
// Both BTE and MTE can be null here, all combinations possible.
197+
// Even though for now at least one should be non-null, we simply haven't
198+
// implemented this case yet (this would be a temporary in the middle of
199+
// nowhere that doesn't have a non-trivial destructor).
200+
}
201+
202+
/// CXXBindTemporaryExpr here is non-null as long as the temporary has
203+
/// a non-trivial destructor.
204+
const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const {
205+
return BTE;
206+
}
207+
208+
/// MaterializeTemporaryExpr is non-null as long as the temporary is actually
209+
/// used after construction, eg. by binding to a reference (lifetime
210+
/// extension), accessing a field, calling a method, or passing it into
211+
/// a function (an elidable copy or move constructor would be a common
212+
/// example) by reference.
213+
const MaterializeTemporaryExpr *getMaterializedTemporaryExpr() const {
214+
return MTE;
215+
}
216+
217+
static bool classof(const ConstructionContext *CC) {
218+
return CC->getKind() == TemporaryObjectKind;
219+
}
220+
};
221+
222+
/// Represents a temporary object that is being immediately returned from a
223+
/// function by value, eg. return t; or return T(123);. In this case there is
224+
/// always going to be a constructor at the return site. However, the usual
225+
/// temporary-related bureaucracy (CXXBindTemporaryExpr,
226+
/// MaterializeTemporaryExpr) is normally located in the caller function's AST.
227+
class ReturnedValueConstructionContext : public ConstructionContext {
228+
const ReturnStmt *RS;
229+
230+
public:
231+
explicit ReturnedValueConstructionContext(const ReturnStmt *RS)
232+
: ConstructionContext(ConstructionContext::ReturnedValueKind), RS(RS) {
233+
assert(RS);
234+
}
235+
236+
const ReturnStmt *getReturnStmt() const { return RS; }
237+
238+
static bool classof(const ConstructionContext *CC) {
239+
return CC->getKind() == ReturnedValueKind;
240+
}
241+
};
242+
243+
} // end namespace clang
244+
245+
#endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H

0 commit comments

Comments
 (0)