diff --git a/.larabot.conf b/.larabot.conf index 824096f103fe25bb3c6b135e0bc84bc9fbda98d0..6a037e0addc616364cb0b049075beb1f7c2d18c7 100644 --- a/.larabot.conf +++ b/.larabot.conf @@ -2,6 +2,7 @@ commands = [ "sbt -batch test" "sbt -batch integration:test" "sbt -batch regression:test" + "sbt -batch genc:test" ] trusted = [ diff --git a/build.sbt b/build.sbt index b825fa5ba9146d4bb9e2c23f41edf3e6f756188f..0e3e147a10e395fb58a0ebb3f981b9919deb56b9 100644 --- a/build.sbt +++ b/build.sbt @@ -141,6 +141,14 @@ parallelExecution in IsabelleTest := false fork in IsabelleTest := true +// GenC Tests +lazy val GenCTest = config("genc") extend(Test) + +testOptions in GenCTest := Seq(Tests.Argument("-oDF"), Tests.Filter(_ startsWith "leon.genc.")) + +parallelExecution in GenCTest := false + + def ghProject(repo: String, version: String) = RootProject(uri(s"${repo}#${version}")) @@ -148,9 +156,11 @@ lazy val bonsai = ghProject("git://github.com/colder/bonsai.git", "10ea lazy val scalaSmtLib = ghProject("git://github.com/regb/scala-smtlib.git", "372bb14d0c84953acc17f9a7e1592087adb0a3e1") lazy val root = (project in file(".")). - configs(RegressionTest, IsabelleTest, IntegrTest). + configs(RegressionTest, IsabelleTest, GenCTest, IntegrTest). dependsOn(bonsai, scalaSmtLib). settings(inConfig(RegressionTest)(Defaults.testTasks ++ testSettings): _*). settings(inConfig(IntegrTest)(Defaults.testTasks ++ testSettings): _*). settings(inConfig(IsabelleTest)(Defaults.testTasks ++ testSettings): _*). + settings(inConfig(GenCTest)(Defaults.testTasks ++ testSettings): _*). settings(inConfig(Test)(Defaults.testTasks ++ testSettings): _*) + diff --git a/src/main/scala/leon/Main.scala b/src/main/scala/leon/Main.scala index 9cc0dc9c6af811444024a05dd95b93c518df31eb..f238d4a854bd881c74a3205ef5b727a220e34ae1 100644 --- a/src/main/scala/leon/Main.scala +++ b/src/main/scala/leon/Main.scala @@ -27,7 +27,9 @@ object Main { solvers.isabelle.AdaptationPhase, solvers.isabelle.IsabellePhase, transformations.InstrumentationPhase, - invariant.engine.InferInvariantsPhase) + invariant.engine.InferInvariantsPhase, + genc.GenerateCPhase, + genc.CFileOutputPhase) } // Add whatever you need here. @@ -53,9 +55,10 @@ object Main { val optHelp = LeonFlagOptionDef("help", "Show help message", false) val optInstrument = LeonFlagOptionDef("instrument", "Instrument the code for inferring time/depth/stack bounds", false) val optInferInv = LeonFlagOptionDef("inferInv", "Infer invariants from (instrumented) the code", false) + val optGenc = LeonFlagOptionDef("genc", "Generate C code", false) override val definedOptions: Set[LeonOptionDef[Any]] = - Set(optTermination, optRepair, optSynthesis, optIsabelle, optNoop, optHelp, optEval, optVerify, optInstrument, optInferInv) + Set(optTermination, optRepair, optSynthesis, optIsabelle, optNoop, optHelp, optEval, optVerify, optInstrument, optInferInv, optGenc) } lazy val allOptions: Set[LeonOptionDef[Any]] = allComponents.flatMap(_.definedOptions) @@ -151,6 +154,8 @@ object Main { import repair.RepairPhase import evaluators.EvaluationPhase import solvers.isabelle.IsabellePhase + import genc.GenerateCPhase + import genc.CFileOutputPhase import MainComponent._ import invariant.engine.InferInvariantsPhase import transformations.InstrumentationPhase @@ -163,11 +168,17 @@ object Main { val isabelleF = ctx.findOptionOrDefault(optIsabelle) val terminationF = ctx.findOptionOrDefault(optTermination) val verifyF = ctx.findOptionOrDefault(optVerify) + val gencF = ctx.findOptionOrDefault(optGenc) val evalF = ctx.findOption(optEval).isDefined val inferInvF = ctx.findOptionOrDefault(optInferInv) val instrumentF = ctx.findOptionOrDefault(optInstrument) val analysisF = verifyF && terminationF + // Check consistency in options + if (gencF && !xlangF) { + ctx.reporter.fatalError("Generating C code with --genc requires --xlang") + } + if (helpF) { displayVersion(ctx.reporter) displayHelp(ctx.reporter, error = false) @@ -175,7 +186,7 @@ object Main { val pipeBegin: Pipeline[List[String], Program] = ClassgenPhase andThen ExtractionPhase andThen - new PreprocessingPhase(xlangF) + new PreprocessingPhase(xlangF, gencF) val verification = if (xlangF) VerificationPhase andThen FixReportLabels else VerificationPhase @@ -189,6 +200,7 @@ object Main { else if (evalF) EvaluationPhase else if (inferInvF) InferInvariantsPhase else if (instrumentF) InstrumentationPhase andThen FileOutputPhase + else if (gencF) GenerateCPhase andThen CFileOutputPhase else verification } diff --git a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala index aa1e07e3c0c98834a538c633558be2c37875794b..3e93664008654edef6fa057c56451db0a108bc6b 100644 --- a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala +++ b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala @@ -153,7 +153,7 @@ trait CodeExtraction extends ASTExtractors { } private def isIgnored(s: Symbol) = { - (annotationsOf(s) contains "ignore") || s.fullName.toString.endsWith(".main") + (annotationsOf(s) contains "ignore") } private def isLibrary(u: CompilationUnit) = Build.libFiles contains u.source.file.absolute.path @@ -625,6 +625,8 @@ trait CodeExtraction extends ASTExtractors { } } + private var isLazy = Set[LeonValDef]() + private var defsToDefs = Map[Symbol, FunDef]() private def defineFunDef(sym: Symbol, within: Option[LeonClassDef] = None)(implicit dctx: DefContext): FunDef = { @@ -638,7 +640,13 @@ trait CodeExtraction extends ASTExtractors { val tpe = if (sym.isByNameParam) FunctionType(Seq(), ptpe) else ptpe val newID = FreshIdentifier(sym.name.toString, tpe).setPos(sym.pos) owners += (newID -> None) - LeonValDef(newID, sym.isByNameParam).setPos(sym.pos) + val vd = LeonValDef(newID).setPos(sym.pos) + + if (sym.isByNameParam) { + isLazy += vd + } + + vd } val tparamsDef = tparams.map(t => TypeParameterDef(t._2)) @@ -1534,7 +1542,7 @@ trait CodeExtraction extends ASTExtractors { val fd = getFunDef(sym, c.pos) val newTps = tps.map(t => extractType(t)) - val argsByName = (fd.params zip args).map(p => if (p._1.isLazy) Lambda(Seq(), p._2) else p._2) + val argsByName = (fd.params zip args).map(p => if (isLazy(p._1)) Lambda(Seq(), p._2) else p._2) FunctionInvocation(fd.typed(newTps), argsByName) @@ -1543,7 +1551,7 @@ trait CodeExtraction extends ASTExtractors { val cd = methodToClass(fd) val newTps = tps.map(t => extractType(t)) - val argsByName = (fd.params zip args).map(p => if (p._1.isLazy) Lambda(Seq(), p._2) else p._2) + val argsByName = (fd.params zip args).map(p => if (isLazy(p._1)) Lambda(Seq(), p._2) else p._2) MethodInvocation(rec, cd, fd.typed(newTps), argsByName) diff --git a/src/main/scala/leon/genc/CAST.scala b/src/main/scala/leon/genc/CAST.scala new file mode 100644 index 0000000000000000000000000000000000000000..9a9242859e799488eeb8a42170f6fcedd1b0dd13 --- /dev/null +++ b/src/main/scala/leon/genc/CAST.scala @@ -0,0 +1,296 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package genc + +import utils.UniqueCounter + +/* + * Here are defined classes used to represent AST of C programs. + */ + +object CAST { // C Abstract Syntax Tree + + sealed abstract class Tree + case object NoTree extends Tree + + + /* ------------------------------------------------------------ Types ----- */ + abstract class Type(val rep: String) extends Tree { + override def toString = rep + + def mutable: Type = this match { + case Const(typ) => typ.mutable + case _ => this + } + } + + /* Type Modifiers */ + case class Const(typ: Type) extends Type(s"$typ const") + case class Pointer(typ: Type) extends Type(s"$typ*") + + /* Primitive Types */ + case object Int32 extends Type("int32_t") // Requires <stdint.h> + case object Bool extends Type("bool") // Requires <stdbool.h> + case object Void extends Type("void") + + /* Compound Types */ + case class Struct(id: Id, fields: Seq[Var]) extends Type(id.name) + + + /* --------------------------------------------------------- Literals ----- */ + case class IntLiteral(v: Int) extends Stmt + case class BoolLiteral(b: Boolean) extends Stmt + + + /* ----------------------------------------------------- Definitions ----- */ + abstract class Def extends Tree + + case class Prog(structs: Seq[Struct], functions: Seq[Fun]) extends Def + + case class Fun(id: Id, retType: Type, params: Seq[Var], body: Stmt) extends Def + + case class Id(name: String) extends Def { + // `|` is used as the margin delimiter and can cause trouble in some situations + def fixMargin = + if (name.size > 0 && name(0) == '|') "| " + name + else name + } + + case class Var(id: Id, typ: Type) extends Def + + /* ----------------------------------------------------------- Stmts ----- */ + abstract class Stmt extends Tree + case object NoStmt extends Stmt + + case class Compound(stmts: Seq[Stmt]) extends Stmt + + case class Assert(pred: Stmt, error: Option[String]) extends Stmt { // Requires <assert.h> + require(pred.isValue) + } + + case class DeclVar(x: Var) extends Stmt + + case class DeclInitVar(x: Var, value: Stmt) extends Stmt { + require(value.isValue) + } + + case class Assign(lhs: Stmt, rhs: Stmt) extends Stmt { + require(lhs.isValue && rhs.isValue) + } + + // Note: we don't need to differentiate between specific + // operators so we only keep track of the "kind" of operator + // with an Id. + case class UnOp(op: Id, rhs: Stmt) extends Stmt { + require(rhs.isValue) + } + + case class MultiOp(op: Id, stmts: Seq[Stmt]) extends Stmt { + require(stmts.length > 1 && stmts.forall { _.isValue }) + } + + case class SubscriptOp(ptr: Stmt, idx: Stmt) extends Stmt { + require(ptr.isValue && idx.isValue) + } + + case object Break extends Stmt + + case class Return(stmt: Stmt) extends Stmt { + require(stmt.isValue) + } + + case class IfElse(cond: Stmt, thn: Stmt, elze: Stmt) extends Stmt { + require(cond.isValue) + } + + case class While(cond: Stmt, body: Stmt) extends Stmt { + require(cond.isValue) + } + + case class AccessVar(id: Id) extends Stmt + case class AccessRef(id: Id) extends Stmt + case class AccessAddr(id: Id) extends Stmt + case class AccessField(struct: Stmt, field: Id) extends Stmt { + require(struct.isValue) + } + + case class Call(id: Id, args: Seq[Stmt]) extends Stmt { + require(args forall { _.isValue }) + } + + case class StructInit(args: Seq[(Id, Stmt)], struct: Struct) extends Stmt { + require(args forall { _._2.isValue }) + } + + case class ArrayInit(length: Stmt, valueType: Type, defaultValue: Stmt) extends Stmt { + require(length.isValue && defaultValue.isValue) + } + + case class ArrayInitWithValues(valueType: Type, values: Seq[Stmt]) extends Stmt { + require(values forall { _.isValue }) + + lazy val length = values.length + } + + + /* -------------------------------------------------------- Factories ----- */ + object Op { + def apply(op: String, rhs: Stmt) = UnOp(Id(op), rhs) + def apply(op: String, rhs: Stmt, lhs: Stmt) = MultiOp(Id(op), rhs :: lhs :: Nil) + def apply(op: String, stmts: Seq[Stmt]) = MultiOp(Id(op), stmts) + } + + object Val { + def apply(id: Id, typ: Type) = typ match { + case Const(_) => Var(id, typ) // avoid const of const + case _ => Var(id, Const(typ)) + } + } + + /* "Templatetized" Types */ + object Tuple { + def apply(bases: Seq[Type]) = { + val name = Id("__leon_tuple_" + bases.mkString("_") + "_t") + + val fields = bases.zipWithIndex map { + case (typ, idx) => Var(getNthId(idx + 1), typ) + } + + Struct(name, fields) + } + + // Indexes start from 1, not 0! + def getNthId(n: Int) = Id("_" + n) + } + + object Array { + def apply(base: Type) = { + val name = Id("__leon_array_" + base + "_t") + val data = Var(dataId, Pointer(base)) + val length = Var(lengthId, Int32) + val fields = data :: length :: Nil + + Struct(name, fields) + } + + def lengthId = Id("length") + def dataId = Id("data") + } + + + /* ---------------------------------------------------- Introspection ----- */ + implicit class IntrospectionOps(val stmt: Stmt) { + def isLiteral = stmt match { + case _: IntLiteral => true + case _: BoolLiteral => true + case _ => false + } + + // True if statement can be used as a value + def isValue: Boolean = isLiteral || { + stmt match { + //case _: Assign => true it's probably the case but for now let's ignore it + case c: Compound => c.stmts.size == 1 && c.stmts.head.isValue + case _: UnOp => true + case _: MultiOp => true + case _: SubscriptOp => true + case _: AccessVar => true + case _: AccessRef => true + case _: AccessAddr => true + case _: AccessField => true + case _: Call => true + case _: StructInit => true + case _: ArrayInit => true + case _: ArrayInitWithValues => true + case _ => false + } + } + + def isPure: Boolean = isLiteral || { + stmt match { + case NoStmt => true + case Compound(stmts) => stmts forall { _.isPure } + case Assert(pred, _) => pred.isPure + case UnOp(_, rhs) => rhs.isPure + case MultiOp(_, stmts) => Compound(stmts).isPure + case SubscriptOp(ptr, idx) => (ptr ~ idx).isPure + case IfElse(c, t, e) => (c ~ t ~ e).isPure + case While(c, b) => (c ~ b).isPure + case AccessVar(_) => true + case AccessRef(_) => true + case AccessAddr(_) => true + case AccessField(s, _) => s.isPure + // case Call(id, args) => true if args are pure and function `id` is pure too + case _ => false + } + } + + def isPureValue = isValue && isPure + } + + + /* ------------------------------------------------------------- DSL ----- */ + // Operator ~~ appends and flattens nested compounds + implicit class StmtOps(val stmt: Stmt) { + // In addition to combining statements together in a compound + // we remove the empty ones and if the resulting compound + // has only one statement we return this one without being + // wrapped into a Compound + def ~(other: Stmt) = { + val stmts = (stmt, other) match { + case (Compound(stmts), Compound(others)) => stmts ++ others + case (stmt , Compound(others)) => stmt +: others + case (Compound(stmts), other ) => stmts :+ other + case (stmt , other ) => stmt :: other :: Nil + } + + def isNoStmt(s: Stmt) = s match { + case NoStmt => true + case _ => false + } + + val compound = Compound(stmts filterNot isNoStmt) + compound match { + case Compound(stmts) if stmts.length == 0 => NoStmt + case Compound(stmts) if stmts.length == 1 => stmts.head + case compound => compound + } + } + + def ~~(others: Seq[Stmt]) = stmt ~ Compound(others) + } + + implicit class StmtsOps(val stmts: Seq[Stmt]) { + def ~~(other: Stmt) = other match { + case Compound(others) => Compound(stmts) ~~ others + case other => Compound(stmts) ~ other + } + + def ~~~(others: Seq[Stmt]) = Compound(stmts) ~~ others + } + + val True = BoolLiteral(true) + val False = BoolLiteral(false) + + + /* ------------------------------------------------ Fresh Generators ----- */ + object FreshId { + private var counter = -1 + private val leonPrefix = "__leon_" + + def apply(prefix: String = ""): Id = { + counter += 1 + Id(leonPrefix + prefix + counter) + } + } + + object FreshVar { + def apply(typ: Type, prefix: String = "") = Var(FreshId(prefix), typ) + } + + object FreshVal { + def apply(typ: Type, prefix: String = "") = Val(FreshId(prefix), typ) + } +} + diff --git a/src/main/scala/leon/genc/CConverter.scala b/src/main/scala/leon/genc/CConverter.scala new file mode 100644 index 0000000000000000000000000000000000000000..a979fd8dd0c28351948270048adf53ed597a1317 --- /dev/null +++ b/src/main/scala/leon/genc/CConverter.scala @@ -0,0 +1,708 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package genc + +import purescala.Common._ +import purescala.Definitions._ +import purescala.Expressions._ +import purescala.Types._ +import xlang.Expressions._ + +import scala.reflect.ClassTag + +// don't import CAST._ to decrease possible confusion between the two ASTs + +class CConverter(val ctx: LeonContext, val prog: Program) { + def convert: CAST.Prog = convertToProg(prog) + + // Global data: keep track of the custom types and function of the input program + // Using sequences and not sets to keep track of order/dependencies + private var typeDecls = Seq[CAST.Struct]() + private var functions = Seq[CAST.Fun]() + + // Extra information about inner functions' context + // See classes VarInfo and FunCtx and functions convertToFun and + // FunctionInvocation conversion + private var funExtraArgss = Map[CAST.Id, Seq[CAST.Id]]() + private val emptyFunCtx = FunCtx(Seq()) + + private def registerType(typ: CAST.Struct) { + // Types might be processed more than once as the corresponding CAST type + // is not cached and need to be reconstructed several time if necessary + if (!typeDecls.contains(typ)) { + typeDecls = typeDecls :+ typ + debug(s"New type registered: $typ") + } + } + + private def registerFun(fun: CAST.Fun) { + // Unlike types, functions should not get defined multiple times as this + // would imply invalidating funExtraArgss + if (functions contains fun) + internalError("Function ${fun.id} defined more than once") + else + functions = functions :+ fun + } + + // Register extra function argument for the function named `id` + private def registerFunExtraArgs(id: CAST.Id, params: Seq[CAST.Id]) { + funExtraArgss = funExtraArgss + ((id, params)) + } + + // Get the extra argument identifiers for the function named `id` + private def getFunExtraArgs(id: CAST.Id) = funExtraArgss.getOrElse(id, Seq()) + + // Apply the conversion function and make sure the resulting AST matches our expectation + private def convertTo[T](tree: Tree)(implicit funCtx: FunCtx, ct: ClassTag[T]): T = convert(tree) match { + case t: T => t + case x => internalError(s"Expected an instance of $ct when converting $tree but got $x") + } + + // Generic conversion function + // Currently simple aliases in case we need later to have special treatment instead + private def convertToType (tree: Tree)(implicit funCtx: FunCtx) = convertTo[CAST.Type](tree) + private def convertToStruct(tree: Tree)(implicit funCtx: FunCtx) = convertTo[CAST.Struct](tree) + private def convertToId (tree: Tree)(implicit funCtx: FunCtx) = convertTo[CAST.Id](tree) + private def convertToStmt (tree: Tree)(implicit funCtx: FunCtx) = convertTo[CAST.Stmt](tree) + private def convertToVar (tree: Tree)(implicit funCtx: FunCtx) = convertTo[CAST.Var](tree) + + private def convertToProg(prog: Program): CAST.Prog = { + // Only process the main unit + val (mainUnits, _) = prog.units partition { _.isMainUnit } + + if (mainUnits.size == 0) fatalError("No main unit in the program") + if (mainUnits.size >= 2) fatalError("Multiple main units in the program") + + val mainUnit = mainUnits.head + + debug(s"Converting the main unit:\n$mainUnit") + collectSymbols(mainUnit) + + CAST.Prog(typeDecls, functions) + } + + // Look for function and structure definitions + private def collectSymbols(unit: UnitDef) { + implicit val defaultFunCtx = emptyFunCtx + + unit.defs.foreach { + case ModuleDef(_, funDefs, _) => + funDefs.foreach { + case fd: FunDef => convertToFun(fd) // the function gets registered here + case cc: CaseClassDef => convertToStruct(cc) // the type declaration gets registered here + + case x => internalError(s"Unknown function definition $x: ${x.getClass}") + } + + case x => internalError(s"Unexpected definition $x instead of a ModuleDef") + } + } + + // A variable can be locally declared (e.g. function parameter or local variable) + // or it can be "inherited" from a more global context (e.g. inner functions have + // access to their outer function parameters). + private case class VarInfo(x: CAST.Var, local: Boolean) { + // Transform a local variable into a global variable + def lift = VarInfo(x, false) + + // Generate CAST variable declaration for function signature + def toParam = CAST.Var(x.id, CAST.Pointer(x.typ)) + + // Generate CAST access statement + def toArg = if (local) CAST.AccessAddr(x.id) else CAST.AccessVar(x.id) + } + + private case class FunCtx(vars: Seq[VarInfo]) { + // Transform local variables into "outer" variables + def lift = FunCtx(vars map { _.lift }) + + // Create a new context with more local variables + def extend(x: CAST.Var): FunCtx = extend(Seq(x)) + def extend(xs: Seq[CAST.Var]): FunCtx = { + val newVars = xs map { VarInfo(_, true) } + FunCtx(vars ++ newVars) + } + + // Check if a given variable's identifier exists in the context and is an "outer" variable + def hasOuterVar(id: CAST.Id) = vars exists { vi => !vi.local && vi.x.id == id } + + // List all variables' ids + def extractIds = vars map { _.x.id } + + // Generate arguments for the given identifiers according to the current context + def toArgs(ids: Seq[CAST.Id]) = { + val filtered = vars filter { ids contains _.x.id } + filtered map { _.toArg } + } + + // Generate parameters (var + type) + def toParams = vars map { _.toParam } + } + + // Extract inner functions too + private def convertToFun(fd: FunDef)(implicit funCtx: FunCtx) = { + // Forbid return of array as they are allocated on the stack + if (containsArrayType(fd.returnType)) { + fatalError("Returning arrays is currently not allowed") + } + + val id = convertToId(fd.id) + val retType = convertToType(fd.returnType) + val stdParams = fd.params map convertToVar + + // Prepend existing variables from the outer function context to + // this function's parameters + val extraParams = funCtx.toParams + val params = extraParams ++ stdParams + + // Function Context: + // 1) Save the variables of the current context for later function invocation + // 2) Lift & augment funCtx with the current function's arguments + // 3) Propagate it to the current function's body + + registerFunExtraArgs(id, funCtx.extractIds) + + val funCtx2 = funCtx.lift.extend(stdParams) + + val b = convertToStmt(fd.fullBody)(funCtx2) + val body = retType match { + case CAST.Void => b + case _ => injectReturn(b) + } + + val fun = CAST.Fun(id, retType, params, body) + registerFun(fun) + + fun + } + + private def convert(tree: Tree)(implicit funCtx: FunCtx): CAST.Tree = tree match { + /* ---------------------------------------------------------- Types ----- */ + case Int32Type => CAST.Int32 + case BooleanType => CAST.Bool + case UnitType => CAST.Void + + case ArrayType(base) => + val typ = CAST.Array(convertToType(base)) + registerType(typ) + typ + + case TupleType(bases) => + val typ = CAST.Tuple(bases map convertToType) + registerType(typ) + typ + + case cd: CaseClassDef => + if (cd.isAbstract) fatalError("Abstract types are not supported") + if (cd.hasParent) fatalError("Inheritance is not supported") + if (cd.isCaseObject) fatalError("Case Objects are not supported") + if (cd.tparams.length > 0) fatalError("Type Parameters are not supported") + if (cd.methods.length > 0) fatalError("Methods are not yet supported") + + val id = convertToId(cd.id) + val fields = cd.fields map convertToVar + val typ = CAST.Struct(id, fields) + + registerType(typ) + typ + + case CaseClassType(cd, _) => convertToStruct(cd) // reuse `case CaseClassDef` + + /* ------------------------------------------------------- Literals ----- */ + case IntLiteral(v) => CAST.IntLiteral(v) + case BooleanLiteral(b) => CAST.BoolLiteral(b) + case UnitLiteral() => CAST.NoStmt + + /* ------------------------------------ Definitions and Statements ----- */ + case id: Identifier => + if (id.name == "main") CAST.Id("main") // and not `main0` + else CAST.Id(id.uniqueName) + + // Function parameter + case vd: ValDef => buildVal(vd.id, vd.getType) + + // Accessing variable + case v: Variable => buildAccessVar(v.id) + + case Block(exprs, last) => + // Interleave the "bodies" of flatten expressions and their values + // and generate a Compound statement + (exprs :+ last) map convertToStmt reduce { _ ~ _ } + + case Let(b, v, r) => buildLet(b, v, r, false) + case LetVar(b, v, r) => buildLet(b, v, r, true) + + case LetDef(fds, rest) => + fds foreach convertToFun // The functions get registered there + convertToStmt(rest) + + case Assignment(varId, expr) => + val f = convertAndFlatten(expr) + val x = buildAccessVar(varId) + + val assign = CAST.Assign(x, f.value) + + f.body ~~ assign + + case tuple @ Tuple(exprs) => + val struct = convertToStruct(tuple.getType) + val types = struct.fields map { _.typ } + val fs = convertAndNormaliseExecution(exprs, types) + val args = fs.values.zipWithIndex map { + case (arg, idx) => (CAST.Tuple.getNthId(idx + 1), arg) + } + + fs.bodies ~~ CAST.StructInit(args, struct) + + case TupleSelect(tuple1, idx) => // here idx is already 1-based + val struct = convertToStruct(tuple1.getType) + val tuple2 = convertToStmt(tuple1) + + val fs = normaliseExecution((tuple2, struct) :: Nil) + + val tuple = fs.values.head + + fs.bodies ~~ CAST.AccessField(tuple, CAST.Tuple.getNthId(idx)) + + case ArrayLength(array1) => + val array2 = convertToStmt(array1) + val arrayType = convertToType(array1.getType) + + val fs = normaliseExecution((array2, arrayType) :: Nil) + + val array = fs.values.head + + fs.bodies ~~ CAST.AccessField(array, CAST.Array.lengthId) + + case ArraySelect(array1, index1) => + val array2 = convertToStmt(array1) + val arrayType = convertToType(array1.getType) + val index2 = convertToStmt(index1) + + val fs = normaliseExecution((array2, arrayType) :: (index2, CAST.Int32) :: Nil) + + val array = fs.values(0) + val index = fs.values(1) + val ptr = CAST.AccessField(array, CAST.Array.dataId) + val select = CAST.SubscriptOp(ptr, index) + + fs.bodies ~~ select + + case NonemptyArray(elems, Some((value1, length1))) if elems.isEmpty => + val length2 = convertToStmt(length1) + val valueType = convertToType(value1.getType) + val value2 = convertToStmt(value1) + + val fs = normaliseExecution((length2, CAST.Int32) :: (value2, valueType) :: Nil) + val length = fs.values(0) + val value = fs.values(1) + + fs.bodies ~~ CAST.ArrayInit(length, valueType, value) + + case NonemptyArray(elems, Some(_)) => + fatalError("NonemptyArray with non empty elements is not supported") + + case NonemptyArray(elems, None) => // Here elems is non-empty + // Sort values according the the key (aka index) + val indexes = elems.keySet.toSeq.sorted + val values = indexes map { elems(_) } + + // Assert all types are the same + val types = values map { e => convertToType(e.getType) } + val typ = types(0) + val allSame = types forall { _ == typ } + if (!allSame) fatalError("Heterogenous arrays are not supported") + + val fs = convertAndNormaliseExecution(values, types) + + fs.bodies ~~ CAST.ArrayInitWithValues(typ, fs.values) + + case ArrayUpdate(array1, index1, newValue1) => + val array2 = convertToStmt(array1) + val index2 = convertToStmt(index1) + val newValue2 = convertToStmt(newValue1) + val values = array2 :: index2 :: newValue2 :: Nil + + val arePure = values forall { _.isPure } + val areValues = array2.isValue && index2.isValue // no newValue here + + newValue2 match { + case CAST.IfElse(cond, thn, elze) if arePure && areValues => + val array = array2 + val index = index2 + val ptr = CAST.AccessField(array, CAST.Array.dataId) + val select = CAST.SubscriptOp(ptr, index) + + val ifelse = buildIfElse(cond, injectAssign(select, thn), + injectAssign(select, elze)) + + ifelse + + case _ => + val arrayType = convertToType(array1.getType) + val indexType = CAST.Int32 + val valueType = convertToType(newValue1.getType) + val types = arrayType :: indexType :: valueType :: Nil + + val fs = normaliseExecution(values, types) + + val array = fs.values(0) + val index = fs.values(1) + val newValue = fs.values(2) + + val ptr = CAST.AccessField(array, CAST.Array.dataId) + val select = CAST.SubscriptOp(ptr, index) + val assign = CAST.Assign(select, newValue) + + fs.bodies ~~ assign + } + + case CaseClass(typ, args1) => + val struct = convertToStruct(typ) + val types = struct.fields map { _.typ } + val argsFs = convertAndNormaliseExecution(args1, types) + val fieldsIds = typ.classDef.fieldsIds map convertToId + val args = fieldsIds zip argsFs.values + + argsFs.bodies ~~ CAST.StructInit(args, struct) + + case CaseClassSelector(_, x1, fieldId) => + val struct = convertToStruct(x1.getType) + val x2 = convertToStmt(x1) + + val fs = normaliseExecution((x2, struct) :: Nil) + val x = fs.values.head + + fs.bodies ~~ CAST.AccessField(x, convertToId(fieldId)) + + case LessThan(lhs, rhs) => buildBinOp(lhs, "<", rhs) + case GreaterThan(lhs, rhs) => buildBinOp(lhs, ">", rhs) + case LessEquals(lhs, rhs) => buildBinOp(lhs, "<=", rhs) + case GreaterEquals(lhs, rhs) => buildBinOp(lhs, ">=", rhs) + case Equals(lhs, rhs) => buildBinOp(lhs, "==", rhs) + + case Not(rhs) => buildUnOp ( "!", rhs) + + case And(exprs) => buildMultiOp("&&", exprs) + case Or(exprs) => buildMultiOp("||", exprs) + + case BVPlus(lhs, rhs) => buildBinOp(lhs, "+", rhs) + case BVMinus(lhs, rhs) => buildBinOp(lhs, "-", rhs) + case BVUMinus(rhs) => buildUnOp ( "-", rhs) + case BVTimes(lhs, rhs) => buildBinOp(lhs, "*", rhs) + case BVDivision(lhs, rhs) => buildBinOp(lhs, "/", rhs) + case BVRemainder(lhs, rhs) => buildBinOp(lhs, "%", rhs) + case BVNot(rhs) => buildUnOp ( "~", rhs) + case BVAnd(lhs, rhs) => buildBinOp(lhs, "&", rhs) + case BVOr(lhs, rhs) => buildBinOp(lhs, "|", rhs) + case BVXOr(lhs, rhs) => buildBinOp(lhs, "^", rhs) + case BVShiftLeft(lhs, rhs) => buildBinOp(lhs, "<<", rhs) + case BVAShiftRight(lhs, rhs) => buildBinOp(lhs, ">>", rhs) + case BVLShiftRight(lhs, rhs) => fatalError("operator >>> not supported") + + // Ignore assertions for now + case Ensuring(body, _) => convert(body) + case Require(_, body) => convert(body) + case Assert(_, _, body) => convert(body) + + case IfExpr(cond1, thn1, elze1) => + val condF = convertAndFlatten(cond1) + val thn = convertToStmt(thn1) + val elze = convertToStmt(elze1) + + condF.body ~~ buildIfElse(condF.value, thn, elze) + + case While(cond1, body1) => + val cond = convertToStmt(cond1) + val body = convertToStmt(body1) + + if (cond.isPureValue) { + CAST.While(cond, body) + } else { + // Transform while (cond) { body } into + // while (true) { if (cond) { body } else { break } } + val condF = flatten(cond) + val ifelse = condF.body ~~ buildIfElse(condF.value, CAST.NoStmt, CAST.Break) + CAST.While(CAST.True, ifelse ~ body) + } + + case FunctionInvocation(tfd @ TypedFunDef(fd, _), stdArgs) => + // In addition to regular function parameters, add the callee's extra parameters + val id = convertToId(fd.id) + val types = tfd.params map { p => convertToType(p.getType) } + val fs = convertAndNormaliseExecution(stdArgs, types) + val extraArgs = funCtx.toArgs(getFunExtraArgs(id)) + val args = extraArgs ++ fs.values + + fs.bodies ~~ CAST.Call(id, args) + + case unsupported => + fatalError(s"$unsupported (of type ${unsupported.getClass}) is currently not supported by GenC") + } + + private def buildVar(id: Identifier, typ: TypeTree)(implicit funCtx: FunCtx) = + CAST.Var(convertToId(id), convertToType(typ)) + + private def buildVal(id: Identifier, typ: TypeTree)(implicit funCtx: FunCtx) = + CAST.Val(convertToId(id), convertToType(typ)) + + private def buildAccessVar(id1: Identifier)(implicit funCtx: FunCtx) = { + // Depending on the context, we have to deference the variable + val id = convertToId(id1) + if (funCtx.hasOuterVar(id)) CAST.AccessRef(id) + else CAST.AccessVar(id) + } + + private def buildLet(id: Identifier, value: Expr, rest1: Expr, forceVar: Boolean) + (implicit funCtx: FunCtx): CAST.Stmt = { + val (x, stmt) = buildDeclInitVar(id, value, forceVar) + + // Augment ctx for the following instructions + val funCtx2 = funCtx.extend(x) + val rest = convertToStmt(rest1)(funCtx2) + + stmt ~ rest + } + + + // Create a new variable for the given value, potentially immutable, and initialize it + private def buildDeclInitVar(id: Identifier, v: Expr, forceVar: Boolean) + (implicit funCtx: FunCtx): (CAST.Var, CAST.Stmt) = { + val valueF = convertAndFlatten(v) + val typ = v.getType + + valueF.value match { + case CAST.IfElse(cond, thn, elze) => + val x = buildVar(id, typ) + val decl = CAST.DeclVar(x) + val ifelse = buildIfElse(cond, injectAssign(x, thn), injectAssign(x, elze)) + val init = decl ~ ifelse + + (x, valueF.body ~~ init) + + case value => + val x = if (forceVar) buildVar(id, typ) else buildVal(id, typ) + val init = CAST.DeclInitVar(x, value) + + (x, valueF.body ~~ init) + } + } + + private def buildBinOp(lhs: Expr, op: String, rhs: Expr)(implicit funCtx: FunCtx) = { + buildMultiOp(op, lhs :: rhs :: Nil) + } + + private def buildUnOp(op: String, rhs1: Expr)(implicit funCtx: FunCtx) = { + val rhsF = convertAndFlatten(rhs1) + rhsF.body ~~ CAST.Op(op, rhsF.value) + } + + private def buildMultiOp(op: String, exprs: Seq[Expr])(implicit funCtx: FunCtx): CAST.Stmt = { + require(exprs.length >= 2) + + val stmts = exprs map convertToStmt + val types = exprs map { e => convertToType(e.getType) } + + buildMultiOp(op, stmts, types) + } + + private def buildMultiOp(op: String, stmts: Seq[CAST.Stmt], types: Seq[CAST.Type]): CAST.Stmt = { + // Default operator constuction when either pure statements are involved + // or no shortcut can happen + def defaultBuild = { + val fs = normaliseExecution(stmts, types) + fs.bodies ~~ CAST.Op(op, fs.values) + } + + if (stmts forall { _.isPureValue }) defaultBuild + else op match { + case "&&" => + // Apply short-circuit if needed + if (stmts.length == 2) { + // Base case: + // { { a; v } && { b; w } } + // is mapped onto + // { a; if (v) { b; w } else { false } } + val av = flatten(stmts(0)) + val bw = stmts(1) + + if (bw.isPureValue) defaultBuild + else av.body ~~ buildIfElse(av.value, bw, CAST.False) + } else { + // Recursive case: + // { { a; v } && ... } + // is mapped onto + // { a; if (v) { ... } else { false } } + val av = flatten(stmts(0)) + val rest = buildMultiOp(op, stmts.tail, types.tail) + + if (rest.isPureValue) defaultBuild + else av.body ~~ buildIfElse(av.value, rest, CAST.False) + } + + case "||" => + // Apply short-circuit if needed + if (stmts.length == 2) { + // Base case: + // { { a; v } || { b; w } } + // is mapped onto + // { a; if (v) { true } else { b; w } } + val av = flatten(stmts(0)) + val bw = stmts(1) + + if (bw.isPureValue) defaultBuild + else av.body ~~ buildIfElse(av.value, CAST.True, bw) + } else { + // Recusrive case: + // { { a; v } || ... } + // is mapped onto + // { a; if (v) { true } else { ... } } + val av = flatten(stmts(0)) + val rest = buildMultiOp(op, stmts.tail, types.tail) + + if (rest.isPureValue) defaultBuild + else av.body ~~ buildIfElse(av.value, CAST.True, rest) + } + + case _ => + defaultBuild + } + } + + // Flatten `if (if (cond1) thn1 else elze1) thn2 else elze2` + // into `if (cond1) { if (thn1) thn2 else elz2 } else { if (elz1) thn2 else elze2 }` + // or, if possible, into `if ((cond1 && thn1) || elz1) thn2 else elz2` + // + // Flatten `if (true) thn else elze` into `thn` + // Flatten `if (false) thn else elze` into `elze` + private def buildIfElse(cond: CAST.Stmt, thn2: CAST.Stmt, elze2: CAST.Stmt): CAST.Stmt = { + val condF = flatten(cond) + + val ifelse = condF.value match { + case CAST.IfElse(cond1, thn1, elze1) => + if (cond1.isPure && thn1.isPure && elze1.isPure) { + val bools = CAST.Bool :: CAST.Bool :: Nil + val ands = cond1 :: thn1 :: Nil + val ors = buildMultiOp("&&", ands, bools) :: elze1 :: Nil + val condX = buildMultiOp("||", ors, bools) + CAST.IfElse(condX, thn2, elze2) + } else { + buildIfElse(cond1, buildIfElse(thn1, thn2, elze2), buildIfElse(elze1, thn2, elze2)) + } + + case CAST.True => thn2 + case CAST.False => elze2 + case cond2 => CAST.IfElse(cond2, thn2, elze2) + } + + condF.body ~~ ifelse + } + + private def injectReturn(stmt: CAST.Stmt): CAST.Stmt = { + val f = flatten(stmt) + + f.value match { + case CAST.IfElse(cond, thn, elze) => + f.body ~~ CAST.IfElse(cond, injectReturn(thn), injectReturn(elze)) + + case _ => + f.body ~~ CAST.Return(f.value) + } + } + + private def injectAssign(x: CAST.Var, stmt: CAST.Stmt): CAST.Stmt = { + injectAssign(CAST.AccessVar(x.id), stmt) + } + + private def injectAssign(x: CAST.Stmt, stmt: CAST.Stmt): CAST.Stmt = { + val f = flatten(stmt) + + f.value match { + case CAST.IfElse(cond, thn, elze) => + f.body ~~ CAST.IfElse(cond, injectAssign(x, thn), injectAssign(x, elze)) + + case _ => + f.body ~~ CAST.Assign(x, f.value) + } + } + + // Flattened represents a non-empty statement { a; b; ...; y; z } + // split into body { a; b; ...; y } and value z + private case class Flattened(value: CAST.Stmt, body: Seq[CAST.Stmt]) + + // FlattenedSeq does the same as Flattened for a sequence of non-empty statements + private case class FlattenedSeq(values: Seq[CAST.Stmt], bodies: Seq[CAST.Stmt]) + + private def flatten(stmt: CAST.Stmt) = stmt match { + case CAST.Compound(stmts) if stmts.isEmpty => internalError(s"Empty compound cannot be flattened") + case CAST.Compound(stmts) => Flattened(stmts.last, stmts.init) + case stmt => Flattened(stmt, Seq()) + } + + private def convertAndFlatten(expr: Expr)(implicit funCtx: FunCtx) = flatten(convertToStmt(expr)) + + // Normalise execution order of, for example, function parameters; + // `types` represents the expected type of the corresponding values + // in case an intermediary variable needs to be created + private def convertAndNormaliseExecution(exprs: Seq[Expr], types: Seq[CAST.Type]) + (implicit funCtx: FunCtx) = { + require(exprs.length == types.length) + normaliseExecution(exprs map convertToStmt, types) + } + + private def normaliseExecution(typedStmts: Seq[(CAST.Stmt, CAST.Type)]): FlattenedSeq = + normaliseExecution(typedStmts map { _._1 }, typedStmts map { _._2 }) + + private def normaliseExecution(stmts: Seq[CAST.Stmt], types: Seq[CAST.Type]): FlattenedSeq = { + require(stmts.length == types.length) + + // Create temporary variables if needed + val stmtsFs = stmts map flatten + val fs = (stmtsFs zip types) map { + case (f, _) if f.value.isPureValue => f + + case (f, typ) => + // Similarly to buildDeclInitVar: + val (tmp, body) = f.value match { + case CAST.IfElse(cond, thn, elze) => + val tmp = CAST.FreshVar(typ.mutable, "normexec") + val decl = CAST.DeclVar(tmp) + val ifelse = buildIfElse(cond, injectAssign(tmp, thn), injectAssign(tmp, elze)) + val body = f.body ~~ decl ~ ifelse + + (tmp, body) + + case value => + val tmp = CAST.FreshVal(typ, "normexec") + val body = f.body ~~ CAST.DeclInitVar(tmp, f.value) + + (tmp, body) + } + + val value = CAST.AccessVar(tmp.id) + flatten(body ~ value) + } + + val empty = Seq[CAST.Stmt]() + val bodies = fs.foldLeft(empty){ _ ++ _.body } + val values = fs map { _.value } + + FlattenedSeq(values, bodies) + } + + private def containsArrayType(typ: TypeTree): Boolean = typ match { + case Int32Type => false + case BooleanType => false + case UnitType => false + case ArrayType(_) => true + case TupleType(bases) => bases exists containsArrayType + case CaseClassType(cd, _) => cd.fields map { _.getType } exists containsArrayType + } + + private def internalError(msg: String) = ctx.reporter.internalError(msg) + private def fatalError(msg: String) = ctx.reporter.fatalError(msg) + private def debug(msg: String) = ctx.reporter.debug(msg)(utils.DebugSectionGenC) + +} + diff --git a/src/main/scala/leon/genc/CFileOutputPhase.scala b/src/main/scala/leon/genc/CFileOutputPhase.scala new file mode 100644 index 0000000000000000000000000000000000000000..f2b7797183da17ccb6e326a85a779a4ef58be9e2 --- /dev/null +++ b/src/main/scala/leon/genc/CFileOutputPhase.scala @@ -0,0 +1,54 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package genc + +import java.io.File +import java.io.FileWriter +import java.io.BufferedWriter + +object CFileOutputPhase extends UnitPhase[CAST.Prog] { + + val name = "C File Output" + val description = "Output converted C program to the specified file (default leon.c)" + + val optOutputFile = new LeonOptionDef[String] { + val name = "o" + val description = "Output file" + val default = "leon.c" + val usageRhs = "file" + val parser = OptionParsers.stringParser + } + + override val definedOptions: Set[LeonOptionDef[Any]] = Set(optOutputFile) + + def apply(ctx: LeonContext, program: CAST.Prog) { + // Get the output file name from command line options, or use default + val outputFile = new File(ctx.findOptionOrDefault(optOutputFile)) + val parent = outputFile.getParentFile() + try { + if (parent != null) { + parent.mkdirs() + } + } catch { + case _ : java.io.IOException => ctx.reporter.fatalError("Could not create directory " + parent) + } + + // Output C code to the file + try { + val fstream = new FileWriter(outputFile) + val out = new BufferedWriter(fstream) + + val p = new CPrinter + p.print(program) + + out.write(p.toString) + out.close() + + ctx.reporter.info(s"Output written to $outputFile") + } catch { + case _ : java.io.IOException => ctx.reporter.fatalError("Could not write on " + outputFile) + } + } + +} diff --git a/src/main/scala/leon/genc/CPrinter.scala b/src/main/scala/leon/genc/CPrinter.scala new file mode 100644 index 0000000000000000000000000000000000000000..eec404f0c5283bbf09e56dbe79e8335286f61ae6 --- /dev/null +++ b/src/main/scala/leon/genc/CPrinter.scala @@ -0,0 +1,198 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package genc + +import CAST._ +import CPrinterHelpers._ + +class CPrinter(val sb: StringBuffer = new StringBuffer) { + override def toString = sb.toString + + def print(tree: Tree) = pp(tree)(PrinterContext(0, this)) + + def pp(tree: Tree)(implicit ctx: PrinterContext): Unit = tree match { + /* ---------------------------------------------------------- Types ----- */ + case typ: Type => c"${typ.toString}" + + + /* ------------------------------------------------------- Literals ----- */ + case IntLiteral(v) => c"$v" + case BoolLiteral(b) => c"$b" + + + /* --------------------------------------------------- Definitions ----- */ + case Prog(structs, functions) => + c"""|/* ------------------------------------ includes ----- */ + | + |${nary(includeStmts, sep = "\n")} + | + |/* ---------------------- data type declarations ----- */ + | + |${nary(structs map StructDecl, sep = "\n")} + | + |/* ----------------------- data type definitions ----- */ + | + |${nary(structs map StructDef, sep = "\n")} + | + |/* ----------------------- function declarations ----- */ + | + |${nary(functions map FunDecl, sep = "\n")} + | + |/* ------------------------ function definitions ----- */ + | + |${nary(functions, sep = "\n")} + |""" + + case f @ Fun(_, _, _, body) => + c"""|${FunSign(f)} + |{ + | $body + |} + |""" + + case Id(name) => c"$name" + + + /* --------------------------------------------------------- Stmts ----- */ + case NoStmt => c"/* empty */" + + case Compound(stmts) => + val lastIdx = stmts.length - 1 + + for ((stmt, idx) <- stmts.zipWithIndex) { + if (stmt.isValue) c"$stmt;" + else c"$stmt" + + if (idx != lastIdx) + c"$NewLine" + } + + case Assert(pred, Some(error)) => c"assert($pred); /* $error */" + case Assert(pred, None) => c"assert($pred);" + + case Var(id, _) => c"$id" + case DeclVar(Var(id, typ)) => c"$typ $id;" + + // If the length is a literal we don't need VLA + case DeclInitVar(Var(id, typ), ai @ ArrayInit(IntLiteral(length), _, _)) => + val buffer = FreshId("buffer") + val values = for (i <- 0 until length) yield ai.defaultValue + c"""|${ai.valueType} $buffer[${ai.length}] = { $values }; + |$typ $id = { .length = ${ai.length}, .data = $buffer }; + |""" + + // TODO depending on the type of array (e.g. `char`) or the value (e.g. `0`), we could use `memset`. + case DeclInitVar(Var(id, typ), ai: ArrayInit) => // Note that `typ` is a struct here + val buffer = FreshId("vla_buffer") + val i = FreshId("i") + c"""|${ai.valueType} $buffer[${ai.length}]; + |for (${Int32} $i = 0; $i < ${ai.length}; ++$i) { + | $buffer[$i] = ${ai.defaultValue}; + |} + |$typ $id = { .length = ${ai.length}, .data = $buffer }; + |""" + + case DeclInitVar(Var(id, typ), ai: ArrayInitWithValues) => // Note that `typ` is a struct here + val buffer = FreshId("buffer") + c"""|${ai.valueType} $buffer[${ai.length}] = { ${ai.values} }; + |$typ $id = { .length = ${ai.length}, .data = $buffer }; + |""" + + case DeclInitVar(Var(id, typ), value) => + c"$typ $id = $value;" + + case Assign(lhs, rhs) => + c"$lhs = $rhs;" + + case UnOp(op, rhs) => c"($op$rhs)" + case MultiOp(op, stmts) => c"""${nary(stmts, sep = s" ${op.fixMargin} ", + opening = "(", closing = ")")}""" + case SubscriptOp(ptr, idx) => c"$ptr[$idx]" + + case Break => c"break;" + case Return(stmt) => c"return $stmt;" + + case IfElse(cond, thn, elze) => + c"""|if ($cond) + |{ + | $thn + |} + |else + |{ + | $elze + |} + |""" + + case While(cond, body) => + c"""|while ($cond) + |{ + | $body + |} + |""" + + case AccessVar(id) => c"$id" + case AccessRef(id) => c"(*$id)" + case AccessAddr(id) => c"(&$id)" + case AccessField(struct, field) => c"$struct.$field" + case Call(id, args) => c"$id($args)" + + case StructInit(args, struct) => + c"(${struct.id}) { " + for ((id, stmt) <- args.init) { + c".$id = $stmt, " + } + if (!args.isEmpty) { + val (id, stmt) = args.last + c".$id = $stmt " + } + c"}" + + /* --------------------------------------------------------- Error ----- */ + case tree => sys.error(s"CPrinter: <<$tree>> was not handled properly") + } + + + def pp(wt: WrapperTree)(implicit ctx: PrinterContext): Unit = wt match { + case FunDecl(f) => + c"${FunSign(f)};$NewLine" + + case FunSign(Fun(id, retType, Nil, _)) => + c"""|$retType + |$id($Void)""" + + case FunSign(Fun(id, retType, params, _)) => + c"""|$retType + |$id(${nary(params map DeclParam)})""" + + case DeclParam(Var(id, typ)) => + c"$typ $id" + + case StructDecl(s) => + c"struct $s;" + + case StructDef(Struct(name, fields)) => + c"""|typedef struct $name { + | ${nary(fields map DeclParam, sep = ";\n", closing = ";")} + |} $name; + |""" + + case NewLine => + c"""| + |""" + } + + /** Hardcoded list of required include files from C standard library **/ + lazy val includes = "assert.h" :: "stdbool.h" :: "stdint.h" :: Nil + lazy val includeStmts = includes map { i => s"#include <$i>" } + + /** Wrappers to distinguish how the data should be printed **/ + sealed abstract class WrapperTree + case class FunDecl(f: Fun) extends WrapperTree + case class FunSign(f: Fun) extends WrapperTree + case class DeclParam(x: Var) extends WrapperTree + case class StructDecl(s: Struct) extends WrapperTree + case class StructDef(s: Struct) extends WrapperTree + case object NewLine extends WrapperTree +} + diff --git a/src/main/scala/leon/genc/CPrinterHelper.scala b/src/main/scala/leon/genc/CPrinterHelper.scala new file mode 100644 index 0000000000000000000000000000000000000000..5c32df8b7289b0ca5510d04680ab449e915a12dd --- /dev/null +++ b/src/main/scala/leon/genc/CPrinterHelper.scala @@ -0,0 +1,86 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package genc + +import CAST.Tree + +/* Printer helpers adapted to C code generation */ + +case class PrinterContext( + indent: Int, + printer: CPrinter +) + +object CPrinterHelpers { + implicit class Printable(val f: PrinterContext => Any) extends AnyVal { + def print(ctx: PrinterContext) = f(ctx) + } + + implicit class PrinterHelper(val sc: StringContext) extends AnyVal { + def c(args: Any*)(implicit ctx: PrinterContext): Unit = { + val printer = ctx.printer + import printer.WrapperTree + val sb = printer.sb + + val strings = sc.parts.iterator + val expressions = args.iterator + + var extraInd = 0 + var firstElem = true + + while(strings.hasNext) { + val s = strings.next.stripMargin + + // Compute indentation + val start = s.lastIndexOf('\n') + if(start >= 0 || firstElem) { + var i = start + 1 + while(i < s.length && s(i) == ' ') { + i += 1 + } + extraInd = (i - start - 1) / 2 + } + + firstElem = false + + // Make sure new lines are also indented + sb.append(s.replaceAll("\n", "\n" + (" " * ctx.indent))) + + val nctx = ctx.copy(indent = ctx.indent + extraInd) + + if (expressions.hasNext) { + val e = expressions.next + + e match { + case ts: Seq[Any] => + nary(ts).print(nctx) + + case t: Tree => + printer.pp(t)(nctx) + + case wt: WrapperTree => + printer.pp(wt)(nctx) + + case p: Printable => + p.print(nctx) + + case e => + sb.append(e.toString) + } + } + } + } + } + + def nary(ls: Seq[Any], sep: String = ", ", opening: String = "", closing: String = ""): Printable = { + val (o, c) = if(ls.isEmpty) ("", "") else (opening, closing) + val strs = o +: List.fill(ls.size-1)(sep) :+ c + + implicit pctx: PrinterContext => + new StringContext(strs: _*).c(ls: _*) + } + +} + + diff --git a/src/main/scala/leon/genc/GenerateCPhase.scala b/src/main/scala/leon/genc/GenerateCPhase.scala new file mode 100644 index 0000000000000000000000000000000000000000..c30d32274db7370a8f9af5616df3cbe505b0389e --- /dev/null +++ b/src/main/scala/leon/genc/GenerateCPhase.scala @@ -0,0 +1,20 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package genc + +import purescala.Definitions.Program + +object GenerateCPhase extends SimpleLeonPhase[Program, CAST.Prog] { + + val name = "Generate C" + val description = "Generate equivalent C code from Leon's AST" + + def apply(ctx: LeonContext, program: Program) = { + ctx.reporter.debug("Running code conversion phase: " + name)(utils.DebugSectionLeon) + val cprogram = new CConverter(ctx, program).convert + cprogram + } + +} + diff --git a/src/main/scala/leon/invariant/engine/UnfoldingTemplateSolver.scala b/src/main/scala/leon/invariant/engine/UnfoldingTemplateSolver.scala index 3fe13e7c93fefdfcc4321159b0fa7b354788a801..d2deb1ced97b635a1459f8e0e7b531588989cf64 100644 --- a/src/main/scala/leon/invariant/engine/UnfoldingTemplateSolver.scala +++ b/src/main/scala/leon/invariant/engine/UnfoldingTemplateSolver.scala @@ -42,7 +42,7 @@ class UnfoldingTemplateSolver(ctx: InferenceContext, program: Program, rootFd: F def constructVC(funDef: FunDef): (Expr, Expr) = { val body = funDef.body.get - val Lambda(Seq(ValDef(resid, _)), _) = funDef.postcondition.get + val Lambda(Seq(ValDef(resid)), _) = funDef.postcondition.get val resvar = resid.toVariable val simpBody = matchToIfThenElse(body) diff --git a/src/main/scala/leon/invariant/util/TreeUtil.scala b/src/main/scala/leon/invariant/util/TreeUtil.scala index 4109e59877c192570e95bb83b819ebd0a59edef7..26398130a447c3eb503e2fe0e72b4cf75a711236 100644 --- a/src/main/scala/leon/invariant/util/TreeUtil.scala +++ b/src/main/scala/leon/invariant/util/TreeUtil.scala @@ -182,7 +182,7 @@ object ProgramUtil { funDef.fullBody match { case Ensuring(_, post) => { post match { - case Lambda(Seq(ValDef(fromRes, _)), _) => Some(fromRes) + case Lambda(Seq(ValDef(fromRes)), _) => Some(fromRes) } } case _ => None diff --git a/src/main/scala/leon/purescala/CallGraph.scala b/src/main/scala/leon/purescala/CallGraph.scala index a07b807ddaad26817322878c46d575f0a4ec1306..edeaab26953ef6734b2a334e440e04dfb59d38c5 100644 --- a/src/main/scala/leon/purescala/CallGraph.scala +++ b/src/main/scala/leon/purescala/CallGraph.scala @@ -7,106 +7,77 @@ import Definitions._ import Expressions._ import ExprOps._ -class CallGraph(p: Program) { +import utils.Graphs._ - private var _calls = Set[(FunDef, FunDef)]() +class CallGraph(p: Program) { - private var _callers = Map[FunDef, Set[FunDef]]() // if 'foo' calls 'bar': Map(bar -> Set(foo)) - private var _callees = Map[FunDef, Set[FunDef]]() // if 'foo' calls 'bar': Map(foo -> Set(bar)) + private def collectCallsInPats(fd: FunDef)(p: Pattern): Set[(FunDef, FunDef)] = + (p match { + case u: UnapplyPattern => Set((fd, u.unapplyFun.fd)) + case _ => Set() + }) ++ p.subPatterns.flatMap(collectCallsInPats(fd)) - private var _transitiveCalls = Set[(FunDef, FunDef)]() - private var _transitiveCallers = Map[FunDef, Set[FunDef]]() - private var _transitiveCallees = Map[FunDef, Set[FunDef]]() + private def collectCalls(fd: FunDef)(e: Expr): Set[(FunDef, FunDef)] = e match { + case f @ FunctionInvocation(f2, _) => Set((fd, f2.fd)) + case MatchExpr(_, cases) => cases.toSet.flatMap((mc: MatchCase) => collectCallsInPats(fd)(mc.pattern)) + case _ => Set() + } - def allCalls = _calls - def allTransitiveCalls = _transitiveCalls + lazy val graph: DiGraph[FunDef, SimpleEdge[FunDef]] = { + var g = DiGraph[FunDef, SimpleEdge[FunDef]]() - def isRecursive(f: FunDef) = transitivelyCalls(f, f) + for (fd <- p.definedFunctions; c <- collect(collectCalls(fd))(fd.fullBody)) { + g += SimpleEdge(c._1, c._2) + } - def calls(from: FunDef, to: FunDef) = _calls contains (from -> to) - def callers(to: FunDef) = _callers.getOrElse(to, Set()) - def callees(from: FunDef) = _callees.getOrElse(from, Set()) + g + } - def transitivelyCalls(from: FunDef, to: FunDef) = _transitiveCalls contains (from -> to) - def transitiveCallers(to: FunDef) = _transitiveCallers.getOrElse(to, Set()) - def transitiveCallees(from: FunDef) = _transitiveCallees.getOrElse(from, Set()) + lazy val allCalls = graph.E.map(e => e._1 -> e._2) - // multi-source/dest - def callees(from: Set[FunDef]): Set[FunDef] = from.flatMap(callees) - def callers(to: Set[FunDef]): Set[FunDef] = to.flatMap(callers) - def transitiveCallees(from: Set[FunDef]): Set[FunDef] = from.flatMap(transitiveCallees) - def transitiveCallers(to: Set[FunDef]): Set[FunDef] = to.flatMap(transitiveCallers) + def isRecursive(f: FunDef) = { + graph.transitiveSucc(f) contains f + } - lazy val stronglyConnectedComponents: Seq[Set[FunDef]] = { - def rec(funs: Set[FunDef]): Seq[Set[FunDef]] = { - if (funs.isEmpty) Seq() - else { - val h = funs.head - val component = transitiveCallees(h).filter{ transitivelyCalls(_, h) } + h - component +: rec(funs -- component) - } - } - rec(p.definedFunctions.toSet) + def calls(from: FunDef, to: FunDef) = { + graph.E contains SimpleEdge(from, to) } - - def stronglyConnectedComponent(fd: FunDef) = - stronglyConnectedComponents.find{ _.contains(fd) }.getOrElse(Set(fd)) - private def init() { - _calls = Set() - _callers = Map() - _callees = Map() + def callers(to: FunDef): Set[FunDef] = { + graph.pred(to) + } + def callers(tos: Set[FunDef]): Set[FunDef] = { + tos.flatMap(callers) + } - // Collect all calls - p.definedFunctions.foreach(scanForCalls) + def callees(from: FunDef): Set[FunDef] = { + graph.succ(from) + } - _transitiveCalls = _calls - _transitiveCallers = _callers - _transitiveCallees = _callees + def callees(froms: Set[FunDef]): Set[FunDef] = { + froms.flatMap(callees) + } - // Transitive calls - transitiveClosure() + def transitiveCallers(to: FunDef): Set[FunDef] = { + graph.transitivePred(to) } - private def collectCallsInPats(fd: FunDef)(p: Pattern): Set[(FunDef, FunDef)] = - (p match { - case u: UnapplyPattern => Set((fd, u.unapplyFun.fd)) - case _ => Set() - }) ++ p.subPatterns.flatMap(collectCallsInPats(fd)) + def transitiveCallers(tos: Set[FunDef]): Set[FunDef] = { + tos.flatMap(transitiveCallers) + } - private def collectCalls(fd: FunDef)(e: Expr): Set[(FunDef, FunDef)] = e match { - case f @ FunctionInvocation(f2, _) => Set((fd, f2.fd)) - case MatchExpr(_, cases) => cases.toSet.flatMap((mc: MatchCase) => collectCallsInPats(fd)(mc.pattern)) - case _ => Set() + def transitiveCallees(from: FunDef): Set[FunDef] = { + graph.transitiveSucc(from) } - private def scanForCalls(fd: FunDef) { - for( (from, to) <- collect(collectCalls(fd))(fd.fullBody) ) { - _calls += (from -> to) - _callees += (from -> (_callees.getOrElse(from, Set()) + to)) - _callers += (to -> (_callers.getOrElse(to, Set()) + from)) - } + def transitiveCallees(froms: Set[FunDef]): Set[FunDef] = { + froms.flatMap(transitiveCallees) } - private def transitiveClosure() { - var changed = true - while(changed) { - val newCalls = _transitiveCalls.flatMap { - case (from, to) => _transitiveCallees.getOrElse(to, Set()).map((from, _)) - } -- _transitiveCalls - - if (newCalls.nonEmpty) { - for ((from, to) <- newCalls) { - _transitiveCalls += (from -> to) - _transitiveCallees += (from -> (_transitiveCallees.getOrElse(from, Set()) + to)) - _transitiveCallers += (to -> (_transitiveCallers.getOrElse(to, Set()) + from)) - } - } else { - changed =false - } - } + def transitivelyCalls(from: FunDef, to: FunDef): Boolean = { + graph.transitiveSucc(from) contains to } - init() + lazy val stronglyConnectedComponents = graph.stronglyConnectedComponents.N } diff --git a/src/main/scala/leon/purescala/Constructors.scala b/src/main/scala/leon/purescala/Constructors.scala index bae200bbe05fa4a2a43b9cae36e0088be3f129e5..23b32e0ee96ec98dca890a4a17ae8541c20244cb 100644 --- a/src/main/scala/leon/purescala/Constructors.scala +++ b/src/main/scala/leon/purescala/Constructors.scala @@ -41,6 +41,17 @@ object Constructors { */ def tupleSelect(t: Expr, index: Int, originalSize: Int): Expr = tupleSelect(t, index, originalSize > 1) + /** $encodingof ``def foo(..) {} ...; e``. + * @see [[purescala.Expressions.LetDef]] + */ + def letDef(defs: Seq[FunDef], e: Expr) = { + if (defs.isEmpty) { + e + } else { + LetDef(defs, e) + } + } + /** $encodingof ``val id = e; bd``, and returns `bd` if the identifier is not bound in `bd`. * @see [[purescala.Expressions.Let]] */ @@ -323,9 +334,9 @@ object Constructors { var defs: Seq[(Identifier, Expr)] = Seq() val subst = formalArgs.zip(realArgs).map { - case (ValDef(from, _), to:Variable) => + case (ValDef(from), to:Variable) => from -> to - case (ValDef(from, _), e) => + case (ValDef(from), e) => val fresh = from.freshen defs :+= (fresh -> e) from -> Variable(fresh) diff --git a/src/main/scala/leon/purescala/Definitions.scala b/src/main/scala/leon/purescala/Definitions.scala index 792e43c56923a73ad3f4a8f42551dd34e6fa7989..8fb753d23d35364059115f7032bef3310c492d82 100644 --- a/src/main/scala/leon/purescala/Definitions.scala +++ b/src/main/scala/leon/purescala/Definitions.scala @@ -45,7 +45,7 @@ object Definitions { * The optional tpe, if present, overrides the type of the underlying Identifier id * This is useful to instantiate argument types of polymorphic functions */ - case class ValDef(id: Identifier, isLazy: Boolean = false) extends Definition with Typed { + case class ValDef(id: Identifier) extends Definition with Typed { self: Serializable => val getType = id.getType diff --git a/src/main/scala/leon/purescala/ExprOps.scala b/src/main/scala/leon/purescala/ExprOps.scala index d15c78c951fe3f3990c16ee51422dc53b8223806..5ce8a11ec204c4fde241797dd2706d1a5be20dc0 100644 --- a/src/main/scala/leon/purescala/ExprOps.scala +++ b/src/main/scala/leon/purescala/ExprOps.scala @@ -1148,6 +1148,15 @@ object ExprOps { case _ => throw LeonFatalError("I can't choose simplest value for type " + tpe) } + /* Checks if a given expression is 'real' and does not contain generic + * values. */ + def isRealExpr(v: Expr): Boolean = { + !exists { + case gv: GenericValue => true + case _ => false + }(v) + } + def valuesOf(tp: TypeTree): Stream[Expr] = { import utils.StreamUtils._ tp match { @@ -1688,7 +1697,7 @@ object ExprOps { case (Lambda(defs, body), Lambda(defs2, body2)) => // We remove variables introduced by lambdas. (isHomo(body, body2) && - (defs zip defs2).mergeall{ case (ValDef(a1, _), ValDef(a2, _)) => Option(Map(a1 -> a2)) } + (defs zip defs2).mergeall{ case (ValDef(a1), ValDef(a2)) => Option(Map(a1 -> a2)) } ) -- (defs.map(_.id)) case (v1, v2) if isValue(v1) && isValue(v2) => diff --git a/src/main/scala/leon/purescala/Expressions.scala b/src/main/scala/leon/purescala/Expressions.scala index 13e4efbef15d754765776de6153c9f5a8b65c6c8..bb70a6676923db89648df770318f8c04129bad3e 100644 --- a/src/main/scala/leon/purescala/Expressions.scala +++ b/src/main/scala/leon/purescala/Expressions.scala @@ -165,6 +165,7 @@ object Expressions { * @param body The body of the expression after the function */ case class LetDef(fds: Seq[FunDef], body: Expr) extends Expr { + assert(fds.nonEmpty) val getType = body.getType } @@ -725,11 +726,11 @@ object Expressions { case class BVShiftLeft(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } - /** $encodingof `... >>> ...` $noteBitvector (logical shift) */ + /** $encodingof `... >> ...` $noteBitvector (arithmetic shift, sign-preserving) */ case class BVAShiftRight(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } - /** $encodingof `... >> ...` $noteBitvector (arithmetic shift, sign-preserving) */ + /** $encodingof `... >>> ...` $noteBitvector (logical shift) */ case class BVLShiftRight(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } diff --git a/src/main/scala/leon/purescala/PrettyPrinter.scala b/src/main/scala/leon/purescala/PrettyPrinter.scala index 7bebde2f800965d26eae30befc00f92512cf72e0..039bef507339294874ad59e7e38dfe335b8f6a2f 100644 --- a/src/main/scala/leon/purescala/PrettyPrinter.scala +++ b/src/main/scala/leon/purescala/PrettyPrinter.scala @@ -11,6 +11,7 @@ import Extractors._ import PrinterHelpers._ import ExprOps.{isListLiteral, simplestValue} import Expressions._ +import Constructors._ import Types._ import org.apache.commons.lang3.StringEscapeUtils @@ -92,7 +93,7 @@ class PrettyPrinter(opts: PrinterOptions, case LetDef(a::q,body) => p"""|$a - |${LetDef(q, body)}""" + |${letDef(q, body)}""" case LetDef(Nil,body) => p"""$body""" @@ -164,6 +165,7 @@ class PrettyPrinter(opts: PrinterOptions, case Or(exprs) => optP { p"${nary(exprs, "| || ")}" } // Ugliness award! The first | is there to shield from stripMargin() case Not(Equals(l, r)) => optP { p"$l \u2260 $r" } case Implies(l,r) => optP { p"$l ==> $r" } + case BVNot(expr) => p"~$expr" case UMinus(expr) => p"-$expr" case BVUMinus(expr) => p"-$expr" case RealUMinus(expr) => p"-$expr" @@ -358,8 +360,8 @@ class PrettyPrinter(opts: PrinterOptions, case Not(expr) => p"\u00AC$expr" - case vd @ ValDef(id, lzy) => - p"$id :${if (lzy) "=> " else ""} ${vd.getType}" + case vd @ ValDef(id) => + p"$id : ${vd.getType}" vd.defaultValue.foreach { fd => p" = ${fd.body.get}" } case This(_) => p"this" diff --git a/src/main/scala/leon/purescala/ScopeSimplifier.scala b/src/main/scala/leon/purescala/ScopeSimplifier.scala index 01e048699ec1654420ba059873dc4299724b842f..f3e7c15886fbce8b4e7c2854f0f83fc2a74e26c7 100644 --- a/src/main/scala/leon/purescala/ScopeSimplifier.scala +++ b/src/main/scala/leon/purescala/ScopeSimplifier.scala @@ -50,10 +50,10 @@ class ScopeSimplifier extends Transformer { val fds_mapping = for((fd, newId) <- fds_newIds) yield { val localScopeToRegister = ListBuffer[(Identifier, Identifier)]() // We record the mapping of these variables only for the function. - val newArgs = for(ValDef(id, tpe) <- fd.params) yield { + val newArgs = for(ValDef(id) <- fd.params) yield { val newArg = genId(id, newScope.register(localScopeToRegister)) localScopeToRegister += (id -> newArg) // This renaming happens only inside the function. - ValDef(newArg, tpe) + ValDef(newArg) } val newFd = fd.duplicate(id = newId, params = newArgs) diff --git a/src/main/scala/leon/purescala/TypeOps.scala b/src/main/scala/leon/purescala/TypeOps.scala index 84825d29a8ea2a293f04732fab06b2a88a36a4b8..db655365c24a7304831e818849238bba0a849de9 100644 --- a/src/main/scala/leon/purescala/TypeOps.scala +++ b/src/main/scala/leon/purescala/TypeOps.scala @@ -167,6 +167,11 @@ object TypeOps { } } + def isParametricType(tpe: TypeTree): Boolean = tpe match { + case (tp: TypeParameter) => true + case NAryType(tps, builder) => tps.exists(isParametricType) + } + // Helpers for instantiateType private def typeParamSubst(map: Map[TypeParameter, TypeTree])(tpe: TypeTree): TypeTree = tpe match { case (tp: TypeParameter) => map.getOrElse(tp, tp) diff --git a/src/main/scala/leon/solvers/SolverFactory.scala b/src/main/scala/leon/solvers/SolverFactory.scala index 6b2d4272b37b4a06ba2731c5049bcc043b87c927..67d28f877019a3e4741df6d268c68fc2d86d17ee 100644 --- a/src/main/scala/leon/solvers/SolverFactory.scala +++ b/src/main/scala/leon/solvers/SolverFactory.scala @@ -177,7 +177,7 @@ object SolverFactory { } lazy val hasZ3 = try { - Z3Interpreter.buildDefault.free() + new Z3Interpreter("z3", Array("-in", "-smt2")) true } catch { case e: java.io.IOException => @@ -185,7 +185,7 @@ object SolverFactory { } lazy val hasCVC4 = try { - CVC4Interpreter.buildDefault.free() + new CVC4Interpreter("cvc4", Array("-q", "--lang", "smt2.5")) true } catch { case e: java.io.IOException => diff --git a/src/main/scala/leon/synthesis/ExamplesFinder.scala b/src/main/scala/leon/synthesis/ExamplesFinder.scala index 1fa660b55bcfc57f980a4e5165d48ead07c42a44..42d6c41ab82fd80e28776b5ce0deeb082212612c 100644 --- a/src/main/scala/leon/synthesis/ExamplesFinder.scala +++ b/src/main/scala/leon/synthesis/ExamplesFinder.scala @@ -34,7 +34,7 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) { def setEvaluationFailOnChoose(b: Boolean) = { evaluator.setEvaluationFailOnChoose(b); this } def extractFromFunDef(fd: FunDef, partition: Boolean): ExamplesBank = fd.postcondition match { - case Some(Lambda(Seq(ValDef(id, _)), post)) => + case Some(Lambda(Seq(ValDef(id)), post)) => // @mk FIXME: make this more general val tests = extractTestsOf(post) diff --git a/src/main/scala/leon/synthesis/disambiguation/ExamplesAdder.scala b/src/main/scala/leon/synthesis/disambiguation/ExamplesAdder.scala index 375052dc7e5626cf635231d1c7687bba2c35f211..ed9f44768752c716b2acf0f6db9863cde1353068 100644 --- a/src/main/scala/leon/synthesis/disambiguation/ExamplesAdder.scala +++ b/src/main/scala/leon/synthesis/disambiguation/ExamplesAdder.scala @@ -60,7 +60,7 @@ class ExamplesAdder(ctx0: LeonContext, program: Program) { val inputVariables = tupleWrap(params.map(p => Variable(p.id): Expr)) val newCases = examples.map{ case (in, out) => exampleToCase(in, out) } fd.postcondition match { - case Some(Lambda(Seq(ValDef(id, tpe)), post)) => + case Some(Lambda(Seq(ValDef(id)), post)) => if(fd.postcondition.exists { e => purescala.ExprOps.exists(_.isInstanceOf[Passes])(e) }) { post match { case TopLevelAnds(exprs) => @@ -69,7 +69,7 @@ class ExamplesAdder(ctx0: LeonContext, program: Program) { case _ => false } } if(i == -1) { - fd.postcondition = Some(Lambda(Seq(ValDef(id, tpe)), and(post, Passes(inputVariables, Variable(id), newCases)))) + fd.postcondition = Some(Lambda(Seq(ValDef(id)), and(post, Passes(inputVariables, Variable(id), newCases)))) //ctx0.reporter.info("No top-level passes in postcondition, adding it: " + fd) } else { val newPasses = exprs(i) match { @@ -78,21 +78,21 @@ class ExamplesAdder(ctx0: LeonContext, program: Program) { case _ => ??? } val newPost = and(exprs.updated(i, newPasses) : _*) - fd.postcondition = Some(Lambda(Seq(ValDef(id, tpe)), newPost)) + fd.postcondition = Some(Lambda(Seq(ValDef(id)), newPost)) //ctx0.reporter.info("Adding the example to the passes postcondition: " + fd) } } } else { - fd.postcondition = Some(Lambda(Seq(ValDef(id, tpe)), and(post, Passes(inputVariables, Variable(id), newCases)))) + fd.postcondition = Some(Lambda(Seq(ValDef(id)), and(post, Passes(inputVariables, Variable(id), newCases)))) //ctx0.reporter.info("No passes in postcondition, adding it:" + fd) } case None => val id = FreshIdentifier("res", fd.returnType, false) - fd.postcondition = Some(Lambda(Seq(ValDef(id, false)), Passes(inputVariables, Variable(id), newCases))) + fd.postcondition = Some(Lambda(Seq(ValDef(id)), Passes(inputVariables, Variable(id), newCases))) //ctx0.reporter.info("No postcondition, adding it: " + fd) } fd.body match { // TODO: Is it correct to discard the choose construct inside the body? - case Some(Choose(Lambda(Seq(ValDef(id, tpe)), bodyChoose))) => fd.body = Some(Hole(id.getType, Nil)) + case Some(Choose(Lambda(Seq(ValDef(id)), bodyChoose))) => fd.body = Some(Hole(id.getType, Nil)) case _ => } } @@ -106,4 +106,4 @@ class ExamplesAdder(ctx0: LeonContext, program: Program) { MatchCase(WildcardPattern(Some(id)), Some(Equals(Variable(id), in)), out) } } - } \ No newline at end of file + } diff --git a/src/main/scala/leon/synthesis/rules/Assert.scala b/src/main/scala/leon/synthesis/rules/Assert.scala index 9bba63c87d50b3ca7f1adf7e445a8a0b54172b2f..d2cdb5b69f1f88b847ad91499e3946257ef6c167 100644 --- a/src/main/scala/leon/synthesis/rules/Assert.scala +++ b/src/main/scala/leon/synthesis/rules/Assert.scala @@ -5,7 +5,9 @@ package synthesis package rules import purescala.ExprOps._ +import purescala.TypeOps._ import purescala.Extractors._ +import purescala.Expressions._ import purescala.Constructors._ /** Moves the preconditions without output variables to the precondition. */ @@ -19,7 +21,13 @@ case object Assert extends NormalizingRule("Assert") { if (exprsA.nonEmpty) { // If there is no more postcondition, then output the solution. if (others.isEmpty) { - Some(solve(Solution(pre=andJoin(exprsA), defs=Set(), term=tupleWrap(p.xs.map(id => simplestValue(id.getType)))))) + val simplestOut = simplestValue(tupleTypeWrap(p.xs.map(_.getType))) + + if (!isRealExpr(simplestOut)) { + None + } else { + Some(solve(Solution(pre = andJoin(exprsA), defs = Set(), term = simplestOut))) + } } else { val sub = p.copy(pc = andJoin(p.pc +: exprsA), phi = andJoin(others), eb = p.qeb.filterIns(andJoin(exprsA))) diff --git a/src/main/scala/leon/synthesis/rules/Ground.scala b/src/main/scala/leon/synthesis/rules/Ground.scala index 49663409fab1fb22dc102e35cfcfd3fb0ab5cdb4..d36cb15c0ff6f487d5d7b5e296fda0c406406886 100644 --- a/src/main/scala/leon/synthesis/rules/Ground.scala +++ b/src/main/scala/leon/synthesis/rules/Ground.scala @@ -19,8 +19,14 @@ case object Ground extends Rule("Ground") { val result = solver.solveSAT(p.phi) match { case (Some(true), model) => - val sol = Solution(BooleanLiteral(true), Set(), tupleWrap(p.xs.map(valuateWithModel(model)))) - RuleClosed(sol) + val solExpr = tupleWrap(p.xs.map(valuateWithModel(model))) + + if (!isRealExpr(solExpr)) { + RuleFailed() + } else { + val sol = Solution(BooleanLiteral(true), Set(), solExpr) + RuleClosed(sol) + } case (Some(false), model) => RuleClosed(Solution.UNSAT(p)) case _ => diff --git a/src/main/scala/leon/synthesis/rules/OptimisticGround.scala b/src/main/scala/leon/synthesis/rules/OptimisticGround.scala index 6085ef9eb2067fa092ffacfdefbf27532b02b5d9..679e49e38fb25ce3c4f79a26e1078651eccb69a5 100644 --- a/src/main/scala/leon/synthesis/rules/OptimisticGround.scala +++ b/src/main/scala/leon/synthesis/rules/OptimisticGround.scala @@ -36,7 +36,6 @@ case object OptimisticGround extends Rule("Optimistic Ground") { //println("SOLVING " + phi + " ...") solver.solveSAT(phi) match { case (Some(true), satModel) => - val newNotPhi = valuateWithModelIn(notPhi, xss, satModel) //println("REFUTING " + Not(newNotPhi) + "...") @@ -46,8 +45,15 @@ case object OptimisticGround extends Rule("Optimistic Ground") { predicates = valuateWithModelIn(phi, ass, invalidModel) +: predicates case (Some(false), _) => - result = Some(RuleClosed(Solution(BooleanLiteral(true), Set(), tupleWrap(p.xs.map(valuateWithModel(satModel)))))) - + // Model apprears valid, but it might be a fake expression (generic values) + val outExpr = tupleWrap(p.xs.map(valuateWithModel(satModel))) + + if (!isRealExpr(outExpr)) { + // It does contain a generic value, we skip + predicates = valuateWithModelIn(phi, xss, satModel) +: predicates + } else { + result = Some(RuleClosed(Solution(BooleanLiteral(true), Set(), outExpr))) + } case _ => continue = false result = None diff --git a/src/main/scala/leon/synthesis/rules/StringRender.scala b/src/main/scala/leon/synthesis/rules/StringRender.scala index cce501ac15e547b53fc7f3a1fcbe7b473bb664bb..05a49a9064142bf537c3789ff28df948511f8dea 100644 --- a/src/main/scala/leon/synthesis/rules/StringRender.scala +++ b/src/main/scala/leon/synthesis/rules/StringRender.scala @@ -344,7 +344,7 @@ case object StringRender extends Rule("StringRender") { val funId = FreshIdentifier(ctx.freshFunName(funName), alwaysShowUniqueID = true) val argId= FreshIdentifier(tpe.typeToConvert.asString(hctx.context).toLowerCase()(0).toString, tpe.typeToConvert) val tparams = hctx.sctx.functionContext.tparams - val fd = new FunDef(funId, tparams, ValDef(argId) :: ctx.provided_functions.map(ValDef(_, false)).toList, StringType) // Empty function. + val fd = new FunDef(funId, tparams, ValDef(argId) :: ctx.provided_functions.map(ValDef(_)).toList, StringType) // Empty function. fd } diff --git a/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala b/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala index f753d3884e71e4ba76c84b72985f6401c0666256..eb3d83ed8d13b8235a98b48d4c209a2db5a7f08c 100644 --- a/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala +++ b/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala @@ -7,10 +7,13 @@ package rules import purescala.Expressions._ import purescala.ExprOps._ import purescala.Constructors._ +import purescala.TypeOps._ case object UnconstrainedOutput extends NormalizingRule("Unconstr.Output") { def instantiateOn(implicit hctx: SearchContext, p: Problem): Traversable[RuleInstantiation] = { - val unconstr = p.xs.toSet -- variablesOf(p.phi) + val unconstr = (p.xs.toSet -- variablesOf(p.phi)).filter { x => + isRealExpr(simplestValue(x.getType)) + } if (unconstr.nonEmpty) { val sub = p.copy(xs = p.xs.filterNot(unconstr), eb = p.qeb.removeOuts(unconstr)) diff --git a/src/main/scala/leon/synthesis/rules/UnusedInput.scala b/src/main/scala/leon/synthesis/rules/UnusedInput.scala index 677c9b852aafeadcfc96bd63f2b98aaaaf289940..4570501d7646b3f8d77d4fb484451ff957ac723f 100644 --- a/src/main/scala/leon/synthesis/rules/UnusedInput.scala +++ b/src/main/scala/leon/synthesis/rules/UnusedInput.scala @@ -5,10 +5,13 @@ package synthesis package rules import purescala.ExprOps._ +import purescala.TypeOps._ case object UnusedInput extends NormalizingRule("UnusedInput") { def instantiateOn(implicit hctx: SearchContext, p: Problem): Traversable[RuleInstantiation] = { - val unused = p.as.toSet -- variablesOf(p.phi) -- variablesOf(p.pc) -- variablesOf(p.ws) + val unused = (p.as.toSet -- variablesOf(p.phi) -- variablesOf(p.pc) -- variablesOf(p.ws)).filter { a => + !isParametricType(a.getType) + } if (unused.nonEmpty) { val sub = p.copy(as = p.as.filterNot(unused), eb = p.qeb.removeIns(unused)) diff --git a/src/main/scala/leon/transformations/IntToRealProgram.scala b/src/main/scala/leon/transformations/IntToRealProgram.scala index 5ab16991398e5dcb4352e870795fff51045fb9c9..0c313f2ff3ce79809420969a49bc2d3b6f11b2f3 100644 --- a/src/main/scala/leon/transformations/IntToRealProgram.scala +++ b/src/main/scala/leon/transformations/IntToRealProgram.scala @@ -140,9 +140,9 @@ abstract class ProgramTypeTransformer { // FIXME //add a new postcondition newfd.fullBody = if (fd.postcondition.isDefined && newfd.body.isDefined) { - val Lambda(Seq(ValDef(resid, lzy)), pexpr) = fd.postcondition.get + val Lambda(Seq(ValDef(resid)), pexpr) = fd.postcondition.get val tempRes = mapId(resid).toVariable - Ensuring(newfd.body.get, Lambda(Seq(ValDef(tempRes.id, lzy)), transformExpr(pexpr))) + Ensuring(newfd.body.get, Lambda(Seq(ValDef(tempRes.id)), transformExpr(pexpr))) // Some(mapId(resid), transformExpr(pexpr)) } else NoTree(fd.returnType) diff --git a/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala b/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala index c4830cf40ca429358b0d2e420bbbe7f7d1f5c276..2e2e5c832e2b4d276ca75b4f2c5470bebb23a608 100644 --- a/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala +++ b/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala @@ -111,7 +111,7 @@ class SerialInstrumenter(ctx: LeonContext, program: Program) { def mapPost(pred: Expr, from: FunDef, to: FunDef) = { pred match { - case Lambda(Seq(ValDef(fromRes, lzy)), postCond) if (instFuncs.contains(from)) => + case Lambda(Seq(ValDef(fromRes)), postCond) if (instFuncs.contains(from)) => val toResId = FreshIdentifier(fromRes.name, to.returnType, true) val newpost = postMap((e: Expr) => e match { case Variable(`fromRes`) => @@ -124,7 +124,7 @@ class SerialInstrumenter(ctx: LeonContext, program: Program) { case _ => None })(postCond) - Lambda(Seq(ValDef(toResId, lzy)), mapExpr(newpost)) + Lambda(Seq(ValDef(toResId)), mapExpr(newpost)) case _ => mapExpr(pred) } diff --git a/src/main/scala/leon/utils/DebugSections.scala b/src/main/scala/leon/utils/DebugSections.scala index a0b790243a6dc309953d07ba02d504e463185880..b3133d700fed92bb500423201dc4288832b07319 100644 --- a/src/main/scala/leon/utils/DebugSections.scala +++ b/src/main/scala/leon/utils/DebugSections.scala @@ -24,6 +24,7 @@ case object DebugSectionXLang extends DebugSection("xlang", 1 << 1 case object DebugSectionTypes extends DebugSection("types", 1 << 13) case object DebugSectionIsabelle extends DebugSection("isabelle", 1 << 14) case object DebugSectionReport extends DebugSection("report", 1 << 15) +case object DebugSectionGenC extends DebugSection("genc", 1 << 16) object DebugSections { val all = Set[DebugSection]( @@ -42,6 +43,7 @@ object DebugSections { DebugSectionXLang, DebugSectionTypes, DebugSectionIsabelle, - DebugSectionReport + DebugSectionReport, + DebugSectionGenC ) } diff --git a/src/main/scala/leon/utils/GraphPrinters.scala b/src/main/scala/leon/utils/GraphPrinters.scala new file mode 100644 index 0000000000000000000000000000000000000000..b86882f2d42d6e7ea1816b249b3d35689b31ab67 --- /dev/null +++ b/src/main/scala/leon/utils/GraphPrinters.scala @@ -0,0 +1,215 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package utils + +import Graphs._ + +object GraphPrinters { + abstract class Printer[N, E <: EdgeLike[N], G <: DiGraphLike[N, E, G]] { + + def asString(g: G): String + + def asFile(g: G, file: String) { + import java.io.{BufferedWriter, FileWriter} + val out = new BufferedWriter(new FileWriter(file)) + out.write(asString(g)) + out.close() + } + } + + case class DotNode(label: String, opts: Map[String, String] = Map()) { + def toOpts(dot: DotHelpers): String = { + s"""[label="${dot.escape(label)}"]""" + } + } + + class DotPrinter[N, E <: EdgeLike[N], G <: DiGraphLike[N, E, G]] extends Printer[N, E, G] { + + val dot = new DotHelpers + + private var nToLabel: N => String = { (n: N) => n.toString } + + private var nToOpts: Seq[N => Seq[(String, String)]] = Nil + + def setNodeLabel(f: N => String): Unit = { + nToLabel = f + } + + def colorize(f: N => Boolean, color: String) = { + nToOpts :+= { (n: N) => + if (f(n)) List(("color", color)) else Nil + } + } + + def highlight(f: N => Boolean) = colorize(f, "red") + + + def drawNode(n: N)(implicit res: StringBuffer): Unit = { + var opts = Map[String, String]() + opts += "label" -> ("\"" + dot.escape(nToLabel(n)) + "\"") + + for (f <- nToOpts) { + opts ++= f(n) + } + + res append (nToS(n) +" "+opts.map{ case (n, v) => s"$n=$v" }.mkString("[", ", ", "]")+";\n") + } + + def nToS(n: N): String = { + dot.uniqueName("n", n) + } + + def eToS(e: EdgeLike[N]): String = { + dot.uniqueName("e", e) + } + + def drawEdge(e: E)(implicit res: StringBuffer): Unit = { + e match { + case le: LabeledEdge[_, N] => + res append dot.box(eToS(e), le.l.toString) + res append dot.arrow(nToS(e._1), eToS(e)) + res append dot.arrow(eToS(e), nToS(e._2)) + case _ => + res append dot.arrow(nToS(e._1), nToS(e._2)) + } + } + + def asString(g: G): String = { + implicit val res = new StringBuffer() + + res append "digraph D {\n" + + g.N.foreach(drawNode) + + g.E.foreach(drawEdge) + + res append "}\n" + + res.toString + } + } + + class DotHelpers { + private var _nextName = 0 + private var _nextColor = 0 + + private def nextName = { + _nextName += 1 + _nextName.toString + } + + private var names = Map[Any, String]() + + def uniqueName(prefix: String, obj: Any) = { + if (!names.contains(obj)) { + names = names + (obj -> (prefix+nextName)) + } + + names(obj) + } + + val bgColors = List("bisque", "khaki", "mistyrose", "lightcyan", "mediumorchid", "aquamarine", "antiquewhite") + + def nextColor = { + _nextColor += 1 + val colornumber: String = if((_nextColor/bgColors.size)%3 == 0) "" else ((_nextColor/bgColors.size)%3)+""; + bgColors(_nextColor%bgColors.size)+colornumber + } + + val colors = List("khaki", "khaki1", "khaki2", "khaki3", + "khaki4", "lavender", "lavenderblush", "lavenderblush1", "lavenderblush2", + "lavenderblush3", "lavenderblush4", "lawngreen", "lemonchiffon", + "lemonchiffon1", "lemonchiffon2", "lemonchiffon3", "lemonchiffon4", + "lightblue", "lightblue1", "lightblue2", "lightblue3", "lightblue4", + "lightcoral", "lightcyan", "lightcyan1", "lightcyan2", "lightcyan3", + "lightcyan4", "lightgoldenrod", "lightgoldenrod1", "lightgoldenrod2", + "lightgoldenrod3", "lightgoldenrod4", "lightgoldenrodyellow", "lightgray", + "lightgrey", "lightpink", "lightpink1", "lightpink2", "lightpink3", + "lightpink4", "lightsalmon", "lightsalmon1", "lightsalmon2", "lightsalmon3", + "lightsalmon4", "lightseagreen", "lightskyblue", "lightskyblue1", + "lightskyblue2", "lightskyblue3", "lightskyblue4", "lightslateblue", + "lightslategray", "lightslategrey", "lightsteelblue", "lightsteelblue1", + "lightsteelblue2", "lightsteelblue3", "lightsteelblue4", "lightyellow", + "lightyellow1", "lightyellow2", "lightyellow3", "lightyellow4", "limegreen", + "linen", "magenta", "magenta1", "magenta2", "magenta3", "magenta4", "maroon", + "maroon1", "maroon2", "maroon3", "maroon4", "mediumaquamarine", "mediumblue", + "mediumorchid", "mediumorchid1", "mediumorchid2", "mediumorchid3", + "mediumorchid4", "mediumpurple", "mediumpurple1", "mediumpurple2", + "mediumpurple3", "mediumpurple4", "mediumseagreen", "mediumslateblue", + "mediumspringgreen", "mediumturquoise", "mediumvioletred", "midnightblue", + "mintcream", "mistyrose", "mistyrose1", "mistyrose2", "mistyrose3", + "mistyrose4", "moccasin", "navajowhite", "navajowhite1", "navajowhite2", + "navajowhite3", "navajowhite4", "navy", "navyblue", "none", "oldlace", + "olivedrab", "olivedrab1", "olivedrab2", "olivedrab3", "olivedrab4", "orange", + "orange1", "orange2", "orange3", "orange4", "orangered", "orangered1", + "orangered2", "orangered3", "orangered4", "orchid", "orchid1", "orchid2", + "orchid3", "orchid4", "palegoldenrod", "palegreen", "palegreen1", "palegreen2", + "palegreen3", "palegreen4", "paleturquoise", "paleturquoise1", + "paleturquoise2", "paleturquoise3", "paleturquoise4", "palevioletred", + "palevioletred1", "palevioletred2", "palevioletred3", "palevioletred4", + "papayawhip", "peachpuff", "peachpuff1", "peachpuff2", "peachpuff3", + "peachpuff4", "peru", "pink", "pink1", "pink2", "pink3", "pink4", "plum", + "plum1", "plum2", "plum3", "plum4", "powderblue", "purple", "purple1", + "purple2", "purple3", "purple4", "red", "red1", "red2", "red3", "red4", + "rosybrown", "rosybrown1", "rosybrown2", "rosybrown3", "rosybrown4", + "royalblue", "royalblue1", "royalblue2", "royalblue3", "royalblue4", + "saddlebrown", "salmon", "salmon1", "salmon2", "salmon3", "salmon4", + "sandybrown", "seagreen", "seagreen1", "seagreen2", "seagreen3", "seagreen4", + "seashell", "seashell1", "seashell2", "seashell3", "seashell4", "sienna", + "sienna1", "sienna2", "sienna3", "sienna4", "skyblue", "skyblue1", "skyblue2", + "skyblue3", "skyblue4", "slateblue", "slateblue1", "slateblue2", "slateblue3", + "slateblue4", "slategray", "slategray1", "slategray2", "slategray3", + "slategray4", "slategrey", "snow", "snow1", "snow2", "snow3", "snow4", + "springgreen", "springgreen1", "springgreen2", "springgreen3", "springgreen4", + "steelblue", "steelblue1", "steelblue2", "steelblue3", "steelblue4", "tan", + "tan1", "tan2", "tan3", "tan4", "thistle", "thistle1", "thistle2", "thistle3", + "thistle4", "tomato", "tomato1", "tomato2", "tomato3", "tomato4", + "transparent", "turquoise", "turquoise1", "turquoise2", "turquoise3", + "turquoise4", "violet", "violetred", "violetred1", "violetred2", "violetred3", + "violetred4", "wheat", "wheat1", "wheat2", "wheat3", "wheat4", "white", + "whitesmoke", "yellow", "yellow1", "yellow2", "yellow3", "yellow4", + "yellowgreen"); + + def randomColor = { + _nextColor += 1 + colors(_nextColor % colors.size) + } + + def escape(s: String) = + s.replaceAll("\\\\n", "__NEWLINE__") + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll("\"", "\\\\\"") + .replaceAll("\\\n", "\\\\n") + .replaceAll("[^<>@a-zA-Z0-9;$.,!# \t=^:_\\\\\"'*+/&()\\[\\]{}\u03B5-]", "?") + .replaceAll("__NEWLINE__", "\\\\n") + + def escapeStrict(s: String) = s.replaceAll("[^a-zA-Z0-9_]", "_") + + def labeledArrow(x: String, label: String, y: String, options: List[String] = Nil) = + arrow(x, y, "label=\""+escape(label)+"\"" :: options) + + def labeledDashedArrow(x: String, label: String, y: String, options: List[String] = Nil) = + arrow(x, y, "label=\""+escape(label)+"\"" :: "style=dashed" :: options) + + def arrow(x: String, y: String, options: List[String] = Nil) = { + " "+x+" -> "+y+options.mkString(" [", " ", "]")+";\n" + } + + def box(id : String, name : String, options: List[String] = Nil) = { + node(id, name, "shape=box" :: "color=lightblue" :: "style=filled" :: options) + } + + def invisNode(id : String, name : String, options: List[String] = Nil) = { + node(id, name, "shape=none" :: options) + } + + def dashedNode(id : String, name : String, options: List[String] = Nil) = { + node(id, name, "style=dashed" :: options) + } + + def node(id: String, name: String, options: List[String] = Nil) = { + id +("label=\""+escape(name)+"\"" :: options).mkString(" [", ", ", "]")+";\n" + } + } +} diff --git a/src/main/scala/leon/utils/Graphs.scala b/src/main/scala/leon/utils/Graphs.scala index b3aeef558790ce9427e376d0e0adba2851813b7b..1910c2aa25715acd325c53c9874bd1b86ec2a6f5 100644 --- a/src/main/scala/leon/utils/Graphs.scala +++ b/src/main/scala/leon/utils/Graphs.scala @@ -4,212 +4,228 @@ package leon package utils object Graphs { - abstract class VertexAbs extends Serializable { - val name: String - - override def toString = name + trait EdgeLike[Node] { + def _1: Node + def _2: Node } - abstract class EdgeAbs[V <: VertexAbs] extends Serializable { - val v1: V - val v2: V - - override def toString = v1 + "->" + v2 - } - - case class SimpleEdge[V <: VertexAbs](v1: V, v2: V) extends EdgeAbs[V] - - abstract class LabeledEdgeAbs[T, V <: VertexAbs] extends EdgeAbs[V] { - val label: T - } - - case class SimpleLabeledEdge[T, V <: VertexAbs](v1: V, label: T, v2: V) extends LabeledEdgeAbs[T, V] - - trait DirectedGraph[V <: VertexAbs, E <: EdgeAbs[V], G <: DirectedGraph[V,E,G]] extends Serializable { - type Vertex = V - type Edge = E - type This = G + case class SimpleEdge[Node](_1: Node, _2: Node) extends EdgeLike[Node] + case class LabeledEdge[Label, Node](_1: Node, l: Label, _2: Node) extends EdgeLike[Node] + trait DiGraphLike[Node, Edge <: EdgeLike[Node], G <: DiGraphLike[Node, Edge, G]] { // The vertices - def V: Set[Vertex] + def N: Set[Node] // The edges def E: Set[Edge] + // Returns the set of incoming edges for a given vertex - def inEdges(v: Vertex) = E.filter(_.v2 == v) + def inEdges(n: Node) = E.filter(_._2 == n) // Returns the set of outgoing edges for a given vertex - def outEdges(v: Vertex) = E.filter(_.v1 == v) + def outEdges(n: Node) = E.filter(_._1 == n) // Returns the set of edges between two vertices - def edgesBetween(from: Vertex, to: Vertex) = { - E.filter(e => e.v1 == from && e.v2 == to) + def edgesBetween(from: Node, to: Node) = { + E.filter(e => e._1 == from && e._2 == to) } - /** - * Basic Graph Operations: - */ - // Adds a new vertex - def + (v: Vertex): This + def + (n: Node): G // Adds new vertices - def ++ (vs: Traversable[Vertex]): This + def ++ (ns: Traversable[Node]): G // Adds a new edge - def + (e: Edge): This + def + (e: Edge): G // Removes a vertex from the graph - def - (from: Vertex): This + def - (from: Node): G // Removes a number of vertices from the graph - def -- (from: Traversable[Vertex]): This + def -- (from: Traversable[Node]): G // Removes an edge from the graph - def - (from: Edge): This - - /** - * Advanced Graph Operations: - */ - - // Merges two graphs - def union(that: This): This - // Return the strongly connected components, sorted topologically - def stronglyConnectedComponents: Seq[Set[Vertex]] - // Topological sorting - def topSort: Option[Seq[Vertex]] - // All nodes leading to v - def transitivePredecessors(v: Vertex): Set[Vertex] - // All nodes reachable from v - def transitiveSuccessors(v: Vertex): Set[Vertex] - // Is v1 reachable from v2 - def isReachable(v1: Vertex, v2: Vertex): Boolean + def - (from: Edge): G } - case class DirectedGraphImp[Vertex <: VertexAbs, Edge <: EdgeAbs[Vertex]]( - vertices: Set[Vertex], - edges: Set[Edge], - ins: Map[VertexAbs, Set[Edge]], - outs: Map[VertexAbs, Set[Edge]] - ) extends DirectedGraph[Vertex, Edge, DirectedGraphImp[Vertex, Edge]] { - - override def equals(o: Any): Boolean = o match { - case other: DirectedGraphImp[_, _] => - this.vertices == other.vertices && - this.edges == other.edges && - (this.ins.keySet ++ other.ins.keySet).forall (k => this.ins(k) == other.ins(k)) && - (this.outs.keySet ++ other.outs.keySet).forall(k => this.outs(k) == other.outs(k)) - - case _ => false + case class DiGraph[Node, Edge <: EdgeLike[Node]](N: Set[Node] = Set[Node](), E: Set[Edge] = Set[Edge]()) extends DiGraphLike[Node, Edge, DiGraph[Node, Edge]] with DiGraphOps[Node, Edge, DiGraph[Node, Edge]]{ + def +(n: Node) = copy(N=N+n) + def ++(ns: Traversable[Node]) = copy(N=N++ns) + def +(e: Edge) = (this+e._1+e._2).copy(E = E + e) + + def -(n: Node) = copy(N = N-n, E = E.filterNot(e => e._1 == n || e._2 == n)) + def --(ns: Traversable[Node]) = { + val toRemove = ns.toSet + copy(N = N--ns, E = E.filterNot(e => toRemove.contains(e._1) || toRemove.contains(e._2))) } - def this (vertices: Set[Vertex], edges: Set[Edge]) = - this(vertices, - edges, - edges.groupBy(_.v2: VertexAbs).withDefaultValue(Set()), - edges.groupBy(_.v1: VertexAbs).withDefaultValue(Set())) - - def this() = this(Set(), Set()) - - val V = vertices - val E = edges - - def + (v: Vertex) = copy( - vertices = vertices+v - ) - - override def inEdges(v: Vertex) = ins(v) - override def outEdges(v: Vertex) = outs(v) - - def ++ (vs: Traversable[Vertex]) = copy( - vertices = vertices++vs - ) - - def -- (vs: Traversable[Vertex]) = copy( - vertices = vertices--vs, - edges = edges -- vs.flatMap(outs) -- vs.flatMap(ins), - ins = ((ins -- vs) map { case (vm, edges) => vm -> (edges -- vs.flatMap(outs)) }).withDefaultValue(Set()) , - outs = ((outs -- vs) map { case (vm, edges) => vm -> (edges -- vs.flatMap(ins)) }).withDefaultValue(Set()) - ) - - def + (e: Edge) = copy( - vertices = vertices + e.v1 + e.v2, - edges = edges + e, - ins = ins + (e.v2 -> (ins(e.v2) + e)), - outs = outs + (e.v1 -> (outs(e.v1) + e)) - ) - - def - (v: Vertex) = copy( - vertices = vertices-v, - edges = edges -- outs(v) -- ins(v), - ins = ((ins - v) map { case (vm, edges) => vm -> (edges -- outs(v)) }).withDefaultValue(Set()) , - outs = ((outs - v) map { case (vm, edges) => vm -> (edges -- ins(v)) }).withDefaultValue(Set()) - ) - - def - (e: Edge) = copy( - vertices = vertices, - edges = edges-e, - ins = ins + (e.v2 -> (ins(e.v2) - e)), - outs = outs + (e.v1 -> (outs(e.v1) - e)) - ) - - def union(that: This): This = copy( - vertices = this.V ++ that.V, - edges = this.E ++ that.E, - ins = ((this.ins.keySet ++ that.ins.keySet) map { k => k -> (this.ins(k) ++ that.ins(k)) }).toMap.withDefaultValue(Set()), - outs = ((this.outs.keySet ++ that.outs.keySet) map { k => k -> (this.outs(k) ++ that.outs(k)) }).toMap.withDefaultValue(Set()) - ) - - def stronglyConnectedComponents: Seq[Set[Vertex]] = ??? - - def topSort = { - val sccs = stronglyConnectedComponents - if (sccs.forall(_.size == 1)) { - Some(sccs.flatten) - } else { - None - } + def -(e: Edge) = copy(E = E-e) + } + + + trait DiGraphOps[Node, Edge <: EdgeLike[Node], G <: DiGraphLike[Node, Edge, G]] { + this: G => + + def sources: Set[Node] = { + N -- E.map(_._2) } - def transitivePredecessors(v: Vertex): Set[Vertex] = { - var seen = Set[Vertex]() - def rec(v: Vertex): Set[Vertex] = { - if (seen(v)) { - Set() - } else { - seen += v - val ins = inEdges(v).map(_.v1) - ins ++ ins.flatMap(rec) + def sinks: Set[Node] = { + N -- E.map(_._1) + } + + def stronglyConnectedComponents: DiGraph[Set[Node], SimpleEdge[Set[Node]]] = { + // Tarjan's algorithm + var index = 0 + var stack = List[Node]() + + var indexes = Map[Node, Int]() + var lowlinks = Map[Node, Int]() + var onStack = Set[Node]() + + var nodesToScc = Map[Node, Set[Node]]() + var res = DiGraph[Set[Node], SimpleEdge[Set[Node]]]() + + def strongConnect(n: Node): Unit = { + indexes += n -> index + lowlinks += n -> index + index += 1 + + stack = n :: stack + onStack += n + + for (m <- succ(n)) { + if (!(indexes contains m)) { + strongConnect(m) + lowlinks += n -> (lowlinks(n) min lowlinks(m)) + } else if (onStack(m)) { + lowlinks += n -> (lowlinks(n) min indexes(m)) + } + } + + if (lowlinks(n) == indexes(n)) { + val i = stack.indexOf(n)+1 + val ns = stack.take(i) + stack = stack.drop(i) + val scc = ns.toSet + onStack --= ns + nodesToScc ++= ns.map(n => n -> scc) + res += scc } } - rec(v) - } - def transitiveSuccessors(v: Vertex): Set[Vertex] = { - var seen = Set[Vertex]() - def rec(v: Vertex): Set[Vertex] = { - if (seen(v)) { - Set() - } else { - seen += v - val outs = outEdges(v).map(_.v2) - outs ++ outs.flatMap(rec) + + for (n <- N if !(indexes contains n)) { + strongConnect(n) + } + + for (e <- E) { + val s1 = nodesToScc(e._1) + val s2 = nodesToScc(e._2) + if (s1 != s2) { + res += SimpleEdge(s1, s2) } } - rec(v) + + res } - def isReachable(v1: Vertex, v2: Vertex): Boolean = { - var seen = Set[Vertex]() - def rec(v: Vertex): Boolean = { - if (seen(v)) { - false - } else { - seen += v - val outs = outEdges(v).map(_.v2) - if (outs(v2)) { - true - } else { - outs.exists(rec) + def topSort: Seq[Node] = { + var res = List[Node]() + + var temp = Set[Node]() + var perm = Set[Node]() + + def visit(n: Node) { + if (temp(n)) { + throw new IllegalArgumentException("Graph is not a DAG") + } else if (!perm(n)) { + temp += n + for (n2 <- succ(n)) { + visit(n2) } + perm += n + temp -= n + res ::= n } } - rec(v1) + + for (n <- N if !temp(n) && !perm(n)) { + visit(n) + } + + res + } + + def depthFirstSearch(from: Node)(f: Node => Unit): Unit = { + var visited = Set[Node]() + + val stack = new collection.mutable.Stack[Node]() + + stack.push(from) + + while(stack.nonEmpty) { + val n = stack.pop + visited += n + f(n) + for (n2 <- succ(n) if !visited(n2)) { + stack.push(n2) + } + } + } + + def fold[T](from: Node)( + follow: Node => Traversable[Node], + map: Node => T, + compose: List[T] => T): T = { + + var visited = Set[Node]() + + def visit(n: Node): T = { + visited += n + + val toFollow = follow(n).filterNot(visited) + visited ++= toFollow + + compose(map(n) :: toFollow.toList.map(visit)) + } + + compose(follow(from).toList.map(visit)) + } + + def succ(from: Node): Set[Node] = { + outEdges(from).map(_._2) + } + + def pred(from: Node): Set[Node] = { + inEdges(from).map(_._1) + } + + def transitiveSucc(from: Node): Set[Node] = { + fold[Set[Node]](from)( + succ, + Set(_), + _.toSet.flatten + ) } - override def toString = "DGraph[V: "+vertices+" | E:"+edges+"]" + def transitivePred(from: Node): Set[Node] = { + fold[Set[Node]](from)( + pred, + Set(_), + _.toSet.flatten + ) + } + + def breadthFirstSearch(from: Node)(f: Node => Unit): Unit = { + var visited = Set[Node]() + + val queue = new collection.mutable.Queue[Node]() + + queue += from + + while(queue.nonEmpty) { + val n = queue.dequeue + visited += n + f(n) + for (n2 <- succ(n) if !visited(n2)) { + queue += n2 + } + } + } } } diff --git a/src/main/scala/leon/utils/PreprocessingPhase.scala b/src/main/scala/leon/utils/PreprocessingPhase.scala index 4a395912a6b9dc92a9828fc90bed421baf009c97..06f35dc3a47df0a48836e6137eaae96218745d39 100644 --- a/src/main/scala/leon/utils/PreprocessingPhase.scala +++ b/src/main/scala/leon/utils/PreprocessingPhase.scala @@ -9,7 +9,7 @@ import leon.solvers.isabelle.AdaptationPhase import leon.verification.InjectAsserts import leon.xlang.{NoXLangFeaturesChecking, XLangDesugaringPhase} -class PreprocessingPhase(desugarXLang: Boolean = false) extends LeonPhase[Program, Program] { +class PreprocessingPhase(desugarXLang: Boolean = false, genc: Boolean = false) extends LeonPhase[Program, Program] { val name = "preprocessing" val description = "Various preprocessings on Leon programs" @@ -39,19 +39,27 @@ class PreprocessingPhase(desugarXLang: Boolean = false) extends LeonPhase[Progra CheckADTFieldsTypes andThen InliningPhase - val pipeX = if(desugarXLang) { + val pipeX = if (!genc && desugarXLang) { + // Do not desugar when generating C code XLangDesugaringPhase andThen debugTrees("Program after xlang desugaring") } else { NoopPhase[Program]() } + def pipeEnd = if (genc) { + // No InjectAsserts, FunctionClosure and AdaptationPhase phases + NoopPhase[Program]() + } else { + InjectAsserts andThen + FunctionClosure andThen + AdaptationPhase + } + val phases = pipeBegin andThen pipeX andThen - InjectAsserts andThen - FunctionClosure andThen - AdaptationPhase andThen + pipeEnd andThen debugTrees("Program after pre-processing") phases.run(ctx, p) diff --git a/src/sphinx/genc.rst b/src/sphinx/genc.rst new file mode 100644 index 0000000000000000000000000000000000000000..faac1f142f843e4014dbac4603784c4fa37ebe16 --- /dev/null +++ b/src/sphinx/genc.rst @@ -0,0 +1,122 @@ +.. _genc: + +Safe C Code +=========== + +Leon can generate from Scala code an equivalent and safe C99 code. Using the verification, repair and +synthesis features of Leon this conversion can be made safely. Additionally, the produced code can be +compiled with any standard-compliant C99 compiler to target the desired hardware architecture +without extra dependencies. + +To convert a Scala program, one can use the ``--genc`` and ``--o=<output.c>`` command line options +of Leon. + +.. NOTE:: + Currently the memory model is limited to stack-allocated memory. Hence, no dynamic allocation + is done using ``malloc`` function family. + + +Supported Features +------------------ + +The supported subset of Scala includes part of the core languages features, as well as some +extensions from :ref:`XLang <xlang>`, while ensuring the same expression execution order in both +languages. + +Currently all type and function definitions need to be included in one top level object. + + +Types +***** + +The following raw types and their corresponding literals are supported: + +.. list-table:: + :header-rows: 1 + + * - Scala + - C99 + * - ``Unit`` + - ``void`` + * - ``Boolean`` + - ``bool`` + * - ``Int`` (32-bit Integer) + - ``int32_t`` + +Tuples +^^^^^^ + +Using ``TupleN[T1, ..., TN]`` results in the creation of a C structure with the same +fields and types for every combination of any supported type ``T1, ..., TN``. The name of the +generated structure will be unique and reflect the sequence of types. + + +Arrays +^^^^^^ + +``Array[T]`` are implemented using regular C array when the array size is known at compile time, or +using Variable Length Array (VLA) when the size is only available at runtime. Both types of array +use the same unique structure type to keep track of the length of the array and its allocated +memory. + +.. NOTE:: + + Arrays live on the stack and therefore cannot be returned by functions. This limitation is + extended to other types having an array as field. + + +Arrays can be created using the companion object, e.g. ``Array(1, 2, 3)``, or using the +``Array.fill`` method, e.g. ``Array.fill(size)(value)``. + + +Case Classes +^^^^^^^^^^^^ + +The support for classes is restricted to non-recursive case classes for which fields are immutable. +Instances of such data-types live on the stack. + + +Functions +********* + +Functions and nested functions are supported, with access to the variables in their respective +scopes. However, higher order functions are as of this moment not supported. + +Since strings of characters are currently not available, to generate an executable program, one has +to define a main function without any argument that returns an integer: ``def main: Int = ...``. + +Both ``val`` and ``var`` are supported with the limitations imposed by the :ref:`XLang <xlang>` +extensions. + + +Constructs +********** + +The idiomatic ``if`` statements such as ``val b = if (x >= 0) true else false`` are converted into +a sequence of equivalent statements. + +Imperative ``while`` loops are also supported. + +Assertions, invariant, pre- and post-conditions are not translated into C99 and are simply ignored. + + +Operators +********* + +The following operators are supported: + +.. list-table:: + :header-rows: 1 + + * - Category + - Operators + * - Boolean operators + - ``&&``, ``||``, ``!``, ``!=``, ``==`` + * - Comparision operators over integers + - ``<``, ``<=``, ``==``, ``!=``, ``>=``, ``>`` + * - Arithmetic operators over integers + - ``+``, ``-`` (unary & binary), ``*``, ``/``, ``%`` + * - Bitwise operators over integers + - ``&``, ``|``, ``^``, ``~``, ``<<``, ``>>`` + + diff --git a/src/sphinx/index.rst b/src/sphinx/index.rst index a20401170d34be2ef293878e72b678aaac462598..cf46d2c300d3821a4cb987d657bded60597ce534 100644 --- a/src/sphinx/index.rst +++ b/src/sphinx/index.rst @@ -25,6 +25,7 @@ Contents: limitations synthesis repair + genc options faq references diff --git a/src/sphinx/options.rst b/src/sphinx/options.rst index 1c929cd29cdc5281378cd322389b867902f0ff2b..a5be01e35269e12864cd6716947727209497b337 100644 --- a/src/sphinx/options.rst +++ b/src/sphinx/options.rst @@ -20,41 +20,46 @@ Choosing which Leon feature to use The first group of options determine which feature of Leon will be used. These options are mutually exclusive (except when noted). By default, ``--verify`` is chosen. -* ``--eval`` - +* ``--eval`` + Evaluates parameterless functions and value definitions. - + * ``--verify`` - + Proves or disproves function contracts, as explained in the :ref:`verification` section. * ``--repair`` - + Runs program :ref:`repair <repair>`. - + * ``--synthesis`` - + Partially synthesizes ``choose()`` constructs (see :ref:`synthesis` section). * ``--termination`` - + Runs termination analysis. Can be used along ``--verify``. -* ``--inferInv`` +* ``--inferInv`` + + Infer invariants from the (instrumented) code (using Orb). - Infer invariants from the (instrumented) code (using Orb) +* ``--instrument`` -* ``--instrument`` + Instrument the code for inferring time/depth/stack bounds (using Orb). - Instrument the code for inferring time/depth/stack bounds (using Orb) +* ``--genc`` + + Translate a Scala program into C99 equivalent code (see :ref:`genc` section); requires + ``--xlang``. * ``--noop`` - + Runs the program through the extraction and preprocessing phases, then outputs it in the specified directory. Used mostly for debugging purposes. * ``--help`` - + Prints a helpful message, then exits. @@ -65,44 +70,46 @@ Additional top-level options These options are available to all Leon components: * ``--debug=d1,d2,...`` - + Enables printing detailed messages for the components d1,d2,... . - Available components are: + Available components are: * ``datagen`` (Data generators) - + * ``eval`` (Evaluators) - + + * ``genc`` (C code generation) + * ``isabelle`` (:ref:`The Isabelle-based solver <isabelle>`) * ``leon`` (The top-level component) - + * ``options`` (Options parsed by Leon) - + * ``positions`` (When printing, attach positions to trees) * ``repair`` (Program repair) - + * ``solver`` (SMT solvers and their wrappers) - + * ``synthesis`` (Program synthesis) - + * ``termination`` (Termination analysis) - + * ``timers`` (Timers, timer pools) - + * ``trees`` (Manipulation of trees) - + * ``types`` (When printing, attach types to expressions) * ``verification`` (Verification) - + * ``xlang`` (Transformation of XLang into Pure Scala programs) * ``--functions=f1,f2,...`` - - Only consider functions f1, f2, ... . This applies to all functionalities + + Only consider functions f1, f2, ... . This applies to all functionalities where Leon manipulates the input in a per-function basis. Leon will match against suffixes of qualified names. For instance: @@ -111,22 +118,22 @@ These options are available to all Leon components: This option supports ``_`` as wildcard: ``--functions=List._`` will match all ``List`` methods. -* ``--solvers=s1,s2,...`` - - Use solvers s1, s2,... . If more than one solver is chosen, all chosen +* ``--solvers=s1,s2,...`` + + Use solvers s1, s2,... . If more than one solver is chosen, all chosen solvers will be used in parallel, and the best result will be presented. By default, the ``fairz3`` solver is picked. - - Some solvers are specialized in proving verification conditions - and will have hard time finding a counterexample in case of an invalid + + Some solvers are specialized in proving verification conditions + and will have hard time finding a counterexample in case of an invalid verification condition, whereas some are specialized in finding counterexamples, and some provide a compromise between the two. Also, some solvers do not as of now support higher-order functions. Available solvers include: - - * ``enum`` - + + * ``enum`` + Uses enumeration-based techniques to discover counterexamples. This solver does not actually invoke an SMT solver, and operates entirely on the level of Leon trees. @@ -134,50 +141,50 @@ These options are available to all Leon components: * ``fairz3`` Native Z3 with z3-templates for unfolding recursive functions (default). - + * ``smt-cvc4`` - - CVC4 through SMT-LIB. An algorithm within Leon takes up the unfolding - of recursive functions, handling of lambdas etc. To use this or any + + CVC4 through SMT-LIB. An algorithm within Leon takes up the unfolding + of recursive functions, handling of lambdas etc. To use this or any of the following CVC4-based solvers, you need to have the ``cvc4`` executable in your system path (the latest unstable version is recommended). - + * ``smt-cvc4-cex`` - + CVC4 through SMT-LIB, in-solver finite-model-finding, for counter-examples only. Recursive functions are not unrolled, but encoded through the ``define-funs-rec`` construct available in the new SMTLIB-2.5 standard. Currently, this solver does not handle higher-order functions. - + * ``smt-cvc4-proof`` - - CVC4 through SMT-LIB, for proofs only. Functions are encoded as in + + CVC4 through SMT-LIB, for proofs only. Functions are encoded as in ``smt-cvc4-cex``. Currently, this solver does not handle higher-order functions. - + * ``smt-z3`` - - Z3 through SMT-LIB. To use this or the next solver, you need to + + Z3 through SMT-LIB. To use this or the next solver, you need to have the ``z3`` executable in your program path (the latest stable version is recommended). Inductive reasoning happens on the Leon side (similarly to ``smt-cvc4``). - + * ``smt-z3-q`` - - Z3 through SMT-LIB, but (recursive) functions are not unrolled and are + + Z3 through SMT-LIB, but (recursive) functions are not unrolled and are instead encoded with universal quantification. For example, ``def foo(x:A) = e`` would be encoded by asserting - + .. math:: - + \forall (x:A). foo(x) = e even if ``e`` contains an invocation to ``foo``. Currently, this solver does not handle higher-order functions. - + * ``unrollz3`` - + Native Z3, but inductive reasoning happens within Leon (similarly to ``smt-z3``). * ``ground`` @@ -187,21 +194,23 @@ These options are available to all Leon components: * ``isabelle`` Solve verification conditions via Isabelle. - + * ``--strict`` - Terminate Leon after each phase if a non-fatal error is encountered + Terminate Leon after each phase if a non-fatal error is encountered (such as a failed verification condition). By default, this option is activated. * ``--timeout=t`` Set a timeout for each attempt to prove one verification condition/ repair one function (in sec.) - + * ``--xlang`` - + Support for additional language constructs described in :ref:`xlang`. - These constructs are desugared into :ref:`purescala` before other operations. + These constructs are desugared into :ref:`purescala` before other operations, + except for the ``--genc`` option which uses the original constructs to generate + :ref:`genc`. Additional Options (by component) --------------------------------- @@ -222,10 +231,12 @@ File Output *********** * ``--o=dir`` - + Output files to the directory ``dir`` (default: leon.out). Used when ``--noop`` is selected. + When used with ``--genc`` this option designates the output *file*. + Code Extraction *************** @@ -245,21 +256,21 @@ Synthesis Shrink non-deterministic programs when tests pruning works well. * ``--cegis:vanuatoo`` - + Generate inputs using new korat-style generator. - + * ``--costmodel=cm`` - + Use a specific cost model for this search. Available: ``Naive``, ``WeightedBranches`` * ``--derivtrees`` - + Generate a derivation tree for every synthesized function. The trees will be output in ``*.dot`` files. * ``--manual=cmd`` - + Override Leon's automated search through the space of programs during synthesis. Instead, the user can navigate the program space manually by choosing which deductive synthesis rules is instantiated each time. @@ -277,26 +288,26 @@ Fair-z3 Solver Double-check counter-examples with evaluator. * ``--codegen`` - + Use compiled evaluator instead of interpreter. * ``--evalground`` - + Use evaluator on functions applied to ground arguments. - + * ``--feelinglucky`` - + Use evaluator to find counter-examples early. * ``--unrollcores`` - + Use unsat-cores to drive unrolling while remaining fair. - + CVC4 Solver *********** * ``--solver:cvc4=<cvc4-opt>`` - + Pass extra command-line arguments to CVC4. Isabelle diff --git a/src/test/resources/regression/genc/invalid/AbsFun.scala b/src/test/resources/regression/genc/invalid/AbsFun.scala new file mode 100644 index 0000000000000000000000000000000000000000..aff9a2c80f70f5e3aef079220c1c36b29b40b2e3 --- /dev/null +++ b/src/test/resources/regression/genc/invalid/AbsFun.scala @@ -0,0 +1,66 @@ +import leon.lang._ + +object AbsFun { + + + def isPositive(a : Array[Int], size : Int) : Boolean = { + require(a.length >= 0 && size <= a.length) + rec(0, a, size) + } + + def rec(i: Int, a: Array[Int], size: Int) : Boolean = { + require(a.length >= 0 && size <= a.length && i >= 0) + + if(i >= size) true + else { + if (a(i) < 0) + false + else + rec(i + 1, a, size) + } + } + + // Returning Arrays is not supported by GenC + def abs(tab: Array[Int]): Array[Int] = { + require(tab.length >= 0) + val t = while0(Array.fill(tab.length)(0), 0, tab) + t._1 + } ensuring(res => isPositive(res, res.length)) + + + def while0(t: Array[Int], k: Int, tab: Array[Int]): (Array[Int], Int) = { + require(tab.length >= 0 && + t.length == tab.length && + k >= 0 && + k <= tab.length && + isPositive(t, k)) + + if(k < tab.length) { + val nt = if(tab(k) < 0) { + t.updated(k, -tab(k)) + } else { + t.updated(k, tab(k)) + } + while0(nt, k+1, tab) + } else { + (t, k) + } + } ensuring(res => + res._2 >= tab.length && + res._1.length == tab.length && + res._2 >= 0 && + res._2 <= tab.length && + isPositive(res._1, res._2)) + + def property(t: Array[Int], k: Int): Boolean = { + require(isPositive(t, k) && t.length >= 0 && k >= 0) + if(k < t.length) { + val nt = if(t(k) < 0) { + t.updated(k, -t(k)) + } else { + t.updated(k, t(k)) + } + isPositive(nt, k+1) + } else true + } holds +} diff --git a/src/test/resources/regression/genc/invalid/LinearSearch.scala b/src/test/resources/regression/genc/invalid/LinearSearch.scala new file mode 100644 index 0000000000000000000000000000000000000000..88cadb0ef7dc5ca5425a3f1661495cb08af4df65 --- /dev/null +++ b/src/test/resources/regression/genc/invalid/LinearSearch.scala @@ -0,0 +1,40 @@ +import leon.lang._ + +/* The calculus of Computation textbook */ + +object LinearSearch { + + def linearSearch(a: Array[Int], c: Int): Boolean = ({ + require(a.length >= 0) + var i = 0 + var found = false + (while(i < a.length) { + if(a(i) == c) + found = true + i = i + 1 + }) invariant( + i <= a.length && + i >= 0 && + (if(found) contains(a, i, c) else !contains(a, i, c)) + ) + found + }) ensuring(res => if(res) contains(a, a.length, c) else !contains(a, a.length, c)) + + def contains(a: Array[Int], size: Int, c: Int): Boolean = { + require(a.length >= 0 && size >= 0 && size <= a.length) + content(a, size).contains(c) + } + + // Set not supported by GenC + def content(a: Array[Int], size: Int): Set[Int] = { + require(a.length >= 0 && size >= 0 && size <= a.length) + var set = Set.empty[Int] + var i = 0 + (while(i < size) { + set = set ++ Set(a(i)) + i = i + 1 + }) invariant(i >= 0 && i <= size) + set + } + +} diff --git a/src/test/resources/regression/genc/unverified/BinarySearch.scala b/src/test/resources/regression/genc/unverified/BinarySearch.scala new file mode 100644 index 0000000000000000000000000000000000000000..aec7d2b5ddabc0132d6f9e885eb18c80617f7dc9 --- /dev/null +++ b/src/test/resources/regression/genc/unverified/BinarySearch.scala @@ -0,0 +1,82 @@ +import leon.lang._ + +/* VSTTE 2008 - Dafny paper */ + +object BinarySearch { + + def binarySearch(a: Array[Int], key: Int): Int = ({ + require(a.length > 0 && sorted(a, 0, a.length - 1)) + var low = 0 + var high = a.length - 1 + var res = -1 + + (while(low <= high && res == -1) { + //val i = (high + low) / 2 + val i = low + ((high - low) / 2) + val v = a(i) + + if(v == key) + res = i + + if(v > key) + high = i - 1 + else if(v < key) + low = i + 1 + }) invariant( + res >= -1 && + res < a.length && + 0 <= low && + low <= high + 1 && + high >= -1 && + high < a.length && + (if (res >= 0) + a(res) == key else + (!occurs(a, 0, low, key) && !occurs(a, high + 1, a.length, key)) + ) + ) + res + }) ensuring(res => + res >= -1 && + res < a.length && + (if(res >= 0) a(res) == key else !occurs(a, 0, a.length, key))) + + + def occurs(a: Array[Int], from: Int, to: Int, key: Int): Boolean = { + require(a.length >= 0 && to <= a.length && from >= 0) + def rec(i: Int): Boolean = { + require(i >= 0) + if(i >= to) + false + else { + if(a(i) == key) true else rec(i+1) + } + } + if(from >= to) + false + else + rec(from) + } + + + def sorted(a: Array[Int], l: Int, u: Int) : Boolean = { + require(a.length >= 0 && l >= 0 && l <= u && u < a.length) + val t = sortedWhile(true, l, l, u, a) + t._1 + } + + def sortedWhile(isSorted: Boolean, k: Int, l: Int, u: Int, a: Array[Int]): (Boolean, Int) = { + require(a.length >= 0 && l >= 0 && l <= u && u < a.length && k >= l && k <= u) + if(k < u) { + sortedWhile(if(a(k) > a(k + 1)) false else isSorted, k + 1, l, u, a) + } else (isSorted, k) + } + + + def main = { + val a = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + val i = binarySearch(a, 2) + i - 2 // i should be 2 + } + +} + diff --git a/src/test/resources/regression/genc/unverified/BinarySearchFun.scala b/src/test/resources/regression/genc/unverified/BinarySearchFun.scala new file mode 100644 index 0000000000000000000000000000000000000000..ebc97f6c6d046e2907bda89c795def7026cb4c8f --- /dev/null +++ b/src/test/resources/regression/genc/unverified/BinarySearchFun.scala @@ -0,0 +1,70 @@ +import leon.lang._ + +object BinarySearchFun { + + def binarySearch(a: Array[Int], key: Int, low: Int, high: Int): Int = ({ + require(a.length > 0 && sorted(a, low, high) && + 0 <= low && low <= high + 1 && high < a.length + ) + + if (low <= high) { + //val i = (high + low) / 2 + val i = low + (high - low) / 2 + + val v = a(i) + + if (v == key) i + else if (v > key) binarySearch(a, key, low, i - 1) + else binarySearch(a, key, i + 1, high) + } else -1 + }) ensuring(res => + res >= -1 && + res < a.length && ( + if (res >= 0) + a(res) == key + else + (high < 0 || (!occurs(a, low, low + (high - low) / 2, key) && + !occurs(a, low + (high - low) / 2, high, key))) + ) + ) + + + def occurs(a: Array[Int], from: Int, to: Int, key: Int): Boolean = { + require(a.length >= 0 && to <= a.length && from >= 0) + def rec(i: Int): Boolean = { + require(i >= 0) + if (i >= to) + false + else { + if (a(i) == key) true else rec(i+1) + } + } + if (from >= to) + false + else + rec(from) + } + + + def sorted(a: Array[Int], l: Int, u: Int) : Boolean = { + require(a.length >= 0 && l >= 0 && l <= u + 1 && u < a.length) + val t = sortedWhile(true, l, l, u, a) + t._1 + } + + def sortedWhile(isSorted: Boolean, k: Int, l: Int, u: Int, a: Array[Int]): (Boolean, Int) = { + require(a.length >= 0 && l >= 0 && l <= u+1 && u < a.length && k >= l && k <= u + 1) + if(k < u) { + sortedWhile(if(a(k) > a(k + 1)) false else isSorted, k + 1, l, u, a) + } else (isSorted, k) + } + + def main = { + val a = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + val i = binarySearch(a, 2, 0, a.length - 1) // should be 2 + val j = binarySearch(a, 11, 0, a.length - 1) // should be -1 + + (i - 2) + (j + 1) // == 0 + } +} + diff --git a/src/test/resources/regression/genc/unverified/MaxSum.scala b/src/test/resources/regression/genc/unverified/MaxSum.scala new file mode 100644 index 0000000000000000000000000000000000000000..033dfed30a6930f4041c60a1c4f8a9dfbb87d934 --- /dev/null +++ b/src/test/resources/regression/genc/unverified/MaxSum.scala @@ -0,0 +1,71 @@ +import leon.lang._ + +/* VSTTE 2010 challenge 1 */ + +object MaxSum { + + def maxSum(a: Array[Int]): (Int, Int) = ({ + require(a.length >= 0 && isPositive(a)) + var sum = 0 + var max = 0 + var i = 0 + (while(i < a.length) { + if(max < a(i)) + max = a(i) + sum = sum + a(i) + i = i + 1 + }) invariant (sum <= i * max && i >= 0 && i <= a.length) + (sum, max) + }) ensuring(res => res._1 <= a.length * res._2) + + + def isPositive(a: Array[Int]): Boolean = { + require(a.length >= 0) + def rec(i: Int): Boolean = { + require(i >= 0) + if(i >= a.length) + true + else { + if(a(i) < 0) + false + else + rec(i+1) + } + } + rec(0) + } + + def summ(to : Int): Int = ({ + require(to >= 0) + var i = 0 + var s = 0 + (while (i < to) { + s = s + i + i = i + 1 + }) invariant (s >= 0 && i >= 0 && s == i*(i-1)/2 && i <= to) + s + }) ensuring(res => res >= 0 && res == to*(to-1)/2) + + + def sumsq(to : Int): Int = ({ + require(to >= 0) + var i = 0 + var s = 0 + (while (i < to) { + s = s + i*i + i = i + 1 + }) invariant (s >= 0 && i >= 0 && s == (i-1)*i*(2*i-1)/6 && i <= to) + s + }) ensuring(res => res >= 0 && res == (to-1)*to*(2*to-1)/6) + + def main = { + val a = Array(1, 4, 6, 0, 234, 999) + val sm = maxSum(a) + val sum = sm._1 + val max = sm._2 + if (sum == 1244 && max == 999) 0 + else -1 + } ensuring { _ == 0 } + +} + diff --git a/src/test/resources/regression/genc/valid/AbsArray.scala b/src/test/resources/regression/genc/valid/AbsArray.scala new file mode 100644 index 0000000000000000000000000000000000000000..581684e7c50d6034e751c152be9afd185e227e73 --- /dev/null +++ b/src/test/resources/regression/genc/valid/AbsArray.scala @@ -0,0 +1,23 @@ +import leon.lang._ + +object AbsArray { + def main = { + val a = Array(0, -1, 2, -3) + + def abs() { + require(a.length < 10000) + + var i = 0; + (while (i < a.length && i < 10000) { + a(i) = if (a(i) < 0) -a(i) else a(i) + i = i + 1 + }) invariant (i >= 0 && i <= 10000) + } + + abs() + + a(0) + a(1) - 1 + a(2) - 2 + a(3) - 3 // == 0 + } ensuring { _ == 0 } +} + + diff --git a/src/test/resources/regression/genc/valid/CaseClass.scala b/src/test/resources/regression/genc/valid/CaseClass.scala new file mode 100644 index 0000000000000000000000000000000000000000..83f5988594336f12e67ca63980334a747fcadbb2 --- /dev/null +++ b/src/test/resources/regression/genc/valid/CaseClass.scala @@ -0,0 +1,20 @@ +import leon.lang._ + +object CaseClass { + + case class Color(r: Int, g: Int, b: Int) + + def red = Color(0, 255, 0) + def cyan = Color(0, 255, 255) + + def sub(c: Color, d: Color) = Color(c.r - d.r, c.g - d.g, c.b - d.b) + + def main = { + val c = red + val d = cyan + val z = sub(c, d).g + z + } ensuring { _ == 0 } + +} + diff --git a/src/test/resources/regression/genc/valid/ExpressionOrder.scala b/src/test/resources/regression/genc/valid/ExpressionOrder.scala new file mode 100644 index 0000000000000000000000000000000000000000..21ea9766dffad887414d56fb3c281934ea429f1d --- /dev/null +++ b/src/test/resources/regression/genc/valid/ExpressionOrder.scala @@ -0,0 +1,153 @@ +import leon.lang._ + +object ExpressionOrder { + case class Pixel(rgb: Int) + case class Matrix(data: Array[Int], w: Int, h: Int) + + def void = () + + def fun = 0xffffff + def foo = 4 + def bar(i: Int) = i * 2 + def baz(i: Int, j: Int) = bar(i) - bar(j) + + def syntaxCheck(i: Int) { + val p = Pixel(fun) + val m = Matrix(Array(0, 1, 2, 3), 2, 2) + + val z = baz(foo, bar(foo)) + val a = Array(0, 1, foo / 2, 3, bar(2), z / 1) + + val t = (true, foo, bar(a(0))) + + val a2 = Array.fill(4)(2) + val a3 = Array.fill(if (i <= 0) 1 else i)(bar(i)) + val b = Array(1, 2, 0) + b(1) = if (bar(b(1)) % 2 == 0) 42 else 58 + + def f1 = (if (i < 0) a else b)(0) + def f2 = (if (i < 0) a else b).length + + //def f3 = (if (i < 0) a else b)(0) = 0 // <- not supported + + val c = (0, true, 2) + val d = (if (i > 0) i else -i, false, 0) + + def f4 = (if (i < 0) d else c)._2 // expression result unused + } + + def main = { + bool2int(test0(false), 1) + + bool2int(test1(42), 2) + + bool2int(test2(58), 4) + + bool2int(test3(false), 8) + + bool2int(test4(false), 16) + + bool2int(test6, 32) + + bool2int(test7, 64) + + bool2int(test8, 128) + } ensuring { _ == 0 } + + def test0(b: Boolean) = { + val f = b && !b // == false + + var c = 0 + + val x = f && { c = 1; true } + + c == 0 + }.holds + + def test1(i: Int) = { + require(i > 0) + + val j = i / i * 3 // == 3 + + var c = 0 + val x = { c = c + 3; j } + { c = c + 1; j } * { c = c * 2; j } + + c == 8 && j == 3 && x == 12 + }.holds + + def test2(i: Int) = { + var c = 0; + val x = if (i < 0) { c = 1; -i } else { c = 2; i } + + if (i < 0) c == 1 + else c == 2 + }.holds + + def test3(b: Boolean) = { + val f = b && !b // == false + + var c = 0 + val x = f || { c = 1; true } || { c = 2; false } + + c == 1 + }.holds + + def test4(b: Boolean) = { + var i = 10 + var c = 0 + + val f = b && !b // == false + val t = b || !b // == true + + // The following condition is executed 11 times, + // and only during the last execution is the last + // operand evaluated + while ({ c = c + 1; t } && i > 0 || { c = c * 2; f }) { + i = i - 1 + } + + i == 0 && c == 22 + }.holds + + def test5(b: Boolean) = { + val f = b && !b // == false + + var c = if (f) 0 else -1 + + c = c + (if (f) 0 else 1) + + c == 0 + }.holds + + def test6 = { + val a = Array(0, 1, 2, 3, 4) + + def rec(b: Boolean, i: Int): Boolean = { + require(i >= 0 && i < 2147483647) // 2^31 - 1 + + if (i + 1 < a.length) rec(if (a(i) < a(i + 1)) b else false, i + 1) + else b + } + + rec(true, 0) + }.holds + + def test7 = { + var c = 1 + + val a = Array(0, 1, 2, 3, 4) + + a(if(a(0) == 0) { c = c + 1; 0 } else { c = c + 2; 1 }) = { c = c * 2; -1 } + + c == 4 + }.holds + + def test8 = { + var x = 0 + + def bar(y: Int) = { + def fun(z: Int) = 1 * x * (y + z) + + fun(3) + } + + bar(2) == 0 + }.holds + + def bool2int(b: Boolean, f: Int) = if (b) 0 else f; +} + + diff --git a/src/test/resources/regression/genc/valid/IntegralColor.scala b/src/test/resources/regression/genc/valid/IntegralColor.scala new file mode 100644 index 0000000000000000000000000000000000000000..c64cb0fa18ac395dad37b2a8a8ffa7fa187872a2 --- /dev/null +++ b/src/test/resources/regression/genc/valid/IntegralColor.scala @@ -0,0 +1,175 @@ +import leon.lang._ + +object IntegralColor { + + def isValidComponent(x: Int) = x >= 0 && x <= 255 + + def getRed(rgb: Int): Int = { + (rgb & 0x00FF0000) >> 16 + } ensuring isValidComponent _ + + def getGreen(rgb: Int): Int = { + (rgb & 0x0000FF00) >> 8 + } ensuring isValidComponent _ + + def getBlue(rgb: Int): Int = { + rgb & 0x000000FF + } ensuring isValidComponent _ + + def getGray(rgb: Int): Int = { + (getRed(rgb) + getGreen(rgb) + getBlue(rgb)) / 3 + } ensuring isValidComponent _ + + def testColorSinglePixel: Boolean = { + val color = 0x20C0FF + + 32 == getRed(color) && 192 == getGreen(color) && + 255 == getBlue(color) && 159 == getGray(color) + }.holds + + case class Color(r: Int, g: Int, b: Int) + + def getColor(rgb: Int) = Color(getRed(rgb), getGreen(rgb), getBlue(rgb)) + + /* + *case class Image(width: Int, height: Int, buffer: Array[Int]) { + * // currently not enforced: + * require(width <= 1000 && height <= 1000 && buffer.length == width * height) + *} + */ + + def matches(value: Array[Int], expected: Array[Int]): Boolean = { + require(value.length == expected.length) + + var test = true + var idx = 0 + (while (idx < value.length) { + test = test && value(idx) == expected(idx) + idx = idx + 1 + }) invariant { idx >= 0 && idx <= value.length } + + test + } + + def testColorWholeImage: Boolean = { + val WIDTH = 2 + val HEIGHT = 2 + + /* + *val source = Image(WIDTH, HEIGHT, Array(0x20c0ff, 0x123456, 0xffffff, 0x000000)) + *val expected = Image(WIDTH, HEIGHT, Array(159, 52, 255, 0)) // gray convertion + *val gray = Image(WIDTH, HEIGHT, Array.fill(4)(0)) + */ + + val source = Array(0x20c0ff, 0x123456, 0xffffff, 0x000000) + val expected = Array(159, 52, 255, 0) // gray convertion + val gray = Array.fill(4)(0) + + // NOTE: Cannot define a toGray function as XLang doesn't allow mutating + // arguments and GenC doesn't allow returning arrays + + var idx = 0 + (while (idx < WIDTH * HEIGHT) { + gray(idx) = getGray(source(idx)) + idx = idx + 1 + }) invariant { idx >= 0 && idx <= WIDTH * HEIGHT && gray.length == WIDTH * HEIGHT } + // NB: the last invariant is very important -- without it the verification times out + + matches(gray, expected) + }.holds + + // Only for square kernels + case class Kernel(size: Int, buffer: Array[Int]) + + def isKernelValid(kernel: Kernel): Boolean = + kernel.size > 0 && kernel.size < 1000 && kernel.size % 2 == 1 && + kernel.buffer.length == kernel.size * kernel.size + + def applyFilter(gray: Array[Int], size: Int, idx: Int, kernel: Kernel): Int = { + require(size > 0 && size < 1000 && + gray.length == size * size && + idx >= 0 && idx < gray.length && + isKernelValid(kernel)) + + def up(x: Int): Int = { + if (x < 0) 0 else x + } ensuring { _ >= 0 } + + def down(x: Int): Int = { + if (x >= size) size - 1 else x + } ensuring { _ < size } + + def fix(x: Int): Int = { + down(up(x)) + } ensuring { res => res >= 0 && res < size } + + def at(row: Int, col: Int): Int = { + val r = fix(row) + val c = fix(col) + + gray(r * size + c) + } + + val mid = kernel.size / 2 + + val i = idx / size + val j = idx % size + + var res = 0 + var p = -mid + (while (p <= mid) { + var q = -mid + + (while (q <= mid) { + val krow = p + mid + val kcol = q + mid + + assert(krow >= 0 && krow < kernel.size) + assert(kcol >= 0 && kcol < kernel.size) + + val kidx = krow * kernel.size + kcol + + res += at(i + p, j + q) * kernel.buffer(kidx) + + q = q + 1 + }) invariant { q >= -mid && q <= mid + 1 } + + p = p + 1 + }) invariant { p >= -mid && p <= mid + 1 } + + res + } + + def testFilterConvolutionSmooth: Boolean = { + val gray = Array(127, 255, 51, 0) + val expected = Array(124, 158, 76, 73) + val size = 2 // grey is size x size + + // NOTE: Cannot define a `smoothed` function as XLang doesn't allow mutating + // arguments and GenC doesn't allow returning arrays + + val kernel = Kernel(3, Array(1, 1, 1, + 1, 2, 1, + 1, 1, 1)) + + val smoothed = Array.fill(gray.length)(0) + assert(smoothed.length == expected.length) + + var idx = 0; + (while (idx < smoothed.length) { + smoothed(idx) = applyFilter(gray, size, idx, kernel) / 10 + idx = idx + 1 + }) invariant { idx >= 0 && idx <= smoothed.length && smoothed.length == gray.length } + + matches(smoothed, expected) + }.holds + + + def main: Int = { + if (testColorSinglePixel && testColorWholeImage && testFilterConvolutionSmooth) 0 + else 1 + } ensuring { _ == 0 } + +} + + diff --git a/src/test/resources/regression/genc/valid/RecursionAndNestedFunctions.scala b/src/test/resources/regression/genc/valid/RecursionAndNestedFunctions.scala new file mode 100644 index 0000000000000000000000000000000000000000..25f69538a19412bd8ae8267446d4eb5f05f6247b --- /dev/null +++ b/src/test/resources/regression/genc/valid/RecursionAndNestedFunctions.scala @@ -0,0 +1,37 @@ +import leon.lang._ + +object RecursionAndNestedFunctions { + + // Complex way to return i + def zzz(i: Int): Int = { + val x = 0 + + def rec(j: Int): Int = { + if (i - x == j) i + else if (j > i) rec(j - 1) + else rec(j + 1) + } ensuring { _ == i } + + rec(4) + } ensuring { _ == i } + + + // Complex way to compute 100 + 2 * i + def foo(i: Int) = { + var j = i + def bar(x: Int) = { + //j = j - 1 <- not supported by leon + val y = x + i + def baz(z: Int) = z + y + i + //j = j + 1 <- not supported by leon + baz(42) + } + bar(58) + j - i + } ensuring { _ == 100 + 2 * i } + + def main() = { + foo(2) - zzz(104) + } ensuring { _ == 0 } + +} + diff --git a/src/test/resources/regression/genc/valid/TupleArray.scala b/src/test/resources/regression/genc/valid/TupleArray.scala new file mode 100644 index 0000000000000000000000000000000000000000..1f2354e0613430fb95a995da6a741dda3939b143 --- /dev/null +++ b/src/test/resources/regression/genc/valid/TupleArray.scala @@ -0,0 +1,25 @@ +import leon.lang._ + +object TupleArray { + def exists(av: (Array[Int], Int)): Boolean = { + require(av._1.length < 10000) + + var i = 0 + var found = false + (while (!found && i < av._1.length) { + found = av._1(i) == av._2 + i = i + 1 + }) invariant (i >= 0 && i < 10000) + found + } + + def main = { + val a = Array(0, 1, 5, -5, 9) + val e1 = exists((a, 0)) + val e2 = exists((a, -1)) + if (e1 && !e2) 0 + else -1 + } ensuring { _ == 0 } + +} + diff --git a/src/test/resources/regression/termination/valid/QuickSort.scala b/src/test/resources/regression/termination/valid/QuickSort.scala index e356bdc2a2b8a1c412e90a3033ae269faf6d7df0..d4fb11ea68a7e8c96f056754c24be2e9a5f9ef4e 100644 --- a/src/test/resources/regression/termination/valid/QuickSort.scala +++ b/src/test/resources/regression/termination/valid/QuickSort.scala @@ -1,5 +1,6 @@ /* Copyright 2009-2015 EPFL, Lausanne */ +import leon.annotation._ import leon.lang._ object QuickSort { @@ -29,7 +30,7 @@ object QuickSort { case Nil() => bList case _ => rev_append(reverse(aList),bList) } - + def greater(n:Int,list:List) : List = list match { case Nil() => Nil() case Cons(x,xs) => if (n < x) Cons(x,greater(n,xs)) else greater(n,xs) @@ -51,6 +52,7 @@ object QuickSort { case Cons(x,xs) => append(append(quickSort(smaller(x,xs)),Cons(x,equals(x,xs))),quickSort(greater(x,xs))) }) ensuring(res => contents(res) == contents(list)) // && is_sorted(res)) + @ignore def main(args: Array[String]): Unit = { val ls: List = Cons(5, Cons(2, Cons(4, Cons(5, Cons(1, Cons(8,Nil())))))) diff --git a/src/test/resources/regression/termination/valid/SimpInterpret.scala b/src/test/resources/regression/termination/valid/SimpInterpret.scala index 76fcd4145509440741b521bf26fa97dcc9af2dd9..c5255506dbb0b20308a0210ea3268a9432abaa14 100644 --- a/src/test/resources/regression/termination/valid/SimpInterpret.scala +++ b/src/test/resources/regression/termination/valid/SimpInterpret.scala @@ -1,15 +1,15 @@ /* Copyright 2009-2015 EPFL, Lausanne */ -//import leon.annotation._ +import leon.annotation._ import leon.lang._ object Interpret { - abstract class BoolTree + abstract class BoolTree case class Eq(t1 : IntTree, t2 : IntTree) extends BoolTree case class And(t1 : BoolTree, t2 : BoolTree) extends BoolTree case class Not(t : BoolTree) extends BoolTree - abstract class IntTree + abstract class IntTree case class Const(c:Int) extends IntTree case class Var() extends IntTree case class Plus(t1 : IntTree, t2 : IntTree) extends IntTree @@ -22,7 +22,7 @@ object Interpret { } def beval(t:BoolTree, x0 : Int) : Boolean = { - t match { + t match { case Less(t1, t2) => ieval(t1,x0) < ieval(t2,x0) case Eq(t1, t2) => ieval(t1,x0) == ieval(t2,x0) case And(t1, t2) => beval(t1,x0) && beval(t2,x0) @@ -62,6 +62,7 @@ object Interpret { !treeBad(If(Less(Const(0),Var()), Var(), Minus(Const(0),Var()))) }.holds + @ignore def main(args : Array[String]) { thereIsGoodTree() } diff --git a/src/test/resources/regression/verification/newsolvers/valid/InsertionSort.scala b/src/test/resources/regression/verification/newsolvers/valid/InsertionSort.scala index 05889fcb726ff88728959172f2127128831802c2..3252c9acf942d682f3cef10cc358cb7c837b44bb 100644 --- a/src/test/resources/regression/verification/newsolvers/valid/InsertionSort.scala +++ b/src/test/resources/regression/verification/newsolvers/valid/InsertionSort.scala @@ -34,7 +34,7 @@ object InsertionSort { case Nil() => true case Cons(x, Nil()) => true case Cons(x, Cons(y, ys)) => x <= y && isSorted(Cons(y, ys)) - } + } /* Inserting element 'e' into a sorted list 'l' produces a sorted list with * the expected content and size */ @@ -43,8 +43,8 @@ object InsertionSort { l match { case Nil() => Cons(e,Nil()) case Cons(x,xs) => if (x <= e) Cons(x,sortedIns(e, xs)) else Cons(e, l) - } - } ensuring(res => contents(res) == contents(l) ++ Set(e) + } + } ensuring(res => contents(res) == contents(l) ++ Set(e) && isSorted(res) && size(res) == size(l) + 1 ) @@ -54,11 +54,12 @@ object InsertionSort { def sort(l: List): List = (l match { case Nil() => Nil() case Cons(x,xs) => sortedIns(x, sort(xs)) - }) ensuring(res => contents(res) == contents(l) + }) ensuring(res => contents(res) == contents(l) && isSorted(res) && size(res) == size(l) ) + @ignore def main(args: Array[String]): Unit = { val ls: List = Cons(5, Cons(2, Cons(4, Cons(5, Cons(1, Cons(8,Nil())))))) diff --git a/src/test/resources/regression/verification/purescala/valid/InsertionSort.scala b/src/test/resources/regression/verification/purescala/valid/InsertionSort.scala index 7c89f364600e95edb8c0f266cb5e22f65d1adbea..0ec78ff0d3050b6333e3fd1e229dd57f2e72cea2 100644 --- a/src/test/resources/regression/verification/purescala/valid/InsertionSort.scala +++ b/src/test/resources/regression/verification/purescala/valid/InsertionSort.scala @@ -34,7 +34,7 @@ object InsertionSort { case Nil() => true case Cons(x, Nil()) => true case Cons(x, Cons(y, ys)) => x <= y && isSorted(Cons(y, ys)) - } + } /* Inserting element 'e' into a sorted list 'l' produces a sorted list with * the expected content and size */ @@ -43,8 +43,8 @@ object InsertionSort { l match { case Nil() => Cons(e,Nil()) case Cons(x,xs) => if (x <= e) Cons(x,sortedIns(e, xs)) else Cons(e, l) - } - } ensuring(res => contents(res) == contents(l) ++ Set(e) + } + } ensuring(res => contents(res) == contents(l) ++ Set(e) && isSorted(res) && size(res) == size(l) + 1 ) @@ -54,11 +54,12 @@ object InsertionSort { def sort(l: List): List = (l match { case Nil() => Nil() case Cons(x,xs) => sortedIns(x, sort(xs)) - }) ensuring(res => contents(res) == contents(l) + }) ensuring(res => contents(res) == contents(l) && isSorted(res) && size(res) == size(l) ) + @ignore def main(args: Array[String]): Unit = { val ls: List = Cons(5, Cons(2, Cons(4, Cons(5, Cons(1, Cons(8,Nil())))))) diff --git a/src/test/scala/leon/genc/GenCSuite.scala b/src/test/scala/leon/genc/GenCSuite.scala new file mode 100644 index 0000000000000000000000000000000000000000..df6d30e5c0869cb6f05ba08195fab4ed73593d89 --- /dev/null +++ b/src/test/scala/leon/genc/GenCSuite.scala @@ -0,0 +1,219 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package genc + +import leon.test.LeonRegressionSuite + +import leon.frontends.scalac.ExtractionPhase +import leon.regression.verification.XLangVerificationSuite +import leon.purescala.Definitions.Program +import leon.utils.{ PreprocessingPhase, UniqueCounter } + +import scala.concurrent._ +import ExecutionContext.Implicits.global +import scala.sys.process._ + +import org.scalatest.{ Args, Status } + +import java.io.ByteArrayInputStream +import java.nio.file.{ Files, Path } + +class GenCSuite extends LeonRegressionSuite { + + private val testDir = "regression/genc/" + private lazy val tmpDir = Files.createTempDirectory("genc") + private val ccflags = "-std=c99 -g -O0" + private val maxExecutionTime = 2 // seconds + + private val counter = new UniqueCounter[Unit] + counter.nextGlobal // Start with 1 + + private case class ExtendedContext(leon: LeonContext, tmpDir: Path, progName: String) + + // Tests are run as follows: + // - before mkTest is run, all valid test are verified using XLangVerificationSuite + // - The classic ExtractionPhase & PreprocessingPhase are run on all input files + // (this way the libraries are evaluated only once) + // - A Program is constructed for each input file + // - At this point no error should have occurred or something would be wrong + // with the extraction phases + // - For each Program P: + // + if P is expected to be convertible to C, then we make sure that: + // * the GenerateCPhase run without trouble, + // * the generated C code compiles using a C99 compiler without error, + // * and that, when run, the exit code is 0 + // + if P is expected to be non-convertible to C, then we make sure that: + // * the GenerateCPhase fails + private def mkTest(files: List[String], cat: String)(block: (ExtendedContext, Program) => Unit) = { + val extraction = + ExtractionPhase andThen + new PreprocessingPhase(true, true) + + val ctx = createLeonContext(files:_*) + + try { + val (_, ast) = extraction.run(ctx, files) + + val programs = { + val (user, lib) = ast.units partition { _.isMainUnit } + user map ( u => Program(u :: lib) ) + } + + for { prog <- programs } { + val name = prog.units.head.id.name + val ctx = createLeonContext(s"--o=$tmpDir/$name.c") + val xCtx = ExtendedContext(ctx, tmpDir, name) + + val displayName = s"$cat/$name.scala" + val index = counter.nextGlobal + + test(f"$index%3d: $displayName") { + block(xCtx, prog) + } + } + } catch { + case fe: LeonFatalError => + test("Compilation") { + fail(ctx, "Unexpected fatal error while setting up tests", fe) + } + } + } + + // Run a process with a timeout and return the status code + private def runProcess(pb: ProcessBuilder): Int = runProcess(pb.run) + private def runProcess(p: Process): Int = { + val f = Future(blocking(p.exitValue())) + try { + Await.result(f, duration.Duration(maxExecutionTime, "sec")) + } catch { + case _: TimeoutException => + p.destroy() + throw LeonFatalError("timeout reached") + } + } + + // Determine which C compiler is available + private def detectCompiler: Option[String] = { + val testCode = "int main() { return 0; }" + val testBinary = s"$tmpDir/test" + + // NOTE this code might print error on stderr when a non-existing compiler + // is used. It seems that even with a special ProcessLogger the RuntimeException + // is printed for some reason. + + def testCompiler(cc: String): Boolean = try { + def input = new ByteArrayInputStream(testCode.getBytes()) + val process = s"$cc $ccflags -o $testBinary -xc -" #< input #&& s"$testBinary" + runProcess(process) == 0 + } catch { + case _: java.lang.RuntimeException => false + } + + val knownCompiler = "cc" :: "clang" :: "gcc" :: "mingw" :: Nil + // Note that VS is not in the list as we cannot specify C99 dialect + + knownCompiler find testCompiler + } + + private def convert(xCtx: ExtendedContext)(prog: Program) = { + try { + GenerateCPhase(xCtx.leon, prog) + } catch { + case fe: LeonFatalError => + fail(xCtx.leon, "Convertion to C unexpectedly failed", fe) + } + } + + private def saveToFile(xCtx: ExtendedContext)(cprog: CAST.Prog) = { + CFileOutputPhase(xCtx.leon, cprog) + } + + private def compile(xCtx: ExtendedContext, cc: String)(unused: Unit) = { + val basename = s"${xCtx.tmpDir}/${xCtx.progName}" + val sourceFile = s"$basename.c" + val compiledProg = basename + + val process = Process(s"$cc $ccflags $sourceFile -o $compiledProg") + val status = runProcess(process) + + assert(status == 0, "Compilation of converted program failed") + } + + private def evaluate(xCtx: ExtendedContext)(unused: Unit) = { + val compiledProg = s"${xCtx.tmpDir}/${xCtx.progName}" + + // TODO memory limit + val process = Process(compiledProg) + + val status = runProcess(process) + assert(status == 0, s"Evaluation of converted program failed with status [$status]") + } + + private def forEachFileIn(cat: String)(block: (ExtendedContext, Program) => Unit) { + val fs = filesInResourceDir(testDir + cat, _.endsWith(".scala")).toList + + fs foreach { file => + assert(file.exists && file.isFile && file.canRead, + s"Benchmark ${file.getName} is not a readable file") + } + + val files = fs map { _.getPath } + + mkTest(files, cat)(block) + } + + protected def testDirectory(cc: String, dir: String) = forEachFileIn(dir) { (xCtx, prog) => + val converter = convert(xCtx) _ + val saver = saveToFile(xCtx) _ + val compiler = compile(xCtx, cc) _ + val evaluator = evaluate(xCtx) _ + + val pipeline = converter andThen saver andThen compiler andThen evaluator + + pipeline(prog) + } + + protected def testValid(cc: String) = testDirectory(cc, "valid") + protected def testUnverified(cc: String) = testDirectory(cc, "unverified"); + + protected def testInvalid() = forEachFileIn("invalid") { (xCtx, prog) => + intercept[LeonFatalError] { + GenerateCPhase(xCtx.leon, prog) + } + } + + class AltVerificationSuite(override val testDir: String) extends XLangVerificationSuite { + override def testAll() = testValid() // Test only the valid ones + + override def suiteName = "Verification Suite For GenC" + + // Add a timeout for the verification + override val optionVariants = List(List("--solvers=smt-z3,ground")) + } + + // Run verification suite as a nested suite + override def nestedSuites = { + // Use our test dir and not the one from XLangVerificationSuite + scala.collection.immutable.IndexedSeq(new AltVerificationSuite(testDir)) + } + + protected def testAll() = { + // Set C compiler according to the platform we're currently running on + detectCompiler match { + case Some(cc) => + testValid(cc) + testUnverified(cc) + case None => + test("dectecting C compiler") { fail("no C compiler found") } + } + + testInvalid() + } + + override def run(testName: Option[String], args: Args): Status = { + testAll() + super.run(testName, args) + } +} + diff --git a/src/test/scala/leon/regression/verification/NewSolversSuite.scala b/src/test/scala/leon/regression/verification/NewSolversSuite.scala index 15e02a693988e44be555c5e954be65cab4203086..5a340d1f870e019f33460fb6e386c50e58fbcbff 100644 --- a/src/test/scala/leon/regression/verification/NewSolversSuite.scala +++ b/src/test/scala/leon/regression/verification/NewSolversSuite.scala @@ -5,6 +5,7 @@ package leon.regression.verification import _root_.smtlib.interpreters._ import leon._ import leon.verification.VerificationPhase +import leon.solvers.SolverFactory /* @EK: Disabled for now as many tests fail class NewSolversSuite extends VerificationSuite { @@ -14,21 +15,9 @@ class NewSolversSuite extends VerificationSuite { val pipeBack = AnalysisPhase val optionVariants: List[List[String]] = { - val isCVC4Available = try { - CVC4Interpreter.buildDefault.free() - true - } catch { - case e: java.io.IOException => - false - } - - val isZ3Available = try { - Z3Interpreter.buildDefault.free() - true - } catch { - case e: java.io.IOException => - false - } + val isCVC4Available = SolverFactory.hasCVC4 + + val isZ3Available = SolverFactory.hasZ3 ( if (isCVC4Available) diff --git a/src/test/scala/leon/regression/verification/VerificationSuite.scala b/src/test/scala/leon/regression/verification/VerificationSuite.scala index b23fb0c4e3516121a1489752ed89dcb0835ee2d2..f2ae97880694baac498b94570f81beb1c21c422f 100644 --- a/src/test/scala/leon/regression/verification/VerificationSuite.scala +++ b/src/test/scala/leon/regression/verification/VerificationSuite.scala @@ -81,7 +81,7 @@ trait VerificationSuite extends LeonRegressionSuite { private[verification] def forEachFileIn(cat: String)(block: Output => Unit) { val fs = filesInResourceDir(testDir + cat, _.endsWith(".scala")).toList - fs foreach { file => + fs foreach { file => assert(file.exists && file.isFile && file.canRead, s"Benchmark ${file.getName} is not a readable file") } diff --git a/src/test/scala/leon/regression/verification/XLangVerificationSuite.scala b/src/test/scala/leon/regression/verification/XLangVerificationSuite.scala index 8511ebfea95d4d6a412d82922ffb1e6fb870d312..6fe7b07408500929063b4f57f24e1147a6cfce35 100644 --- a/src/test/scala/leon/regression/verification/XLangVerificationSuite.scala +++ b/src/test/scala/leon/regression/verification/XLangVerificationSuite.scala @@ -3,19 +3,14 @@ package leon.regression.verification import smtlib.interpreters.Z3Interpreter +import leon.solvers.SolverFactory // If you add another regression test, make sure it contains exactly one object, whose name matches the file name. // This is because we compile all tests from each folder together. class XLangVerificationSuite extends VerificationSuite { val optionVariants: List[List[String]] = { - val isZ3Available = try { - Z3Interpreter.buildDefault.free() - true - } catch { - case e: java.io.IOException => - false - } + val isZ3Available = SolverFactory.hasZ3 List( List(), @@ -30,3 +25,4 @@ class XLangVerificationSuite extends VerificationSuite { val testDir: String = "regression/verification/xlang/" override val desugarXLang = true } + diff --git a/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala b/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala index 569eb95df2f7f66a05d7a825765adb6123b4d854..6c450c1c8613f74c5a6679aaaf6035f59e593e66 100644 --- a/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala +++ b/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala @@ -4,6 +4,7 @@ package leon.regression.verification package purescala import smtlib.interpreters.{CVC4Interpreter, Z3Interpreter} +import leon.solvers.SolverFactory // If you add another regression test, make sure it contains one object whose name matches the file name // This is because we compile all tests from each folder together. @@ -11,21 +12,9 @@ abstract class PureScalaVerificationSuite extends VerificationSuite { val testDir = "regression/verification/purescala/" - val isZ3Available = try { - Z3Interpreter.buildDefault.free() - true - } catch { - case e: java.io.IOException => - false - } + val isZ3Available = SolverFactory.hasZ3 - val isCVC4Available = try { - CVC4Interpreter.buildDefault.free() - true - } catch { - case e: java.io.IOException => - false - } + val isCVC4Available = SolverFactory.hasCVC4 val opts: List[List[String]] = { List( diff --git a/src/test/scala/leon/unit/utils/GraphsSuite.scala b/src/test/scala/leon/unit/utils/GraphsSuite.scala new file mode 100644 index 0000000000000000000000000000000000000000..984a427fafa5632aacacd2d667f5f1d0dec7a435 --- /dev/null +++ b/src/test/scala/leon/unit/utils/GraphsSuite.scala @@ -0,0 +1,275 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon.unit.utils + +import leon.test._ +import leon.purescala.Common._ +import leon.utils.Graphs._ + +class GraphsSuite extends LeonTestSuite { + abstract class Node + case object A extends Node + case object B extends Node + case object C extends Node + case object D extends Node + case object E extends Node + case object F extends Node + case object G extends Node + case object H extends Node + + import scala.language.implicitConversions + + implicit def tToEdge(t: (Node, Node)): SimpleEdge[Node] = { + SimpleEdge(t._1, t._2) + } + + def g1 = { + var g = new DiGraph[Node, SimpleEdge[Node]]() + + g ++= Set(A, B, C, D) + + g += A -> B + g += B -> A + + g += B -> C + + g += C -> D + g += D -> C + + // A D + // ↕ ↕ + // B → C + + g + } + + def g2 = { + var g = new DiGraph[Node, SimpleEdge[Node]]() + + g ++= Set(A, B, C, D) + + g += A -> B + g += B -> B + g += B -> C + g += C -> D + + // A → B → C → D + // ↻ + + g + } + + def g3 = { + var g = new DiGraph[Node, SimpleEdge[Node]]() + + g ++= Set(A, B, C, D, E, F, G, H) + + g += A -> B + g += B -> C + g += C -> D + + g += E -> A + g += A -> F + g += B -> F + g += F -> E + g += F -> G + + g += C -> G + g += G -> C + g += H -> G + + + // A → B → C → D + // ↑↘ ↓ ↕ + // E ← F → G ← H + + g + } + + def g4 = { + var g = new DiGraph[Node, SimpleEdge[Node]]() + + g ++= Set(A, B, C, D, E, F, G, H) + + g += A -> B + g += B -> C + g += C -> D + + g += A -> F + g += B -> F + g += F -> E + g += F -> G + + g += C -> G + g += H -> G + + + // A → B → C → D + // ↘ ↓ ↓ + // E ← F → G ← H + + g + } + + def g5 = { + var g = new DiGraph[Node, SimpleEdge[Node]]() + + g ++= Set(A, B, C, D, E, F, G, H) + + g += A -> B + g += B -> C + + g += A -> D + g += D -> E + + + // A → B → C F G H + // ↘ + // D → E + + g + } + + + test("Graphs basic 1") { ctx => + val g = g1 + assert(g.N.size === 4) + assert(g.E.size === 5) + + assert(g.outEdges(A) === Set(SimpleEdge(A, B))) + assert(g.outEdges(B) === Set(SimpleEdge(B, A), SimpleEdge(B, C))) + + assert(g.inEdges(B) === Set(SimpleEdge(A, B))) + assert(g.inEdges(C) === Set(SimpleEdge(B, C), SimpleEdge(D, C))) + + assert(g.edgesBetween(A, B).size === 1) + } + + test("Graphs sinks/sources 1") { ctx => + val g = g1 + + assert(g.sources == Set()) + assert(g.sinks == Set()) + } + + test("Graphs sinks/sources 2") { ctx => + val g = g2 + + assert(g.N.size === 4) + assert(g.E.size === 4) + + assert(g.sources == Set(A)) + assert(g.sinks == Set(D)) + } + + test("Graphs SCC 1") { ctx => + val g = g1 + + val gs = g.stronglyConnectedComponents + + assert(gs.N.size === 2) + assert(gs.E.size === 1) + } + + test("Graphs SCC 2") { ctx => + val g = g2 + + val gs = g.stronglyConnectedComponents + + assert(gs.N.size === 4) + assert(gs.E.size === 3) + } + + test("Graphs SCC 3") { ctx => + val g = g3 + + val gs = g.stronglyConnectedComponents + + assert(gs.N === Set(Set(A, B, E, F), Set(C, G), Set(D), Set(H))) + } + + def assertBefore[T](s: Seq[T])(n1: T, n2: T) { + assert(s.indexOf(n1) < s.indexOf(n2), s"Node '$n1' should be before '$n2'"); + } + + test("Graphs top-sort 1") { ctx => + val g = g4 + + + val seq = g.topSort + + val before = assertBefore(seq)_ + + before(A, B) + before(B, F) + before(A, C) + } + + test("Graphs top-sort 2 (cyclic graph fails)") { ctx => + val g = g1 + + intercept[IllegalArgumentException] { + g.topSort + } + } + + test("Graphs top-sort 3 (SCC is acyclic)") { ctx => + val g = g3 + + val gs = g.stronglyConnectedComponents + + val c1: Set[Node] = Set(A, B, E, F) + val c2: Set[Node] = Set(C, G) + val c3: Set[Node] = Set(D) + val c4: Set[Node] = Set(H) + + + val ns = gs.topSort + + val before = assertBefore(ns)_ + before(c1, c2) + before(c2, c3) + before(c4, c2) + } + + test("Graphs DFS") { ctx => + val g = g5 + + var visited = List[Node]() + g.depthFirstSearch(A) { n => + visited ::= n + } + visited = visited.reverse + + def isBefore(a: Node, b: Node) = visited.indexOf(a) < visited.indexOf(b) + + assert(!isBefore(B, D) || isBefore(C, E)) + assert(!isBefore(D, B) || isBefore(E, C)) + } + + test("Graphs BFS") { ctx => + val g = g5 + + var visited = List[Node]() + g.breadthFirstSearch(A) { n => + visited ::= n + } + visited = visited.reverse + + def isBefore(a: Node, b: Node) = visited.indexOf(a) < visited.indexOf(b) + + assert(isBefore(B, E)) + assert(isBefore(D, C)) + } + + test("Graphs pred/succ 1") { ctx => + val g = g2 + + assert(g.succ(B) == Set(B, C)) + assert(g.pred(B) == Set(B, A)) + + assert(g.transitiveSucc(B) == Set(B, C, D)) + assert(g.transitivePred(B) == Set(A, B)) + assert(g.transitivePred(C) == Set(A, B)) + } +} diff --git a/testcases/repair/DaysToYears/DaysToYears.scala b/testcases/repair/DaysToYears/DaysToYears.scala index bb6c518b3b743ea02d0188c8a9b1a802e6685e16..3a71a49602ff9d818df2cf2e1d2476a78f9e0b3a 100644 --- a/testcases/repair/DaysToYears/DaysToYears.scala +++ b/testcases/repair/DaysToYears/DaysToYears.scala @@ -1,10 +1,11 @@ +import leon.annotation._ import leon.lang._ object DaysToYears { val base : Int = 1980 - + def isLeapYear(y : Int): Boolean = y % 4 == 0 - + def daysToYears(days : Int): Int = { require(days > 0) daysToYears1(base, days)._1 @@ -17,16 +18,17 @@ object DaysToYears { else if (days > 365 && !isLeapYear(year)) daysToYears1(year + 1, days - 365) else (year, days) - } ensuring { res => + } ensuring { res => res._2 <= 366 && - res._2 > 0 && + res._2 > 0 && res._1 >= base && - (((year,days), res) passes { + (((year,days), res) passes { case (1980, 366 ) => (1980, 366) case (1980, 1000) => (1982, 269) }) - } + } + @ignore def main(args : Array[String]) = { println(daysToYears1(base, 10593 )) println(daysToYears1(base, 366 )) diff --git a/testcases/repair/DaysToYears/DaysToYears1.scala b/testcases/repair/DaysToYears/DaysToYears1.scala index 13eec09ae4ee1a611fc5a7d916ed92e061086112..1ef2d337989c7dc1a718637777d6e97726d7a276 100644 --- a/testcases/repair/DaysToYears/DaysToYears1.scala +++ b/testcases/repair/DaysToYears/DaysToYears1.scala @@ -1,10 +1,11 @@ +import leon.annotation._ import leon.lang._ object DaysToYears { val base : Int = 1980 - + def isLeapYear(y : Int): Boolean = y % 4 == 0 - + def daysToYears(days : Int): Int = { require(days > 0) daysToYears1(base, days)._1 @@ -17,17 +18,18 @@ object DaysToYears { else if (days > 365 && !isLeapYear(year)) daysToYears1(year, days - 365) // FIXME forgot +1 else (year, days) - } ensuring { res => + } ensuring { res => res._2 <= 366 && - res._2 > 0 && + res._2 > 0 && res._1 >= base && - (((year,days), res) passes { + (((year,days), res) passes { case (1999, 14 ) => (1999, 14) case (1980, 366) => (1980, 366) case (1981, 366) => (1982, 1) }) - } + } + @ignore def main(args : Array[String]) = { println(daysToYears1(base, 10593 )) println(daysToYears1(base, 366 )) diff --git a/testcases/repair/DaysToYears/DaysToYears2.scala b/testcases/repair/DaysToYears/DaysToYears2.scala index de0d31a707bda925004cca4dd3af1784995d0051..40396f05c6c3e40c4cfd0beccee02ed7d4c59553 100644 --- a/testcases/repair/DaysToYears/DaysToYears2.scala +++ b/testcases/repair/DaysToYears/DaysToYears2.scala @@ -1,10 +1,11 @@ +import leon.annotation._ import leon.lang._ object DaysToYears { val base : Int = 1980 - + def isLeapYear(y : Int): Boolean = y % 4 == 0 - + def daysToYears(days : Int): Int = { require(days > 0) daysToYears1(base, days)._1 @@ -16,17 +17,18 @@ object DaysToYears { daysToYears1(year + 1, days - 366) // TODO this branch cannot be solved although it is correct because it depends on the erroneous branch else if (days > 365 && !isLeapYear(year)) daysToYears1(year + 1, days - 365) - else (year + 1, days) // FIXME +1 - } ensuring { res => + else (year + 1, days) // FIXME +1 + } ensuring { res => res._2 <= 366 && - res._2 > 0 && + res._2 > 0 && res._1 >= base && - (((year,days), res) passes { + (((year,days), res) passes { case (1980, 366 ) => (1980, 366) case (1980, 1000) => (1982, 269) }) - } + } + @ignore def main(args : Array[String]) = { println(daysToYears1(base, 10593 )) println(daysToYears1(base, 366 )) diff --git a/testcases/runtime/SquareRoot.scala b/testcases/runtime/SquareRoot.scala index 87b3e985a6209da18606c28ca189440043b6e25d..95bdf52f6c9e0967da3af474ba70cadc30e9cc45 100644 --- a/testcases/runtime/SquareRoot.scala +++ b/testcases/runtime/SquareRoot.scala @@ -1,3 +1,4 @@ +import leon.annotation._ import leon.lang._ import leon.lang.synthesis._ @@ -23,6 +24,7 @@ object SquareRoot { } } + @ignore def main(a: Array[String]) { isqrt2(42) } diff --git a/testcases/synthesis/condabd/benchmarks/BatchedQueue/BatchedQueue.scala b/testcases/synthesis/condabd/benchmarks/BatchedQueue/BatchedQueue.scala index 64a8ae73ddd8553ff4ca364814aa880c41b503f8..3cf66229cb37d2c00e41ab63d0fbe4fb28aa4945 100644 --- a/testcases/synthesis/condabd/benchmarks/BatchedQueue/BatchedQueue.scala +++ b/testcases/synthesis/condabd/benchmarks/BatchedQueue/BatchedQueue.scala @@ -1,3 +1,4 @@ +import leon.annotation._ import leon.lang._ import leon.collection._ @@ -50,7 +51,7 @@ object BatchedQueue { case Cons(_, xs) => checkf(xs, p.r) } } - // + // // def last(p: Queue): Int = { // require(!isEmpty(p)) // p.r match { @@ -66,6 +67,7 @@ object BatchedQueue { (if (isEmpty(p)) true else content(tail(res)) ++ Set(x) == content(tail(res)))) + @ignore def main(args: Array[String]): Unit = { val pair = Queue(Cons(4, Nil), Cons(3, Nil)) diff --git a/testcases/verification/case-studies/Robot3po.scala b/testcases/verification/case-studies/Robot3po.scala index 56709fc5555175ede38fac4f00ec6e772ecb9d7d..b189db5e98852b61f9463232d1036a5fa519a8aa 100644 --- a/testcases/verification/case-studies/Robot3po.scala +++ b/testcases/verification/case-studies/Robot3po.scala @@ -365,7 +365,7 @@ object Robot { } def validState(rs: RobotState)(implicit w: World): Boolean = { - + // 6) Sensors have consistent data val recentData = rs.ns.validData && rs.hs.validData @@ -509,6 +509,7 @@ object Robot { } + @ignore def main(a: Array[String]): Unit = { val map0 = """|XXXXXXXXX |XR FF X diff --git a/testcases/verification/compilation/Interpreter.scala b/testcases/verification/compilation/Interpreter.scala index 40dcab73cdb00b0b65dd960ca3e2427e4fa503c7..1c8e3bbde2b6dc18ba42211f2bfc498a0bcfaf71 100644 --- a/testcases/verification/compilation/Interpreter.scala +++ b/testcases/verification/compilation/Interpreter.scala @@ -1,12 +1,13 @@ +import leon.annotation._ import leon.lang._ object Interpret { - abstract class BoolTree + abstract class BoolTree case class Eq(t1 : IntTree, t2 : IntTree) extends BoolTree case class And(t1 : BoolTree, t2 : BoolTree) extends BoolTree case class Not(t : BoolTree) extends BoolTree - abstract class IntTree + abstract class IntTree case class Const(c:Int) extends IntTree case class Var(index:Int) extends IntTree case class Plus(t1 : IntTree, t2 : IntTree) extends IntTree @@ -19,7 +20,7 @@ object Interpret { } def beval(t:BoolTree, x0 : Int) : Boolean = { - t match { + t match { case Less(t1, t2) => ieval(t1,x0) < ieval(t2,x0) case Eq(t1, t2) => ieval(t1,x0) == ieval(t2,x0) case And(t1, t2) => beval(t1,x0) && beval(t2,x0) @@ -56,6 +57,7 @@ object Interpret { !treeBad(If(Less(Const(0),Var(0)), Var(0), Minus(Const(0),Var(0)))) } holds + @ignore def main(args : Array[String]) { thereIsGoodTree() } diff --git a/testcases/verification/compilation/SimpInterpret.scala b/testcases/verification/compilation/SimpInterpret.scala index fab5035897d86b4b587715cb4d7ef3459c1fed2f..6c61bad49173ecc0ff9edafc432d11eb6ffbbadb 100644 --- a/testcases/verification/compilation/SimpInterpret.scala +++ b/testcases/verification/compilation/SimpInterpret.scala @@ -1,5 +1,5 @@ //import scala.collection.immutable.Set -//import leon.annotation._ +import leon.annotation._ import leon.lang._ object Interpret { @@ -61,6 +61,7 @@ object Interpret { !treeBad(If(Less(Const(0),Var()), Var(), Minus(Const(0),Var()))) } holds + @ignore def main(args : Array[String]) { thereIsGoodTree() } diff --git a/testcases/verification/graph/MST/MSTMap.scala b/testcases/verification/graph/MST/MSTMap.scala index 1ef72da321d96c6b65977427c2aff485212c990e..e718256b6d226dea77efddadc970ffc44c7dab0f 100644 --- a/testcases/verification/graph/MST/MSTMap.scala +++ b/testcases/verification/graph/MST/MSTMap.scala @@ -15,16 +15,16 @@ object MSTMap { def mst(g : Graph) : Map[(Int,Int), Int] = { require(invariant(g) && isUndirected(g) && isConnected(g) && g.nVertices <= 3) - + var uf_map = Map.empty[Int, Int] //map to represent parent //relationship to model union find var spanningTree = Map.empty[(Int,Int), Int] var alreadyTested = Map.empty[(Int, Int), Int] - + (while(!isConnected(spanningTree, g.nVertices)) { val next_edge = getSmallestEdge(g.nVertices, g.edges, alreadyTested) - + val p1 = uFind(next_edge._1, uf_map) val p2 = uFind(next_edge._2, uf_map) if(p1 != p2) { @@ -45,18 +45,18 @@ object MSTMap { // We only verify that the edge set returned corresponds to some // spanning tree, but not that it is of minimal weight. - + // Here, we always take the smallest edge regardless whether it // creates a cycle or not, // Leon loops def mstBogus(g : Graph) : Map[(Int,Int), Int] = { require(invariant(g) && isUndirected(g) && isConnected(g) && g.nVertices <= 4) - + var edges = g.edges var spanningTree = Map.empty[(Int,Int), Int] var alreadyTested = Map.empty[(Int, Int), Int] - + (while(!isConnected(spanningTree, g.nVertices)) { val next_edge = getSmallestEdge(g.nVertices, edges, alreadyTested) @@ -73,13 +73,13 @@ object MSTMap { spanningTree } ensuring(x => isAcyclic(x, g.nVertices) && isConnected(x, g.nVertices)) - + /* ----------------------------------------------------------------------------- GRAPH FUNCTIONS ----------------------------------------------------------------------------- */ - + def invariant(g : Graph) = { def noSelfLoops(i : Int) : Boolean = { @@ -89,10 +89,10 @@ object MSTMap { else noSelfLoops(i+1) } - + g.nVertices >= 0 && noSelfLoops(0) } - + /** Tests, if g is undirected, that is if for every edge (i,j) there is also and edge (j,i) @@ -108,11 +108,11 @@ object MSTMap { var j = 0 while(j < g.nVertices && res) { res = !edgeSet.isDefinedAt(i,j) || edgeSet.isDefinedAt(j,i) - + //If weights should considered if(res && edgeSet.isDefinedAt(i,j)) res = edgeSet(i,j) == edgeSet(j,i) - + j += 1 } i += 1 @@ -136,11 +136,11 @@ object MSTMap { it this function would always return a valid edge, however, this property is not easily expressible in Leon. */ - + var i = 0 val big = 100000000 var res = (-1,-1,big) - + while(i < nVertices) { var j = 0 while(j < nVertices) { @@ -171,7 +171,7 @@ object MSTMap { require(g.nVertices >= 0 && isUndirected(g)) isConnected(g.edges, g.nVertices) } - + def isConnected(edges : Map[(Int,Int), Int], nVertices : Int) : Boolean = { require(nVertices >= 0) val uf = calculateUF(edges, nVertices)._1 @@ -204,7 +204,7 @@ object MSTMap { def calculateUF(edgeSet : Map[(Int,Int), Int], nVertices : Int) : (Map[Int, Int], Boolean)= { require(nVertices >= 0) - + var i = 0 var uf = Map.empty[Int, Int] var cycle = false @@ -223,7 +223,7 @@ object MSTMap { //"remove" twin edge edges = edges.updated((j,i), -1) - } + } } j += 1 } @@ -232,7 +232,7 @@ object MSTMap { (uf, cycle) } - + /* ************************************* Union find *************************************** */ @@ -246,7 +246,7 @@ object MSTMap { // naive union val p1 = uFind(e1,s) val p2 = uFind(e2, s) - + if(p1 != p2) //naive union s.updated(p1, p2) //only union if theiy are really in different trees @@ -257,7 +257,8 @@ object MSTMap { // fsc -d classes -classpath // ../../leon-2.0/target/scala-2.9.1-1/classes MST.scala - // scala -classpath classes MST + // scala -classpath classes MST + @ignore def main(args: Array[String]) { val edges = Map((1,0) -> 10, (0,1) -> 10, (1,2) -> 12, (2,1) -> 12, (0,2) -> 18, (2,0) -> 18, (3,1) -> 20, (1,3) -> 20) @@ -267,5 +268,5 @@ object MSTMap { println(mst(g)); //works println(mstBogus(g)); // crashes because postcondition doensn't hold } - + } diff --git a/testcases/verification/graph/dijkstras/DijkstrasSortedList.scala b/testcases/verification/graph/dijkstras/DijkstrasSortedList.scala index 4ac5d863e880a484a00a8fef939964607320e6db..b79751c692cf8ebcd7c2f24a85804dc210568a50 100644 --- a/testcases/verification/graph/dijkstras/DijkstrasSortedList.scala +++ b/testcases/verification/graph/dijkstras/DijkstrasSortedList.scala @@ -11,14 +11,14 @@ object DijkstrasSortedList { * Graph representation and algorithms **************************************************************************/ case class Graph(nVertices : Int, edges : Map[(Int,Int), Int]) - + // Disallow self edges? Not actually important since they can be ignored. def invariant(g : Graph) = g.nVertices >= 0 - + // true if x & y have same number of nodes and y has all x's edges def isSubGraph(x : Graph, y : Graph) : Boolean = { require(invariant(x) && invariant(y)) - + var ret : Boolean = (x.nVertices == y.nVertices) if (ret){ var i = 0 @@ -34,11 +34,11 @@ object DijkstrasSortedList { } ret } - + // true if every edge has a weight of at least 0 def nonnegativeWeights(g : Graph) : Boolean = { require(invariant(g)) - + var ret : Boolean = true var i = 0 while(i<g.nVertices) { @@ -54,20 +54,20 @@ object DijkstrasSortedList { } ret } - + // true if b is reachable from a def isReachable(g : Graph, a : Int, b : Int) : Boolean = { require(invariant(g) && a >= 0 && a < g.nVertices && b >= 0 && b < g.nVertices) - + //TODO false } - - + + /*************************************************************************** * Sorted list representation and algorithims **************************************************************************/ - + /** A list containing node, dist pairs. * * Sorted by distance, nearest first. Distance must be non-negative. Node @@ -76,7 +76,7 @@ object DijkstrasSortedList { abstract class SortedNDList case class ListNode(node : Int, dist : Int, next : SortedNDList) extends SortedNDList case class ListEnd() extends SortedNDList - + /** List invariant (see description above) */ @induct def sndInvariant(list : SortedNDList) : Boolean = { @@ -91,18 +91,18 @@ object DijkstrasSortedList { false case ListEnd() => true //len <= 3 } - + invRec(list/*, Set.empty, 0, 0*/) } - + /** Look for node in list and remove, if it exists. */ @induct def removeFromList(list : SortedNDList, node : Int) : SortedNDList ={ // something about this times out require(sndInvariant(list)) - + //println("removeFromList: "+list) - + list match { case ListNode(n,d,next) => if (n == node) @@ -113,7 +113,7 @@ object DijkstrasSortedList { case ListEnd() => list } } ensuring(sndInvariant(_)) // something about this generates an error: is the precondition not checked for _all_ elements or something? - + /** Return true if list contains node */ @induct def listContains(list : SortedNDList, node : Int) : Boolean ={ @@ -126,14 +126,14 @@ object DijkstrasSortedList { case ListEnd() => false } } - + /** Add a new node to the list, such that list remains sorted by distance. * Assume node is not already in list. */ @induct def addSorted(list : SortedNDList, node : Int, dist : Int) : SortedNDList = { // something to do with this times out require(sndInvariant(list) && !listContains(list, node) && node >= 0 && dist >= 0) - + list match { case ListNode(n,d,next) => if (d > dist) // insert here @@ -144,37 +144,37 @@ object DijkstrasSortedList { ListNode(node, dist, list) } } ensuring (sndInvariant(_)) // something to do with this times out - + /** Update node with distance minimum of dist and current value. Add if not * in list, and maintain sorted order. */ @induct def updateDistInList(list : SortedNDList, node : Int, dist : Int) : SortedNDList = { require(sndInvariant(list) && node >= 0 && dist >= 0) - + val listRemoved = removeFromList(list, node) addSorted(listRemoved, node, dist) } ensuring(sndInvariant(_)) - - + + /*************************************************************************** * Dijkstra's algorithm **************************************************************************/ - + def isNodeNotB(list : SortedNDList, b : Int) : Boolean = list match { case ListNode(n, _, _) => n!=b case ListEnd() => false } - + // common precondition: g is a valid graph and a and b are valid nodes def bounds(g : Graph, a : Int, b : Int) : Boolean = invariant(g) && 0 <= a && a < g.nVertices && 0 <= b && b < g.nVertices - + // find the shortest path from a to b in g, and return its distance // return -1 if the two aren't connected def shortestPath(g : Graph, a : Int, b : Int) : Int = { require(bounds(g,a,b) && nonnegativeWeights(g)) - + // We should always have at least some node if we haven't reached b (so // long as b is in the graph and connected to a). @induct @@ -184,20 +184,20 @@ object DijkstrasSortedList { * We should check same invariant at function exit. But there's no point: * it's too much for Leon to reason about at once! */ - + list match { case ListNode(node, dist, next) if (node==b) => list case ListNode(node, dist, next) => var n = 0 var tail : SortedNDList = next - + (while (n < g.nVertices){ if (n != node && !visited.contains(n) && g.edges.isDefinedAt((node,n))) tail = updateDistInList(tail, n, dist+g.edges((node,n))) n = n + 1 }) invariant(sndInvariant(list) && n >= 0 && n <= g.nVertices) - + spVisit (tail, visited ++ Set(node)) case ListEnd() => list @@ -206,7 +206,7 @@ object DijkstrasSortedList { case ListEnd() => !isReachable(g,a,b) case ListNode(node,_,_) => node==b }) - + // We start from a, which has distance 0. All other nodes implicitly have // infinite distance. val startingList : SortedNDList = ListNode(a, 0, ListEnd()) @@ -221,12 +221,13 @@ object DijkstrasSortedList { -1 } } ensuring (res => res >= -1 /*(if (isReachable(g,a,b)) res>=0 else res== -1)*/) - + + @ignore def main(args: Array[String]) { val spanningTreeE = Map((0,1) -> 1, (0,2) -> 2, (2,3) -> 5, (0,3) -> 10, (3,2) -> 0) val spanningTree = Graph(4, spanningTreeE) val singleGraph = Graph(1, Map.empty) - + println(spanningTree) println("from 0 to 3 (should be 7): "+shortestPath(spanningTree,0,3)) println("from 3 to 1 (no path): "+shortestPath(spanningTree,3,1)) diff --git a/testcases/verification/list-algorithms/InsertionSort.scala b/testcases/verification/list-algorithms/InsertionSort.scala index 35062e263c1a9b869bf2c34a25196ae4fbce3811..980927204dad1e609e93dd8dcf8a364d027e113d 100644 --- a/testcases/verification/list-algorithms/InsertionSort.scala +++ b/testcases/verification/list-algorithms/InsertionSort.scala @@ -51,7 +51,7 @@ object InsertionSort { case Nil() => true case Cons(x, Nil()) => true case Cons(x, Cons(y, ys)) => x <= y && isSorted(Cons(y, ys)) - } + } /* Inserting element 'e' into a sorted list 'l' produces a sorted list with * the expected content and size */ @@ -60,8 +60,8 @@ object InsertionSort { l match { case Nil() => Cons(e,Nil()) case Cons(x,xs) => if (x <= e) Cons(x,sortedIns(e, xs)) else Cons(e, l) - } - } ensuring(res => contents(res) == contents(l) ++ Set(e) + } + } ensuring(res => contents(res) == contents(l) ++ Set(e) && isSorted(res) && size(res) == size(l) + 1 ) @@ -73,8 +73,8 @@ object InsertionSort { l match { case Nil() => Cons(e,Nil()) case Cons(x,xs) => if (x <= e) Cons(x,buggySortedIns(e, xs)) else Cons(e, l) - } - } ensuring(res => contents(res) == contents(l) ++ Set(e) + } + } ensuring(res => contents(res) == contents(l) ++ Set(e) && isSorted(res) && size(res) == size(l) + 1 ) @@ -84,11 +84,12 @@ object InsertionSort { def sort(l: List): List = (l match { case Nil() => Nil() case Cons(x,xs) => sortedIns(x, sort(xs)) - }) ensuring(res => contents(res) == contents(l) + }) ensuring(res => contents(res) == contents(l) && isSorted(res) && size(res) == size(l) ) + @ignore def main(args: Array[String]): Unit = { val ls: List = Cons(5, Cons(2, Cons(4, Cons(5, Cons(1, Cons(8,Nil())))))) diff --git a/testcases/verification/list-algorithms/MergeSort.scala b/testcases/verification/list-algorithms/MergeSort.scala index 1bbd7f607528a9549363dec4d3e73f547f602565..5fc80d117c9eae3bddec01377683eac54f0c37cd 100644 --- a/testcases/verification/list-algorithms/MergeSort.scala +++ b/testcases/verification/list-algorithms/MergeSort.scala @@ -1,4 +1,5 @@ import leon.lang._ +import leon.annotation._ object MergeSort { sealed abstract class List @@ -57,6 +58,7 @@ object MergeSort { }) ensuring(res => contents(res) == contents(list) && is_sorted(res)) + @ignore def main(args: Array[String]): Unit = { val ls: List = Cons(5, Cons(2, Cons(4, Cons(5, Cons(1, Cons(8,Nil())))))) println(ls) diff --git a/testcases/verification/list-algorithms/QuickSort.scala b/testcases/verification/list-algorithms/QuickSort.scala index 30b3621438bd0ef944b61f831b36379deb1470f6..1afbc3a2b4d7c5f5a54560a6aa401d396646390c 100644 --- a/testcases/verification/list-algorithms/QuickSort.scala +++ b/testcases/verification/list-algorithms/QuickSort.scala @@ -53,7 +53,7 @@ object QuickSort { case Nil() => bList case _ => rev_append(reverse(aList),bList) }) ensuring(content(_) == content(aList) ++ content(bList)) - + def greater(n:Int,list:List) : List = list match { case Nil() => Nil() case Cons(x,xs) => if (n < x) Cons(x,greater(n,xs)) else greater(n,xs) @@ -69,7 +69,7 @@ object QuickSort { case Nil() => Nil() case Cons(x,xs) => if (n>x) Cons(x,smaller(n,xs)) else smaller(n,xs) } - + @induct def smallerProp(n: Int, list: List): Boolean = (max(smaller(n, list)) match { case Some(m) => n > m @@ -117,6 +117,7 @@ object QuickSort { case Cons(x,xs) => append(append(quickSort(smaller(x,xs)),Cons(x,equals(x,xs))),quickSort(greater(x,xs))) }) ensuring(res => content(res) == content(list)) // && isSorted(res)) + @ignore def main(args: Array[String]): Unit = { val ls: List = Cons(5, Cons(2, Cons(4, Cons(5, Cons(1, Cons(8,Nil())))))) diff --git a/testcases/verification/math/Prime.scala b/testcases/verification/math/Prime.scala index d8de43f86cf8ec2528e54125dc42148f4cc49e84..1e928dcff5af0eb281f2b192bdd0a88a4cbe0fb5 100644 --- a/testcases/verification/math/Prime.scala +++ b/testcases/verification/math/Prime.scala @@ -1,3 +1,5 @@ +import leon.annotation._ + object Prime { // an attempt at defining isPrime in PureScala... @@ -36,6 +38,7 @@ object Prime { } ensuring(res => !res) // Just for testing. + @ignore def main(args : Array[String]) : Unit = { def test(n : BigInt) : Unit = { println("Is " + n + " prime ? -> " + isPrime(n)) diff --git a/testcases/verification/xlang/InsertionSort.scala b/testcases/verification/xlang/InsertionSort.scala index 0a53cb55f4ef32b775646e1ea43a8c74b33a80c7..71dab126cda6927380c9395b39dcc49f3a550801 100644 --- a/testcases/verification/xlang/InsertionSort.scala +++ b/testcases/verification/xlang/InsertionSort.scala @@ -32,7 +32,7 @@ object InsertionSort { case Nil() => true case Cons(x, Nil()) => true case Cons(x, Cons(y, ys)) => x <= y && isSorted(Cons(y, ys)) - } + } def isReversedSorted(l: List): Boolean = l match { case Nil() => true case Cons(x, Nil()) => true @@ -66,8 +66,8 @@ object InsertionSort { l match { case Nil() => Cons(e,Nil()) case Cons(x,xs) => if (x <= e) Cons(x,sortedIns(e, xs)) else Cons(e, l) - } - } ensuring(res => contents(res) == contents(l) ++ Set(e) + } + } ensuring(res => contents(res) == contents(l) ++ Set(e) && isSorted(res) && size(res) == size(l) + 1 ) @@ -79,8 +79,8 @@ object InsertionSort { l match { case Nil() => Cons(e,Nil()) case Cons(x,xs) => if (x <= e) Cons(x,buggySortedIns(e, xs)) else Cons(e, l) - } - } ensuring(res => contents(res) == contents(l) ++ Set(e) + } + } ensuring(res => contents(res) == contents(l) ++ Set(e) && isSorted(res) && size(res) == size(l) + 1 ) @@ -90,11 +90,12 @@ object InsertionSort { def sort(l: List): List = (l match { case Nil() => Nil() case Cons(x,xs) => sortedIns(x, sort(xs)) - }) ensuring(res => contents(res) == contents(l) + }) ensuring(res => contents(res) == contents(l) && isSorted(res) && size(res) == size(l) ) + @ignore def main(args: Array[String]): Unit = { val ls: List = Cons(5, Cons(2, Cons(4, Cons(5, Cons(1, Cons(8,Nil())))))) diff --git a/testcases/web/demos/008_Tutorial-Robot.scala b/testcases/web/demos/008_Tutorial-Robot.scala index c78dc15f2c48703bae470cd41578c7b141d7d9ee..bca1f367a26df843e65cea76de7029652f0b4906 100644 --- a/testcases/web/demos/008_Tutorial-Robot.scala +++ b/testcases/web/demos/008_Tutorial-Robot.scala @@ -365,7 +365,7 @@ object Robot { } def validState(rs: RobotState)(implicit w: World): Boolean = { - + // 6) Sensors have consistent data val recentData = rs.ns.validData && rs.hs.validData @@ -509,6 +509,7 @@ object Robot { } + @ignore def main(a: Array[String]): Unit = { val map0 = """|XXXXXXXXX |XR FF X diff --git a/testcases/web/verification/03_Insertion_Sort.scala b/testcases/web/verification/03_Insertion_Sort.scala index e53b89b1732bed1dfb1c1389feacc70d4490285d..4b1c8ef7cd0b61ab0c2f91533598be792dd1b809 100644 --- a/testcases/web/verification/03_Insertion_Sort.scala +++ b/testcases/web/verification/03_Insertion_Sort.scala @@ -24,7 +24,7 @@ object InsertionSort { case Nil => true case Cons(x, Nil) => true case Cons(x, Cons(y, ys)) => x <= y && isSorted(Cons(y, ys)) - } + } /* Inserting element 'e' into a sorted list 'l' produces a sorted list with * the expected content and size */ @@ -33,8 +33,8 @@ object InsertionSort { l match { case Nil => Cons(e,Nil) case Cons(x,xs) => if (x <= e) Cons(x,sortedIns(e, xs)) else Cons(e, l) - } - } ensuring(res => contents(res) == contents(l) ++ Set(e) + } + } ensuring(res => contents(res) == contents(l) ++ Set(e) && isSorted(res) && size(res) == size(l) + 1 ) @@ -44,8 +44,8 @@ object InsertionSort { l match { case Nil => Cons(e,Nil) case Cons(x,xs) => if (x <= e) Cons(x,buggySortedIns(e, xs)) else Cons(e, l) - } - } ensuring(res => contents(res) == contents(l) ++ Set(e) + } + } ensuring(res => contents(res) == contents(l) ++ Set(e) && isSorted(res) && size(res) == size(l) + 1 ) @@ -55,7 +55,7 @@ object InsertionSort { def sort(l: List): List = (l match { case Nil => Nil case Cons(x,xs) => sortedIns(x, sort(xs)) - }) ensuring(res => contents(res) == contents(l) + }) ensuring(res => contents(res) == contents(l) && isSorted(res) && size(res) == size(l) ) @@ -69,6 +69,7 @@ object InsertionSort { } } ensuring(res => contents(res) == contents(l1) ++ contents(l2) && isSorted(res)) + @ignore def main(args: Array[String]): Unit = { val ls: List = Cons(5, Cons(2, Cons(4, Cons(5, Cons(1, Cons(8,Nil))))))