//===- ThreadSafetyTraverse.h -----------------------------------*- C++ -*-===//
 
//
 
// 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 a framework for doing generic traversals and rewriting
 
// operations over the Thread Safety TIL.
 
//
 
// UNDER CONSTRUCTION.  USE AT YOUR OWN RISK.
 
//
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETYTRAVERSE_H
 
#define LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETYTRAVERSE_H
 
 
 
#include "clang/AST/Decl.h"
 
#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
 
#include "clang/Analysis/Analyses/ThreadSafetyUtil.h"
 
#include "clang/Basic/LLVM.h"
 
#include "llvm/ADT/StringRef.h"
 
#include "llvm/Support/Casting.h"
 
#include <cstdint>
 
#include <ostream>
 
 
 
namespace clang {
 
namespace threadSafety {
 
namespace til {
 
 
 
// Defines an interface used to traverse SExprs.  Traversals have been made as
 
// generic as possible, and are intended to handle any kind of pass over the
 
// AST, e.g. visitors, copying, non-destructive rewriting, destructive
 
// (in-place) rewriting, hashing, typing, etc.
 
//
 
// Traversals implement the functional notion of a "fold" operation on SExprs.
 
// Each SExpr class provides a traverse method, which does the following:
 
//   * e->traverse(v):
 
//       // compute a result r_i for each subexpression e_i
 
//       for (i = 1..n)  r_i = v.traverse(e_i);
 
//       // combine results into a result for e,  where X is the class of e
 
//       return v.reduceX(*e, r_1, .. r_n).
 
//
 
// A visitor can control the traversal by overriding the following methods:
 
//   * v.traverse(e):
 
//       return v.traverseByCase(e), which returns v.traverseX(e)
 
//   * v.traverseX(e):   (X is the class of e)
 
//       return e->traverse(v).
 
//   * v.reduceX(*e, r_1, .. r_n):
 
//       compute a result for a node of type X
 
//
 
// The reduceX methods control the kind of traversal (visitor, copy, etc.).
 
// They are defined in derived classes.
 
//
 
// Class R defines the basic interface types (R_SExpr).
 
template <class Self, class R>
 
class Traversal {
 
public:
 
  Self *self() { return static_cast<Self *>(this); }
 
 
 
  // Traverse an expression -- returning a result of type R_SExpr.
 
  // Override this method to do something for every expression, regardless
 
  // of which kind it is.
 
  // E is a reference, so this can be use for in-place updates.
 
  // The type T must be a subclass of SExpr.
 
  template <class T>
 
  typename R::R_SExpr traverse(T* &E, typename R::R_Ctx Ctx) {
 
    return traverseSExpr(E, Ctx);
 
  }
 
 
 
  // Override this method to do something for every expression.
 
  // Does not allow in-place updates.
 
  typename R::R_SExpr traverseSExpr(SExpr *E, typename R::R_Ctx Ctx) {
 
    return traverseByCase(E, Ctx);
 
  }
 
 
 
  // Helper method to call traverseX(e) on the appropriate type.
 
  typename R::R_SExpr traverseByCase(SExpr *E, typename R::R_Ctx Ctx) {
 
    switch (E->opcode()) {
 
#define TIL_OPCODE_DEF(X)                                                   \
 
    case COP_##X:                                                           \
 
      return self()->traverse##X(cast<X>(E), Ctx);
 
#include "ThreadSafetyOps.def"
 
#undef TIL_OPCODE_DEF
 
    }
 
    return self()->reduceNull();
 
  }
 
 
 
// Traverse e, by static dispatch on the type "X" of e.
 
// Override these methods to do something for a particular kind of term.
 
#define TIL_OPCODE_DEF(X)                                                   \
 
  typename R::R_SExpr traverse##X(X *e, typename R::R_Ctx Ctx) {            \
 
    return e->traverse(*self(), Ctx);                                       \
 
  }
 
#include "ThreadSafetyOps.def"
 
#undef TIL_OPCODE_DEF
 
};
 
 
 
// Base class for simple reducers that don't much care about the context.
 
class SimpleReducerBase {
 
public:
 
  enum TraversalKind {
 
    // Ordinary subexpressions.
 
    TRV_Normal,
 
 
 
    // Declarations (e.g. function bodies).
 
    TRV_Decl,
 
 
 
    // Expressions that require lazy evaluation.
 
    TRV_Lazy,
 
 
 
    // Type expressions.
 
    TRV_Type
 
  };
 
 
 
