diff --git a/src/main/scala/leon/purescala/ExprOps.scala b/src/main/scala/leon/purescala/ExprOps.scala index 0be34a227e952eb5876440a21c388a78ba4b23aa..7a0a76427b085588831add1d3e7a18f44817b3ea 100644 --- a/src/main/scala/leon/purescala/ExprOps.scala +++ b/src/main/scala/leon/purescala/ExprOps.scala @@ -1647,6 +1647,16 @@ object ExprOps { * ================= */ + /** Replaces the precondition of an existing [[Expressions.Expr]] with a new one. + * + * If no precondition is provided, removes any existing precondition. + * Else, wraps the expression with a [[Expressions.Require]] clause referring to the new precondition. + * + * @param expr The current expression + * @param pred An optional precondition. Setting it to None removes any precondition. + * @see [[Expressions.Ensuring]] + * @see [[Expressions.Require]] + */ def withPrecondition(expr: Expr, pred: Option[Expr]): Expr = (pred, expr) match { case (Some(newPre), Require(pre, b)) => Require(newPre, b) case (Some(newPre), Ensuring(Require(pre, b), p)) => Ensuring(Require(newPre, b), p) @@ -1657,6 +1667,16 @@ object ExprOps { case (None, b) => b } + /** Replaces the postcondition of an existing [[Expressions.Expr]] with a new one. + * + * If no postcondition is provided, removes any existing postcondition. + * Else, wraps the expression with a [[Expressions.Ensuring]] clause referring to the new postcondition. + * + * @param expr The current expression + * @param pred An optional postcondition. Setting it to None removes any postcondition. + * @see [[Expressions.Ensuring]] + * @see [[Expressions.Require]] + */ def withPostcondition(expr: Expr, oie: Option[Expr]) = (oie, expr) match { case (Some(npost), Ensuring(b, post)) => Ensuring(b, npost) case (Some(npost), b) => Ensuring(b, npost) @@ -1664,6 +1684,14 @@ object ExprOps { case (None, b) => b } + /** Adds a body to a specification + * + * @param expr The specification expression [[Expressions.Ensuring]] or [[Expressions.Require]]. If none of these, the argument is discarded. + * @param body An option of [[Expressions.Expr]] possibly containing an expression body. + * @return The post/pre condition with the body. If no body is provided, returns [[Expressions.NoTree]] + * @see [[Expressions.Ensuring]] + * @see [[Expressions.Require]] + */ def withBody(expr: Expr, body: Option[Expr]) = expr match { case Require(pre, _) => Require(pre, body.getOrElse(NoTree(expr.getType))) case Ensuring(Require(pre, _), post) => Ensuring(Require(pre, body.getOrElse(NoTree(expr.getType))), post) @@ -1671,6 +1699,15 @@ object ExprOps { case _ => body.getOrElse(NoTree(expr.getType)) } + /** Extracts the body without its specification + * + * [[Expressions.Expr]] trees contain its specifications as part of certain nodes. + * This function helps extracting only the body part of an expression + * + * @return An option type with the resulting expression if not [[Expressions.NoTree]] + * @see [[Expressions.Ensuring]] + * @see [[Expressions.Require]] + */ def withoutSpec(expr: Expr) = expr match { case Require(pre, b) => Option(b).filterNot(_.isInstanceOf[NoTree]) case Ensuring(Require(pre, b), post) => Option(b).filterNot(_.isInstanceOf[NoTree]) @@ -1678,17 +1715,20 @@ object ExprOps { case b => Option(b).filterNot(_.isInstanceOf[NoTree]) } + /** Returns the precondition of an expression wrapped in Option */ def preconditionOf(expr: Expr) = expr match { case Require(pre, _) => Some(pre) case Ensuring(Require(pre, _), _) => Some(pre) case b => None } + /** Returns the postcondition of an expression wrapped in Option */ def postconditionOf(expr: Expr) = expr match { case Ensuring(_, post) => Some(post) case _ => None } + /** Returns a tuple of precondition, the raw body and the postcondition of an expression */ def breakDownSpecs(e : Expr) = (preconditionOf(e), withoutSpec(e), postconditionOf(e)) def preTraversalWithParent(f: (Expr, Option[Tree]) => Unit, initParent: Option[Tree] = None)(e: Expr): Unit = { diff --git a/src/main/scala/leon/purescala/Expressions.scala b/src/main/scala/leon/purescala/Expressions.scala index c23aa1f6e0ee183e85df3f88448c727dc7df98f4..5a0626bd0c99f249b412076f87240ecfc05af7f8 100644 --- a/src/main/scala/leon/purescala/Expressions.scala +++ b/src/main/scala/leon/purescala/Expressions.scala @@ -26,6 +26,10 @@ import ExprOps.replaceFromIDs * optimization opportunities. Unless you need exact control on the structure * of the trees, you should use constructors in [[purescala.Constructors]], that * simplify the trees they produce. + * + * @define encodingof Encoding of + * @define note_bitvector (32-bit vector) + * @define note_real (Real) */ object Expressions { @@ -53,7 +57,7 @@ object Expressions { } - /* Stands for an undefined Expr, similar to ??? or null */ + /** Stands for an undefined Expr, similar to ??? or null */ case class NoTree(tpe: TypeTree) extends Expr with Terminal { val getType = tpe } @@ -61,14 +65,18 @@ object Expressions { /* Specifications */ - /* This describes computational errors (unmatched case, taking min of an - * empty set, division by zero, etc.). It should always be typed according to - * the expected type. */ + /** This describes computational errors (unmatched case, taking min of an + * empty set, division by zero, etc.). It should always be typed according to + * the expected type. */ case class Error(tpe: TypeTree, description: String) extends Expr with Terminal { val getType = tpe } - // Preconditions + /** Precondition of an [[Expressions.Expr]]. Corresponds to the Leon keyword *require* + * + * @param pred The precondition formula inside ``require(...)`` + * @param body The body following the ``require(...)`` + */ case class Require(pred: Expr, body: Expr) extends Expr { val getType = { if (pred.getType == BooleanType) @@ -77,7 +85,10 @@ object Expressions { } } - // Postconditions + /** Postcondition of an [[Expressions.Expr]]. Corresponds to the Leon keyword *ensuring* + * + * @param body The body of the expression. It can contain at most one [[Expressions.Require]] sub-expression. + * @param pred The predicate to satisfy. It should be a function whose argument's type can handle the type of the body */ case class Ensuring(body: Expr, pred: Expr) extends Expr { val getType = pred.getType match { case FunctionType(Seq(bodyType), BooleanType) if isSubtypeOf(body.getType, bodyType) => @@ -85,6 +96,7 @@ object Expressions { case _ => untyped } + /** Converts this ensuring clause to the body followed by an assert statement */ def toAssert: Expr = { val res = FreshIdentifier("res", getType, true) Let(res, body, Assert( @@ -94,7 +106,12 @@ object Expressions { } } - // Local assertions + /** Local assertions with customizable error message + * + * @param pred The predicate, first argument of `assert(..., ...)` + * @param error An optional error string to display if the assert fails. Second argument of `assert(..., ...)` + * @param body The expression following `assert(..., ...)` + */ case class Assert(pred: Expr, error: Option[String], body: Expr) extends Expr { val getType = { if (pred.getType == BooleanType) @@ -104,13 +121,22 @@ object Expressions { } - /* Variables */ + /** Variable + * @param id The identifier of this variable */ case class Variable(id: Identifier) extends Expr with Terminal { val getType = id.getType } - /* Local val's and def's */ + /** $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 + */ case class Let(binder: Identifier, value: Expr, body: Expr) extends Expr { val getType = { // We can't demand anything sticter here, because some binders are @@ -123,17 +149,27 @@ object Expressions { } } + /** $encodingof `def ... = ...; ...` (lazy evaluation) + * + * @param id The function definition. + * @param body The body of the expression after the function + */ case class LetDef(fd: FunDef, body: Expr) extends Expr { val getType = body.getType } - /** - * OO Trees - * - * Both MethodInvocation and This get removed by phase MethodLifting. Methods become functions, - * This becomes first argument, and MethodInvocation becomes FunctionInvocation. - */ + // OO Trees + /** $encodingof `(...).method(args)` (method invocation) + * + * Both [[Expressions.MethodInvocation]] and [[Expressions.This]] get removed by phase [[MethodLifting]]. Methods become functions, + * [[Expressions.This]] becomes first argument, and [[Expressions.MethodInvocation]] becomes [[Expressions.FunctionInvocation]]. + * + * @param rec The expression evaluating to an object + * @param cd The class definition typing `rec` + * @param tfd The typed function definition of the method + * @param args The arguments provided to the method + */ case class MethodInvocation(rec: Expr, cd: ClassDef, tfd: TypedFunDef, args: Seq[Expr]) extends Expr { val getType = { // We need ot instantiate the type based on the type of the function as well as receiver @@ -148,12 +184,19 @@ object Expressions { } } + /** The '''this''' keyword */ case class This(ct: ClassType) extends Expr with Terminal { val getType = ct } - /* HOFs */ + /* HOFs (Higher-order Formulas) */ + + /** $encodingof `callee.apply(args...)` + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#application purescala's constructor application]] + */ case class Application(callee: Expr, args: Seq[Expr]) extends Expr { val getType = callee.getType match { case FunctionType(from, to) => @@ -163,6 +206,7 @@ object Expressions { } } + /** $encodingof `(args) => body` */ case class Lambda(args: Seq[ValDef], body: Expr) extends Expr { val getType = FunctionType(args.map(_.getType), body.getType).unveilUntyped def paramSubst(realArgs: Seq[Expr]) = { @@ -176,24 +220,45 @@ object Expressions { /* Control flow */ + /** $encodingof `function(...)` (function invocation) */ case class FunctionInvocation(tfd: TypedFunDef, args: Seq[Expr]) extends Expr { require(tfd.params.size == args.size) val getType = checkParamTypes(args, tfd.params, tfd.returnType) } + /** $encodingof `if(...) ... else ...` */ case class IfExpr(cond: Expr, thenn: Expr, elze: Expr) extends Expr { val getType = leastUpperBound(thenn.getType, elze.getType).getOrElse(untyped).unveilUntyped } + /** $encodingof `... match { ... }` + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#matchExpr purescala's constructor matchExpr]] + * + * @param scrutinee Expression to the left of the '''match''' keyword + * @param cases A sequence of cases to match `scrutinee` against + */ case class MatchExpr(scrutinee: Expr, cases: Seq[MatchCase]) extends Expr { require(cases.nonEmpty) val getType = leastUpperBound(cases.map(_.rhs.getType)).getOrElse(untyped).unveilUntyped } + /** $encodingof `case ... [if ...] => ... ` + * + * @param pattern The pattern just to the right of the '''case''' keyword + * @param optGuard An optional if-condition just to the left of the `=>` + * @param rhs The expression to the right of `=>` + * @see [[Expressions.MatchExpr]] + */ case class MatchCase(pattern : Pattern, optGuard : Option[Expr], rhs: Expr) extends Tree { def expressions: Seq[Expr] = optGuard.toList :+ rhs } + /** $encodingof a pattern after a '''case''' keyword. + * + * @see [[Expressions.MatchCase]] + */ sealed abstract class Pattern extends Tree { val subPatterns: Seq[Pattern] val binder: Option[Identifier] @@ -207,20 +272,25 @@ 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 val subPatterns = Seq() } case class WildcardPattern(binder: Option[Identifier]) extends Pattern { // c @ _ val subPatterns = Seq() } + /** Pattern encoding `case CaseClass(...) =>` */ case class CaseClassPattern(binder: Option[Identifier], ct: CaseClassType, subPatterns: Seq[Pattern]) extends Pattern + /** Pattern encoding tuple pattern `case (...) =>` */ case class TuplePattern(binder: Option[Identifier], subPatterns: Seq[Pattern]) extends Pattern + /** Pattern encoding like `case 0 => ...` or `case "Foo" => ...` */ case class LiteralPattern[+T](binder: Option[Identifier], lit : Literal[T]) extends Pattern { val subPatterns = Seq() } + /** Any pattern having an `unapply` function */ case class UnapplyPattern(binder: Option[Identifier], unapplyFun: TypedFunDef, subPatterns: Seq[Pattern]) extends Pattern { // Hacky, but ok lazy val optionType = unapplyFun.returnType.asInstanceOf[AbstractClassType] @@ -258,7 +328,11 @@ object Expressions { ) } - /* Symbolic IO examples */ + /** Symbolic IO examples as a match/case + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#passes purescala's constructor passes]] + */ case class Passes(in: Expr, out : Expr, cases : Seq[MatchCase]) extends Expr { require(cases.nonEmpty) @@ -274,7 +348,7 @@ object Expressions { } - /* Literals */ + /** Literals */ sealed abstract class Literal[+T] extends Expr with Terminal { val value: T } @@ -310,15 +384,24 @@ object Expressions { } - /* Case classes */ + /** $encodingof `case class ...(...)` + * @param ct The case class name and inherited attributes + * @param args The arguments of the case class + */ case class CaseClass(ct: CaseClassType, args: Seq[Expr]) extends Expr { val getType = checkParamTypes(args, ct.fieldsTypes, ct) } + /** $encodingof `.isInstanceOf[...]` */ case class IsInstanceOf(classType: ClassType, expr: Expr) extends Expr { val getType = BooleanType } + /** $encodingof `value.selector` where value is a case class + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#caseClassSelector purescala's constructor caseClassSelector]] + */ case class CaseClassSelector(classType: CaseClassType, caseClass: Expr, selector: Identifier) extends Expr { val selectorIndex = classType.classDef.selectorID2Index(selector) val getType = { @@ -334,7 +417,11 @@ object Expressions { } - /* Equality */ + /** $encodingof `... == ...` + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#equality purescala's constructor equality]] + */ case class Equals(lhs: Expr, rhs: Expr) extends Expr { val getType = { if (typesCompatible(lhs.getType, rhs.getType)) BooleanType @@ -346,6 +433,11 @@ 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]] + */ case class And(exprs: Seq[Expr]) extends Expr { require(exprs.size >= 2) val getType = { @@ -358,6 +450,11 @@ object Expressions { def apply(a: Expr, b: Expr): Expr = And(Seq(a, b)) } + /** $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]] + */ case class Or(exprs: Seq[Expr]) extends Expr { require(exprs.size >= 2) val getType = { @@ -370,6 +467,11 @@ object Expressions { def apply(a: Expr, b: Expr): Expr = Or(Seq(a, b)) } + /** Implication. + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#implies purescala's constructor implies]] + */ case class Implies(lhs: Expr, rhs: Expr) extends Expr { val getType = { if(lhs.getType == BooleanType && rhs.getType == BooleanType) BooleanType @@ -377,6 +479,11 @@ object Expressions { } } + /** $encodingof `!...` + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#not purescala's constructor not]] + */ case class Not(expr: Expr) extends Expr { val getType = { if (expr.getType == BooleanType) BooleanType @@ -386,137 +493,184 @@ object Expressions { /* Integer arithmetic */ + /** $encodingof `... + ...` + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#plus purescala's constructor plus]] + */ case class Plus(lhs: Expr, rhs: Expr) extends Expr { val getType = { if (lhs.getType == IntegerType && rhs.getType == IntegerType) IntegerType else untyped } } + /** $encodingof `... - ...` + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#matchExpr purescala's constructor matchExpr]] + */ case class Minus(lhs: Expr, rhs: Expr) extends Expr { val getType = { if (lhs.getType == IntegerType && rhs.getType == IntegerType) IntegerType else untyped } } + /** $encodingof `- ...`*/ case class UMinus(expr: Expr) extends Expr { val getType = { if (expr.getType == IntegerType) IntegerType else untyped } } + /** $encodingof `... * ...` + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#times purescala's constructor times]] + */ case class Times(lhs: Expr, rhs: Expr) extends Expr { val getType = { if (lhs.getType == IntegerType && rhs.getType == IntegerType) IntegerType else untyped } } - /* - * Division and Remainder follows Java/Scala semantics. Division corresponds - * to / operator on BigInt and Remainder corresponds to %. Note that in - * Java/Scala % is called remainder and the "mod" operator (Modulo in Leon) is also - * defined on BigInteger and differs from Remainder. The "mod" operator - * returns an always positive remainder, while Remainder could return - * a negative remainder. The following must hold: - * - * Division(x, y) * y + Remainder(x, y) == x - */ + /** $encodingof `... / ...` + * + * Division and Remainder follows Java/Scala semantics. Division corresponds + * to / operator on BigInt and Remainder corresponds to %. Note that in + * Java/Scala % is called remainder and the "mod" operator (Modulo in Leon) is also + * defined on BigInteger and differs from Remainder. The "mod" operator + * returns an always positive remainder, while Remainder could return + * a negative remainder. The following must hold: + * + * Division(x, y) * y + Remainder(x, y) == x + */ case class Division(lhs: Expr, rhs: Expr) extends Expr { val getType = { if (lhs.getType == IntegerType && rhs.getType == IntegerType) IntegerType else untyped } } + /** $encodingof `... % ...` (can return negative numbers) + * + * @see [[Expressions.Division]] + */ case class Remainder(lhs: Expr, rhs: Expr) extends Expr { val getType = { if (lhs.getType == IntegerType && rhs.getType == IntegerType) IntegerType else untyped } } + /** $encodingof `... mod ...` (cannot return negative numbers) + * + * @see [[Expressions.Division]] + */ case class Modulo(lhs: Expr, rhs: Expr) extends Expr { val getType = { if (lhs.getType == IntegerType && rhs.getType == IntegerType) IntegerType else untyped } } + /** $encodingof `... < ...`*/ case class LessThan(lhs: Expr, rhs: Expr) extends Expr { val getType = BooleanType } + /** $encodingof `... > ...`*/ case class GreaterThan(lhs: Expr, rhs: Expr) extends Expr { val getType = BooleanType } + /** $encodingof `... <= ...`*/ case class LessEquals(lhs: Expr, rhs: Expr) extends Expr { val getType = BooleanType } + /** $encodingof `... >= ...`*/ case class GreaterEquals(lhs: Expr, rhs: Expr) extends Expr { val getType = BooleanType } /* Bit-vector arithmetic */ + /** $encodingof `... + ...` $note_bitvector*/ case class BVPlus(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == Int32Type && rhs.getType == Int32Type) val getType = Int32Type } + /** $encodingof `... - ...` $note_bitvector*/ case class BVMinus(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == Int32Type && rhs.getType == Int32Type) val getType = Int32Type } + /** $encodingof `- ...` $note_bitvector*/ case class BVUMinus(expr: Expr) extends Expr { require(expr.getType == Int32Type) val getType = Int32Type } + /** $encodingof `... * ...` $note_bitvector*/ case class BVTimes(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == Int32Type && rhs.getType == Int32Type) val getType = Int32Type } + /** $encodingof `... / ...` $note_bitvector*/ case class BVDivision(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == Int32Type && rhs.getType == Int32Type) val getType = Int32Type } + /** $encodingof `... % ...` $note_bitvector*/ case class BVRemainder(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == Int32Type && rhs.getType == Int32Type) val getType = Int32Type } + /** $encodingof `! ...` $note_bitvector*/ case class BVNot(expr: Expr) extends Expr { val getType = Int32Type } + /** $encodingof `... & ...` $note_bitvector*/ case class BVAnd(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } + /** $encodingof `... | ...` $note_bitvector*/ case class BVOr(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } + /** $encodingof `... ^ ...` $note_bitvector*/ case class BVXOr(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } + /** $encodingof `... << ...` $note_bitvector*/ case class BVShiftLeft(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } + /** $encodingof `... >>> ...` $note_bitvector (logical shift)*/ case class BVAShiftRight(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } + /** $encodingof `... >> ...` $note_bitvector (arighmetic shift, sign-preserving)*/ case class BVLShiftRight(lhs: Expr, rhs: Expr) extends Expr { val getType = Int32Type } - /* Integer arithmetic */ + /* Real arithmetic */ + /** $encodingof `... + ...` $note_real */ case class RealPlus(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == RealType && rhs.getType == RealType) val getType = RealType } + /** $encodingof `... - ...` $note_real */ case class RealMinus(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == RealType && rhs.getType == RealType) val getType = RealType } + /** $encodingof `- ...` $note_real */ case class RealUMinus(expr: Expr) extends Expr { require(expr.getType == RealType) val getType = RealType } + /** $encodingof `... * ...` $note_real */ case class RealTimes(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == RealType && rhs.getType == RealType) val getType = RealType } + /** $encodingof `... / ...` $note_real */ case class RealDivision(lhs: Expr, rhs: Expr) extends Expr { require(lhs.getType == RealType && rhs.getType == RealType) val getType = RealType @@ -525,18 +679,22 @@ object Expressions { /* Tuple operations */ - // If you are not sure about the requirement you should use - // the tupleWrap in purescala.Constructors + /** $encodingof `(..., ....)` (tuple) + * + * If you are not sure about the requirement you should use + * [[purescala.Constructors#tupleWrap purescala's constructor tupleWrap]] + */ case class Tuple (exprs: Seq[Expr]) extends Expr { require(exprs.size >= 2) val getType = TupleType(exprs.map(_.getType)).unveilUntyped } - /* - * Index is 1-based, first element of tuple is 1. - * If you are not sure that tuple is indeed of a TupleType, - * you should use tupleSelect in pureScala.Constructors - */ + /** $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, + * 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 { require(index >= 1) @@ -552,24 +710,31 @@ object Expressions { /* Set operations */ + /** $encodingof `Set(elements)` */ case class FiniteSet(elements: Set[Expr], base: TypeTree) extends Expr { val getType = SetType(base).unveilUntyped } + /** $encodingof `set.contains(elements)` or `set(elements)` */ case class ElementOfSet(element: Expr, set: Expr) extends Expr { val getType = BooleanType } + /** $encodingof `set.length` */ case class SetCardinality(set: Expr) extends Expr { val getType = Int32Type } + /** $encodingof `set.subsetOf(set2)` */ case class SubsetOf(set1: Expr, set2: Expr) extends Expr { val getType = BooleanType } + /** $encodingof `set.intersect(set2)` */ case class SetIntersection(set1: Expr, set2: Expr) extends Expr { val getType = leastUpperBound(Seq(set1, set2).map(_.getType)).getOrElse(untyped).unveilUntyped } + /** $encodingof `set ++ set2` */ case class SetUnion(set1: Expr, set2: Expr) extends Expr { val getType = leastUpperBound(Seq(set1, set2).map(_.getType)).getOrElse(untyped).unveilUntyped } + /** $encodingof `set -- set2` */ case class SetDifference(set1: Expr, set2: Expr) extends Expr { val getType = leastUpperBound(Seq(set1, set2).map(_.getType)).getOrElse(untyped).unveilUntyped } @@ -577,9 +742,11 @@ object Expressions { // TODO: Add checks for these expressions too /* Map operations */ + /** $encodingof `Map(key -> value, key2 -> value2 ...)` */ case class FiniteMap(singletons: Seq[(Expr, Expr)], keyType: TypeTree, valueType: TypeTree) extends Expr { val getType = MapType(keyType, valueType).unveilUntyped } + /** $encodingof `map.get(key)` */ case class MapGet(map: Expr, key: Expr) extends Expr { val getType = map.getType match { case MapType(from, to) if isSubtypeOf(key.getType, from) => @@ -587,18 +754,22 @@ object Expressions { case _ => untyped } } + /** $encodingof `map ++ map2` */ case class MapUnion(map1: Expr, map2: Expr) extends Expr { val getType = leastUpperBound(Seq(map1, map2).map(_.getType)).getOrElse(untyped).unveilUntyped } + /** $encodingof `map -- map2` */ case class MapDifference(map: Expr, keys: Expr) extends Expr { val getType = map.getType } + /** $encodingof `map.isDefinedAt(key)` */ case class MapIsDefinedAt(map: Expr, key: Expr) extends Expr { val getType = BooleanType } /* Array operations */ + /** $encodingof `array(key)` */ case class ArraySelect(array: Expr, index: Expr) extends Expr { val getType = array.getType match { case ArrayType(base) => @@ -608,6 +779,7 @@ object Expressions { } } + /** $encodingof `array.updated(key, index)` */ case class ArrayUpdated(array: Expr, index: Expr, newValue: Expr) extends Expr { val getType = array.getType match { case ArrayType(base) => @@ -617,22 +789,25 @@ object Expressions { } } + /** $encodingof `array.length` */ case class ArrayLength(array: Expr) extends Expr { val getType = Int32Type } + /** $encodingof `array.nonempty` */ 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 */ 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]] */ case class Choose(pred: Expr) extends Expr { val getType = pred.getType match { case FunctionType(from, BooleanType) if from.nonEmpty => // @mk why nonEmpty? @@ -653,6 +828,7 @@ object Expressions { } } + /** $encodingof `???[tpe]` */ case class Hole(tpe: TypeTree, alts: Seq[Expr]) extends Expr with Extractable { val getType = tpe