diff --git a/src/main/scala/leon/xlang/AntiAliasingPhase.scala b/src/main/scala/leon/xlang/AntiAliasingPhase.scala index 0a7b0c56316365a91414615b9814bde7f8f4bca9..9c2623b8dba9815f8e75b89e6a2d4a1eccb86180 100644 --- a/src/main/scala/leon/xlang/AntiAliasingPhase.scala +++ b/src/main/scala/leon/xlang/AntiAliasingPhase.scala @@ -63,6 +63,10 @@ object AntiAliasingPhase extends TransformationPhase { * Adapt the signature to express its effects. In case the * function has no effect, this will still return the original * fundef. + * + * Also update FunctionType parameters that need to become explicit + * about the effect they could perform (returning any mutable type that + * they receive). */ private def updateFunDef(fd: FunDef, effects: Effects)(ctx: LeonContext): FunDef = { @@ -72,22 +76,21 @@ object AntiAliasingPhase extends TransformationPhase { case _ => None }.map(_.id) + val newParams = fd.params.map(vd => vd.getType match { + case (ft: FunctionType) => ValDef(vd.id.duplicate(tpe = makeFunctionTypeExplicit(ft))) + case _ => vd + }) + + fd.body.foreach(body => getReturnedExpr(body).foreach{ case v@Variable(id) if aliasedParams.contains(id) => ctx.reporter.fatalError(v.getPos, "Cannot return a shared reference to a mutable object") case _ => () }) - //val allBodies: Set[Expr] = - // fd.body.toSet.flatMap((bd: Expr) => nestedFunDefsOf(bd).flatMap(_.body)) ++ fd.body - //allBodies.foreach(body => getReturnedExpr(body).foreach{ - // case v@Variable(id) if aliasedParams.contains(id) => - // ctx.reporter.fatalError(v.getPos, "Cannot return a shared reference to a mutable object: "k+ v) - // case _ => () - //}) - - if(aliasedParams.isEmpty) fd else { + + if(aliasedParams.isEmpty && fd.params == newParams) fd else { val newReturnType: TypeTree = tupleTypeWrap(fd.returnType +: aliasedParams.map(_.getType)) - val newFunDef = new FunDef(fd.id.freshen, fd.tparams, fd.params, newReturnType) + val newFunDef = new FunDef(fd.id.freshen, fd.tparams, newParams, newReturnType) newFunDef.addFlags(fd.flags) newFunDef.setPos(fd) newFunDef @@ -107,7 +110,7 @@ object AntiAliasingPhase extends TransformationPhase { if(aliasedParams.isEmpty) { val newBody = fd.body.map(body => { - makeSideEffectsExplicit(body, Seq(), effects, updatedFunDefs, varsInScope)(ctx) + makeSideEffectsExplicit(body, fd, Seq(), effects, updatedFunDefs, varsInScope)(ctx) }) newFunDef.body = newBody newFunDef.precondition = fd.precondition @@ -119,7 +122,7 @@ object AntiAliasingPhase extends TransformationPhase { val newBody = fd.body.map(body => { val freshBody = replaceFromIDs(rewritingMap.map(p => (p._1, p._2.toVariable)), body) - val explicitBody = makeSideEffectsExplicit(freshBody, freshLocalVars, effects, updatedFunDefs, varsInScope)(ctx) + val explicitBody = makeSideEffectsExplicit(freshBody, fd, freshLocalVars, effects, updatedFunDefs, varsInScope)(ctx) //WARNING: only works if side effects in Tuples are extracted from left to right, // in the ImperativeTransformation phase. @@ -154,8 +157,46 @@ object AntiAliasingPhase extends TransformationPhase { //We turn all local val of mutable objects into vars and explicit side effects //using assignments. We also make sure that no aliasing is being done. private def makeSideEffectsExplicit - (body: Expr, aliasedParams: Seq[Identifier], effects: Effects, updatedFunDefs: Map[FunDef, FunDef], varsInScope: Map[FunDef, Set[Identifier]]) + (body: Expr, originalFd: FunDef, aliasedParams: Seq[Identifier], effects: Effects, updatedFunDefs: Map[FunDef, FunDef], varsInScope: Map[FunDef, Set[Identifier]]) (ctx: LeonContext): Expr = { + + val newFunDef = updatedFunDefs(originalFd) + + def mapApplication(args: Seq[Expr], nfi: Expr, nfiType: TypeTree, fiEffects: Set[Int], rewritings: Map[Identifier, Expr]): Expr = { + if(fiEffects.nonEmpty) { + val modifiedArgs: Seq[(Identifier, Expr)] = + args.zipWithIndex.filter{ case (arg, i) => fiEffects.contains(i) } + .map(arg => { + val rArg = replaceFromIDs(rewritings, arg._1) + (findReceiverId(rArg).get, rArg) + }) + + val duplicatedParams = modifiedArgs.diff(modifiedArgs.distinct).distinct + if(duplicatedParams.nonEmpty) + ctx.reporter.fatalError(nfi.getPos, "Illegal passing of aliased parameter: " + duplicatedParams.head) + + val freshRes = FreshIdentifier("res", nfiType) + + val extractResults = Block( + modifiedArgs.zipWithIndex.map{ case ((id, expr), index) => { + val resSelect = TupleSelect(freshRes.toVariable, index + 2) + expr match { + case CaseClassSelector(_, obj, mid) => + Assignment(id, deepCopy(obj, mid, resSelect)) + case _ => + Assignment(id, resSelect) + } + }}, + TupleSelect(freshRes.toVariable, 1)) + + + val newExpr = Let(freshRes, nfi, extractResults) + newExpr + } else { + nfi + } + } + preMapWithContext[(Set[Identifier], Map[Identifier, Expr])]((expr, context) => { val bindings = context._1 val rewritings = context._2 @@ -212,6 +253,36 @@ object AntiAliasingPhase extends TransformationPhase { (Some(LetDef(nfds, body).copiedFrom(l)), context) } + case lambda@Lambda(params, body) => { + val ft@FunctionType(_, _) = lambda.getType + val ownEffects = functionTypeEffects(ft) + val aliasedParams: Seq[Identifier] = params.zipWithIndex.flatMap{ + case (vd, i) if ownEffects.contains(i) => Some(vd) + case _ => None + }.map(_.id) + + if(aliasedParams.isEmpty) { + (None, context) + } else { + val freshLocalVars: Seq[Identifier] = aliasedParams.map(v => v.freshen) + val rewritingMap: Map[Identifier, Identifier] = aliasedParams.zip(freshLocalVars).toMap + val freshBody = replaceFromIDs(rewritingMap.map(p => (p._1, p._2.toVariable)), body) + val explicitBody = makeSideEffectsExplicit(freshBody, originalFd, freshLocalVars, effects, updatedFunDefs, varsInScope)(ctx) + + //WARNING: only works if side effects in Tuples are extracted from left to right, + // in the ImperativeTransformation phase. + val finalBody: Expr = Tuple(explicitBody +: freshLocalVars.map(_.toVariable)) + + val wrappedBody: Expr = freshLocalVars.zip(aliasedParams).foldLeft(finalBody)((bd, vp) => { + LetVar(vp._1, Variable(vp._2), bd) + }) + val finalLambda = Lambda(params, wrappedBody).copiedFrom(lambda) + + (Some(finalLambda), context) + } + + } + case fi@FunctionInvocation(fd, args) => { val vis: Set[Identifier] = varsInScope.get(fd.fd).getOrElse(Set()) @@ -221,47 +292,35 @@ object AntiAliasingPhase extends TransformationPhase { }).foreach(aliasedArg => ctx.reporter.fatalError(aliasedArg.getPos, "Illegal passing of aliased parameter: " + aliasedArg)) + + updatedFunDefs.get(fd.fd) match { case None => (None, context) case Some(nfd) => { val nfi = FunctionInvocation(nfd.typed(fd.tps), args.map(arg => replaceFromIDs(rewritings, arg))).copiedFrom(fi) val fiEffects = effects.getOrElse(fd.fd, Set()) - if(fiEffects.nonEmpty) { - val modifiedArgs: Seq[(Identifier, Expr)] = - args.zipWithIndex.filter{ case (arg, i) => fiEffects.contains(i) } - .map(arg => { - val rArg = replaceFromIDs(rewritings, arg._1) - (findReceiverId(rArg).get, rArg) - }) - - val duplicatedParams = modifiedArgs.diff(modifiedArgs.distinct).distinct - if(duplicatedParams.nonEmpty) - ctx.reporter.fatalError(fi.getPos, "Illegal passing of aliased parameter: " + duplicatedParams.head) - - val freshRes = FreshIdentifier("res", nfd.typed(fd.tps).returnType) - - val extractResults = Block( - modifiedArgs.zipWithIndex.map{ case ((id, expr), index) => { - val resSelect = TupleSelect(freshRes.toVariable, index + 2) - expr match { - case CaseClassSelector(_, obj, mid) => - Assignment(id, deepCopy(obj, mid, resSelect)) - case _ => - Assignment(id, resSelect) - } - }}, - TupleSelect(freshRes.toVariable, 1)) - - - val newExpr = Let(freshRes, nfi, extractResults) - (Some(newExpr), context) - } else { - (Some(nfi), context) - } + (Some(mapApplication(args, nfi, nfd.typed(fd.tps).returnType, fiEffects, rewritings)), context) } } } + case app@Application(callee@Variable(id), args) => { + originalFd.params.zip(newFunDef.params) + .find(p => p._1.id == id) + .map(p => p._2.id) match { + case Some(newId) => + val ft@FunctionType(_, _) = callee.getType + val nft = makeFunctionTypeExplicit(ft) + + if(ft == nft) (None, context) else { + val nfi = Application(Variable(newId).copiedFrom(callee), args.map(arg => replaceFromIDs(rewritings, arg))).copiedFrom(app) + val fiEffects = functionTypeEffects(ft) + (Some(mapApplication(args, nfi, nft.to, fiEffects, rewritings)), context) + } + case None => (None, context) + } + } + case _ => (None, context) } @@ -346,6 +405,25 @@ object AntiAliasingPhase extends TransformationPhase { effects } + //convert a function type with mutable parameters, into a function type + //that returns the mutable parameters. This makes explicit all possible + //effects of the function. This should be used for higher order functions + //declared as parameters. + def makeFunctionTypeExplicit(tpe: FunctionType): FunctionType = { + val newReturnTypes = tpe.from.filter(t => isMutableType(t)) + if(newReturnTypes.isEmpty) + tpe + else { + FunctionType(tpe.from, TupleType(tpe.to +: newReturnTypes)) + } + } + + def functionTypeEffects(ft: FunctionType): Set[Int] = { + ft.from.zipWithIndex.flatMap{ case (vd, i) => + if(isMutableType(vd.getType)) Some(i) else None + }.toSet + } + //for a given id, compute the identifiers that alias it or some part of the object refered by id def computeLocalAliases(id: Identifier, body: Expr): Set[Identifier] = { def pre(expr: Expr, ids: Set[Identifier]): Set[Identifier] = expr match { @@ -455,6 +533,14 @@ object AntiAliasingPhase extends TransformationPhase { private def isMutationOf(expr: Expr, id: Identifier): Boolean = expr match { case ArrayUpdate(Variable(a), _, _) => a == id case FieldAssignment(obj, _, _) => findReceiverId(obj).exists(_ == id) + case Application(callee, args) => { + val ft@FunctionType(_, _) = callee.getType + val effects = functionTypeEffects(ft) + args.zipWithIndex.exists{ + case (Variable(argId), index) => argId == id && effects.contains(index) + case _ => false + } + } case _ => false }