  // R_Ctx defines a "context" for the traversal, which encodes information
 
  // about where a term appears.  This can be used to encoding the
 
  // "current continuation" for CPS transforms, or other information.
 
  using R_Ctx = TraversalKind;
 
 
 
  // Create context for an ordinary subexpression.
 
  R_Ctx subExprCtx(R_Ctx Ctx) { return TRV_Normal; }
 
 
 
  // Create context for a subexpression that occurs in a declaration position
 
  // (e.g. function body).
 
  R_Ctx declCtx(R_Ctx Ctx) { return TRV_Decl; }
 
 
 
  // Create context for a subexpression that occurs in a position that
 
  // should be reduced lazily.  (e.g. code body).
 
  R_Ctx lazyCtx(R_Ctx Ctx) { return TRV_Lazy; }
 
 
 
  // Create context for a subexpression that occurs in a type position.
 
  R_Ctx typeCtx(R_Ctx Ctx) { return TRV_Type; }
 
};
 
 
 
// Base class for traversals that rewrite an SExpr to another SExpr.
 
class CopyReducerBase : public SimpleReducerBase {
 
public:
 
  // R_SExpr is the result type for a traversal.
 
  // A copy or non-destructive rewrite returns a newly allocated term.
 
  using R_SExpr = SExpr *;
 
  using R_BasicBlock = BasicBlock *;
 
 
 
  // Container is a minimal interface used to store results when traversing
 
  // SExprs of variable arity, such as Phi, Goto, and SCFG.
 
  template <class T> class Container {
 
  public:
 
    // Allocate a new container with a capacity for n elements.
 
    Container(CopyReducerBase &S, unsigned N) : Elems(S.Arena, N) {}
 
 
 
    // Push a new element onto the container.
 
    void push_back(T E) { Elems.push_back(E); }
 
 
 
    SimpleArray<T> Elems;
 
  };
 
 
 
  CopyReducerBase(MemRegionRef A) : Arena(A) {}
 
 
 
protected:
 
  MemRegionRef Arena;
 
};
 
 
 
// Base class for visit traversals.
 
class VisitReducerBase : public SimpleReducerBase {
 
public:
 
  // A visitor returns a bool, representing success or failure.
 
  using R_SExpr = bool;
 
  using R_BasicBlock = bool;
 
 
 
  // A visitor "container" is a single bool, which accumulates success.
 
  template <class T> class Container {
 
  public:
 
    bool Success = true;
 
 
 
    Container(VisitReducerBase &S, unsigned N) {}
 
 
 
    void push_back(bool E) { Success = Success && E; }
 
  };
 
};
 
 
 
// Implements a traversal that visits each subexpression, and returns either
 
// true or false.
 
template <class Self>
 
class VisitReducer : public Traversal<Self, VisitReducerBase>,
 
