From d96392be9823a8bae1f766e0993a7a319d10f5d4 Mon Sep 17 00:00:00 2001 From: Andrew Ruef Date: Wed, 24 Aug 2016 10:28:27 -0700 Subject: [PATCH 1/3] Initial version of the checked-c conversion tool. Current features: - Processes single C files or multiple C files (up to whole programs) - Infers "ptr-ness" for local variables used in those programs. - Re-write many source files - Supports many aspects of the C grammar - Contains unit tests Current weaknesses: - Macro support is limited - Casting support is limited, needs structural type equality checks - No inherent support for standard library functions. - Doesn't honor existing checked C types --- include/clang/Constraints/Constraints.h | 457 ++++++++++++++++++ lib/CMakeLists.txt | 1 + lib/Constraints/CMakeLists.txt | 5 + lib/Constraints/Constraints.cpp | 320 ++++++++++++ test/CheckedCRewriter/lit.local.cfg | 1 + test/CheckedCRewriter/simple_locals.c | 152 ++++++ tools/CMakeLists.txt | 1 + tools/checked-c-convert/CMakeLists.txt | 28 ++ tools/checked-c-convert/CheckedCConvert.cpp | 354 ++++++++++++++ tools/checked-c-convert/ConstraintBuilder.cpp | 374 ++++++++++++++ tools/checked-c-convert/ConstraintBuilder.h | 26 + tools/checked-c-convert/MappingVisitor.cpp | 71 +++ tools/checked-c-convert/MappingVisitor.h | 58 +++ tools/checked-c-convert/NewTyp.cpp | 114 +++++ tools/checked-c-convert/NewTyp.h | 98 ++++ .../checked-c-convert/PersistentSourceLoc.cpp | 45 ++ tools/checked-c-convert/PersistentSourceLoc.h | 70 +++ tools/checked-c-convert/ProgramInfo.cpp | 346 +++++++++++++ tools/checked-c-convert/ProgramInfo.h | 139 ++++++ tools/checked-c-convert/utils.h | 22 + unittests/CMakeLists.txt | 1 + unittests/CheckedCRewriter/CMakeLists.txt | 5 + .../CheckedCRewriter/ConstraintTests.cpp | 119 +++++ 23 files changed, 2807 insertions(+) create mode 100644 include/clang/Constraints/Constraints.h create mode 100644 lib/Constraints/CMakeLists.txt create mode 100644 lib/Constraints/Constraints.cpp create mode 100644 test/CheckedCRewriter/lit.local.cfg create mode 100644 test/CheckedCRewriter/simple_locals.c create mode 100644 tools/checked-c-convert/CMakeLists.txt create mode 100644 tools/checked-c-convert/CheckedCConvert.cpp create mode 100644 tools/checked-c-convert/ConstraintBuilder.cpp create mode 100644 tools/checked-c-convert/ConstraintBuilder.h create mode 100644 tools/checked-c-convert/MappingVisitor.cpp create mode 100644 tools/checked-c-convert/MappingVisitor.h create mode 100644 tools/checked-c-convert/NewTyp.cpp create mode 100644 tools/checked-c-convert/NewTyp.h create mode 100644 tools/checked-c-convert/PersistentSourceLoc.cpp create mode 100644 tools/checked-c-convert/PersistentSourceLoc.h create mode 100644 tools/checked-c-convert/ProgramInfo.cpp create mode 100644 tools/checked-c-convert/ProgramInfo.h create mode 100644 tools/checked-c-convert/utils.h create mode 100644 unittests/CheckedCRewriter/CMakeLists.txt create mode 100644 unittests/CheckedCRewriter/ConstraintTests.cpp diff --git a/include/clang/Constraints/Constraints.h b/include/clang/Constraints/Constraints.h new file mode 100644 index 000000000000..878b839c0b19 --- /dev/null +++ b/include/clang/Constraints/Constraints.h @@ -0,0 +1,457 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This implements a simple constraint solver for expressions of the form: +// a = b +// not a +// a implies b +// +// The Checked C converter tool performs type inference to identify locations +// where a C type might be replaced with a Checked C type. This interface +// does the solving to figure out where those substitions might happen. +// +//===----------------------------------------------------------------------===// +#ifndef _CONSTRAINTS_H +#define _CONSTRAINTS_H +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +class Constraint; +class Constraints; + +template +struct PComp +{ + bool operator()(const T lhs, const T rhs) const { + return *lhs < *rhs; + } +}; + +// Represents atomic values that can occur at positions in constraints. +class Atom { +public: + enum AtomKind { + A_Var, + A_Ptr, + A_Arr, + A_Wild, + A_Const + }; +private: + const AtomKind Kind; +public: + Atom(AtomKind K) : Kind(K) {} + virtual ~Atom() {} + + AtomKind getKind() const { return Kind; } + + virtual void print(llvm::raw_ostream &) = 0; + virtual void dump(void) = 0; + virtual bool operator==(const Atom &) const = 0; + virtual bool operator!=(const Atom &) const = 0; + virtual bool operator<(const Atom &other) const = 0; +}; + +// This refers to a location that we are trying to solve for. +class VarAtom : public Atom { + friend class Constraints; +public: + VarAtom(uint32_t D) : Atom(A_Var), Loc(D) {} + + static bool classof(const Atom *S) { + return S->getKind() == A_Var; + } + + void print(llvm::raw_ostream &O) { + O << "q_" << Loc; + } + + void dump(void) { + print(llvm::errs()); + } + + bool operator==(const Atom &other) const { + if (const VarAtom *V = llvm::dyn_cast(&other)) + return V->Loc == Loc; + else + return false; + } + + bool operator!=(const Atom &other) const { + return !(*this == other); + } + + bool operator<(const Atom &other) const { + if (const VarAtom *V = llvm::dyn_cast(&other)) + return Loc < V->Loc; + else + return false; + } + + uint32_t getLoc() const { + return Loc; + } + +private: + uint32_t Loc; + // The constraint expressions where this variable is mentioned on the + // LHS of an equality. + std::set> Constraints; +}; + +class ConstAtom : public Atom { +public: + ConstAtom() : Atom(A_Const) {} + ConstAtom(AtomKind K) : Atom(K) {} + + static bool classof(const Atom *A) { + // Something is a class of ConstAtom if it ISN'T a Var. + return A->getKind() != A_Var; + } +}; + +// This refers to the constant PTR. +class PtrAtom : public ConstAtom { +public: + PtrAtom() : ConstAtom(A_Ptr) {} + + static bool classof(const Atom *S) { + return S->getKind() == A_Ptr; + } + + void print(llvm::raw_ostream &O) { + O << "PTR"; + } + + void dump(void) { + print(llvm::errs()); + } + + bool operator==(const Atom &other) const { + return llvm::isa (&other); + } + + bool operator!=(const Atom &other) const { + return !(*this == other); + } + + bool operator<(const Atom &other) const { + return *this != other; + } +}; + +// This refers to the constant ARR. +class ArrAtom : public ConstAtom { +public: + ArrAtom() : ConstAtom(A_Arr) {} + + static bool classof(const Atom *S) { + return S->getKind() == A_Arr; + } + + void print(llvm::raw_ostream &O) { + O << "ARR"; + } + + void dump(void) { + print(llvm::errs()); + } + + bool operator==(const Atom &other) const { + return llvm::isa(&other); + } + + bool operator!=(const Atom &other) const { + return !(*this == other); + } + + bool operator<(const Atom &other) const { + if (llvm::isa(&other) || *this == other) + return false; + else + return true; + } +}; + +// This refers to the constant WILD. +class WildAtom : public ConstAtom { +public: + WildAtom() : ConstAtom(A_Wild) {} + + static bool classof(const Atom *S) { + return S->getKind() == A_Wild; + } + + void print(llvm::raw_ostream &O) { + O << "WILD"; + } + + void dump(void) { + print(llvm::errs()); + } + + bool operator==(const Atom &other) const { + if (llvm::isa(&other)) + return true; + else + return false; + } + + bool operator!=(const Atom &other) const { + return !(*this == other); + } + + bool operator<(const Atom &other) const { + if (llvm::isa(&other) || llvm::isa(&other) || *this == other) + return false; + else + return true; + } +}; + +// Represents constraints of the form: +// - a = b +// - not a +// - a => b +class Constraint { +public: + enum ConstraintKind { + C_Eq, + C_Not, + C_Imp + }; +private: + const ConstraintKind Kind; +public: + Constraint(ConstraintKind K) : Kind(K) {} + virtual ~Constraint() {} + + ConstraintKind getKind() const { return Kind; } + + virtual void print(llvm::raw_ostream &) = 0; + virtual void dump(void) = 0; + virtual bool operator==(const Constraint &other) const = 0; + virtual bool operator!=(const Constraint &other) const = 0; + virtual bool operator<(const Constraint &other) const = 0; +}; + +// a = b +class Eq : public Constraint { +public: + + Eq(Atom *lhs, Atom *rhs) + : Constraint(C_Eq), lhs(lhs), rhs(rhs) {} + + static bool classof(const Constraint *C) { + return C->getKind() == C_Eq; + } + + void print(llvm::raw_ostream &O) { + lhs->print(O); + O << " == "; + rhs->print(O); + } + + void dump(void) { + print(llvm::errs()); + } + + Atom *getLHS(void) const { return lhs; } + Atom *getRHS(void) const { return rhs; } + + bool operator==(const Constraint &other) const { + if (const Eq *E = llvm::dyn_cast(&other)) + return *lhs == *E->lhs && *rhs == *E->rhs; + else + return false; + } + + bool operator!=(const Constraint &other) const { + return !(*this == other); + } + + bool operator<(const Constraint &other) const { + ConstraintKind K = other.getKind(); + if (K == C_Eq) { + const Eq *E = llvm::dyn_cast(&other); + assert(E != NULL); + + if (*lhs == *E->lhs && *rhs == *E->rhs) + return false; + else if (*lhs == *E->lhs && *rhs != *E->rhs) + return *rhs < *E->rhs; + else + return *lhs < *E->lhs; + } + else + return C_Eq < K; + } + +private: + Atom *lhs; + Atom *rhs; +}; + +// not a +class Not : public Constraint { +public: + Not(Constraint *b) + : Constraint(C_Not), body(b) {} + + static bool classof(const Constraint *C) { + return C->getKind() == C_Not; + } + + void print(llvm::raw_ostream &O) { + O << "~("; + body->print(O); + O << ")"; + } + + void dump(void) { + print(llvm::errs()); + } + + bool operator==(const Constraint &other) const { + if (const Not *N = llvm::dyn_cast(&other)) + return *body == *N->body; + else + return false; + } + + bool operator!=(const Constraint &other) const { + return !(*this == other); + } + + bool operator<(const Constraint &other) const { + ConstraintKind K = other.getKind(); + if (K == C_Not) { + const Not *N = llvm::dyn_cast(&other); + assert(N != NULL); + + return *body < *N->body; + } + else + return C_Not < K; + } + + Constraint *getBody() const { + return body; + } + +private: + Constraint *body; +}; + +// a => b +class Implies : public Constraint { +public: + + Implies(Constraint *premise, Constraint *conclusion) + : Constraint(C_Imp), premise(premise), conclusion(conclusion) {} + + static bool classof(const Constraint *C) { + return C->getKind() == C_Imp; + } + + Constraint *getPremise() const { return premise; } + Constraint *getConclusion() const { return conclusion; } + + void print(llvm::raw_ostream &O) { + premise->print(O); + O << " => "; + conclusion->print(O); + } + + void dump(void) { + print(llvm::errs()); + } + + bool operator==(const Constraint &other) const { + if (const Implies *I = llvm::dyn_cast(&other)) + return *premise == *I->premise && *conclusion == *I->conclusion; + else + return false; + } + + bool operator!=(const Constraint &other) const { + return !(*this == other); + } + + bool operator<(const Constraint &other) const { + ConstraintKind K = other.getKind(); + if (K == C_Imp) { + const Implies *I = llvm::dyn_cast(&other); + assert(I != NULL); + + if (*premise == *I->premise && *conclusion == *I->conclusion) + return false; + else if (*premise == *I->premise && *conclusion != *I->conclusion) + return *conclusion < *I->conclusion; + else + return *premise < *I->premise; + } + else + return C_Imp < K; + } + +private: + Constraint *premise; + Constraint *conclusion; +}; + +class Constraints { +public: + Constraints(); + ~Constraints(); + + typedef std::set > ConstraintSet; + // The environment maps from Vars to Consts (one of Ptr, Arr, Wild). + typedef std::map > EnvironmentMap; + + bool addConstraint(Constraint *c); + ConstraintSet getConstraints() { return constraints; } + EnvironmentMap getVariables() { return environment; } + bool solve(void); + void dump(); + void print(llvm::raw_ostream &); + + Eq *createEq(Atom *lhs, Atom *rhs); + Not *createNot(Constraint *body); + Implies *createImplies(Constraint *premise, Constraint *conclusion); + + VarAtom *getOrCreateVar(uint32_t v); + PtrAtom *getPtr() const; + ArrAtom *getArr() const; + WildAtom *getWild() const; + +private: + ConstraintSet constraints; + EnvironmentMap environment; + + bool step_solve(EnvironmentMap &); + bool check(Constraint *C); + + template + bool + propEq(EnvironmentMap &env, Eq *Dyn, T *A, ConstraintSet &R, + EnvironmentMap::iterator &CurValLHS); + + template + bool + propImp(Implies *, T*, ConstraintSet &, ConstAtom *); + + // These atoms can be singletons, so we'll store them in the + // Constraints class. + PtrAtom *prebuiltPtr; + ArrAtom *prebuiltArr; + WildAtom *prebuiltWild; +}; + +#endif diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index dfd819a407e9..d23341653476 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,3 +22,4 @@ if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(StaticAnalyzer) endif() add_subdirectory(Format) +add_subdirectory(Constraints) \ No newline at end of file diff --git a/lib/Constraints/CMakeLists.txt b/lib/Constraints/CMakeLists.txt new file mode 100644 index 000000000000..6afb9d598c17 --- /dev/null +++ b/lib/Constraints/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(Constraints + Constraints.cpp +) \ No newline at end of file diff --git a/lib/Constraints/Constraints.cpp b/lib/Constraints/Constraints.cpp new file mode 100644 index 000000000000..0c7705d57ea0 --- /dev/null +++ b/lib/Constraints/Constraints.cpp @@ -0,0 +1,320 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "clang/Constraints/Constraints.h" +#include "llvm/Support/CommandLine.h" +#include + +using namespace llvm; + +static cl::OptionCategory SolverCategory("solver options"); +static cl::opt DebugSolver("debug-solver", + cl::desc("Dump intermediate solver state"), + cl::init(false), cl::cat(SolverCategory)); + +// Add a constraint to the set of constraints. If the constraint is already +// present (by syntactic equality) return false. +bool Constraints::addConstraint(Constraint *C) { + // Validate the constraint to be added. + assert(check(C)); + + // Check if C is already in the set of constraints. + if (constraints.find(C) == constraints.end()) { + constraints.insert(C); + + // Update the variables that depend on this constraint + if (Eq *E = dyn_cast(C)) { + if (VarAtom *vLHS = dyn_cast(E->getLHS())) + vLHS->Constraints.insert(C); + } + else if (Not *N = dyn_cast(C)) { + if (Eq *E = dyn_cast(N->getBody())) { + if (VarAtom *vLHS = dyn_cast(E->getLHS())) + vLHS->Constraints.insert(C); + + } + } + else if (Implies *I = dyn_cast(C)) { + if (Eq *E = dyn_cast(I->getPremise())) { + if (VarAtom *vLHS = dyn_cast(E->getLHS())) + vLHS->Constraints.insert(C); + } + } + else + llvm_unreachable("unsupported constraint"); + + return true; + } + + return false; +} + +// Checks to see if the constraint is of a form that we expect. +// The expected forms are the following: +// EQ : (q_i = A) | (q_i = q_k) for A constant or +// NOT : NOT(q_i = A) for A constant or +// IMPLIES : (q_i = A) => (q_k = B) for A,B constant +bool Constraints::check(Constraint *C) { + + if (Not *N = dyn_cast(C)) { + if(Eq *E = dyn_cast(N->getBody())) + if (!isa(E->getLHS()) || isa(E->getRHS())) + return false; + } + else if (Implies *I = dyn_cast(C)) { + if (Eq *P = dyn_cast(I->getPremise())) { + if (!isa(P->getLHS()) || isa(P->getRHS())) + return false; + } + else { + return false; + } + + if (Eq *CO = dyn_cast(I->getConclusion())) { + if (!isa(CO->getLHS()) || isa(CO->getRHS())) + return false; + } + else { + return false; + } + } + else if (Eq *E = dyn_cast(C)) { + + if (!isa(E->getLHS())) + return false; + } + + return true; +} + +// Given an equality constraint _Dyn_, and a current variable binding +// _CurValLHS_, where _CurValLHS_ represents the pair (q_i:C) and the +// equality constraint _Dyn_ := q_i == K, pattern match over K. It +// could be either a constant value such as WildAtom, or, it could be +// another variable. +// +// T is constrained to be one of the types from the constant lattice. +// T is parametric because the logic for equality propagation is common +// between different cases of constraint solving. +// +// Return true if propEq modified the binding of (q_i:C) or the binding +// of (q_j:K) if _Dyn_ was of the form q_i == q_k. +template +bool +Constraints::propEq(EnvironmentMap &env, Eq *Dyn, T *A, ConstraintSet &R, + EnvironmentMap::iterator &CurValLHS) { + bool changedEnvironment = false; + + if (isa(Dyn->getRHS())) { + if (*(CurValLHS->second) < *A) { + CurValLHS->second = A; + R.insert(Dyn); + changedEnvironment = true; + } + } // Also propagate from equality when v = v'. + else if (VarAtom *RHSVar = dyn_cast(Dyn->getRHS())) { + EnvironmentMap::iterator CurValRHS = env.find(RHSVar); + assert(CurValRHS != env.end()); // The var on the RHS should be in the env. + + if (*(CurValLHS->second) < *(CurValRHS->second)) { + CurValLHS->second = CurValRHS->second; + changedEnvironment = true; + } + else if (*(CurValRHS->second) < *(CurValLHS->second)) { + CurValRHS->second = CurValLHS->second; + changedEnvironment = true; + } + else + assert(*(CurValRHS->second) == *(CurValLHS->second)); + } + + return changedEnvironment; +} + +// Propagates implication through the environment for a single +// variable (whose value is given by _V_) used in an implication +// constraint _Imp_. +template +bool +Constraints::propImp(Implies *Imp, T *A, ConstraintSet &R, ConstAtom *V) { + Constraint *Con = NULL; + bool changedEnvironment = false; + + if (Eq *DynP = dyn_cast(Imp->getPremise())) + if (isa(DynP->getRHS()) && *V == *A) { + Con = Imp->getConclusion(); + R.insert(Imp); + addConstraint(Con); + changedEnvironment = true; + } + + return changedEnvironment; +} + +// Takes one iteration to solve the system of constraints. Each step +// involves the propagation of quantifiers and the potential firing of +// implications. Accepts a single parameter, _env_, that is a map of +// variables to their current value in the ConstAtom lattice. +// +// Returns true if the step didn't change any bindings of variables in +// the environment. +bool Constraints::step_solve(EnvironmentMap &env) { + bool changedEnvironment = false; + + EnvironmentMap::iterator VI = env.begin(); + // Step 1. Propagate any WILD constraint as far as we can. + while(VI != env.end()) { + // Iterate over the environment, VI is a pair of a variable q_i and + // the constant (one of Ptr, Arr, Wild) that the variable is bound to. + VarAtom *Var = VI->first; + ConstAtom *Val = VI->second; + + ConstraintSet rmConstraints; + for (const auto &C : Var->Constraints) + if (Eq *E = dyn_cast(C)) + changedEnvironment |= propEq(env, E, getWild(), rmConstraints, VI); + else if (Implies *Imp = dyn_cast(C)) + changedEnvironment |= propImp(Imp, getWild(), rmConstraints, Val); + + for (const auto &RC : rmConstraints) + Var->Constraints.erase(RC); + + ++VI; + } + + VI = env.begin(); + // Step 2. Propagate any ARITH constraints. + while(VI != env.end()) { + VarAtom *Var = VI->first; + ConstAtom *Val = VI->second; + + ConstraintSet rmConstraints; + for (const auto &C : Var->Constraints) { + // Propagate the Neg constraint. + if (Not *N = dyn_cast(C)) { + if (Eq *E = dyn_cast(N->getBody())) + // If this is Not ( q == Ptr ) and the current value + // of q is Ptr ( < *getArr() ) then bump q up to Arr. + if (isa(E->getRHS())) + if (*Val < *getArr()) { + VI->second = getArr(); + changedEnvironment = true; + } + } + else if (Eq *E = dyn_cast(C)) + changedEnvironment |= propEq(env, E, getArr(), rmConstraints, VI); + else if (Implies *Imp = dyn_cast(C)) + changedEnvironment |= propImp(Imp, getArr(), rmConstraints, Val); + } + + for (const auto &RC : rmConstraints) + Var->Constraints.erase(RC); + + ++VI; + } + + return (changedEnvironment == false); +} + +bool Constraints::solve(void) { + bool fixed = false; + + if (DebugSolver) { + errs() << "constraints beginning solve\n"; + dump(); + } + + // It's (probably) possible that a pathologically constructed environment + // could cause us to loop n**2 times. It would be ideal to have an upper + // bound of k*n for k lattice levels and n variables. This will require + // some dependency tracking, we will do that later. + while (fixed == false) { + + if (DebugSolver) { + errs() << "constraints pre step\n"; + dump(); + } + + fixed = step_solve(environment); + + if (DebugSolver) { + errs() << "constraints post step\n"; + dump(); + } + } + + return true; +} + +void Constraints::print(raw_ostream &O) { + O << "CONSTRAINTS: \n"; + for (const auto &C : constraints) { + C->print(O); + O << "\n"; + } + + O << "ENVIRONMENT: \n"; + for (const auto &V : environment) { + V.first->print(O); + O << " = "; + V.second->print(O); + O << "\n"; + } +} + +void Constraints::dump(void) { + print(errs()); +} + +VarAtom *Constraints::getOrCreateVar(uint32_t v) { + VarAtom tv(v); + EnvironmentMap::iterator I = environment.find(&tv); + + if (I != environment.end()) + return I->first; + else { + VarAtom *V = new VarAtom(tv); + environment[V] = getPtr(); + return V; + } +} + +PtrAtom *Constraints::getPtr() const { + return prebuiltPtr; +} +ArrAtom *Constraints::getArr() const { + return prebuiltArr; +} +WildAtom *Constraints::getWild() const { + return prebuiltWild; +} + +Eq *Constraints::createEq(Atom *lhs, Atom *rhs) { + return new Eq(lhs, rhs); +} + +Not *Constraints::createNot(Constraint *body) { + return new Not(body); +} + +Implies *Constraints::createImplies(Constraint *premise, Constraint *conclusion) { + return new Implies(premise, conclusion); +} + +Constraints::Constraints() { + prebuiltPtr = new PtrAtom(); + prebuiltArr = new ArrAtom(); + prebuiltWild = new WildAtom(); +} + +Constraints::~Constraints() { + delete prebuiltPtr; + delete prebuiltArr; + delete prebuiltWild; +} diff --git a/test/CheckedCRewriter/lit.local.cfg b/test/CheckedCRewriter/lit.local.cfg new file mode 100644 index 000000000000..856a54932f0b --- /dev/null +++ b/test/CheckedCRewriter/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.c'] diff --git a/test/CheckedCRewriter/simple_locals.c b/test/CheckedCRewriter/simple_locals.c new file mode 100644 index 000000000000..8038facf643d --- /dev/null +++ b/test/CheckedCRewriter/simple_locals.c @@ -0,0 +1,152 @@ +// Tests for Checked C rewriter tool. +// +// Checks very simple inference properties for local variables. +// +// RUN: checked-c-convert %s -- | FileCheck %s +// RUN: checked-c-convert %s -- | %clang_cc1 -verify -fcheckedc-extension -x c - +// expected-no-diagnostics + +void f1() { + int b = 0; + int *a = &b; + *a = 1; +} +// CHECK: void f1() { +// CHECK-NEXT: int b = 0; +// CHECK-NEXT: ptr a = &b; + +void f2() { + char b = 'a'; + char *a = &b; + *a = 'b'; +} +//CHECK: void f2() { +//CHECK-NEXT: char b = 'a'; +//CHECK-NEXT: ptr a = &b; + +typedef struct _BarRec { + int a; + int b; + int c; + int *d; +} BarRec; + +void upd(BarRec *P, int a) { + P->a = a; +} +//CHECK: void upd(ptr P, int a) { +//CHECK-NEXT: P->a = a; +//CHECK-NEXT: } + +void canthelp(int *a, int b, int c) { + *(a + b) = c; +} +//CHECK: void canthelp(int *a, int b, int c) { +//CHECK-NEXT: *(a + b) = c; +//CHECK-NEXT: } + +void partialhelp(int *a, int b, int c) { + int *d = a; + *d = 0; + *(a + b) = c; +} +//CHECK: void partialhelp(int *a, int b, int c) { +//CHECK-NEXT: ptr d = a; +//CHECK-NEXT: *d = 0; +//CHECK-NEXT: *(a + b) = c; +//CHECK-NEXT: } + +void g(void) { + int a = 0, *b = &a; + *b = 1; +} +//CHECK: void g(void) { +//CHECK-NEXT: int a = 0; +//CHECK-NEXT: ptr b = &a; + +void gg(void) { + int a = 0, *b = &a, **c = &b; + + *b = 1; + **c = 2; +} +//CHECK: void gg(void) { +//CHECK-NEXT: int a = 0; +//CHECK-NEXT: ptr b = &a; +//CHECK-NEXT: ptr > c = &b; + +#define ONE 1 + +int goo(int *, int); +//CHECK: int goo(ptr, int); + +struct blah { + int a; + int b; + struct blah *c; +}; + +int bar(int, int); + +int foo(int a, int b) { + int tmp = a + ONE; + int *tmp2 = &tmp; + return tmp + b + *tmp2; +} +//CHECK: int foo(int a, int b) { +//CHECK-NEXT: int tmp = a + ONE; +//CHECK-NEXT: ptr tmp2 = &tmp; +//CHECK-NEXT: return tmp + b + *tmp2; +//CHECK-NEXT: } + +int bar(int a, int b) { + return a + b; +} +//CHECK: int bar(int a, int b) { +//CHECK-NEXT: return a + b; +//CHECK-NEXT: } + +int baz(int *a, int b, int c) { + int tmp = b + c; + int *aa = a; + *aa = tmp; + return tmp; +} +//CHECK: int baz(ptr a, int b, int c) { +//CHECK-NEXT: int tmp = b + c; +//CHECK-NEXT: ptr aa = a; +//CHECK-NEXT: *aa = tmp; +//CHECK-NEXT: return tmp; + +int arrcheck(int *a, int b) { + return a[b]; +} +//CHECK: int arrcheck(int *a, int b) { +//CHECK-NEXT: return a[b]; +//CHECK-NEXT: } + +int badcall(int *a, int b) { + return arrcheck(a, b); +} +//CHECK: int badcall(int *a, int b) { +//CHECK-NEXT: return arrcheck(a, b); +//CHECK-NEXT: } + +void pullit(char *base, char *out, int *index) { + char tmp = base[*index]; + *out = tmp; + *index = *index + 1; + + return; +} +//CHECK: void pullit(char* base, ptr out, ptr index) { + +void driver() { + char buf[10] = { 0 }; + int index = 0; + char v; + + pullit(buf, &v, &index); + pullit(buf, &v, &index); + pullit(buf, &v, &index); +} \ No newline at end of file diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index d734493c619e..ec6011744afe 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_subdirectory(driver) add_clang_subdirectory(clang-format) add_clang_subdirectory(clang-format-vs) add_clang_subdirectory(clang-fuzzer) +add_clang_subdirectory(checked-c-convert) add_clang_subdirectory(c-index-test) diff --git a/tools/checked-c-convert/CMakeLists.txt b/tools/checked-c-convert/CMakeLists.txt new file mode 100644 index 000000000000..4a2cf722749e --- /dev/null +++ b/tools/checked-c-convert/CMakeLists.txt @@ -0,0 +1,28 @@ +set( LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Option + Support + ) + +add_clang_executable(checked-c-convert + CheckedCConvert.cpp + NewTyp.cpp + ProgramInfo.cpp + MappingVisitor.cpp + ConstraintBuilder.cpp + PersistentSourceLoc.cpp + ) + +target_link_libraries(checked-c-convert + Constraints + clangAST + clangBasic + clangDriver + clangFrontend + clangRewriteFrontend + clangStaticAnalyzerFrontend + clangTooling + ) + +install(TARGETS checked-c-convert + RUNTIME DESTINATION bin) diff --git a/tools/checked-c-convert/CheckedCConvert.cpp b/tools/checked-c-convert/CheckedCConvert.cpp new file mode 100644 index 000000000000..5f9260c81e4a --- /dev/null +++ b/tools/checked-c-convert/CheckedCConvert.cpp @@ -0,0 +1,354 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetSelect.h" + +#include +#include +#include + +#include "clang/Constraints/Constraints.h" + +#include "ConstraintBuilder.h" +#include "NewTyp.h" +#include "PersistentSourceLoc.h" +#include "ProgramInfo.h" + +using namespace clang::driver; +using namespace clang::tooling; +using namespace clang; +using namespace llvm; + +static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); +static cl::extrahelp MoreHelp(""); + +static cl::OptionCategory ConvertCategory("checked-c-convert options"); +static cl::opt DumpIntermediate("dump-intermediate", + cl::desc("Dump intermediate information"), + cl::init(false), + cl::cat(ConvertCategory)); + +static cl::opt + OutputPostfix("output-postfix", + cl::desc("Postfix to add to the names of rewritten files, if " + "not supplied writes to STDOUT"), + cl::init("-"), cl::cat(ConvertCategory)); + +static cl::opt RewriteHeaders( + "rewrite-headers", + cl::desc("Rewrite header files as well as the specified source files"), + cl::init(false), cl::cat(ConvertCategory)); + +// Visit each Decl in toRewrite and apply the appropriate pointer type +// to that Decl. The state of the rewrite is contained within R, which +// is both input and output. R is initialized to point to the 'main' +// source file for this transformation. toRewrite contains the set of +// declarations to rewrite. S is passed for source-level information +// about the current compilation unit. +void rewrite(Rewriter &R, std::set &toRewrite, SourceManager &S, + ASTContext &A, std::set &Files) { + std::set skip; + + for (const auto &N : toRewrite) { + Decl *D = N->getDecl(); + DeclStmt *Where = N->getWhere(); + + if (ParmVarDecl *PV = dyn_cast(D)) { + assert(Where == NULL); + // Is it a parameter type? + + // First, find all the declarations of the containing function. + if (DeclContext *DF = PV->getParentFunctionOrMethod()) { + if (FunctionDecl *FD = dyn_cast(DF)) { + // For each function, determine which parameter in the declaration + // matches PV, then, get the type location of that parameter + // declaration and re-write. + + // This is kind of hacky, maybe we should record the index of the + // parameter when we find it, instead of re-discovering it here. + int parmIndex = -1; + int c = 0; + for (const auto &I : FD->params()) { + if (I == PV) { + parmIndex = c; + break; + } + c++; + } + assert(parmIndex >= 0); + + for (FunctionDecl *toRewrite = FD; toRewrite != NULL; + toRewrite = toRewrite->getPreviousDecl()) { + // TODO these declarations could get us into deeper header files. + ParmVarDecl *Rewrite = toRewrite->getParamDecl(parmIndex); + assert(Rewrite != NULL); + SourceRange TR = + Rewrite->getTypeSourceInfo()->getTypeLoc().getSourceRange(); + + // Get a FullSourceLoc for the start location and add it to the + // list of file ID's we've touched. + FullSourceLoc FSL(TR.getBegin(), S); + Files.insert(FSL.getFileID()); + R.ReplaceText(TR, N->mkStr()); + } + } else + llvm_unreachable("no function or method"); + } else + llvm_unreachable("no parent function or method for decl"); + + } else if (VarDecl *VD = dyn_cast(D)) { + assert(Where != NULL); + SourceRange TR = VD->getTypeSourceInfo()->getTypeLoc().getSourceRange(); + + // Is it a variable type? This is the easy case, we can re-write it + // locally, at the site of the declaration. + + // Get a FullSourceLoc for the start location and add it to the + // list of file ID's we've touched. + FullSourceLoc FSL(TR.getBegin(), S); + Files.insert(FSL.getFileID()); + if (Where->isSingleDecl()) + R.ReplaceText(TR, N->mkStr()); + else if (!(Where->isSingleDecl()) && skip.find(N) == skip.end()) { + // Hack time! + // Sometimes, like in the case of a decl on a single line, we'll need to + // do multiple NewTyps at once. In that case, in the inner loop, we'll + // re-scan and find all of the NewTyps related to that line and do + // everything at once. That means sometimes we'll get NewTyps that + // we don't want to process twice. We'll skip them here. + + // Step 1: get the re-written types. + std::set rewritesForThisDecl; + auto I = toRewrite.find(N); + while (I != toRewrite.end()) { + NewTyp *tmp = *I; + if (tmp->getWhere() == Where) + rewritesForThisDecl.insert(tmp); + ++I; + } + + // Step 2: remove the original line from the program. + SourceRange DR = Where->getSourceRange(); + R.RemoveText(DR); + + // Step 3: for each decl in the original, build up a new string + // and if the original decl was re-written, write that + // out instead (WITH the initializer). + std::string newMultiLineDeclS = ""; + raw_string_ostream newMLDecl(newMultiLineDeclS); + for (const auto &DL : Where->decls()) { + NewTyp *N = NULL; + VarDecl *VDL = dyn_cast(DL); + assert(VDL != NULL); + + for (const auto &NLT : rewritesForThisDecl) + if (NLT->getDecl() == DL) { + N = NLT; + break; + } + + if (N) { + newMLDecl << N->mkStr(); + newMLDecl << " " << VDL->getNameAsString(); + if (Expr *E = VDL->getInit()) { + newMLDecl << " = "; + E->printPretty(newMLDecl, nullptr, A.getPrintingPolicy()); + } + newMLDecl << ";\n"; + } else { + DL->print(newMLDecl); + newMLDecl << ";\n"; + } + } + + // Step 4: Write out the string built up in step 3. + R.InsertTextAfter(DR.getEnd(), newMLDecl.str()); + + // Step 5: Be sure and skip all of the NewTyps that we dealt with + // during this time of hacking, by adding them to the + // skip set. + + for (const auto &TN : rewritesForThisDecl) + skip.insert(TN); + } + } else if (FunctionDecl *UD = dyn_cast(D)) { + // TODO: If the return type is a fully-specified function pointer, + // then clang will give back an invalid source range for the + // return type source range. For now, check that the source + // range is valid. + SourceRange SR = UD->getReturnTypeSourceRange(); + if(SR.isValid()) + R.ReplaceText(SR, N->mkStr()); + } + } +} + +void emit(Rewriter &R, ASTContext &C, std::set &Files) { + // Check if we are outputing to stdout or not, if we are, just output the + // main file ID to stdout. + SourceManager &SM = C.getSourceManager(); + if (OutputPostfix == "-") { + if (const RewriteBuffer *B = R.getRewriteBufferFor(SM.getMainFileID())) + B->write(outs()); + } else + for (const auto &F : Files) + if (const RewriteBuffer *B = R.getRewriteBufferFor(F)) + if (const FileEntry *FE = SM.getFileEntryForID(F)) { + assert(FE->isValid()); + + // Produce a path/file name for the rewritten source file. + // That path should be the same as the old one, with a + // suffix added between the file name and the extension. + // For example \foo\bar\a.c should become \foo\bar\a.checked.c + // if the OutputPostfix parameter is "checked" . + + StringRef pfName = sys::path::filename(FE->getName()); + StringRef dirName = sys::path::parent_path(FE->getName()); + StringRef fileName = sys::path::remove_leading_dotslash(pfName); + StringRef ext = sys::path::extension(fileName); + StringRef stem = sys::path::stem(fileName); + Twine nFileName = stem + "." + OutputPostfix + ext; + Twine nFile = dirName + sys::path::get_separator() + nFileName; + + if (ext != ".h" || (ext == ".h" && RewriteHeaders)) { + std::error_code EC; + raw_fd_ostream out(nFile.str(), EC, sys::fs::F_None); + + if (!EC) + B->write(out); + else + errs() << "could not open file " << nFile << "\n"; + // This is awkward. What to do? Since we're iterating, + // we could have created other files successfully. Do we go back + // and erase them? Is that surprising? For now, let's just keep + // going. + } + } +} + +class RewriteConsumer : public ASTConsumer { +public: + explicit RewriteConsumer(ProgramInfo &I, ASTContext *Context) : Info(I) {} + + virtual void HandleTranslationUnit(ASTContext &Context) { + Info.enterCompilationUnit(Context); + + std::set rewriteThese; + Constraints &CS = Info.getConstraints(); + for (const auto &V : Info.getVarMap()) { + Decl *J = V.first; + DeclStmt *K = NULL; + Info.getDeclStmtForDecl(J, K); + + NewTyp *NT = NewTyp::mkTypForConstrainedType(CS, J, K, Info.getVarMap()); + rewriteThese.insert(NT); + } + + Rewriter R(Context.getSourceManager(), Context.getLangOpts()); + std::set Files; + rewrite(R, rewriteThese, Context.getSourceManager(), Context, Files); + + // Output files. + emit(R, Context, Files); + + Info.exitCompilationUnit(); + return; + } + +private: + ProgramInfo &Info; +}; + +template +class GenericAction : public ASTFrontendAction { +public: + GenericAction(ProgramInfo &I) : Info(I) {} + + virtual std::unique_ptr + CreateASTConsumer(CompilerInstance &Compiler, StringRef InFile) { + return std::unique_ptr(new T(Info, &Compiler.getASTContext())); + } + +private: + V &Info; +}; + +template +std::unique_ptr +newFrontendActionFactoryA(ProgramInfo &I) { + class ArgFrontendActionFactory : public FrontendActionFactory { + public: + explicit ArgFrontendActionFactory(ProgramInfo &I) : Info(I) {} + + FrontendAction *create() override { return new T(Info); } + + private: + ProgramInfo &Info; + }; + + return std::unique_ptr( + new ArgFrontendActionFactory(I)); +} + +int main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(); + + // Initialize targets for clang module support. + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); + + CommonOptionsParser OptionsParser(argc, argv, ConvertCategory); + + ClangTool Tool(OptionsParser.getCompilations(), + OptionsParser.getSourcePathList()); + + if (OutputPostfix == "-" && RewriteHeaders == true) { + errs() << "If rewriting headers, can't output to stdout\n"; + return 1; + } + + ProgramInfo Info; + + // 1. Gather constraints. + std::unique_ptr ConstraintTool = newFrontendActionFactoryA< + GenericAction>(Info); + + if (ConstraintTool) + Tool.run(ConstraintTool.get()); + else + llvm_unreachable("No action"); + + // 2. Solve constraints. + Constraints &CS = Info.getConstraints(); + CS.solve(); + + // 3. Re-write based on constraints. + std::unique_ptr RewriteTool = + newFrontendActionFactoryA>( + Info); + + if (RewriteTool) + Tool.run(RewriteTool.get()); + else + llvm_unreachable("No action"); + + return 0; +} diff --git a/tools/checked-c-convert/ConstraintBuilder.cpp b/tools/checked-c-convert/ConstraintBuilder.cpp new file mode 100644 index 000000000000..a54457fd8763 --- /dev/null +++ b/tools/checked-c-convert/ConstraintBuilder.cpp @@ -0,0 +1,374 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation of visitor methods for the FunctionVisitor class. These +// visitors create constraints based on the AST of the program. +//===----------------------------------------------------------------------===// +#include "ConstraintBuilder.h" + +using namespace llvm; +using namespace clang; + +// This class visits functions and adds constraints to the +// Constraints instance assigned to it. +// Each VisitXXX method is responsible either for looking inside statements +// to find constraints +// The results of this class are returned via the ProgramInfo +// parameter to the user. +class FunctionVisitor : public RecursiveASTVisitor { +public: + explicit FunctionVisitor(ASTContext *C, ProgramInfo &I, FunctionDecl *FD) + : Context(C), Info(I), Function(FD) {} + + // Introduce a variable into the environment. + bool MyVisitVarDecl(VarDecl *D, DeclStmt *S) { + if (D->isLocalVarDecl()) { + FullSourceLoc FL = Context->getFullLoc(D->getLocStart()); + SourceRange SR = D->getSourceRange(); + + if (SR.isValid() && FL.isValid() && !FL.isInSystemHeader() && + D->getType()->isPointerType()) { + Info.addVariable(D, S, Context); + } + } + + return true; + } + + // Adds constraints for the case where an expression RHS is being assigned + // to a variable V. There are a few different cases: + // 1. Straight-up assignment, i.e. int * a = b; with no casting. In this + // case, the rule would be that q_a = q_b. + // 2. Assignment from a constant. If the constant is NULL, then V + // is left as constrained as it was before. If the constant is any + // other value, then we constrain V to be wild. + // 3. Assignment from the address-taken of a variable. If no casts are + // involved, this is safe. We don't have a constraint variable for the + // address-taken variable, since it's referring to something "one-higher" + // however sometimes you could, like if you do: + // int **a = ...; + // int ** b = &(*(a)); + // and the & * cancel each other out. + // 4. Assignments from casts. Here, we use the implication rule. + // + // In any of these cases, due to conditional expressions, the number of + // variables on the RHS could be 0 or more. We just do the same rule + // for each pair of q_i to q_j \forall j in variables_on_rhs. + void constrainAssign(uint32_t V, Expr *RHS) { + if (!RHS) + return; + Constraints &CS = Info.getConstraints(); + std::set W; + Info.getVariable(RHS, W, Context); + if (W.size() > 0) { + // Case 1. + for (const auto &I : W) + CS.addConstraint( + CS.createEq(CS.getOrCreateVar(V), CS.getOrCreateVar(I))); + } else { + // Cases 2-4. + if (RHS->isIntegerConstantExpr(*Context)) { + // Case 2. + if (!RHS->isNullPointerConstant(*Context, + Expr::NPC_ValueDependentIsNotNull)) + CS.addConstraint(CS.createEq(CS.getOrCreateVar(V), CS.getWild())); + } else { + // Cases 3-4. + if (UnaryOperator *UO = dyn_cast(RHS)) { + if (UO->getOpcode() == UO_AddrOf) { + // Case 3. + // Is there anything to do here, or is it implicitly handled? + } + } else if (CStyleCastExpr *C = dyn_cast(RHS)) { + // Case 4. + Info.getVariable(C->getSubExpr(), W, Context); + bool safe = true; + for (const auto &I : W) + safe &= Info.checkStructuralEquality(V, I); + + for (const auto &I : W) + if (safe) + CS.addConstraint(CS.createImplies( + CS.createEq(CS.getOrCreateVar(V), CS.getWild()), + CS.createEq(CS.getOrCreateVar(I), CS.getWild()))); + else + CS.addConstraint( + CS.createEq(CS.getOrCreateVar(V), CS.getWild())); + } + } + } + } + + bool VisitDeclStmt(DeclStmt *S) { + // Introduce variables as needed. + if (S->isSingleDecl()) { + if (VarDecl *VD = dyn_cast(S->getSingleDecl())) + MyVisitVarDecl(VD, S); + } else + for (const auto &D : S->decls()) + if (VarDecl *VD = dyn_cast(D)) + MyVisitVarDecl(VD, S); + + // Build rules based on initializers. + for (const auto &D : S->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + std::set V; + Expr *InitE = VD->getInit(); + Info.getVariable(VD, V, Context); + if (V.size() > 0) + for (const auto &I : V) + constrainAssign(I, InitE); + } + } + + return true; + } + + // TODO: other visitors to visit statements and expressions that we use to + // gather constraints. + + bool VisitBinAssign(BinaryOperator *O) { + Expr *LHS = O->getLHS(); + Expr *RHS = O->getRHS(); + std::set V; + Info.getVariable(LHS, V, Context); + for (const auto &I : V) + constrainAssign(I, RHS); + + return true; + } + + bool VisitCallExpr(CallExpr *E) { + Decl *D = E->getCalleeDecl(); + if (!D) + return true; + + if (FunctionDecl *FD = dyn_cast(D)) { + Constraints &CS = Info.getConstraints(); + unsigned i = 0; + for (const auto &A : E->arguments()) { + std::set Ws; + if (i < FD->getNumParams()) { + Info.getVariable(FD->getParamDecl(i), Ws, Context); + if (Ws.size() > 0) { + assert(Ws.size() == 1); + uint32_t W = *Ws.begin(); + + std::set V; + Info.getVariable(A, V, Context); + for (const auto &I : V) + CS.addConstraint( + CS.createEq(CS.getOrCreateVar(W), CS.getOrCreateVar(I))); + } + } + i++; + } + } + + return true; + } + + bool VisitArraySubscriptExpr(ArraySubscriptExpr *E) { + std::set V; + Constraints &CS = Info.getConstraints(); + Info.getVariable(E->getBase(), V, Context); + for (const auto &I : V) + CS.addConstraint(CS.createEq(CS.getOrCreateVar(I), CS.getArr())); + return true; + } + + bool VisitReturnStmt(ReturnStmt *S) { + std::set V; + std::set F; + Constraints &CS = Info.getConstraints(); + Info.getVariable(S->getRetValue(), V, Context); + Info.getVariable(Function, F, Context); + if (F.size() > 0) { + assert(F.size() == 1); + for (const auto &I : V) + CS.addConstraint( + CS.createEq(CS.getOrCreateVar(*F.begin()), CS.getOrCreateVar(I))); + } + + return true; + } + + bool VisitUnaryPreInc(UnaryOperator *O) { + std::set V; + Constraints &CS = Info.getConstraints(); + Info.getVariable(O->getSubExpr(), V, Context); + for (const auto &I : V) + CS.addConstraint( + CS.createNot(CS.createEq(CS.getOrCreateVar(I), CS.getPtr()))); + + return true; + } + + bool VisitUnaryPostInc(UnaryOperator *O) { + std::set V; + Constraints &CS = Info.getConstraints(); + Info.getVariable(O->getSubExpr(), V, Context); + for (const auto &I : V) + CS.addConstraint( + CS.createNot(CS.createEq(CS.getOrCreateVar(I), CS.getPtr()))); + + return true; + } + + bool VisitUnaryPreDec(UnaryOperator *O) { + std::set V; + Constraints &CS = Info.getConstraints(); + Info.getVariable(O->getSubExpr(), V, Context); + for (const auto &I : V) + CS.addConstraint( + CS.createNot(CS.createEq(CS.getOrCreateVar(I), CS.getPtr()))); + + return true; + } + + bool VisitUnaryPostDec(UnaryOperator *O) { + std::set V; + Constraints &CS = Info.getConstraints(); + Info.getVariable(O->getSubExpr(), V, Context); + for (const auto &I : V) + CS.addConstraint( + CS.createNot(CS.createEq(CS.getOrCreateVar(I), CS.getPtr()))); + + return true; + } + + bool VisitBinAdd(BinaryOperator *O) { + std::set V1; + std::set V2; + Constraints &CS = Info.getConstraints(); + Info.getVariable(O->getLHS(), V1, Context); + Info.getVariable(O->getRHS(), V2, Context); + + for (const auto &I : V1) + CS.addConstraint( + CS.createNot(CS.createEq(CS.getOrCreateVar(I), CS.getPtr()))); + + for (const auto &I : V2) + CS.addConstraint( + CS.createNot(CS.createEq(CS.getOrCreateVar(I), CS.getPtr()))); + + return true; + } + + bool VisitBinSub(BinaryOperator *O) { + std::set V1; + std::set V2; + Constraints &CS = Info.getConstraints(); + Info.getVariable(O->getLHS(), V1, Context); + Info.getVariable(O->getRHS(), V2, Context); + + for (const auto &I : V1) + CS.addConstraint( + CS.createNot(CS.createEq(CS.getOrCreateVar(I), CS.getPtr()))); + + for (const auto &I : V2) + CS.addConstraint( + CS.createNot(CS.createEq(CS.getOrCreateVar(I), CS.getPtr()))); + + return true; + } + +private: + ASTContext *Context; + ProgramInfo &Info; + FunctionDecl *Function; +}; + +// This class visits a global declaration and either +// - Builds an _enviornment_ and _constraints_ for each function +// - Builds _constraints_ for declared struct/records in the translation unit +// The results are returned in the ProgramInfo parameter to the user. +class GlobalVisitor : public RecursiveASTVisitor { +public: + explicit GlobalVisitor(ASTContext *Context, ProgramInfo &I) + : Context(Context), Info(I) {} + + bool VisitFunctionDecl(FunctionDecl *Declaration) { + if (Declaration->hasBody() && Declaration->isThisDeclarationADefinition()) { + Stmt *Body = Declaration->getBody(); + FullSourceLoc FL = Context->getFullLoc(Declaration->getLocStart()); + + // For now, don't look at function definitions that clang + // identifies as in "system headers." Re-visit this at + // some point in the future, when "system headers" will include + // information relevant to our constraints. + if (FL.isValid() && !FL.isInSystemHeader()) { + FunctionVisitor FV = FunctionVisitor(Context, Info, Declaration); + + // Add variables for each parameter declared for the function. + for (const auto &P : Declaration->params()) + if (P->getType()->isPointerType()) + Info.addVariable(P, NULL, Context); + + // Add variables for the value returned from the function. + if (Declaration->getReturnType()->isPointerType()) + Info.addVariable(Declaration, NULL, Context); + + // Visit the body of the function and build up information. + FV.TraverseStmt(Body); + } + } + + return true; + } + + bool VisitRecordDecl(RecordDecl *Declaration) { + if (RecordDecl *Definition = Declaration->getDefinition()) { + FullSourceLoc FL = Context->getFullLoc(Definition->getLocStart()); + + if (FL.isValid() && !FL.isInSystemHeader()) { + SourceManager &SM = Context->getSourceManager(); + FileID FID = FL.getFileID(); + const FileEntry *FE = SM.getFileEntryForID(FID); + + if (FE && FE->isValid()) { + // We only want to re-write a record if it contains + // any pointer types. Most record types probably do, + // but let's scan it and not consider any records + // that don't have any pointers. + + bool anyPointers = false; + + for (const auto &F : Definition->fields()) { + if (F->getType()->isPointerType()) { + anyPointers = true; + break; + } + } + + if (anyPointers) { + Info.addRecordDecl(Definition); + } + } + } + } + + return true; + } + +private: + ASTContext *Context; + ProgramInfo &Info; +}; + +void ConstraintBuilderConsumer::HandleTranslationUnit(ASTContext &C) { + Info.enterCompilationUnit(C); + + GlobalVisitor GV = GlobalVisitor(&C, Info); + TranslationUnitDecl *TUD = C.getTranslationUnitDecl(); + // Generate constraints. + for (const auto &D : TUD->decls()) { + GV.TraverseDecl(D); + } + + Info.exitCompilationUnit(); + return; +} diff --git a/tools/checked-c-convert/ConstraintBuilder.h b/tools/checked-c-convert/ConstraintBuilder.h new file mode 100644 index 000000000000..f3b9415b0266 --- /dev/null +++ b/tools/checked-c-convert/ConstraintBuilder.h @@ -0,0 +1,26 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// +#ifndef _CONSTRAINT_BUILDER +#define _CONSTRAINT_BUILDER +#include "clang/AST/ASTConsumer.h" + +#include "ProgramInfo.h" + +class ConstraintBuilderConsumer : public clang::ASTConsumer { +public: + explicit ConstraintBuilderConsumer(ProgramInfo &I, clang::ASTContext *C) : + Info(I) { } + + virtual void HandleTranslationUnit(clang::ASTContext &); + +private: + ProgramInfo &Info; +}; + +#endif diff --git a/tools/checked-c-convert/MappingVisitor.cpp b/tools/checked-c-convert/MappingVisitor.cpp new file mode 100644 index 000000000000..df3f931973d9 --- /dev/null +++ b/tools/checked-c-convert/MappingVisitor.cpp @@ -0,0 +1,71 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementations of the MappingVisitor functions for VisitStmt and VisitDecl. +//===----------------------------------------------------------------------===// +#include "MappingVisitor.h" +#include "llvm/Support/Path.h" + +using namespace clang; +using namespace llvm; + +bool MappingVisitor::VisitDeclStmt(DeclStmt *S) { + PersistentSourceLoc PSL = + PersistentSourceLoc::mkPSL(S->getLocStart(), Context); + + if (PSL.valid()) { + + // Check to see if the source location as described by the current location + // of S appears in the set of PersistentSourceLocs we are tasked to + // resolve. If it is, then create a mapping mapping the current + // PersistentSourceLocation to the Stmt object S. + std::set::iterator I = SourceLocs.find(PSL); + if (I != SourceLocs.end()) { + Decl *D = NULL; + Stmt *So = NULL; + Type *T = NULL; + std::tie(So, D, T) = PSLtoSDT[PSL]; + if (So != NULL) { + So->dump(); + errs() << "\n"; + S->dump(); + errs() << "\n"; + } + assert(So == NULL); + PSLtoSDT[PSL] = StmtDeclOrType(S, D, T); + } + + if (DeclStmt *DL = dyn_cast(S)) { + if (DL->isSingleDecl()) { + if (VarDecl *VD = dyn_cast(DL->getSingleDecl())) + DeclToDeclStmt[VD] = DL; + } + else + for (auto I : DL->decls()) + DeclToDeclStmt[I] = DL; + } + } + + return true; +} + +bool MappingVisitor::VisitDecl(Decl *D) { + PersistentSourceLoc PSL = + PersistentSourceLoc::mkPSL(D->getLocation(), Context); + if (PSL.valid()) { + std::set::iterator I = SourceLocs.find(PSL); + if (I != SourceLocs.end()) { + Decl *Do = NULL; + Stmt *S = NULL; + Type *T = NULL; + std::tie(S, Do, T) = PSLtoSDT[PSL]; + assert(Do == NULL); + PSLtoSDT[PSL] = StmtDeclOrType(S, D, T); + } + } + + return true; +} diff --git a/tools/checked-c-convert/MappingVisitor.h b/tools/checked-c-convert/MappingVisitor.h new file mode 100644 index 000000000000..55005da7318e --- /dev/null +++ b/tools/checked-c-convert/MappingVisitor.h @@ -0,0 +1,58 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// The MappingVisitor is used to traverse an AST and re-define a mapping from +// PersistendSourceLocations to "live" AST objects. This is needed to support +// multi-compilation unit analyses, where after each compilation unit is +// analyzed, the state of the analysis is "shelved" and all references to AST +// data structures are replaced with data structures that survive the clang +// constructed AST. +//===----------------------------------------------------------------------===// +#ifndef _MAPPING_VISITOR_H +#define _MAPPING_VISITOR_H +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" + +#include "utils.h" +#include "PersistentSourceLoc.h" + +class MappingVisitor + : public clang::RecursiveASTVisitor { +public: + MappingVisitor(std::set S, clang::ASTContext &C) : + SourceLocs(S),Context(C) {} + + // TODO: It's possible the Type field in this tuple isn't needed. + typedef std::tuple + StmtDeclOrType; + + bool VisitDeclStmt(clang::DeclStmt *S); + + bool VisitDecl(clang::Decl *D); + + std::pair, + VariableDecltoStmtMap> + getResults() + { + return std::pair, + VariableDecltoStmtMap>(PSLtoSDT, DeclToDeclStmt); + } + +private: + // A map from a PersistentSourceLoc to a tuple describing a statement, decl + // or type. + std::map PSLtoSDT; + // The set of PersistentSourceLoc's this instance of MappingVisitor is tasked + // with re-instantiating as either a Stmt, Decl or Type. + std::set SourceLocs; + // The ASTContext for the particular AST that the MappingVisitor is + // traversing. + clang::ASTContext &Context; + // A mapping of individual Decls to the DeclStmt that contains them. + VariableDecltoStmtMap DeclToDeclStmt; +}; + +#endif diff --git a/tools/checked-c-convert/NewTyp.cpp b/tools/checked-c-convert/NewTyp.cpp new file mode 100644 index 000000000000..17f40145410e --- /dev/null +++ b/tools/checked-c-convert/NewTyp.cpp @@ -0,0 +1,114 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// +#include "NewTyp.h" + +using namespace clang; +using namespace llvm; + +// Given a solved set of constraints CS and a declaration D, produce a +// NewTyp data structure that describes how the type declaration for D +// might be re-written. The NewTyp data structure is needed because the +// replacement of the type declaration in the original source code needs +// to be done all at once via the Rewriter. +NewTyp *NewTyp::mkTypForConstrainedType(Constraints &CS, Decl *D, DeclStmt *K, + VariableMap &VM) { + // By cases, D could be a param, field, function, or variable decl. Get the + // type location for the declaration D, that will allow us to iterate over the + // different levels of the type. + TypeLoc TL; + if (VarDecl *VD = dyn_cast(D)) + TL = VD->getTypeSourceInfo()->getTypeLoc(); + else if (ParmVarDecl *PD = dyn_cast(D)) + TL = VD->getTypeSourceInfo()->getTypeLoc(); + else if (FieldDecl *FD = dyn_cast(D)) + TL = FD->getTypeSourceInfo()->getTypeLoc(); + else if (FunctionDecl *UD = dyn_cast(D)) + TL = UD->getTypeSourceInfo()->getTypeLoc(); + else + llvm_unreachable("unknown decl type"); + assert(!TL.isNull()); + + // Get the "base" variable for the declaration. + auto baseQVKeyI = VM.find(D); + assert(baseQVKeyI != VM.end()); + uint32_t baseQVKey = baseQVKeyI->second; + + // Now, build up a NewTyp type. + NewTyp *T = NULL; + NewTyp *Cur = T; + uint32_t curKey = baseQVKey; + Constraints::EnvironmentMap env = CS.getVariables(); + + // Strip off function definitions from the type. + while (!TL.isNull()) { + QualType T = TL.getType(); + if (T->isFunctionNoProtoType() || T->isFunctionType() || + T->isFunctionProtoType()) + TL = TL.getNextTypeLoc(); + else + break; + } + + while (!TL.isNull()) { + // What should the current type be qualified as? This can be answered by + // looking the constraint up for the current variable. + + NewTyp *tmp; + if (TL.getType()->isPointerType()) { + VarAtom toFind(curKey); + auto I = env.find(&toFind); + assert(I != env.end()); + ConstAtom *C = I->second; + + // How is the current type index qualified? This controls which base + // class we fill in. + + switch (C->getKind()) { + case Atom::A_Wild: + tmp = new WildTyp(); + break; + case Atom::A_Ptr: + tmp = new PtrTyp(); + break; + case Atom::A_Arr: + tmp = new ArrTyp(); + break; + case Atom::A_Var: + case Atom::A_Const: + llvm_unreachable("bad value in environment map"); + break; + } + + curKey++; + } else + tmp = new BaseNonPointerTyp(TL.getType()); + + // If this is our first trip through the loop, update the Cur variable + // to point to the NewTyp we created. Otherwise, update the ReferentTyp + // field of Cur. Also, if this is our first trip through the loop, + // update T to be the value we produced. + if (Cur == NULL) + Cur = tmp; + else + Cur->ReferentTyp = tmp; + + if (T == NULL) { + T = Cur; + T->DeclRewrite = D; + T->StmtWhere = K; + } + + Cur = tmp; + + TL = TL.getNextTypeLoc(); + } + + assert(T != NULL); + return T; +} diff --git a/tools/checked-c-convert/NewTyp.h b/tools/checked-c-convert/NewTyp.h new file mode 100644 index 000000000000..b56435e2068c --- /dev/null +++ b/tools/checked-c-convert/NewTyp.h @@ -0,0 +1,98 @@ +#ifndef _NEWTYP_H +#define _NEWTYP_H +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Tooling/Tooling.h" + +#include "clang/Constraints/Constraints.h" + +#include "utils.h" + +// Intermediate data structures that describe a type to be re-written. +class WildTyp; +class PtrTyp; +class ArrTyp; + +// The base class for a new type to be inserted into the transformed program. +class NewTyp { +public: + enum NewTypKind { + N_BaseNonPointer, + N_Ptr, + N_Arr, + N_Wild + }; + + NewTyp(NewTypKind K) : Kind(K), ReferentTyp(NULL), DeclRewrite(NULL) {} + virtual ~NewTyp() {} + NewTypKind getKind() const { return Kind; } + + // Given a set of solved constraints CS and a declaration D, produce a + // new checked C type + static NewTyp *mkTypForConstrainedType(Constraints &CS, clang::Decl *D, + clang::DeclStmt *K, VariableMap &VM); + + // Returns the C-formatted type declaration of the new type, suitable for + // insertion into the source code. + virtual std::string mkStr() = 0; + + clang::Decl *getDecl() { return DeclRewrite; } + clang::DeclStmt *getWhere() { return StmtWhere; } + +private: + NewTypKind Kind; +protected: + // Each type (except for BaseNonPointerTyp) wraps a sub-NewTyp value. + NewTyp *ReferentTyp; + // The outer-most NewTyp contains a reference to the Decl that this + // NewTyp refers to. + clang::Decl *DeclRewrite; + clang::DeclStmt *StmtWhere; +}; + +// Represents a non-pointer type, a wrapper around a QualType from the +// original program. +class BaseNonPointerTyp : public NewTyp { +public: + BaseNonPointerTyp(clang::QualType _T) : T(_T), NewTyp(N_BaseNonPointer) {} + + std::string mkStr() { + return T.getAsString(); + } + +private: + clang::QualType T; +}; + +// Represents a Checked C ptr type. +class PtrTyp : public NewTyp { +public: + PtrTyp() : NewTyp(N_Ptr) {} + + std::string mkStr() { + return "ptr<" + ReferentTyp->mkStr() + "> "; + } +}; + +// Represents a Checked C array_ptr type. Currently unused. +class ArrTyp : public NewTyp { +public: + ArrTyp() : NewTyp(N_Arr) {} + + std::string mkStr() { + return ReferentTyp->mkStr() + "* "; + } +}; + +// Represents an unchecked pointer type. +class WildTyp : public NewTyp { +public: + WildTyp() : NewTyp(N_Wild) {} + + std::string mkStr() { + return ReferentTyp->mkStr() + "* "; + } +}; +#endif diff --git a/tools/checked-c-convert/PersistentSourceLoc.cpp b/tools/checked-c-convert/PersistentSourceLoc.cpp new file mode 100644 index 000000000000..10dd5b8d425b --- /dev/null +++ b/tools/checked-c-convert/PersistentSourceLoc.cpp @@ -0,0 +1,45 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// +#include "PersistentSourceLoc.h" + +using namespace clang; +using namespace llvm; + +PersistentSourceLoc +PersistentSourceLoc::mkPSL(SourceLocation SL, ASTContext &Context) { + FullSourceLoc FSL = Context.getFullLoc(SL); + + if (!FSL.isValid()) + return PersistentSourceLoc(); + + assert(FSL.isValid()); + //TODO: this should go back to being the spelling loc..?? + // why can you only look up the FileEntry from a spelling loc?? + clang::FileID fID = Context.getSourceManager().getFileID(SL); + if (!fID.isValid()) { + FSL.dump(); + } + assert(fID.isValid()); + + const clang::FileEntry *FE = Context.getSourceManager().getFileEntryForID(fID); + + std::string fn = ""; + + if (!FE) { + fn = ""; + } + else { + assert(FE->isValid()); + fn = llvm::sys::path::filename(FE->getName()).str(); + } + + PersistentSourceLoc PSL(fn, FSL.getExpansionLineNumber(), FSL.getExpansionColumnNumber()); + + return PSL; +} diff --git a/tools/checked-c-convert/PersistentSourceLoc.h b/tools/checked-c-convert/PersistentSourceLoc.h new file mode 100644 index 000000000000..25d5006f202c --- /dev/null +++ b/tools/checked-c-convert/PersistentSourceLoc.h @@ -0,0 +1,70 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This class specifies a location in a source file that persists across +// invocations of the frontend. Given a Decl/Stmt/Expr, the FullSourceLoc +// of that value can be compared with an instance of this class for +// equality. If they are equal, then you can substitute the Decl/Stmt/Expr +// for the instance of this class. +//===----------------------------------------------------------------------===// +#ifndef _PERSISTENT_SOURCE_LOC_H +#define _PERSISTENT_SOURCE_LOC_H +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Tooling/Tooling.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ErrorHandling.h" + +class PersistentSourceLoc { +protected: + PersistentSourceLoc(std::string f, uint32_t l, uint32_t c) : + fileName(f), lineNo(l), colNo(c), isValid(true) {} + +public: + PersistentSourceLoc() : fileName(""), lineNo(0), colNo(0), isValid(false) {} + std::string getFileName() { return fileName; } + uint32_t getLineNo() { return lineNo; } + uint32_t getColNo() { return colNo; } + bool valid() { return isValid; } + + bool operator<(const PersistentSourceLoc &o) const { + if (fileName == o.fileName) + if (lineNo == o.lineNo) + if (colNo == o.colNo) + return false; + else + return colNo < o.colNo; + else + return lineNo < o.lineNo; + else + return fileName < o.fileName; + } + + void print(llvm::raw_ostream &O) { + O << fileName << ":" << lineNo << ":" << colNo << "\n"; + } + + void dump() { print(llvm::errs()); } + + static + PersistentSourceLoc mkPSL(clang::SourceLocation SL, clang::ASTContext &Context); + +private: + std::string fileName; + uint32_t lineNo; + uint32_t colNo; + bool isValid; +}; + +typedef std::pair +PersistentSourceRange; + +#endif diff --git a/tools/checked-c-convert/ProgramInfo.cpp b/tools/checked-c-convert/ProgramInfo.cpp new file mode 100644 index 000000000000..30a46cccfefd --- /dev/null +++ b/tools/checked-c-convert/ProgramInfo.cpp @@ -0,0 +1,346 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation of ProgramInfo methods. +//===----------------------------------------------------------------------===// +#include "ProgramInfo.h" +#include "MappingVisitor.h" + +using namespace clang; +using namespace llvm; + +void ProgramInfo::dump() { + CS.dump(); + errs() << "\n"; + + errs() << "Variables\n"; + for (const auto &I : Variables) { + I.first->dump(); + errs() << " ==> \n"; + errs() << I.second << "\n"; + } + + return; +} + +bool ProgramInfo::checkStructuralEquality(uint32_t V, uint32_t U) { + // TODO: implement structural equality checking. + return false; +} + +// Populate Variables, VarDeclToStatement, RVariables, and DepthMap with +// AST data structures that correspond do the data stored in PDMap and +// ReversePDMap. +void ProgramInfo::enterCompilationUnit(ASTContext &Context) { + assert(persisted == true); + // Get a set of all of the PersistentSourceLoc's we need to fill in + std::set P; + for (auto I : PersistentVariables) + P.insert(I.first); + + // Resolve the PersistentSourceLoc to one of Decl,Stmt,Type. + MappingVisitor V(P, Context); + TranslationUnitDecl *TUD = Context.getTranslationUnitDecl(); + for (const auto &D : TUD->decls()) + V.TraverseDecl(D); + std::pair, + VariableDecltoStmtMap> + res = V.getResults(); + std::map + PSLtoDecl = res.first; + + // Re-populate Variables. + assert(Variables.empty()); + for (auto I : PersistentVariables) { + PersistentSourceLoc PL = I.first; + uint32_t V = I.second; + std::map::iterator K = + PSLtoDecl.find(PL); + if (K != PSLtoDecl.end()) { + Decl *D; + Stmt *S; + Type *T; + std::tie(S, D, T) = K->second; + if (D == NULL) { + PL.dump(); + errs() << "\n"; + } + assert(D != NULL); + Variables[D] = V; + } + } + + // Re-populate RVariables. + assert(RVariables.empty()); + for (auto I : PersistentRVariables) { + PersistentSourceLoc PL = I.second; + uint32_t V = I.first; + std::map::iterator K = + PSLtoDecl.find(PL); + if (K != PSLtoDecl.end()) { + Decl *D; + Stmt *S; + Type *T; + std::tie(S, D, T) = K->second; + assert(D != NULL); + RVariables[V] = D; + } + } + + // Re-populate DepthMap. + assert(DepthMap.empty()); + for (auto I : PersistentDepthMap) { + PersistentSourceLoc PL = I.first; + uint32_t V = I.second; + std::map::iterator K = + PSLtoDecl.find(PL); + if (K != PSLtoDecl.end()) { + Decl *D; + Stmt *S; + Type *T; + std::tie(S, D, T) = K->second; + assert(D != NULL); + DepthMap[D] = V; + } + } + + // Re-populate VarDeclToStatement. + VarDeclToStatement = res.second; + + persisted = false; + return; +} + +// Remove any references we maintain to AST data structure pointers. +// After this, the Variables, VarDeclToStatement, RVariables, and DepthMap +// should all be empty. +void ProgramInfo::exitCompilationUnit() { + assert(persisted == false); + Variables.clear(); + VarDeclToStatement.clear(); + RVariables.clear(); + DepthMap.clear(); + persisted = true; + return; +} + +// For each pointer type in the declaration of D, add a variable to the +// constraint system for that pointer type. +bool ProgramInfo::addVariable(Decl *D, DeclStmt *St, ASTContext *C) { + assert(persisted == false); + PersistentSourceLoc PLoc = + PersistentSourceLoc::mkPSL(D->getLocation(), *C); + + assert(PLoc.valid()); + + // Check if we already have this Decl. + if (Variables.find(D) == Variables.end()) { + std::map::iterator Itmp = + PersistentVariables.find(PLoc); + if (Itmp != PersistentVariables.end()) { + D->dump(); + PersistentSourceLoc PSLtmp = Itmp->first; + PSLtmp.dump(); + errs() << "\n"; + PLoc.dump(); + errs() << "\n"; + } + + assert(PersistentVariables.find(PLoc) == PersistentVariables.end()); + + uint32_t thisKey = freeKey; + Variables.insert(std::pair(D, thisKey)); + PersistentVariables[PLoc] = thisKey; + + if (St && VarDeclToStatement.find(D) == VarDeclToStatement.end()) + VarDeclToStatement.insert(std::pair(D, St)); + + // Get a type to tear apart piece by piece. + TypeLoc TL; + if (VarDecl *VD = dyn_cast(D)) + TL = VD->getTypeSourceInfo()->getTypeLoc(); + else if (ParmVarDecl *PD = dyn_cast(D)) + TL = VD->getTypeSourceInfo()->getTypeLoc(); + else if (FieldDecl *FD = dyn_cast(D)) + TL = FD->getTypeSourceInfo()->getTypeLoc(); + else if (FunctionDecl *UD = dyn_cast(D)) + TL = UD->getTypeSourceInfo()->getTypeLoc(); + else + llvm_unreachable("unknown decl type"); + + assert(!TL.isNull()); + + while (!TL.isNull()) { + if (TL.getTypePtr()->isPointerType()) { + RVariables.insert(std::pair(thisKey, D)); + PersistentRVariables[thisKey] = PLoc; + CS.getOrCreateVar(thisKey); + + thisKey++; + freeKey++; + } + + TL = TL.getNextTypeLoc(); + } + + DepthMap.insert(std::pair(D, freeKey)); + PersistentDepthMap[PLoc] = freeKey; + + return true; + } else { + assert(PersistentVariables.find(PLoc) != PersistentVariables.end()); + return false; + } +} + +bool ProgramInfo::getDeclStmtForDecl(Decl *D, DeclStmt *&St) { + assert(persisted == false); + auto I = VarDeclToStatement.find(D); + if (I != VarDeclToStatement.end()) { + St = I->second; + return true; + } else + return false; +} + +// This is a bit of a hack. What we need to do is traverse the AST in a +// bottom-up manner, and, for a given expression, decide which singular, +// if any, constraint variable is involved in that expression. However, +// in the current version of clang (3.8.1), bottom-up traversal is not +// supported. So instead, we do a manual top-down traversal, considering +// the different cases and their meaning on the value of the constraint +// variable involved. This is probably incomplete, but, we're going to +// go with it for now. +// +// V is (currentVariable, baseVariable, limitVariable) +// E is an expression to recursively traverse. +// +// Returns true if E resolves to a constraint variable q_i and the +// currentVariable field of V is that constraint variable. Returns false if +// a constraint variable cannot be found. +bool +ProgramInfo::getVariableHelper(Expr *E, + std::set > &V, + ASTContext *C) { + if (DeclRefExpr *DRE = dyn_cast(E)) { + Decl *D = DRE->getDecl(); + PersistentSourceLoc PSL = + PersistentSourceLoc::mkPSL(D->getLocation(), *C); + assert(PSL.valid()); + + VariableMap::iterator I = Variables.find(D); + std::map::iterator IN = + PersistentVariables.find(PSL); + + if (I != Variables.end()) { + assert(IN != PersistentVariables.end()); + DeclMap::iterator DI = DepthMap.find(D); + assert(DI != DepthMap.end()); + V.insert(std::tuple + (I->second, I->second, DI->second)); + return true; + } else { + return false; + } + } else if (BinaryOperator *BO = dyn_cast(E)) { + return getVariableHelper(BO->getLHS(), V, C) || + getVariableHelper(BO->getRHS(), V, C); + } else if (UnaryOperator *UO = dyn_cast(E)) { + if (getVariableHelper(UO->getSubExpr(), V, C)) { + if (UO->getOpcode() == UO_Deref) { + bool b = true; + std::set< std::tuple > R; + + for (std::set< std::tuple >::iterator I = + V.begin(); I != V.end(); ++I) + { + uint32_t curVar, baseVar, limVar; + std::tie(curVar, baseVar, limVar) = *I; + uint32_t tmpVar = curVar + 1; + R.insert(std::tuple + (tmpVar, baseVar, limVar)); + b &= (tmpVar >= baseVar && tmpVar < limVar); + } + + V.swap(R); + return b; + } + // TODO: Should UO_AddrOf be handled here too? + return true; + } + return false; + } else if (ImplicitCastExpr *IE = dyn_cast(E)) { + return getVariableHelper(IE->getSubExpr(), V, C); + } else if (ParenExpr *PE = dyn_cast(E)) { + return getVariableHelper(PE->getSubExpr(), V, C); + } else if (CallExpr *CE = dyn_cast(E)) { + return getVariableHelper(CE->getCallee(), V, C); + } else if(ConditionalOperator *CO = dyn_cast(E)) { + // Explore the three exprs individually. + // TODO: Do we need to give these three sub-explorations their own sets + // and merge them at this point? + bool r = false; + r |= getVariableHelper(CO->getCond(), V, C); + r |= getVariableHelper(CO->getLHS(), V, C); + r |= getVariableHelper(CO->getRHS(), V, C); + return r; + } else { + return false; + } +} + +// Given some expression E, what is the top-most constraint variable that +// E refers to? It could be none, in which case V is empty. Otherwise, V +// contains the constraint variable(s) that E refers to. +void ProgramInfo::getVariable(Expr *E, std::set &V, ASTContext *C) { + assert(persisted == false); + if (!E) + return; + + std::set > VandDepth; + if (getVariableHelper(E, VandDepth, C)) { + for (auto I : VandDepth) { + uint32_t var, base, lim; + std::tie(var, base, lim) = I; + V.insert(var); + } + return; + } + + return; +} + +// Given a decl, return the variable for the top-most constraint of that decl. +// Unlike the case for expressions above, this can only ever return a single +// variable. +void ProgramInfo::getVariable(Decl *D, std::set &V, ASTContext *C) { + assert(persisted == false); + if (!D) + return; + + VariableMap::iterator I = Variables.find(D); + if (I != Variables.end()) { + V.insert(I->second); + return; + } + + return; +} + +// Given a constraint variable identifier K, find the Decl that +// corresponds to that variable. Note that multiple constraint +// variables map to a single decl, as in the case of +// int **b; for example. In this case, there would be two variables +// for that Decl, read out like int * q_0 * q_1 b; +// Returns NULL if there is no Decl for that varabiel. +Decl *ProgramInfo::getDecl(uint32_t K) { + assert(persisted == false); + auto I = RVariables.find(K); + if (I == RVariables.end()) + return NULL; + + return I->second; +} diff --git a/tools/checked-c-convert/ProgramInfo.h b/tools/checked-c-convert/ProgramInfo.h new file mode 100644 index 000000000000..d5f599460f75 --- /dev/null +++ b/tools/checked-c-convert/ProgramInfo.h @@ -0,0 +1,139 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This class is used to collect information for the program being analyzed. +// The class allocates constraint variables and maps the constraint variables +// to AST elements of the program. +// +// The allocation of constraint variables is a little nuanced. For a given +// variable, there might be multiple constraint variables. For example, some +// declaration of the form: +// +// int **p = ... ; +// +// would be given two constraint variables, visualized like this: +// +// int * q_(i+1) * q_i p = ... ; +// +// The constraint variable at the "highest" or outer-most level of the type +// is the lowest numbered constraint variable for a given declaration. +//===----------------------------------------------------------------------===// +#ifndef _PROGRAM_INFO_H +#define _PROGRAM_INFO_H +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Tooling/Tooling.h" + +#include "clang/Constraints/Constraints.h" +#include "utils.h" +#include "PersistentSourceLoc.h" + +class ProgramInfo { +public: + ProgramInfo() : freeKey(0), persisted(true) {} + + void dump(); + + Constraints &getConstraints() { return CS; } + void addRecordDecl(clang::RecordDecl *R) { Records.push_front(R); } + + // Populate Variables, VarDeclToStatement, RVariables, and DepthMap with + // AST data structures that correspond do the data stored in PDMap and + // ReversePDMap. + void enterCompilationUnit(clang::ASTContext &Context); + + // Remove any references we maintain to AST data structure pointers. + // After this, the Variables, VarDeclToStatement, RVariables, and DepthMap + // should all be empty. + void exitCompilationUnit(); + + // For each pointer type in the declaration of D, add a variable to the + // constraint system for that pointer type. + bool addVariable(clang::Decl *D, clang::DeclStmt *St, clang::ASTContext *C); + + bool getDeclStmtForDecl(clang::Decl *D, clang::DeclStmt *&St); + + // Checks the structural type equality of two constraint variables. This is + // needed if you are casting from U to V. If this returns true, then it's + // safe to add an implication that if U is wild, then V is wild. However, + // if this returns false, then both U and V must be constrained to wild. + bool checkStructuralEquality(uint32_t V, uint32_t U); + + // This is a bit of a hack. What we need to do is traverse the AST in a + // bottom-up manner, and, for a given expression, decide which, + // if any, constraint variable(s) are involved in that expression. However, + // in the current version of clang (3.8.1), bottom-up traversal is not + // supported. So instead, we do a manual top-down traversal, considering + // the different cases and their meaning on the value of the constraint + // variable involved. This is probably incomplete, but, we're going to + // go with it for now. + // + // V is (currentVariable, baseVariable, limitVariable) + // E is an expression to recursively traverse. + // + // Returns true if E resolves to a constraint variable q_i and the + // currentVariable field of V is that constraint variable. Returns false if + // a constraint variable cannot be found. + bool getVariableHelper(clang::Expr *E, + std::set > &V, + clang::ASTContext *C) ; + + // Given some expression E, what is the top-most constraint variable that + // E refers to? It could be none, in which case V is empty. Otherwise, V + // contains the constraint variable(s) that E refers to. + void getVariable(clang::Expr *E, std::set &V, clang::ASTContext *C); + + // The Decl version of getVariable can only return at most one variable + // in the set V, so you can rely on that behavior. It might seem un-necessary + // to have this function return a set that can contain only 0 or 1 elements, + // however C++ sort of forces us into this position. + void getVariable(clang::Decl *D, std::set &V, clang::ASTContext *C); + + // Given a constraint variable identifier K, find the Decl that + // corresponds to that variable. Note that multiple constraint + // variables map to a single decl, as in the case of + // int **b; for example. In this case, there would be two variables + // for that Decl, read out like int * q_0 * q_1 b; + // Returns NULL if there is no Decl for that varabiel. + clang::Decl *getDecl(uint32_t K); + + VariableMap &getVarMap() { return Variables; } + +private: + std::list Records; + // Next available integer to assign to a variable. + uint32_t freeKey; + // Map from a Decl to the DeclStmt that contains the Decl. + // I can't figure out how to go backwards from a VarDecl to a DeclStmt, so + // this infrastructure is here so that the re-writer can do that to figure + // out how to break up variable declarations that should span lines in the + // new program. + VariableDecltoStmtMap VarDeclToStatement; + // Map from a Decl* to a uint32_t variable identifier, that variable + // is the smallest variable for that Decl. + // It can be the case that |Variables| <= |PersistentVariables|, because + // PersistentVariables can refer to a variable that is only visible locally + // within some other file that we have processed. That is fine. + VariableMap Variables; + // Map from a uint32_t variable identifier to the Decl* for that variable. + ReverseVariableMap RVariables; + // Map from a Decl to the highest variable value in that Decl. + DeclMap DepthMap; + // Persists information in Variables. + std::map PersistentVariables; + // Persists information in DepthMap. + std::map PersistentDepthMap; + // Persists RVariables. + std::map PersistentRVariables; + // Constraint system. + Constraints CS; + // Is the ProgramInfo persisted? Only tested in asserts. Starts at true. + bool persisted; +}; + +#endif diff --git a/tools/checked-c-convert/utils.h b/tools/checked-c-convert/utils.h new file mode 100644 index 000000000000..15e8eff1728b --- /dev/null +++ b/tools/checked-c-convert/utils.h @@ -0,0 +1,22 @@ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Type declarations for map data structures. +//===----------------------------------------------------------------------===// +#ifndef _UTILS_H +#define _UTILS_H + +// Maps a Decl to the first constraint variable for the variable defined by +// that Decl. +typedef std::map VariableMap; +// Maps a constraint variable to the Decl that defines the variable the +// constraint variable refers to. +typedef std::map ReverseVariableMap; +typedef std::map DeclMap; +// Maps a Decl to the DeclStmt that defines the Decl. +typedef std::map VariableDecltoStmtMap; + +#endif diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index b85ec7e6dfa0..69fe918728f4 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -23,6 +23,7 @@ add_subdirectory(Format) add_subdirectory(Rewrite) add_subdirectory(Sema) add_subdirectory(CodeGen) +add_subdirectory(CheckedCRewriter) # FIXME: libclang unit tests are disabled on Windows due # to failures, mostly in libclang.VirtualFileOverlay_*. if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD) diff --git a/unittests/CheckedCRewriter/CMakeLists.txt b/unittests/CheckedCRewriter/CMakeLists.txt new file mode 100644 index 000000000000..8b08698fc7c4 --- /dev/null +++ b/unittests/CheckedCRewriter/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS Support) + +add_clang_unittest(CheckedCRewriterTests ConstraintTests.cpp) + +target_link_libraries(CheckedCRewriterTests Constraints) diff --git a/unittests/CheckedCRewriter/ConstraintTests.cpp b/unittests/CheckedCRewriter/ConstraintTests.cpp new file mode 100644 index 000000000000..b06d5421301b --- /dev/null +++ b/unittests/CheckedCRewriter/ConstraintTests.cpp @@ -0,0 +1,119 @@ +#include "gtest/gtest.h" +#include "clang/Constraints/Constraints.h" + +TEST(BasicConstraintTest, insert) { + Constraints CS; + VarAtom *q_0 = CS.getOrCreateVar(0); + VarAtom *q_1 = CS.getOrCreateVar(1); + VarAtom *q_2 = CS.getOrCreateVar(2); + + Constraints::ConstraintSet csInsert; + csInsert.insert(CS.createEq(q_0, CS.getWild())); + csInsert.insert(CS.createEq(q_1, CS.getArr())); + csInsert.insert(CS.createNot(CS.createEq(q_2, CS.getPtr()))); + + for (const auto &C : csInsert) { + EXPECT_TRUE(CS.addConstraint(C)); + } + + Constraints::ConstraintSet csInserted = CS.getConstraints(); + + EXPECT_EQ(csInsert, csInserted); + + EXPECT_FALSE(CS.addConstraint(CS.createEq(q_0, CS.getWild()))); + + csInserted = CS.getConstraints(); + + EXPECT_EQ(csInsert, csInserted); +} + +TEST(BasicConstraintTest, ordering) { + Constraints CS; + + PtrAtom *P = CS.getPtr(); + ArrAtom *A = CS.getArr(); + WildAtom *W = CS.getWild(); + VarAtom *q_0 = CS.getOrCreateVar(0); + VarAtom *q_1 = CS.getOrCreateVar(1); + VarAtom *q_2 = CS.getOrCreateVar(2); + Constraint *C1 = CS.createEq(q_0, CS.getWild()); + Constraint *C2 = CS.createEq(q_1, CS.getWild()); + Constraint *C4 = CS.createEq(q_1, CS.getArr()); + Constraint *C3 = CS.createEq(q_2, CS.getArr()); + + EXPECT_TRUE(*P < *A); + EXPECT_TRUE(*A < *W); + EXPECT_TRUE(*P < *W); + EXPECT_TRUE(!(*A < *P)); + EXPECT_TRUE(!(*W < *A)); + EXPECT_TRUE(!(*W < *P)); + EXPECT_TRUE(!(*P < *P)); + EXPECT_TRUE(!(*A < *A)); + EXPECT_TRUE(!(*W < *W)); + + EXPECT_TRUE(*P < *q_0); + EXPECT_TRUE(*A < *q_0); + EXPECT_TRUE(*W < *q_0); + EXPECT_TRUE(*q_0 < *q_1); + EXPECT_TRUE(*q_1 < *q_2); + EXPECT_TRUE(!(*q_1 < *q_0)); + EXPECT_TRUE(!(*q_1 < *q_1)); + EXPECT_TRUE(*C1 < *C2); + EXPECT_TRUE(*C4 < *C2); + EXPECT_TRUE(*C2 < *C3); +} + +TEST(BasicConstraintTest, solve) { + Constraints CS; + + EXPECT_TRUE(CS.addConstraint(CS.createEq(CS.getOrCreateVar(0), CS.getWild()))); + EXPECT_TRUE(CS.addConstraint(CS.createNot(CS.createEq(CS.getOrCreateVar(2), CS.getPtr())))); + EXPECT_TRUE(CS.addConstraint(CS.createImplies(CS.createEq(CS.getOrCreateVar(0), CS.getWild()), + CS.createEq(CS.getOrCreateVar(1), CS.getWild())))); + EXPECT_TRUE(CS.addConstraint(CS.createEq(CS.getOrCreateVar(3), CS.getOrCreateVar(2)))); + + EXPECT_TRUE(CS.addConstraint(CS.createEq(CS.getOrCreateVar(4), CS.getOrCreateVar(5)))); + EXPECT_TRUE(CS.addConstraint(CS.createEq(CS.getOrCreateVar(5), CS.getOrCreateVar(4)))); + EXPECT_TRUE(CS.addConstraint(CS.createEq(CS.getOrCreateVar(4), CS.getWild()))); + EXPECT_TRUE(CS.addConstraint(CS.createEq(CS.getOrCreateVar(5), CS.getOrCreateVar(5)))); + EXPECT_TRUE(CS.addConstraint(CS.createEq(CS.getOrCreateVar(6), CS.getOrCreateVar(6)))); + EXPECT_TRUE(CS.addConstraint(CS.createImplies(CS.createEq(CS.getOrCreateVar(3), CS.getArr()), + CS.createEq(CS.getOrCreateVar(7), CS.getArr())))); + EXPECT_TRUE(CS.addConstraint(CS.createImplies(CS.createEq(CS.getOrCreateVar(3), CS.getWild()), + CS.createEq(CS.getOrCreateVar(8), CS.getWild())))); + + EXPECT_TRUE(CS.solve()); + Constraints::EnvironmentMap env = CS.getVariables(); + + EXPECT_TRUE(*env[CS.getOrCreateVar(0)] == *CS.getWild()); + EXPECT_TRUE(*env[CS.getOrCreateVar(1)] == *CS.getWild()); + EXPECT_TRUE(*env[CS.getOrCreateVar(2)] == *CS.getArr()); + EXPECT_TRUE(*env[CS.getOrCreateVar(3)] == *CS.getArr()); + EXPECT_TRUE(*env[CS.getOrCreateVar(4)] == *CS.getWild()); + EXPECT_TRUE(*env[CS.getOrCreateVar(5)] == *CS.getWild()); + EXPECT_TRUE(*env[CS.getOrCreateVar(6)] == *CS.getPtr()); + EXPECT_TRUE(*env[CS.getOrCreateVar(7)] == *CS.getArr()); + EXPECT_TRUE(*env[CS.getOrCreateVar(8)] == *CS.getPtr()); +} + +TEST(BasicConstraintTest, equality) { + Constraints CS; + + // q_0 = WILD + // q_1 = PTR + // q_0 = q_1 + // + // should derive + // q_0 = WILD + // q_1 = WILD + + EXPECT_TRUE(CS.addConstraint(CS.createEq(CS.getOrCreateVar(0), CS.getWild()))); + EXPECT_TRUE(CS.addConstraint(CS.createEq(CS.getOrCreateVar(1), CS.getPtr()))); + EXPECT_TRUE(CS.addConstraint(CS.createEq(CS.getOrCreateVar(0), CS.getOrCreateVar(1)))); + + EXPECT_TRUE(CS.solve()); + Constraints::EnvironmentMap env = CS.getVariables(); + + EXPECT_TRUE(*env[CS.getOrCreateVar(0)] == *CS.getWild()); + EXPECT_TRUE(*env[CS.getOrCreateVar(1)] == *CS.getWild()); +} \ No newline at end of file From 4d92ed1f24c4d498983b3de6e27323830f69f32e Mon Sep 17 00:00:00 2001 From: Andrew Ruef Date: Wed, 24 Aug 2016 14:14:39 -0700 Subject: [PATCH 2/3] Moved Constraint library into the tool. --- lib/CMakeLists.txt | 1 - lib/Constraints/CMakeLists.txt | 5 ----- tools/checked-c-convert/CMakeLists.txt | 1 + tools/checked-c-convert/CheckedCConvert.cpp | 2 +- {lib/Constraints => tools/checked-c-convert}/Constraints.cpp | 2 +- .../Constraints => tools/checked-c-convert}/Constraints.h | 0 tools/checked-c-convert/NewTyp.h | 2 +- tools/checked-c-convert/ProgramInfo.h | 2 +- 8 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 lib/Constraints/CMakeLists.txt rename {lib/Constraints => tools/checked-c-convert}/Constraints.cpp (99%) rename {include/clang/Constraints => tools/checked-c-convert}/Constraints.h (100%) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index d23341653476..dfd819a407e9 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,4 +22,3 @@ if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(StaticAnalyzer) endif() add_subdirectory(Format) -add_subdirectory(Constraints) \ No newline at end of file diff --git a/lib/Constraints/CMakeLists.txt b/lib/Constraints/CMakeLists.txt deleted file mode 100644 index 6afb9d598c17..000000000000 --- a/lib/Constraints/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(LLVM_LINK_COMPONENTS support) - -add_clang_library(Constraints - Constraints.cpp -) \ No newline at end of file diff --git a/tools/checked-c-convert/CMakeLists.txt b/tools/checked-c-convert/CMakeLists.txt index 4a2cf722749e..426c3b59a803 100644 --- a/tools/checked-c-convert/CMakeLists.txt +++ b/tools/checked-c-convert/CMakeLists.txt @@ -11,6 +11,7 @@ add_clang_executable(checked-c-convert MappingVisitor.cpp ConstraintBuilder.cpp PersistentSourceLoc.cpp + Constraints.cpp ) target_link_libraries(checked-c-convert diff --git a/tools/checked-c-convert/CheckedCConvert.cpp b/tools/checked-c-convert/CheckedCConvert.cpp index 5f9260c81e4a..cc44c46a81c2 100644 --- a/tools/checked-c-convert/CheckedCConvert.cpp +++ b/tools/checked-c-convert/CheckedCConvert.cpp @@ -23,7 +23,7 @@ #include #include -#include "clang/Constraints/Constraints.h" +#include "Constraints.h" #include "ConstraintBuilder.h" #include "NewTyp.h" diff --git a/lib/Constraints/Constraints.cpp b/tools/checked-c-convert/Constraints.cpp similarity index 99% rename from lib/Constraints/Constraints.cpp rename to tools/checked-c-convert/Constraints.cpp index 0c7705d57ea0..578f52a9c76f 100644 --- a/lib/Constraints/Constraints.cpp +++ b/tools/checked-c-convert/Constraints.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/Constraints/Constraints.h" +#include "Constraints.h" #include "llvm/Support/CommandLine.h" #include diff --git a/include/clang/Constraints/Constraints.h b/tools/checked-c-convert/Constraints.h similarity index 100% rename from include/clang/Constraints/Constraints.h rename to tools/checked-c-convert/Constraints.h diff --git a/tools/checked-c-convert/NewTyp.h b/tools/checked-c-convert/NewTyp.h index b56435e2068c..b36fd19af866 100644 --- a/tools/checked-c-convert/NewTyp.h +++ b/tools/checked-c-convert/NewTyp.h @@ -6,7 +6,7 @@ #include "clang/Frontend/FrontendAction.h" #include "clang/Tooling/Tooling.h" -#include "clang/Constraints/Constraints.h" +#include "Constraints.h" #include "utils.h" diff --git a/tools/checked-c-convert/ProgramInfo.h b/tools/checked-c-convert/ProgramInfo.h index d5f599460f75..e0f3b030aa5c 100644 --- a/tools/checked-c-convert/ProgramInfo.h +++ b/tools/checked-c-convert/ProgramInfo.h @@ -29,7 +29,7 @@ #include "clang/Frontend/FrontendAction.h" #include "clang/Tooling/Tooling.h" -#include "clang/Constraints/Constraints.h" +#include "Constraints.h" #include "utils.h" #include "PersistentSourceLoc.h" From 93b9606b8865faee6c23134321e21643f5812a02 Mon Sep 17 00:00:00 2001 From: Andrew Ruef Date: Wed, 24 Aug 2016 14:21:39 -0700 Subject: [PATCH 3/3] Update build infrastructure to not use the constraint library any more. --- tools/checked-c-convert/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/checked-c-convert/CMakeLists.txt b/tools/checked-c-convert/CMakeLists.txt index 426c3b59a803..0b6f0a86f72c 100644 --- a/tools/checked-c-convert/CMakeLists.txt +++ b/tools/checked-c-convert/CMakeLists.txt @@ -15,7 +15,6 @@ add_clang_executable(checked-c-convert ) target_link_libraries(checked-c-convert - Constraints clangAST clangBasic clangDriver