From 3d1a47cfeea9a22533339969938d3858d68b4837 Mon Sep 17 00:00:00 2001 From: Manos Koukoutos <emmanouil.koukoutos@epfl.ch> Date: Fri, 28 Aug 2015 12:01:23 +0200 Subject: [PATCH] Type FunDef identifiers during CodeExtraction, and some API improvements --- .../frontends/scalac/CodeExtraction.scala | 14 +- .../scala/leon/purescala/Definitions.scala | 34 ++-- .../scala/leon/purescala/Expressions.scala | 161 +++++++++++------- 3 files changed, 132 insertions(+), 77 deletions(-) diff --git a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala index 9f9db555e..f39b29472 100644 --- a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala +++ b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala @@ -640,7 +640,15 @@ trait CodeExtraction extends ASTExtractors { val returnType = leonType(sym.info.finalResultType)(nctx, sym.pos) - val id = overridenOrFresh(sym, within) + // @mk: We type the identifiers of methods during code extraction because + // a possible implementing/overriding field will use this same Identifier + val idType = { + val argTypes = newParams map { _.getType } + if (argTypes.nonEmpty) FunctionType(argTypes, returnType) + else returnType + } + + val id = overridenOrFresh(sym, within, idType) val fd = new FunDef(id.setPos(sym.pos), tparamsDef, returnType, newParams) @@ -663,7 +671,9 @@ trait CodeExtraction extends ASTExtractors { val returnType = leonType(sym.info.finalResultType)(nctx, sym.pos) - val id = overridenOrFresh(sym, within) + // @mk: We type the identifiers of methods during code extraction because + // a possible implementing/overriding field will use this same Identifier + val id = overridenOrFresh(sym, within, returnType) val fd = new FunDef(id.setPos(sym.pos), Seq(), returnType, Seq()) fd.setPos(sym.pos) diff --git a/src/main/scala/leon/purescala/Definitions.scala b/src/main/scala/leon/purescala/Definitions.scala index 94c59a85b..cd1948c26 100644 --- a/src/main/scala/leon/purescala/Definitions.scala +++ b/src/main/scala/leon/purescala/Definitions.scala @@ -40,11 +40,13 @@ object Definitions { } } - /** - * A ValDef declares a new identifier to be of a certain type. - * The optional tpe, if present, overrides the type of the underlying Identifier id - * This is useful to instantiate argument types of polymorphic functions - */ + /** A ValDef represents a parameter of a [[purescala.Definitions.FunDef function]] or + * a [[purescala.Definitions.CaseClassDef case class]]. + * + * The optional [[tpe]], if present, overrides the type of the underlying Identifier [[id]]. + * This is useful to instantiate argument types of polymorphic classes. To be consistent, + * never use the type of [[id]] directly; use [[ValDef#getType]] instead. + */ case class ValDef(id: Identifier, tpe: Option[TypeTree] = None) extends Definition with Typed { self: Serializable => @@ -54,8 +56,11 @@ object Definitions { def subDefinitions = Seq() - // Warning: the variable will not have the same type as the ValDef, but - // the Identifier type is enough for all use cases in Leon + /** Transform this [[ValDef]] into a [[Expressions.Variable Variable]] + * + * Warning: the variable will not have the same type as this ValDef, but currently + * the Identifier type is enough for all uses in Leon. + */ def toVariable : Variable = Variable(id) } @@ -327,13 +332,18 @@ object Definitions { // Is inlined case object IsInlined extends FunctionFlag - /** Functions - * This class represents methods or fields of objects. By "fields" we mean - * fields defined in the body, not the constructor arguments of a case class. + /** Function/method definition. + * + * This class represents methods or fields of objects or classes. By "fields" we mean + * fields defined in the body of a class/object, not the constructor arguments of a case class + * (those are accessible through [[leon.purescala.Definitions.ClassDef.fields]]). + * * When it comes to verification, all are treated the same (as functions). * They are only differentiated when it comes to code generation/ pretty printing. - * By default, the FunDef represents a function/method, unless otherwise specified - * by its flags. + * By default, the FunDef represents a function/method as opposed to a field, + * unless otherwise specified by its flags. + * + * Bear in mind that [[id]] will not be consistently typed. */ class FunDef( val id: Identifier, diff --git a/src/main/scala/leon/purescala/Expressions.scala b/src/main/scala/leon/purescala/Expressions.scala index f25810f3c..96881669c 100644 --- a/src/main/scala/leon/purescala/Expressions.scala +++ b/src/main/scala/leon/purescala/Expressions.scala @@ -27,8 +27,8 @@ import ExprOps.replaceFromIDs * simplify the trees they produce. * * @define encodingof Encoding of - * @define note_bitvector (32-bit vector) - * @define note_real (Real) + * @define noteBitvector (32-bit vector) + * @define noteReal (Real) */ object Expressions { @@ -43,14 +43,20 @@ object Expressions { } } + /** Represents an expression in Leon. */ abstract class Expr extends Tree with Typed + /** Trait which gets mixed-in to expressions without subexpressions */ trait Terminal { self: Expr => } - /** Stands for an undefined Expr, similar to ??? or null */ + /** Stands for an undefined Expr, similar to `???` or `null` + * + * During code generation, it gets compiled to `null`, or the 0 of the + * respective type for value types. + */ case class NoTree(tpe: TypeTree) extends Expr with Terminal { val getType = tpe } @@ -129,13 +135,11 @@ object Expressions { /** $encodingof `val ... = ...; ...` - * - * If you are not sure about the requirement you should use - * [[purescala.Constructors#let purescala's constructor let]] or [[purescala.Constructors#letTuple purescala's constructor letTuple]] - * + * * @param binder The identifier used in body, defined just after '''val''' * @param value The value assigned to the identifier, after the '''=''' sign * @param body The expression following the ``val ... = ... ;`` construct + * @see [[purescala.Constructors#let purescala's constructor let]] */ case class Let(binder: Identifier, value: Expr, body: Expr) extends Expr { val getType = { @@ -198,7 +202,7 @@ object Expressions { /* Higher-order Functions */ - /** $encodingof `callee(args...)` */ + /** $encodingof `callee(args...)`, where [[callee]] is an expression of a function type (not a method) */ case class Application(callee: Expr, args: Seq[Expr]) extends Expr { val getType = callee.getType match { case FunctionType(from, to) => @@ -236,7 +240,7 @@ object Expressions { /** $encodingof `... match { ... }` * - * If `cases` might be empty you should use + * '''cases''' should be nonempty. If you are not sure about this, you should use * [[purescala.Constructors#matchExpr purescala's constructor matchExpr]] * * @param scrutinee Expression to the left of the '''match''' keyword @@ -247,7 +251,7 @@ object Expressions { val getType = leastUpperBound(cases.map(_.rhs.getType)).getOrElse(Untyped).unveilUntyped } - /** $encodingof `case ... [if ...] => ... ` + /** $encodingof `case pattern [if optGuard] => rhs` * * @param pattern The pattern just to the right of the '''case''' keyword * @param optGuard An optional if-condition just to the left of the `=>` @@ -275,21 +279,33 @@ object Expressions { }}.copiedFrom(this) } - /** Pattern encoding `case a: ct` making sure an identifier is of the given type */ - case class InstanceOfPattern(binder: Option[Identifier], ct: ClassType) extends Pattern { // c: Class + /** Pattern encoding `case binder: ct` + * + * If [[binder]] is empty, consider a wildcard `_` in its place. + */ + case class InstanceOfPattern(binder: Option[Identifier], ct: ClassType) extends Pattern { val subPatterns = Seq() } - /** Pattern encoding `case binder @ _ => ` with optional identifier `binder` */ + /** Pattern encoding `case _ => `, or `case binder => ` if identifier [[binder]] is present */ case class WildcardPattern(binder: Option[Identifier]) extends Pattern { // c @ _ val subPatterns = Seq() } - /** Pattern encoding `case CaseClass(...) =>` */ + /** Pattern encoding `case binder @ ct(subPatterns...) =>` + * + * If [[binder]] is empty, consider a wildcard `_` in its place. + */ case class CaseClassPattern(binder: Option[Identifier], ct: CaseClassType, subPatterns: Seq[Pattern]) extends Pattern - /** Pattern encoding tuple pattern `case (...) =>` */ + /** Pattern encoding tuple pattern `case binder @ (subPatterns...) =>` + * + * If [[binder]] is empty, consider a wildcard `_` in its place. + */ case class TuplePattern(binder: Option[Identifier], subPatterns: Seq[Pattern]) extends Pattern - /** Pattern encoding like `case 0 => ...` or `case "Foo" => ...` */ + /** Pattern encoding like `case binder @ 0 => ...` or `case binder @ "Foo" => ...` + * + * If [[binder]] is empty, consider a wildcard `_` in its place. + */ case class LiteralPattern[+T](binder: Option[Identifier], lit : Literal[T]) extends Pattern { val subPatterns = Seq() } @@ -335,11 +351,12 @@ object Expressions { /** Symbolic I/O examples as a match/case. * $encodingof `out == (in match { cases; case _ => out })` * - * If you are not sure if the cases are nonempty, you should use + * [[cases]] should be nonempty. If you are not sure about this, you should use * [[purescala.Constructors#passes purescala's constructor passes]] * * @param in * @param out + * @param cases */ case class Passes(in: Expr, out : Expr, cases : Seq[MatchCase]) extends Expr { require(cases.nonEmpty) @@ -361,7 +378,7 @@ object Expressions { sealed abstract class Literal[+T] extends Expr with Terminal { val value: T } - /** $encodingof a char literal */ + /** $encodingof a character literal */ case class CharLiteral(value: Char) extends Literal[Char] { val getType = CharType } @@ -381,7 +398,7 @@ object Expressions { case class BooleanLiteral(value: Boolean) extends Literal[Boolean] { val getType = BooleanType } - /** $encodingof a unit literal `()` */ + /** $encodingof the unit literal `()` */ case class UnitLiteral() extends Literal[Unit] { val getType = UnitType val value = () @@ -397,7 +414,8 @@ object Expressions { } - /** $encodingof `CaseClass(args...)` + /** $encodingof `ct(args...)` + * * @param ct The case class name and inherited attributes * @param args The arguments of the case class */ @@ -410,11 +428,11 @@ object Expressions { val getType = BooleanType } - /** - * $encodingof `.asInstanceOf[...]` - * Introduced by matchToIfThenElse to transform match-cases to type-correct - * if bodies. - */ + /** $encodingof `expr.asInstanceOf[tpe]` + * + * Introduced by matchToIfThenElse to transform match-cases to type-correct + * if bodies. + */ case class AsInstanceOf(expr: Expr, tpe: ClassType) extends Expr { val getType = tpe } @@ -453,8 +471,9 @@ object Expressions { /* Propositional logic */ /** $encodingof `... && ...` * - * If you are not sure about the requirement you should use - * [[purescala.Constructors#and purescala's constructor and]] or [[purescala.Constructors#andJoin purescala's constructor andJoin]] + * [[exprs]] must contain at least two elements; if you are not sure about this, + * you should use [[purescala.Constructors#and purescala's constructor and]] + * or [[purescala.Constructors#andJoin purescala's constructor andJoin]] */ case class And(exprs: Seq[Expr]) extends Expr { require(exprs.size >= 2) @@ -470,8 +489,9 @@ object Expressions { /** $encodingof `... || ...` * - * If you are not sure about the requirement you should use - * [[purescala.Constructors#or purescala's constructor or]] or [[purescala.Constructors#orJoin purescala's constructor orJoin]] + * [[exprs]] must contain at least two elements; if you are not sure about this, + * you should use [[purescala.Constructors#or purescala's constructor or]] or + * [[purescala.Constructors#orJoin purescala's constructor orJoin]] */ case class Or(exprs: Seq[Expr]) extends Expr { require(exprs.size >= 2) @@ -485,10 +505,12 @@ object Expressions { def apply(a: Expr, b: Expr): Expr = Or(Seq(a, b)) } - /** $encodingof `... ==> ...` (logical implication) - * - * If you are not sure about the requirement you should use - * [[purescala.Constructors#implies purescala's constructor implies]] + /** $encodingof `... ==> ...` (logical implication). + * + * This is not a standard Scala operator, but it results from an implicit + * conversion in the Leon library. + * + * @see [[leon.purescala.Constructors.implies]] */ case class Implies(lhs: Expr, rhs: Expr) extends Expr { val getType = { @@ -497,7 +519,10 @@ object Expressions { } } - /** $encodingof `!...` */ + /** $encodingof `!...` + * + * @see [[leon.purescala.Constructors.not]] + */ case class Not(expr: Expr) extends Expr { val getType = { if (expr.getType == BooleanType) BooleanType @@ -592,88 +617,88 @@ object Expressions { /* Bit-vector arithmetic */ - /** $encodingof `... + ...` $note_bitvector*/ + /** $encodingof `... + ...` $noteBitvector*/ case class BVPlus(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == Int32Type && rhs.getType == Int32Type) val getType = Int32Type } - /** $encodingof `... - ...` $note_bitvector*/ + /** $encodingof `... - ...` $noteBitvector*/ case class BVMinus(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == Int32Type && rhs.getType == Int32Type) val getType = Int32Type } - /** $encodingof `- ...` $note_bitvector*/ + /** $encodingof `- ...` $noteBitvector*/ case class BVUMinus(expr: Expr) extends Expr { require(expr.getType == Int32Type) val getType = Int32Type } - /** $encodingof `... * ...` $note_bitvector*/ + /** $encodingof `... * ...` $noteBitvector*/ case class BVTimes(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == Int32Type && rhs.getType == Int32Type) val getType = Int32Type } - /** $encodingof `... / ...` $note_bitvector*/ + /** $encodingof `... / ...` $noteBitvector*/ case class BVDivision(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == Int32Type && rhs.getType == Int32Type) val getType = Int32Type } - /** $encodingof `... % ...` $note_bitvector*/ + /** $encodingof `... % ...` $noteBitvector*/ case class BVRemainder(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == Int32Type && rhs.getType == Int32Type) val getType = Int32Type } - /** $encodingof `! ...` $note_bitvector*/ + /** $encodingof `! ...` $noteBitvector */ case class BVNot(expr: Expr) extends Expr { val getType = Int32Type } - /** $encodingof `... & ...` $note_bitvector*/ + /** $encodingof `... & ...` $noteBitvector */ case class BVAnd(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } - /** $encodingof `... | ...` $note_bitvector*/ + /** $encodingof `... | ...` $noteBitvector */ case class BVOr(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } - /** $encodingof `... ^ ...` $note_bitvector*/ + /** $encodingof `... ^ ...` $noteBitvector */ case class BVXOr(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } - /** $encodingof `... << ...` $note_bitvector*/ + /** $encodingof `... << ...` $noteBitvector */ case class BVShiftLeft(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } - /** $encodingof `... >>> ...` $note_bitvector (logical shift)*/ + /** $encodingof `... >>> ...` $noteBitvector (logical shift) */ case class BVAShiftRight(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } - /** $encodingof `... >> ...` $note_bitvector (arighmetic shift, sign-preserving)*/ + /** $encodingof `... >> ...` $noteBitvector (arithmetic shift, sign-preserving) */ case class BVLShiftRight(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } /* Real arithmetic */ - /** $encodingof `... + ...` $note_real */ + /** $encodingof `... + ...` $noteReal */ case class RealPlus(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == RealType && rhs.getType == RealType) val getType = RealType } - /** $encodingof `... - ...` $note_real */ + /** $encodingof `... - ...` $noteReal */ case class RealMinus(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == RealType && rhs.getType == RealType) val getType = RealType } - /** $encodingof `- ...` $note_real */ + /** $encodingof `- ...` $noteReal */ case class RealUMinus(expr: Expr) extends Expr { require(expr.getType == RealType) val getType = RealType } - /** $encodingof `... * ...` $note_real */ + /** $encodingof `... * ...` $noteReal */ case class RealTimes(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == RealType && rhs.getType == RealType) val getType = RealType } - /** $encodingof `... / ...` $note_real */ + /** $encodingof `... / ...` $noteReal */ case class RealDivision(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == RealType && rhs.getType == RealType) val getType = RealType @@ -684,7 +709,7 @@ object Expressions { /** $encodingof `(..., ....)` (tuple) * - * Tuples should always contain at least 2 elements. + * [[exprs]] should always contain at least 2 elements. * If you are not sure about this requirement, you should use * [[purescala.Constructors#tupleWrap purescala's constructor tupleWrap]] * @@ -698,7 +723,7 @@ object Expressions { /** $encodingof `(tuple)._i` * * Index is 1-based, first element of tuple is 1. - * If you are not sure that tuple is indeed of a TupleType, + * If you are not sure that [[tuple]] is indeed of a TupleType, * you should use [[purescala.Constructors$.tupleSelect(t:leon\.purescala\.Expressions\.Expr,index:Int,isTuple:Boolean):leon\.purescala\.Expressions\.Expr* purescala's constructor tupleSelect]] */ case class TupleSelect(tuple: Expr, index: Int) extends Expr { @@ -716,11 +741,12 @@ object Expressions { /* Set operations */ - /** $encodingof `Set(elements)` */ + + /** $encodingof `Set[base](elements)` */ case class FiniteSet(elements: Set[Expr], base: TypeTree) extends Expr { val getType = SetType(base).unveilUntyped } - /** $encodingof `set.contains(elements)` or `set(elements)` */ + /** $encodingof `set.contains(element)` or `set(element)` */ case class ElementOfSet(element: Expr, set: Expr) extends Expr { val getType = BooleanType } @@ -748,7 +774,7 @@ object Expressions { // TODO: Add checks for these expressions too /* Map operations */ - /** $encodingof `Map(key -> value, key2 -> value2 ...)` */ + /** $encodingof `Map[keyType, valueType](key1 -> value1, key2 -> value2 ...)` */ case class FiniteMap(singletons: Seq[(Expr, Expr)], keyType: TypeTree, valueType: TypeTree) extends Expr { val getType = MapType(keyType, valueType).unveilUntyped } @@ -800,23 +826,28 @@ object Expressions { val getType = Int32Type } - /** $encodingof Array(...) with predetermined elements + /** $encodingof Array(elems...) with predetermined elements * @param elems The map from the position to the elements. - * @param defaultLength An optional pair where the first element is the default value and the second is the size of the array. + * @param defaultLength An optional pair where the first element is the default value + * and the second is the size of the array. Set this for big arrays + * with a default value (as genereted with `Array.fill` in Scala). */ case class NonemptyArray(elems: Map[Int, Expr], defaultLength: Option[(Expr, Expr)]) extends Expr { private val elements = elems.values.toList ++ defaultLength.map{_._1} val getType = ArrayType(optionToType(leastUpperBound(elements map { _.getType}))).unveilUntyped } - /** $encodingof `Array()` with a given type */ + /** $encodingof `Array[tpe]()` */ case class EmptyArray(tpe: TypeTree) extends Expr with Terminal { val getType = ArrayType(tpe).unveilUntyped } /* Special trees for synthesis */ - /** $encodingof `choose(pred)` where pred should be a [[Types.FunctionType]] */ + /** $encodingof `choose(pred)`, the non-deterministic choice in Leon. + * + * @note [[pred]] should be a of a [[Types.FunctionType]]. + */ case class Choose(pred: Expr) extends Expr { val getType = pred.getType match { case FunctionType(from, BooleanType) if from.nonEmpty => // @mk why nonEmpty? @@ -826,7 +857,7 @@ object Expressions { } } - // Provide an oracle (synthesizable, all-seeing choose) + /** Provide an oracle (synthesizable, all-seeing choose) */ case class WithOracle(oracles: List[Identifier], body: Expr) extends Expr with Extractable { require(oracles.nonEmpty) @@ -837,7 +868,11 @@ object Expressions { } } - /** $encodingof `???[tpe]` */ + /** $encodingof a synthesizable hole in a program. Represented by `???[tpe]` + * in Leon source code. + * + * A [[Hole]] gets transformed into a [[Choose]] construct during [[leon.synthesis.ConvertHoles the ConvertHoles phase]]. + */ case class Hole(tpe: TypeTree, alts: Seq[Expr]) extends Expr with Extractable { val getType = tpe -- GitLab