                     public VisitReducerBase {
 
public:
 
  VisitReducer() = default;
 
 
 
public:
 
  R_SExpr reduceNull() { return true; }
 
  R_SExpr reduceUndefined(Undefined &Orig) { return true; }
 
  R_SExpr reduceWildcard(Wildcard &Orig) { return true; }
 
 
 
  R_SExpr reduceLiteral(Literal &Orig) { return true; }
 
  template<class T>
 
  R_SExpr reduceLiteralT(LiteralT<T> &Orig) { return true; }
 
  R_SExpr reduceLiteralPtr(Literal &Orig) { return true; }
 
 
 
  R_SExpr reduceFunction(Function &Orig, Variable *Nvd, R_SExpr E0) {
 
    return Nvd && E0;
 
  }
 
 
 
  R_SExpr reduceSFunction(SFunction &Orig, Variable *Nvd, R_SExpr E0) {
 
    return Nvd && E0;
 
  }
 
 
 
  R_SExpr reduceCode(Code &Orig, R_SExpr E0, R_SExpr E1) {
 
    return E0 && E1;
 
  }
 
 
 
  R_SExpr reduceField(Field &Orig, R_SExpr E0, R_SExpr E1) {
 
    return E0 && E1;
 
  }
 
 
 
  R_SExpr reduceApply(Apply &Orig, R_SExpr E0, R_SExpr E1) {
 
    return E0 && E1;
 
  }
 
 
 
  R_SExpr reduceSApply(SApply &Orig, R_SExpr E0, R_SExpr E1) {
 
    return E0 && E1;
 
  }
 
 
 
  R_SExpr reduceProject(Project &Orig, R_SExpr E0) { return E0; }
 
  R_SExpr reduceCall(Call &Orig, R_SExpr E0) { return E0; }
 
  R_SExpr reduceAlloc(Alloc &Orig, R_SExpr E0) { return E0; }
 
  R_SExpr reduceLoad(Load &Orig, R_SExpr E0) { return E0; }
 
  R_SExpr reduceStore(Store &Orig, R_SExpr E0, R_SExpr E1) { return E0 && E1; }
 
 
 
  R_SExpr reduceArrayIndex(Store &Orig, R_SExpr E0, R_SExpr E1) {
 
    return E0 && E1;
 
  }
 
 
 
  R_SExpr reduceArrayAdd(Store &Orig, R_SExpr E0, R_SExpr E1) {
 
    return E0 && E1;
 
  }
 
 
 
  R_SExpr reduceUnaryOp(UnaryOp &Orig, R_SExpr E0) { return E0; }
 
 
 
  R_SExpr reduceBinaryOp(BinaryOp &Orig, R_SExpr E0, R_SExpr E1) {
 
    return E0 && E1;
 
  }
 
 
 
  R_SExpr reduceCast(Cast &Orig, R_SExpr E0) { return E0; }
 
 
 
  R_SExpr reduceSCFG(SCFG &Orig, Container<BasicBlock *> Bbs) {
 
    return Bbs.Success;
 
  }
 
 
 
  R_BasicBlock reduceBasicBlock(BasicBlock &Orig, Container<R_SExpr> &As,
 
                                Container<R_SExpr> &Is, R_SExpr T) {
 
    return (As.Success && Is.Success && T);
 
  }
 
 
 
  R_SExpr reducePhi(Phi &Orig, Container<R_SExpr> &As) {
 
    return As.Success;
 
  }
 
 
 
  R_SExpr reduceGoto(Goto &Orig, BasicBlock *B) {
 
    return true;
 
  }
 
 
 
  R_SExpr reduceBranch(Branch &O, R_SExpr C, BasicBlock *B0, BasicBlock *B1) {
 
    return C;
 
  }
 
 
 
  R_SExpr reduceReturn(Return &O, R_SExpr E) {
 
    return E;
 
  }
 
 
 
  R_SExpr reduceIdentifier(Identifier &Orig) {
 
    return true;
 
  }
 
 
 
  R_SExpr reduceIfThenElse(IfThenElse &Orig, R_SExpr C, R_SExpr T, R_SExpr E) {
 
    return C && T && E;
 
  }
 
 
 
  R_SExpr reduceLet(Let &Orig, Variable *Nvd, R_SExpr B) {
 
    return Nvd && B;
 
  }
 
 
 
  Variable *enterScope(Variable &Orig, R_SExpr E0) { return &Orig; }
 
  void exitScope(const Variable &Orig) {}
 
  void enterCFG(SCFG &Cfg) {}
 
  void exitCFG(SCFG &Cfg) {}
 
  void enterBasicBlock(BasicBlock &BB) {}
 
  void exitBasicBlock(BasicBlock &BB) {}
 
 
 
  Variable *reduceVariableRef(Variable *Ovd) { return Ovd; }
 
  BasicBlock *reduceBasicBlockRef(BasicBlock *Obb) { return Obb; }
 
 
 
public:
 
  bool traverse(SExpr *E, TraversalKind K = TRV_Normal) {
 
    Success = Success && this->traverseByCase(E);
 
    return Success;
 
  }
 
 
 
  static bool visit(SExpr *E) {
 
    Self Visitor;
 
    return Visitor.traverse(E, TRV_Normal);
 
  }
 
 
 
private:
 
  bool Success;
 
};
 
 
 
// Basic class for comparison operations over expressions.
 
template <typename Self>
 
class Comparator {
 
protected:
 
  Self *self() { return reinterpret_cast<Self *>(this); }
 
 
 
public:
 
  bool compareByCase(const SExpr *E1, const SExpr* E2) {
 
    switch (E1->opcode()) {
 
#define TIL_OPCODE_DEF(X)                                                     \
 
    case COP_##X:                                                             \
 
      return cast<X>(E1)->compare(cast<X>(E2), *self());
 
#include "ThreadSafetyOps.def"
 
#undef TIL_OPCODE_DEF
 
    }
 
    return false;
 
  }
 
};
 
 
 
class EqualsComparator : public Comparator<EqualsComparator> {
 
public:
 
  // Result type for the comparison, e.g. bool for simple equality,
 
