1 //===- ThreadSafetyTraverse.h ----------------------------------*- C++ --*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines a framework for doing generic traversals and rewriting
11 // operations over the Thread Safety TIL.
13 // UNDER CONSTRUCTION. USE AT YOUR OWN RISK.
15 //===----------------------------------------------------------------------===//
17 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETYTRAVERSE_H
18 #define LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETYTRAVERSE_H
20 #include "ThreadSafetyTIL.h"
24 namespace threadSafety {
27 // Defines an interface used to traverse SExprs. Traversals have been made as
28 // generic as possible, and are intended to handle any kind of pass over the
29 // AST, e.g. visiters, copying, non-destructive rewriting, destructive
30 // (in-place) rewriting, hashing, typing, etc.
32 // Traversals implement the functional notion of a "fold" operation on SExprs.
33 // Each SExpr class provides a traverse method, which does the following:
35 // // compute a result r_i for each subexpression e_i
36 // for (i = 1..n) r_i = v.traverse(e_i);
37 // // combine results into a result for e, where X is the class of e
38 // return v.reduceX(*e, r_1, .. r_n).
40 // A visitor can control the traversal by overriding the following methods:
42 // return v.traverseByCase(e), which returns v.traverseX(e)
43 // * v.traverseX(e): (X is the class of e)
44 // return e->traverse(v).
45 // * v.reduceX(*e, r_1, .. r_n):
46 // compute a result for a node of type X
48 // The reduceX methods control the kind of traversal (visitor, copy, etc.).
49 // They are defined in derived classes.
51 // Class R defines the basic interface types (R_SExpr).
52 template <class Self, class R>
55 Self *self() { return static_cast<Self *>(this); }
57 // Traverse an expression -- returning a result of type R_SExpr.
58 // Override this method to do something for every expression, regardless
59 // of which kind it is.
60 // E is a reference, so this can be use for in-place updates.
61 // The type T must be a subclass of SExpr.
63 typename R::R_SExpr traverse(T* &E, typename R::R_Ctx Ctx) {
64 return traverseSExpr(E, Ctx);
67 // Override this method to do something for every expression.
68 // Does not allow in-place updates.
69 typename R::R_SExpr traverseSExpr(SExpr *E, typename R::R_Ctx Ctx) {
70 return traverseByCase(E, Ctx);
73 // Helper method to call traverseX(e) on the appropriate type.
74 typename R::R_SExpr traverseByCase(SExpr *E, typename R::R_Ctx Ctx) {
75 switch (E->opcode()) {
76 #define TIL_OPCODE_DEF(X) \
78 return self()->traverse##X(cast<X>(E), Ctx);
79 #include "ThreadSafetyOps.def"
82 return self()->reduceNull();
85 // Traverse e, by static dispatch on the type "X" of e.
86 // Override these methods to do something for a particular kind of term.
87 #define TIL_OPCODE_DEF(X) \
88 typename R::R_SExpr traverse##X(X *e, typename R::R_Ctx Ctx) { \
89 return e->traverse(*self(), Ctx); \
91 #include "ThreadSafetyOps.def"
96 // Base class for simple reducers that don't much care about the context.
97 class SimpleReducerBase {
100 TRV_Normal, // ordinary subexpressions
101 TRV_Decl, // declarations (e.g. function bodies)
102 TRV_Lazy, // expressions that require lazy evaluation
103 TRV_Type // type expressions
106 // R_Ctx defines a "context" for the traversal, which encodes information
107 // about where a term appears. This can be used to encoding the
108 // "current continuation" for CPS transforms, or other information.
109 typedef TraversalKind R_Ctx;
111 // Create context for an ordinary subexpression.
112 R_Ctx subExprCtx(R_Ctx Ctx) { return TRV_Normal; }
114 // Create context for a subexpression that occurs in a declaration position
115 // (e.g. function body).
116 R_Ctx declCtx(R_Ctx Ctx) { return TRV_Decl; }
118 // Create context for a subexpression that occurs in a position that
119 // should be reduced lazily. (e.g. code body).
120 R_Ctx lazyCtx(R_Ctx Ctx) { return TRV_Lazy; }
122 // Create context for a subexpression that occurs in a type position.
123 R_Ctx typeCtx(R_Ctx Ctx) { return TRV_Type; }
127 // Base class for traversals that rewrite an SExpr to another SExpr.
128 class CopyReducerBase : public SimpleReducerBase {
130 // R_SExpr is the result type for a traversal.
131 // A copy or non-destructive rewrite returns a newly allocated term.
132 typedef SExpr *R_SExpr;
133 typedef BasicBlock *R_BasicBlock;
135 // Container is a minimal interface used to store results when traversing
136 // SExprs of variable arity, such as Phi, Goto, and SCFG.
137 template <class T> class Container {
139 // Allocate a new container with a capacity for n elements.
140 Container(CopyReducerBase &S, unsigned N) : Elems(S.Arena, N) {}
142 // Push a new element onto the container.
143 void push_back(T E) { Elems.push_back(E); }
145 SimpleArray<T> Elems;
148 CopyReducerBase(MemRegionRef A) : Arena(A) {}
155 // Base class for visit traversals.
156 class VisitReducerBase : public SimpleReducerBase {
158 // A visitor returns a bool, representing success or failure.
159 typedef bool R_SExpr;
160 typedef bool R_BasicBlock;
162 // A visitor "container" is a single bool, which accumulates success.
163 template <class T> class Container {
165 Container(VisitReducerBase &S, unsigned N) : Success(true) {}
166 void push_back(bool E) { Success = Success && E; }
173 // Implements a traversal that visits each subexpression, and returns either
175 template <class Self>
176 class VisitReducer : public Traversal<Self, VisitReducerBase>,
177 public VisitReducerBase {
182 R_SExpr reduceNull() { return true; }
183 R_SExpr reduceUndefined(Undefined &Orig) { return true; }
184 R_SExpr reduceWildcard(Wildcard &Orig) { return true; }
186 R_SExpr reduceLiteral(Literal &Orig) { return true; }
188 R_SExpr reduceLiteralT(LiteralT<T> &Orig) { return true; }
189 R_SExpr reduceLiteralPtr(Literal &Orig) { return true; }
191 R_SExpr reduceFunction(Function &Orig, Variable *Nvd, R_SExpr E0) {
194 R_SExpr reduceSFunction(SFunction &Orig, Variable *Nvd, R_SExpr E0) {
197 R_SExpr reduceCode(Code &Orig, R_SExpr E0, R_SExpr E1) {
200 R_SExpr reduceField(Field &Orig, R_SExpr E0, R_SExpr E1) {
203 R_SExpr reduceApply(Apply &Orig, R_SExpr E0, R_SExpr E1) {
206 R_SExpr reduceSApply(SApply &Orig, R_SExpr E0, R_SExpr E1) {
209 R_SExpr reduceProject(Project &Orig, R_SExpr E0) { return E0; }
210 R_SExpr reduceCall(Call &Orig, R_SExpr E0) { return E0; }
211 R_SExpr reduceAlloc(Alloc &Orig, R_SExpr E0) { return E0; }
212 R_SExpr reduceLoad(Load &Orig, R_SExpr E0) { return E0; }
213 R_SExpr reduceStore(Store &Orig, R_SExpr E0, R_SExpr E1) { return E0 && E1; }
214 R_SExpr reduceArrayIndex(Store &Orig, R_SExpr E0, R_SExpr E1) {
217 R_SExpr reduceArrayAdd(Store &Orig, R_SExpr E0, R_SExpr E1) {
220 R_SExpr reduceUnaryOp(UnaryOp &Orig, R_SExpr E0) { return E0; }
221 R_SExpr reduceBinaryOp(BinaryOp &Orig, R_SExpr E0, R_SExpr E1) {
224 R_SExpr reduceCast(Cast &Orig, R_SExpr E0) { return E0; }
226 R_SExpr reduceSCFG(SCFG &Orig, Container<BasicBlock *> Bbs) {
229 R_BasicBlock reduceBasicBlock(BasicBlock &Orig, Container<R_SExpr> &As,
230 Container<R_SExpr> &Is, R_SExpr T) {
231 return (As.Success && Is.Success && T);
233 R_SExpr reducePhi(Phi &Orig, Container<R_SExpr> &As) {
236 R_SExpr reduceGoto(Goto &Orig, BasicBlock *B) {
239 R_SExpr reduceBranch(Branch &O, R_SExpr C, BasicBlock *B0, BasicBlock *B1) {
242 R_SExpr reduceReturn(Return &O, R_SExpr E) {
246 R_SExpr reduceIdentifier(Identifier &Orig) {
249 R_SExpr reduceIfThenElse(IfThenElse &Orig, R_SExpr C, R_SExpr T, R_SExpr E) {
252 R_SExpr reduceLet(Let &Orig, Variable *Nvd, R_SExpr B) {
256 Variable *enterScope(Variable &Orig, R_SExpr E0) { return &Orig; }
257 void exitScope(const Variable &Orig) {}
258 void enterCFG(SCFG &Cfg) {}
259 void exitCFG(SCFG &Cfg) {}
260 void enterBasicBlock(BasicBlock &BB) {}
261 void exitBasicBlock(BasicBlock &BB) {}
263 Variable *reduceVariableRef (Variable *Ovd) { return Ovd; }
264 BasicBlock *reduceBasicBlockRef(BasicBlock *Obb) { return Obb; }
267 bool traverse(SExpr *E, TraversalKind K = TRV_Normal) {
268 Success = Success && this->traverseByCase(E);
272 static bool visit(SExpr *E) {
274 return Visitor.traverse(E, TRV_Normal);
282 // Basic class for comparison operations over expressions.
283 template <typename Self>
286 Self *self() { return reinterpret_cast<Self *>(this); }
289 bool compareByCase(const SExpr *E1, const SExpr* E2) {
290 switch (E1->opcode()) {
291 #define TIL_OPCODE_DEF(X) \
293 return cast<X>(E1)->compare(cast<X>(E2), *self());
294 #include "ThreadSafetyOps.def"
295 #undef TIL_OPCODE_DEF
302 class EqualsComparator : public Comparator<EqualsComparator> {
304 // Result type for the comparison, e.g. bool for simple equality,
305 // or int for lexigraphic comparison (-1, 0, 1). Must have one value which
309 CType trueResult() { return true; }
310 bool notTrue(CType ct) { return !ct; }
312 bool compareIntegers(unsigned i, unsigned j) { return i == j; }
313 bool compareStrings (StringRef s, StringRef r) { return s == r; }
314 bool comparePointers(const void* P, const void* Q) { return P == Q; }
316 bool compare(const SExpr *E1, const SExpr* E2) {
317 if (E1->opcode() != E2->opcode())
319 return compareByCase(E1, E2);
322 // TODO -- handle alpha-renaming of variables
323 void enterScope(const Variable* V1, const Variable* V2) { }
324 void leaveScope() { }
326 bool compareVariableRefs(const Variable* V1, const Variable* V2) {
330 static bool compareExprs(const SExpr *E1, const SExpr* E2) {
332 return Eq.compare(E1, E2);
338 class MatchComparator : public Comparator<MatchComparator> {
340 // Result type for the comparison, e.g. bool for simple equality,
341 // or int for lexigraphic comparison (-1, 0, 1). Must have one value which
345 CType trueResult() { return true; }
346 bool notTrue(CType ct) { return !ct; }
348 bool compareIntegers(unsigned i, unsigned j) { return i == j; }
349 bool compareStrings (StringRef s, StringRef r) { return s == r; }
350 bool comparePointers(const void* P, const void* Q) { return P == Q; }
352 bool compare(const SExpr *E1, const SExpr* E2) {
353 // Wildcards match anything.
354 if (E1->opcode() == COP_Wildcard || E2->opcode() == COP_Wildcard)
356 // otherwise normal equality.
357 if (E1->opcode() != E2->opcode())
359 return compareByCase(E1, E2);
362 // TODO -- handle alpha-renaming of variables
363 void enterScope(const Variable* V1, const Variable* V2) { }
364 void leaveScope() { }
366 bool compareVariableRefs(const Variable* V1, const Variable* V2) {
370 static bool compareExprs(const SExpr *E1, const SExpr* E2) {
371 MatchComparator Matcher;
372 return Matcher.compare(E1, E2);
378 // inline std::ostream& operator<<(std::ostream& SS, StringRef R) {
379 // return SS.write(R.data(), R.size());
382 // Pretty printer for TIL expressions
383 template <typename Self, typename StreamType>
384 class PrettyPrinter {
386 bool Verbose; // Print out additional information
387 bool Cleanup; // Omit redundant decls.
388 bool CStyle; // Print exprs in C-like syntax.
391 PrettyPrinter(bool V = false, bool C = true, bool CS = true)
392 : Verbose(V), Cleanup(C), CStyle(CS)
395 static void print(const SExpr *E, StreamType &SS) {
397 printer.printSExpr(E, SS, Prec_MAX);
401 Self *self() { return reinterpret_cast<Self *>(this); }
403 void newline(StreamType &SS) {
407 // TODO: further distinguish between binary operations.
408 static const unsigned Prec_Atom = 0;
409 static const unsigned Prec_Postfix = 1;
410 static const unsigned Prec_Unary = 2;
411 static const unsigned Prec_Binary = 3;
412 static const unsigned Prec_Other = 4;
413 static const unsigned Prec_Decl = 5;
414 static const unsigned Prec_MAX = 6;
416 // Return the precedence of a given node, for use in pretty printing.
417 unsigned precedence(const SExpr *E) {
418 switch (E->opcode()) {
419 case COP_Future: return Prec_Atom;
420 case COP_Undefined: return Prec_Atom;
421 case COP_Wildcard: return Prec_Atom;
423 case COP_Literal: return Prec_Atom;
424 case COP_LiteralPtr: return Prec_Atom;
425 case COP_Variable: return Prec_Atom;
426 case COP_Function: return Prec_Decl;
427 case COP_SFunction: return Prec_Decl;
428 case COP_Code: return Prec_Decl;
429 case COP_Field: return Prec_Decl;
431 case COP_Apply: return Prec_Postfix;
432 case COP_SApply: return Prec_Postfix;
433 case COP_Project: return Prec_Postfix;
435 case COP_Call: return Prec_Postfix;
436 case COP_Alloc: return Prec_Other;
437 case COP_Load: return Prec_Postfix;
438 case COP_Store: return Prec_Other;
439 case COP_ArrayIndex: return Prec_Postfix;
440 case COP_ArrayAdd: return Prec_Postfix;
442 case COP_UnaryOp: return Prec_Unary;
443 case COP_BinaryOp: return Prec_Binary;
444 case COP_Cast: return Prec_Atom;
446 case COP_SCFG: return Prec_Decl;
447 case COP_BasicBlock: return Prec_MAX;
448 case COP_Phi: return Prec_Atom;
449 case COP_Goto: return Prec_Atom;
450 case COP_Branch: return Prec_Atom;
451 case COP_Return: return Prec_Other;
453 case COP_Identifier: return Prec_Atom;
454 case COP_IfThenElse: return Prec_Other;
455 case COP_Let: return Prec_Decl;
460 void printBlockLabel(StreamType & SS, const BasicBlock *BB, int index) {
474 void printSExpr(const SExpr *E, StreamType &SS, unsigned P, bool Sub=true) {
476 self()->printNull(SS);
479 if (Sub && E->block() && E->opcode() != COP_Variable) {
480 SS << "_x" << E->id();
483 if (self()->precedence(E) > P) {
484 // Wrap expr in () if necessary.
486 self()->printSExpr(E, SS, Prec_MAX);
491 switch (E->opcode()) {
492 #define TIL_OPCODE_DEF(X) \
494 self()->print##X(cast<X>(E), SS); \
496 #include "ThreadSafetyOps.def"
497 #undef TIL_OPCODE_DEF
501 void printNull(StreamType &SS) {
505 void printFuture(const Future *E, StreamType &SS) {
506 self()->printSExpr(E->maybeGetResult(), SS, Prec_Atom);
509 void printUndefined(const Undefined *E, StreamType &SS) {
513 void printWildcard(const Wildcard *E, StreamType &SS) {
518 void printLiteralT(const LiteralT<T> *E, StreamType &SS) {
522 void printLiteralT(const LiteralT<uint8_t> *E, StreamType &SS) {
523 SS << "'" << E->value() << "'";
526 void printLiteral(const Literal *E, StreamType &SS) {
527 if (E->clangExpr()) {
528 SS << getSourceLiteralString(E->clangExpr());
532 ValueType VT = E->valueType();
534 case ValueType::BT_Void: {
538 case ValueType::BT_Bool: {
539 if (E->as<bool>().value())
545 case ValueType::BT_Int: {
547 case ValueType::ST_8:
549 printLiteralT(&E->as<int8_t>(), SS);
551 printLiteralT(&E->as<uint8_t>(), SS);
553 case ValueType::ST_16:
555 printLiteralT(&E->as<int16_t>(), SS);
557 printLiteralT(&E->as<uint16_t>(), SS);
559 case ValueType::ST_32:
561 printLiteralT(&E->as<int32_t>(), SS);
563 printLiteralT(&E->as<uint32_t>(), SS);
565 case ValueType::ST_64:
567 printLiteralT(&E->as<int64_t>(), SS);
569 printLiteralT(&E->as<uint64_t>(), SS);
576 case ValueType::BT_Float: {
578 case ValueType::ST_32:
579 printLiteralT(&E->as<float>(), SS);
581 case ValueType::ST_64:
582 printLiteralT(&E->as<double>(), SS);
589 case ValueType::BT_String: {
591 printLiteralT(&E->as<StringRef>(), SS);
595 case ValueType::BT_Pointer: {
599 case ValueType::BT_ValueRef: {
608 void printLiteralPtr(const LiteralPtr *E, StreamType &SS) {
609 SS << E->clangDecl()->getNameAsString();
612 void printVariable(const Variable *V, StreamType &SS, bool IsVarDecl=false) {
613 if (CStyle && V->kind() == Variable::VK_SFun)
616 SS << V->name() << V->id();
619 void printFunction(const Function *E, StreamType &SS, unsigned sugared = 0) {
622 SS << "\\("; // Lambda
625 SS << "("; // Slot declarations
628 SS << ", "; // Curried functions
631 self()->printVariable(E->variableDecl(), SS, true);
633 self()->printSExpr(E->variableDecl()->definition(), SS, Prec_MAX);
635 const SExpr *B = E->body();
636 if (B && B->opcode() == COP_Function)
637 self()->printFunction(cast<Function>(B), SS, 2);
640 self()->printSExpr(B, SS, Prec_Decl);
644 void printSFunction(const SFunction *E, StreamType &SS) {
646 self()->printVariable(E->variableDecl(), SS, true);
648 self()->printSExpr(E->body(), SS, Prec_Decl);
651 void printCode(const Code *E, StreamType &SS) {
653 self()->printSExpr(E->returnType(), SS, Prec_Decl-1);
655 self()->printSExpr(E->body(), SS, Prec_Decl);
658 void printField(const Field *E, StreamType &SS) {
660 self()->printSExpr(E->range(), SS, Prec_Decl-1);
662 self()->printSExpr(E->body(), SS, Prec_Decl);
665 void printApply(const Apply *E, StreamType &SS, bool sugared = false) {
666 const SExpr *F = E->fun();
667 if (F->opcode() == COP_Apply) {
668 printApply(cast<Apply>(F), SS, true);
671 self()->printSExpr(F, SS, Prec_Postfix);
674 self()->printSExpr(E->arg(), SS, Prec_MAX);
679 void printSApply(const SApply *E, StreamType &SS) {
680 self()->printSExpr(E->sfun(), SS, Prec_Postfix);
681 if (E->isDelegation()) {
683 self()->printSExpr(E->arg(), SS, Prec_MAX);
688 void printProject(const Project *E, StreamType &SS) {
691 if (const SApply *SAP = dyn_cast<SApply>(E->record())) {
692 if (const Variable *V = dyn_cast<Variable>(SAP->sfun())) {
693 if (!SAP->isDelegation() && V->kind() == Variable::VK_SFun) {
699 if (isa<Wildcard>(E->record())) {
700 // handle existentials
702 SS << E->clangDecl()->getQualifiedNameAsString();
706 self()->printSExpr(E->record(), SS, Prec_Postfix);
707 if (CStyle && E->isArrow()) {
716 void printCall(const Call *E, StreamType &SS) {
717 const SExpr *T = E->target();
718 if (T->opcode() == COP_Apply) {
719 self()->printApply(cast<Apply>(T), SS, true);
723 self()->printSExpr(T, SS, Prec_Postfix);
728 void printAlloc(const Alloc *E, StreamType &SS) {
730 self()->printSExpr(E->dataType(), SS, Prec_Other-1);
733 void printLoad(const Load *E, StreamType &SS) {
734 self()->printSExpr(E->pointer(), SS, Prec_Postfix);
739 void printStore(const Store *E, StreamType &SS) {
740 self()->printSExpr(E->destination(), SS, Prec_Other-1);
742 self()->printSExpr(E->source(), SS, Prec_Other-1);
745 void printArrayIndex(const ArrayIndex *E, StreamType &SS) {
746 self()->printSExpr(E->array(), SS, Prec_Postfix);
748 self()->printSExpr(E->index(), SS, Prec_MAX);
752 void printArrayAdd(const ArrayAdd *E, StreamType &SS) {
753 self()->printSExpr(E->array(), SS, Prec_Postfix);
755 self()->printSExpr(E->index(), SS, Prec_Atom);
758 void printUnaryOp(const UnaryOp *E, StreamType &SS) {
759 SS << getUnaryOpcodeString(E->unaryOpcode());
760 self()->printSExpr(E->expr(), SS, Prec_Unary);
763 void printBinaryOp(const BinaryOp *E, StreamType &SS) {
764 self()->printSExpr(E->expr0(), SS, Prec_Binary-1);
765 SS << " " << getBinaryOpcodeString(E->binaryOpcode()) << " ";
766 self()->printSExpr(E->expr1(), SS, Prec_Binary-1);
769 void printCast(const Cast *E, StreamType &SS) {
772 SS << E->castOpcode();
774 self()->printSExpr(E->expr(), SS, Prec_Unary);
778 self()->printSExpr(E->expr(), SS, Prec_Unary);
781 void printSCFG(const SCFG *E, StreamType &SS) {
783 for (auto BBI : *E) {
784 printBasicBlock(BBI, SS);
791 void printBBInstr(const SExpr *E, StreamType &SS) {
793 if (E->opcode() == COP_Variable) {
794 auto *V = cast<Variable>(E);
795 SS << "let " << V->name() << V->id() << " = ";
799 else if (E->opcode() != COP_Store) {
800 SS << "let _x" << E->id() << " = ";
802 self()->printSExpr(E, SS, Prec_MAX, Sub);
807 void printBasicBlock(const BasicBlock *E, StreamType &SS) {
808 SS << "BB_" << E->blockID() << ":";
810 SS << " BB_" << E->parent()->blockID();
813 for (auto *A : E->arguments())
816 for (auto *I : E->instructions())
819 const SExpr *T = E->terminator();
821 self()->printSExpr(T, SS, Prec_MAX, false);
828 void printPhi(const Phi *E, StreamType &SS) {
830 if (E->status() == Phi::PH_SingleVal)
831 self()->printSExpr(E->values()[0], SS, Prec_MAX);
834 for (auto V : E->values()) {
837 self()->printSExpr(V, SS, Prec_MAX);
843 void printGoto(const Goto *E, StreamType &SS) {
845 printBlockLabel(SS, E->targetBlock(), E->index());
848 void printBranch(const Branch *E, StreamType &SS) {
850 self()->printSExpr(E->condition(), SS, Prec_MAX);
852 printBlockLabel(SS, E->thenBlock(), -1);
854 printBlockLabel(SS, E->elseBlock(), -1);
857 void printReturn(const Return *E, StreamType &SS) {
859 self()->printSExpr(E->returnValue(), SS, Prec_Other);
862 void printIdentifier(const Identifier *E, StreamType &SS) {
866 void printIfThenElse(const IfThenElse *E, StreamType &SS) {
868 printSExpr(E->condition(), SS, Prec_Unary);
870 printSExpr(E->thenExpr(), SS, Prec_Unary);
872 printSExpr(E->elseExpr(), SS, Prec_Unary);
876 printSExpr(E->condition(), SS, Prec_MAX);
878 printSExpr(E->thenExpr(), SS, Prec_Other);
880 printSExpr(E->elseExpr(), SS, Prec_Other);
883 void printLet(const Let *E, StreamType &SS) {
885 printVariable(E->variableDecl(), SS, true);
887 printSExpr(E->variableDecl()->definition(), SS, Prec_Decl-1);
889 printSExpr(E->body(), SS, Prec_Decl-1);
894 class StdPrinter : public PrettyPrinter<StdPrinter, std::ostream> { };
898 } // end namespace til
899 } // end namespace threadSafety
900 } // end namespace clang
902 #endif // LLVM_CLANG_THREAD_SAFETY_TRAVERSE_H