-
Notifications
You must be signed in to change notification settings - Fork 7
/
TypeAnnotations.h
376 lines (324 loc) · 11 KB
/
TypeAnnotations.h
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
#ifndef TYPE_ANNOTATIONS_H
#define TYPE_ANNOTATIONS_H
#include "clang/AST/RecursiveASTVisitor.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "llvm/Support/Debug.h"
#include "clang/Frontend/MultiplexConsumer.h"
#include <algorithm>
#define DEBUG_TYPE "quala"
namespace clang {
// DANGEROUS HACK
// This is to gain access to the frontend's consumer order so we can change it
// (below).
class HackyMultiplexConsumer : public SemaConsumer {
public:
std::vector<ASTConsumer*> Consumers;
};
template<typename ImplClass>
class Annotator : public StmtVisitor<ImplClass> {
public:
CompilerInstance &CI;
ImplClass *impl;
FunctionDecl *CurFunc;
bool Instrument;
Annotator(CompilerInstance &_ci, bool _instrument) :
CI(_ci),
impl(static_cast<ImplClass*>(this)),
CurFunc(NULL),
Instrument(_instrument)
{};
/*** ANNOTATION ASSIGNMENT HELPERS ***/
void AddAnnotation(Expr *E, StringRef A) const {
// TODO check whether it already has the annotation & do nothing
if (A.size()) {
E->setType(CI.getASTContext().getAnnotatedType(E->getType(), A));
}
}
// Remove an annotated type *at the outermost level of the type tree*. For
// example, this will not remove annotations under typedefs (which seems
// impossible).
void RemoveAnnotation(Expr *E) const {
// TODO remove a specific annotation? or all, if multiple?
// Look for an AnnotatedType in the desugaring chain.
QualType T = E->getType();
if (auto *AT = dyn_cast<AnnotatedType>(T)) {
E->setType(AT->getBaseType());
}
}
/*** ANNOTATION LOOKUP HELPERS ***/
// Override this to provide annotations on types regardless of where they
// appear.
llvm::StringRef ImplicitAnnotation(const QualType QT) const {
return StringRef();
}
llvm::StringRef AnnotationOf(const Type *T) const {
// TODO multiple annotations?
if (auto *AT = llvm::dyn_cast<AnnotatedType>(T)) {
return AT->getAnnotation();
} else {
return StringRef();
}
}
llvm::StringRef AnnotationOf(QualType QT) const {
// Try implicit annotation.
StringRef ImplicitAnn = impl->ImplicitAnnotation(QT);
if (ImplicitAnn.size()) {
return ImplicitAnn;
}
// Look up the annotation.
auto &Ctx = CI.getASTContext();
for (;;) {
const Type *T = QT.getTypePtrOrNull();
if (!T) {
return StringRef();
}
StringRef Ann = AnnotationOf(T);
if (Ann.size())
return Ann;
// Try stripping away one level of sugar.
QualType DT = QT.getSingleStepDesugaredType(Ctx);
if (DT == QT) {
break;
} else {
QT = DT;
}
}
// No annotations found in desugaring sequence.
return StringRef();
}
llvm::StringRef AnnotationOf(const Expr *E) const {
if (!E) {
return StringRef();
} else {
return AnnotationOf(E->getType());
}
}
llvm::StringRef AnnotationOf(const ValueDecl *D) const {
if (!D) {
return StringRef();
} else {
return AnnotationOf(D->getType());
}
}
bool SamePointerTypeAnnotations(QualType T1, QualType T2,
bool outer=true) const {
// Optionally check the annotation on the types themselves.
if (outer && AnnotationOf(T1) != AnnotationOf(T2))
return false;
// Unwrap pointer types to check that they have identical qualifiers in
// their pointed-to types.
ASTContext &Ctx = CI.getASTContext();
while (Ctx.UnwrapSimilarPointerTypes(T1, T2)) {
if (AnnotationOf(T1) != AnnotationOf(T2)) {
return false;
}
}
return true; // Identical annotations.
}
// Utility for checking compatibility when pointer types are invariant
// (i.e., if you are sane). Returns true for non-pointer types. For pointer
// (and reference types), ensures that every annotation *below the top
// layer* is identical between the two types. You are supposed to check the
// top layer yourself, since those annotations apply to value types (i.e.,
// the pointers themselves).
bool CheckPointerInvariance(QualType LTy, QualType RTy) const {
if (LTy->isReferenceType() && !RTy->isReferenceType()) {
// Reference binding. Strip off the LHS's reference and compare from
// there.
return SamePointerTypeAnnotations(LTy->getPointeeType(), RTy, true);
} else if (LTy->isPointerType() && RTy->isPointerType()) {
// Must have identical annotations (either direction of flow is an
// error). Enforce comparison after the top level.
return SamePointerTypeAnnotations(LTy, RTy, false);
} else {
// Non-pointer type. Above check suffices.
return true;
}
}
/*** SUBTYPING (COMPATIBILITY) CHECKS ***/
// For subclasses to override: determine compatibility of two types.
bool Compatible(QualType LTy, QualType RTy) const {
return true;
}
// Raise an "incompatible" error if the expressions are not compatible. Don't
// override this.
void AssertCompatible(Stmt *S, QualType LTy, QualType RTy) const {
if (!impl->Compatible(LTy, RTy)) {
impl->EmitIncompatibleError(S, LTy, RTy);
}
}
DiagnosticsEngine &Diags() const {
return CI.getDiagnostics();
}
void EmitIncompatibleError(clang::Stmt* S, QualType LTy,
QualType RTy) {
unsigned did = Diags().getCustomDiagID(
DiagnosticsEngine::Error,
"%0 incompatible with %1"
);
StringRef LAnn = AnnotationOf(LTy);
StringRef RAnn = AnnotationOf(RTy);
Diags().Report(S->getLocStart(), did)
<< (RAnn.size() ? RAnn : "unannotated")
<< (LAnn.size() ? LAnn : "unannotated")
<< CharSourceRange(S->getSourceRange(), false);
}
/*** DEFAULT TYPING RULES ***/
// Assignment compatibility.
void VisitBinAssign(BinaryOperator *E) {
AssertCompatible(E, E->getLHS()->getType(), E->getRHS()->getType());
AddAnnotation(E, AnnotationOf(E->getLHS()));
}
void VisitCompoundAssignOperator(CompoundAssignOperator *E) {
AssertCompatible(E, E->getLHS()->getType(), E->getRHS()->getType());
AddAnnotation(E, AnnotationOf(E->getLHS()));
}
// Declaration initializers treated like assignments.
void VisitDeclStmt(DeclStmt *S) {
for (auto i = S->decl_begin(); i != S->decl_end(); ++i) {
auto *VD = dyn_cast<VarDecl>(*i);
if (VD) {
Expr *Init = VD->getInit();
if (Init) {
AssertCompatible(Init, VD->getType(), Init->getType());
}
}
}
}
// Propagate types through implicit casts and other "invisible" expressions.
void VisitImplicitCastExpr(ImplicitCastExpr *E) {
AddAnnotation(E, AnnotationOf(E->getSubExpr()));
}
void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) {
AddAnnotation(E, AnnotationOf(E->GetTemporaryExpr()));
}
// Visit all kinds of call expressions the same way.
void visitCall(CallExpr *E) {
// Check parameter types.
FunctionDecl *D = E->getDirectCallee();
if (D) {
// Check parameter types.
if (D->param_size() == E->getNumArgs()) {
auto pi = D->param_begin();
auto ai = E->arg_begin();
for (; pi != D->param_end() && ai != E->arg_end(); ++pi, ++ai) {
AssertCompatible(*ai, (*pi)->getType(), (*ai)->getType());
}
} else {
// Parameter list length mismatch. Probably a varargs function. FIXME?
DEBUG(llvm::errs() << "UNSOUND: varargs function\n");
}
AddAnnotation(E, AnnotationOf(D->getReturnType()));
} else {
// We couldn't determine which function is being called. Unsoundly, we
// check nothing and return the null type. FIXME?
DEBUG(llvm::errs() << "UNSOUND: indirect call\n");
}
}
void VisitCallExpr(CallExpr *E) {
visitCall(E);
}
void VisitCXXMemberCallExpr(CallExpr *E) {
visitCall(E);
}
void VisitCXXOperatorCallExpr(CallExpr *E) {
visitCall(E);
}
void VisitReturnStmt(ReturnStmt *S) {
Expr *E = S->getRetValue();
if (E) {
assert(CurFunc && "return outside of function?");
AssertCompatible(S, CurFunc->getReturnType(), E->getType());
}
}
};
// Hack to go bottom-up (postorder) on statements.
template<typename AnnotatorClass>
class TAVisitor : public RecursiveASTVisitor< TAVisitor<AnnotatorClass> > {
public:
AnnotatorClass *Annotator;
bool SkipHeaders;
bool TraverseStmt(Stmt *S) {
// Super traversal: visit children.
RecursiveASTVisitor<TAVisitor>::TraverseStmt(S);
// Now give type to parent.
if (S) {
Annotator->Visit(S);
}
return true;
}
bool TraverseDecl(Decl *D) {
// Skip traversal of declarations in system headers.
if (SkipHeaders) {
auto Loc = D->getLocation();
if (Loc.isValid()) {
auto &Ctx = Annotator->CI.getASTContext();
if (Ctx.getSourceManager().isInSystemHeader(Loc)) {
// Do not traverse any children.
return true;
}
}
}
// Tell the annotator which function it's inside.
auto *Func = dyn_cast<FunctionDecl>(D);
if (Func)
Annotator->CurFunc = Func;
bool r = RecursiveASTVisitor< TAVisitor<AnnotatorClass> >::TraverseDecl(D);
if (Func)
Annotator->CurFunc = NULL;
return r;
}
// Disable "data recursion", which skips calls to Traverse*.
bool shouldUseDataRecursionFor(Stmt *S) const { return false; }
};
template<typename AnnotatorClass>
class TAConsumer : public ASTConsumer {
public:
CompilerInstance &CI;
TAVisitor<AnnotatorClass> Visitor;
AnnotatorClass Annotator;
bool Instrument;
TAConsumer(CompilerInstance &_ci, bool _instrument) :
CI(_ci),
Annotator(_ci, _instrument),
Instrument(_instrument)
{}
virtual void Initialize(ASTContext &Context) {
Visitor.Annotator = &Annotator;
Visitor.SkipHeaders = true; // TODO configurable?
if (Instrument) {
// DANGEROUS HACK
// Change the order of the frontend's AST consumers. The
// MultiplexConsumer contains references to (a) the "real" Clang
// consumer, which does codegen and such, and (b) all the plugin
// consumers. We need to move this plugin to the front of the list to
// run before codegen. This is *certainly* unsupported and, to make it
// even worse, requires hackery to access the private consumer vector
// in MultiplexConsumer. Too bad.
auto *mxc = cast<MultiplexConsumer>(&CI.getASTConsumer());
auto *hmxc = (HackyMultiplexConsumer *)mxc;
// Find this consumer in the list.
auto it = std::find(hmxc->Consumers.begin(),
hmxc->Consumers.end(),
this);
assert(*it == this && "consumer not found in multiplex list");
// Move this from its current position to the first position.
hmxc->Consumers.erase(it);
hmxc->Consumers.insert(hmxc->Consumers.begin(), this);
}
}
virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
for (auto it : DG) {
Visitor.TraverseDecl(it);
}
return true;
}
};
}
#undef DEBUG_TYPE
#endif