  // or int for lexigraphic comparison (-1, 0, 1).  Must have one value which
 
  // denotes "true".
 
  using CType = bool;
 
 
 
  CType trueResult() { return true; }
 
  bool notTrue(CType ct) { return !ct; }
 
 
 
  bool compareIntegers(unsigned i, unsigned j) { return i == j; }
 
  bool compareStrings (StringRef s, StringRef r) { return s == r; }
 
  bool comparePointers(const void* P, const void* Q) { return P == Q; }
 
 
 
  bool compare(const SExpr *E1, const SExpr* E2) {
 
    if (E1->opcode() != E2->opcode())
 
      return false;
 
    return compareByCase(E1, E2);
 
  }
 
 
 
  // TODO -- handle alpha-renaming of variables
 
  void enterScope(const Variable *V1, const Variable *V2) {}
 
  void leaveScope() {}
 
 
 
  bool compareVariableRefs(const Variable *V1, const Variable *V2) {
 
    return V1 == V2;
 
  }
 
 
 
  static bool compareExprs(const SExpr *E1, const SExpr* E2) {
 
    EqualsComparator Eq;
 
    return Eq.compare(E1, E2);
 
  }
 
};
 
 
 
class MatchComparator : public Comparator<MatchComparator> {
 
public:
 
  // Result type for the comparison, e.g. bool for simple equality,
 
  // or int for lexigraphic comparison (-1, 0, 1).  Must have one value which
 
  // denotes "true".
 
  using CType = bool;
 
 
 
  CType trueResult() { return true; }
 
  bool notTrue(CType ct) { return !ct; }
 
 
 
  bool compareIntegers(unsigned i, unsigned j) { return i == j; }
 
  bool compareStrings (StringRef s, StringRef r) { return s == r; }
 
  bool comparePointers(const void *P, const void *Q) { return P == Q; }
 
 
 
  bool compare(const SExpr *E1, const SExpr *E2) {
 
    // Wildcards match anything.
 
    if (E1->opcode() == COP_Wildcard || E2->opcode() == COP_Wildcard)
 
      return true;
 
    // otherwise normal equality.
 
    if (E1->opcode() != E2->opcode())
 
      return false;
 
    return compareByCase(E1, E2);
 
  }
 
 
 
  // TODO -- handle alpha-renaming of variables
 
  void enterScope(const Variable* V1, const Variable* V2) {}
 
  void leaveScope() {}
 
 
 
  bool compareVariableRefs(const Variable* V1, const Variable* V2) {
 
    return V1 == V2;
 
  }
 
 
 
  static bool compareExprs(const SExpr *E1, const SExpr* E2) {
 
    MatchComparator Matcher;
 
    return Matcher.compare(E1, E2);
 
  }
 
};
 
 
 
// inline std::ostream& operator<<(std::ostream& SS, StringRef R) {
 
//   return SS.write(R.data(), R.size());
 
// }
 
 
 
// Pretty printer for TIL expressions
 
template <typename Self, typename StreamType>
 
class PrettyPrinter {
 
private:
 
  // Print out additional information.
 
  bool Verbose;
 
 
 
  // Omit redundant decls.
 
  bool Cleanup;
 
 
 
  // Print exprs in C-like syntax.
 
  bool CStyle;
 
 
 
public:
 
  PrettyPrinter(bool V = false, bool C = true, bool CS = true)
 
      : Verbose(V), Cleanup(C), CStyle(CS) {}
 
 
 
  static void print(const SExpr *E, StreamType &SS) {
 
    Self printer;
 
    printer.printSExpr(E, SS, Prec_MAX);
 
  }
 
 
 
protected:
 
  Self *self() { return reinterpret_cast<Self *>(this); }
 
 
 
  void newline(StreamType &SS) {
 
    SS << "\n";
 
  }
 
 
 
  // TODO: further distinguish between binary operations.
 
  static const unsigned Prec_Atom = 0;
 
  static const unsigned Prec_Postfix = 1;
 
  static const unsigned Prec_Unary = 2;
 
  static const unsigned Prec_Binary = 3;
 
  static const unsigned Prec_Other = 4;
 
  static const unsigned Prec_Decl = 5;
 
  static const unsigned Prec_MAX = 6;
 
 
 
  // Return the precedence of a given node, for use in pretty printing.
 
  unsigned precedence(const SExpr *E) {
 
    switch (E->opcode()) {
 
      case COP_Future:     return Prec_Atom;
 
      case COP_Undefined:  return Prec_Atom;
 
      case COP_Wildcard:   return Prec_Atom;
 
 
 
      case COP_Literal:    return Prec_Atom;
 
      case COP_LiteralPtr: return Prec_Atom;
 
      case COP_Variable:   return Prec_Atom;
 
      case COP_Function:   return Prec_Decl;
 
      case COP_SFunction:  return Prec_Decl;
 
      case COP_Code:       return Prec_Decl;
 
      case COP_Field:      return Prec_Decl;
 
 
 
      case COP_Apply:      return Prec_Postfix;
 
      case COP_SApply:     return Prec_Postfix;
 
      case COP_Project:    return Prec_Postfix;
 
 
 
      case COP_Call:       return Prec_Postfix;
 
      case COP_Alloc:      return Prec_Other;
 
      case COP_Load:       return Prec_Postfix;
 
      case COP_Store:      return Prec_Other;
 
      case COP_ArrayIndex: return Prec_Postfix;
 
      case COP_ArrayAdd:   return Prec_Postfix;
 
 
 
      case COP_UnaryOp:    return Prec_Unary;
 
      case COP_BinaryOp:   return Prec_Binary;
 
      case COP_Cast:       return Prec_Atom;
 
 
 
      case COP_SCFG:       return Prec_Decl;
 
      case COP_BasicBlock: return Prec_MAX;
 
      case COP_Phi:        return Prec_Atom;
 
      case COP_Goto:       return Prec_Atom;
 
      case COP_Branch:     return Prec_Atom;
 
      case COP_Return:     return Prec_Other;
 
 
 
      case COP_Identifier: return Prec_Atom;
 
      case COP_IfThenElse: return Prec_Other;
 
      case COP_Let:        return Prec_Decl;
 
    }
 
    return Prec_MAX;
 
  }
 
 
 
  void printBlockLabel(StreamType & SS, const BasicBlock *BB, int index) {
 
    if (!BB) {
 
      SS << "BB_null";
 
      return;
 
    }
 
    SS << "BB_";
 
    SS << BB->blockID();
 
    if (index >= 0) {
 
      SS << ":";
 
      SS << index;
 
    }
 
  }
 
 
 
  void printSExpr(const SExpr *E, StreamType &SS, unsigned P, bool Sub=true) {
 
    if (!E) {
 
      self()->printNull(SS);
 
      return;
 
    }
 
    if (Sub && E->block() && E->opcode() != COP_Variable) {
 
      SS << "_x" << E->id();
 
      return;
 
    }
 
    if (self()->precedence(E) > P) {
 
      // Wrap expr in () if necessary.
 
      SS << "(";
 
      self()->printSExpr(E, SS, Prec_MAX);
 
      SS << ")";
 
      return;
 
    }
 
 
 
    switch (E->opcode()) {
 
#define TIL_OPCODE_DEF(X)                                                  \
 
    case COP_##X:                                                          \
 
      self()->print##X(cast<X>(E), SS);                                    \
 
      return;
 
#include "ThreadSafetyOps.def"
 
#undef TIL_OPCODE_DEF
 
    }
 
  }
 
 
 
  void printNull(StreamType &SS) {
 
    SS << "#null";
 
  }
 
 
 
  void printFuture(const Future *E, StreamType &SS) {
 
    self()->printSExpr(E->maybeGetResult(), SS, Prec_Atom);
 
  }
 
 
 
  void printUndefined(const Undefined *E, StreamType &SS) {
 
    SS << "#undefined";
 
  }
 
 
 
  void printWildcard(const Wildcard *E, StreamType &SS) {
 
    SS << "*";
 
  }
 
 
 
  template<class T>
 
  void printLiteralT(const LiteralT<T> *E, StreamType &SS) {
 
    SS << E->value();
 
  }
 
 
 
  void printLiteralT(const LiteralT<uint8_t> *E, StreamType &SS) {
 
    SS << "'" << E->value() << "'";
 
  }
 
 
 
  void printLiteral(const Literal *E, StreamType &SS) {
 
    if (E->clangExpr()) {
 
      SS << getSourceLiteralString(E->clangExpr());
 
      return;
 
    }
 
    else {
 
      ValueType VT = E->valueType();
 
      switch (VT.Base) {
 
      case ValueType::BT_Void:
 
        SS << "void";
 
        return;
 
      case ValueType::BT_Bool:
 
        if (E->as<bool>().value())
 
          SS << "true";
 
        else
 
          SS << "false";
 
        return;
 
      case ValueType::BT_Int:
 
        switch (VT.Size) {
 
        case ValueType::ST_8:
 
          if (VT.Signed)
 
            printLiteralT(&E->as<int8_t>(), SS);
 
          else
 
            printLiteralT(&E->as<uint8_t>(), SS);
 
          return;
 
        case ValueType::ST_16:
 
          if (VT.Signed)
 
            printLiteralT(&E->as<int16_t>(), SS);
 
          else
 
            printLiteralT(&E->as<uint16_t>(), SS);
 
          return;
 
        case ValueType::ST_32:
 
          if (VT.Signed)
 
            printLiteralT(&E->as<int32_t>(), SS);
 
          else
 
            printLiteralT(&E->as<uint32_t>(), SS);
 
          return;
 
        case ValueType::ST_64:
 
          if (VT.Signed)
 
            printLiteralT(&E->as<int64_t>(), SS);
 
          else
 
            printLiteralT(&E->as<uint64_t>(), SS);
 
          return;
 
        default:
 
          break;
 
        }
 
        break;
 
      case ValueType::BT_Float:
 
        switch (VT.Size) {
 
        case ValueType::ST_32:
 
          printLiteralT(&E->as<float>(), SS);
 
          return;
 
        case ValueType::ST_64:
 
          printLiteralT(&E->as<double>(), SS);
 
          return;
 
        default:
 
          break;
 
        }
 
        break;
 
      case ValueType::BT_String:
 
        SS << "\"";
 
        printLiteralT(&E->as<StringRef>(), SS);
 
        SS << "\"";
 
        return;
 
      case ValueType::BT_Pointer:
 
        SS << "#ptr";
 
        return;
 
      case ValueType::BT_ValueRef:
 
        SS << "#vref";
 
        return;
 
      }
 
    }
 
    SS << "#lit";
 
  }
 
 
 
  void printLiteralPtr(const LiteralPtr *E, StreamType &SS) {
 
    if (const NamedDecl *D = E->clangDecl())
 
      SS << D->getNameAsString();
 
    else
 
      SS << "<temporary>";
 
  }
 
 
 
  void printVariable(const Variable *V, StreamType &SS, bool IsVarDecl=false) {
 
    if (CStyle && V->kind() == Variable::VK_SFun)
 
      SS << "this";
 
    else
 
      SS << V->name() << V->id();
 
  }
 
 
 
  void printFunction(const Function *E, StreamType &SS, unsigned sugared = 0) {
 
    switch (sugared) {
 
      default:
 
        SS << "\\(";   // Lambda
 
        break;
 
      case 1:
 
        SS << "(";     // Slot declarations
 
        break;
 
      case 2:
 
        SS << ", ";    // Curried functions
 
        break;
 
    }
 
    self()->printVariable(E->variableDecl(), SS, true);
 
    SS << ": ";
 
    self()->printSExpr(E->variableDecl()->definition(), SS, Prec_MAX);
 
 
 
    const SExpr *B = E->body();
 
    if (B && B->opcode() == COP_Function)
 
      self()->printFunction(cast<Function>(B), SS, 2);
 
    else {
 
      SS << ")";
 
      self()->printSExpr(B, SS, Prec_Decl);
 
    }
 
  }
 
 
 
  void printSFunction(const SFunction *E, StreamType &SS) {
 
    SS << "@";
 
    self()->printVariable(E->variableDecl(), SS, true);
 
    SS << " ";
 
    self()->printSExpr(E->body(), SS, Prec_Decl);
 
  }
 
 
 
  void printCode(const Code *E, StreamType &SS) {
 
    SS << ": ";
 
    self()->printSExpr(E->returnType(), SS, Prec_Decl-1);
 
    SS << " -> ";
 
    self()->printSExpr(E->body(), SS, Prec_Decl);
 
  }
 
 
 
  void printField(const Field *E, StreamType &SS) {
 
    SS << ": ";
 
    self()->printSExpr(E->range(), SS, Prec_Decl-1);
 
    SS << " = ";
 
    self()->printSExpr(E->body(), SS, Prec_Decl);
 
  }
 
 
 
  void printApply(const Apply *E, StreamType &SS, bool sugared = false) {
 
    const SExpr *F = E->fun();
 
    if (F->opcode() == COP_Apply) {
 
      printApply(cast<Apply>(F), SS, true);
 
      SS << ", ";
 
    } else {
 
      self()->printSExpr(F, SS, Prec_Postfix);
 
      SS << "(";
 
    }
 
    self()->printSExpr(E->arg(), SS, Prec_MAX);
 
    if (!sugared)
 
      SS << ")$";
 
  }
 
 
 
  void printSApply(const SApply *E, StreamType &SS) {
 
    self()->printSExpr(E->sfun(), SS, Prec_Postfix);
 
    if (E->isDelegation()) {
 
      SS << "@(";
 
      self()->printSExpr(E->arg(), SS, Prec_MAX);
 
      SS << ")";
 
    }
 
  }
 
 
 
  void printProject(const Project *E, StreamType &SS) {
 
    if (CStyle) {
 
      // Omit the  this->
 
      if (const auto *SAP = dyn_cast<SApply>(E->record())) {
 
        if (const auto *V = dyn_cast<Variable>(SAP->sfun())) {
 
          if (!SAP->isDelegation() && V->kind() == Variable::VK_SFun) {
 
            SS << E->slotName();
 
            return;
 
          }
 
        }
 
      }
 
      if (isa<Wildcard>(E->record())) {
 
        // handle existentials
 
        SS << "&";
 
        SS << E->clangDecl()->getQualifiedNameAsString();
 
        return;
 
      }
 
    }
 
    self()->printSExpr(E->record(), SS, Prec_Postfix);
 
    if (CStyle && E->isArrow())
 
      SS << "->";
 
    else
 
      SS << ".";
 
    SS << E->slotName();
 
  }
 
 
 
  void printCall(const Call *E, StreamType &SS) {
 
    const SExpr *T = E->target();
 
    if (T->opcode() == COP_Apply) {
 
      self()->printApply(cast<Apply>(T), SS, true);
 
      SS << ")";
 
    }
 
    else {
 
      self()->printSExpr(T, SS, Prec_Postfix);
 
      SS << "()";
 
    }
 
  }
 
 
 
  void printAlloc(const Alloc *E, StreamType &SS) {
 
    SS << "new ";
 
    self()->printSExpr(E->dataType(), SS, Prec_Other-1);
 
  }
 
 
 
  void printLoad(const Load *E, StreamType &SS) {
 
    self()->printSExpr(E->pointer(), SS, Prec_Postfix);
 
    if (!CStyle)
 
      SS << "^";
 
  }
 
 
 
  void printStore(const Store *E, StreamType &SS) {
 
    self()->printSExpr(E->destination(), SS, Prec_Other-1);
 
    SS << " := ";
 
    self()->printSExpr(E->source(), SS, Prec_Other-1);
 
  }
 
 
 
  void printArrayIndex(const ArrayIndex *E, StreamType &SS) {
 
    self()->printSExpr(E->array(), SS, Prec_Postfix);
 
    SS << "[";
 
    self()->printSExpr(E->index(), SS, Prec_MAX);
 
    SS << "]";
 
  }
 
 
 
  void printArrayAdd(const ArrayAdd *E, StreamType &SS) {
 
    self()->printSExpr(E->array(), SS, Prec_Postfix);
 
    SS << " + ";
 
    self()->printSExpr(E->index(), SS, Prec_Atom);
 
  }
 
 
 
  void printUnaryOp(const UnaryOp *E, StreamType &SS) {
 
    SS << getUnaryOpcodeString(E->unaryOpcode());
 
    self()->printSExpr(E->expr(), SS, Prec_Unary);
 
  }
 
 
 
  void printBinaryOp(const BinaryOp *E, StreamType &SS) {
 
    self()->printSExpr(E->expr0(), SS, Prec_Binary-1);
 
    SS << " " << getBinaryOpcodeString(E->binaryOpcode()) << " ";
 
    self()->printSExpr(E->expr1(), SS, Prec_Binary-1);
 
  }
 
 
 
  void printCast(const Cast *E, StreamType &SS) {
 
    if (!CStyle) {
 
      SS << "cast[";
 
      switch (E->castOpcode()) {
 
      case CAST_none:
 
        SS << "none";
 
        break;
 
      case CAST_extendNum:
 
        SS << "extendNum";
 
        break;
 
      case CAST_truncNum:
 
        SS << "truncNum";
 
        break;
 
      case CAST_toFloat:
 
        SS << "toFloat";
 
        break;
 
      case CAST_toInt:
 
        SS << "toInt";
 
        break;
 
      case CAST_objToPtr:
 
        SS << "objToPtr";
 
        break;
 
      }
 
      SS << "](";
 
      self()->printSExpr(E->expr(), SS, Prec_Unary);
 
      SS << ")";
 
      return;
 
    }
 
    self()->printSExpr(E->expr(), SS, Prec_Unary);
 
  }
 
 
 
  void printSCFG(const SCFG *E, StreamType &SS) {
 
    SS << "CFG {\n";
 
    for (const auto *BBI : *E)
 
      printBasicBlock(BBI, SS);
 
    SS << "}";
 
    newline(SS);
 
  }
 
 
 
  void printBBInstr(const SExpr *E, StreamType &SS) {
 
    bool Sub = false;
 
    if (E->opcode() == COP_Variable) {
 
      const auto *V = cast<Variable>(E);
 
      SS << "let " << V->name() << V->id() << " = ";
 
      E = V->definition();
 
      Sub = true;
 
    }
 
    else if (E->opcode() != COP_Store) {
 
      SS << "let _x" << E->id() << " = ";
 
    }
 
    self()->printSExpr(E, SS, Prec_MAX, Sub);
 
    SS << ";";
 
    newline(SS);
 
  }
 
 
 
  void printBasicBlock(const BasicBlock *E, StreamType &SS) {
 
    SS << "BB_" << E->blockID() << ":";
 
    if (E->parent())
 
      SS << " BB_" << E->parent()->blockID();
 
    newline(SS);
 
 
 
    for (const auto *A : E->arguments())
 
      printBBInstr(A, SS);
 
 
 
    for (const auto *I : E->instructions())
 
      printBBInstr(I, SS);
 
 
 
    const SExpr *T = E->terminator();
 
    if (T) {
 
      self()->printSExpr(T, SS, Prec_MAX, false);
 
      SS << ";";
 
      newline(SS);
 
    }
 
    newline(SS);
 
  }
 
 
 
  void printPhi(const Phi *E, StreamType &SS) {
 
    SS << "phi(";
 
    if (E->status() == Phi::PH_SingleVal)
 
      self()->printSExpr(E->values()[0], SS, Prec_MAX);
 
    else {
 
      unsigned i = 0;
 
      for (const auto *V : E->values()) {
 
        if (i++ > 0)
 
          SS << ", ";
 
        self()->printSExpr(V, SS, Prec_MAX);
 
      }
 
    }
 
    SS << ")";
 
  }
 
 
 
  void printGoto(const Goto *E, StreamType &SS) {
 
    SS << "goto ";
 
    printBlockLabel(SS, E->targetBlock(), E->index());
 
  }
 
 
 
  void printBranch(const Branch *E, StreamType &SS) {
 
    SS << "branch (";
 
    self()->printSExpr(E->condition(), SS, Prec_MAX);
 
    SS << ") ";
 
    printBlockLabel(SS, E->thenBlock(), -1);
 
    SS << " ";
 
    printBlockLabel(SS, E->elseBlock(), -1);
 
  }
 
 
 
  void printReturn(const Return *E, StreamType &SS) {
 
    SS << "return ";
 
    self()->printSExpr(E->returnValue(), SS, Prec_Other);
 
  }
 
 
 
  void printIdentifier(const Identifier *E, StreamType &SS) {
 
    SS << E->name();
 
  }
 
 
 
  void printIfThenElse(const IfThenElse *E, StreamType &SS) {
 
    if (CStyle) {
 
      printSExpr(E->condition(), SS, Prec_Unary);
 
      SS << " ? ";
 
      printSExpr(E->thenExpr(), SS, Prec_Unary);
 
      SS << " : ";
 
      printSExpr(E->elseExpr(), SS, Prec_Unary);
 
      return;
 
    }
 
    SS << "if (";
 
    printSExpr(E->condition(), SS, Prec_MAX);
 
    SS << ") then ";
 
    printSExpr(E->thenExpr(), SS, Prec_Other);
 
    SS << " else ";
 
    printSExpr(E->elseExpr(), SS, Prec_Other);
 
  }
 
 
 
  void printLet(const Let *E, StreamType &SS) {
 
    SS << "let ";
 
    printVariable(E->variableDecl(), SS, true);
 
    SS << " = ";
 
    printSExpr(E->variableDecl()->definition(), SS, Prec_Decl-1);
 
    SS << "; ";
 
    printSExpr(E->body(), SS, Prec_Decl-1);
 
  }
 
};
 
 
 
class StdPrinter : public PrettyPrinter<StdPrinter, std::ostream> {};
 
 
 
} // namespace til
 
} // namespace threadSafety
 
} // namespace clang
 
 
 
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETYTRAVERSE_H