From f683a6b3cfe7e1c2ef944f8163dc7043cfed188c Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 22 Jul 2021 12:08:55 +0300 Subject: [PATCH] Fix incorrect compilation with arguments duplication (#211) --- aqua-src/test.aqua | 17 ++++------ .../scala/aqua/model/func/FuncCallable.scala | 17 ++++++---- .../scala/aqua/model/func/raw/FuncOp.scala | 34 +++++++++++-------- 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/aqua-src/test.aqua b/aqua-src/test.aqua index f7bf3354..60140f23 100644 --- a/aqua-src/test.aqua +++ b/aqua-src/test.aqua @@ -1,11 +1,8 @@ -service OpH("op"): - get_str(s: string) -> string - get_arr() -> []string - identity: ⊤ -> ⊥ +service AquaDHT("aqua-dht"): + put_host_value(key: string, value: string, service_id: []string) -func registerKeyPutValue(node_id: string) -> []string: - nodes <- OpH.get_arr() - for n <- nodes par: - on n: - OpH.get_str(node_id) - <- nodes +func putHostValue(key: string, value: string, service_id: ?string): + AquaDHT.put_host_value(key, value, service_id) + +func create_client_util(service_id: string): + putHostValue("client-util", service_id, nil) \ No newline at end of file diff --git a/model/src/main/scala/aqua/model/func/FuncCallable.scala b/model/src/main/scala/aqua/model/func/FuncCallable.scala index 37d00634..a5ebcc27 100644 --- a/model/src/main/scala/aqua/model/func/FuncCallable.scala +++ b/model/src/main/scala/aqua/model/func/FuncCallable.scala @@ -1,6 +1,6 @@ package aqua.model.func -import aqua.model.func.raw.{AssignmentTag, CallArrowTag, CallServiceTag, FuncOp, RawTag} +import aqua.model.func.raw.{AssignmentTag, CallArrowTag, FuncOp, RawTag} import aqua.model.{Model, ValueModel, VarModel} import aqua.types.{ArrowType, Type} import cats.Eval @@ -47,15 +47,21 @@ case class FuncCallable( // Collect all arguments: what names are used inside the function, what values are received val argsFull = args.call(call) // DataType arguments - val argsToData = argsFull.dataArgs + val argsToDataRaw = argsFull.dataArgs // Arrow arguments: expected type is Arrow, given by-name - val argsToArrows = argsFull.arrowArgs(arrows) + val argsToArrowsRaw = argsFull.arrowArgs(arrows) + + // Find all duplicates in arguments + val argsShouldRename = findNewNames(forbiddenNames, (argsToDataRaw ++ argsToArrowsRaw).keySet) + val argsToData = argsToDataRaw.map { case (k, v) => argsShouldRename.getOrElse(k, k) -> v } + val argsToArrows = argsToArrowsRaw.map { case (k, v) => argsShouldRename.getOrElse(k, k) -> v } // Going to resolve arrows: collect them all. Names should never collide: it's semantically checked val allArrows = capturedArrows ++ argsToArrows // Substitute arguments (referenced by name and optional lambda expressions) with values - val treeWithValues = body.resolveValues(argsToData) + // Also rename all renamed arguments in the body + val treeWithValues = body.rename(argsShouldRename).resolveValues(argsToData) // Function body on its own defines some values; collect their names val treeDefines = treeWithValues.definesVarNames.value -- call.exportTo.map(_.name) @@ -63,8 +69,7 @@ case class FuncCallable( // We have some names in scope (forbiddenNames), can't introduce them again; so find new names val shouldRename = findNewNames(forbiddenNames, treeDefines) // If there was a collision, rename exports and usages with new names - val treeRenamed = - if (shouldRename.isEmpty) treeWithValues else treeWithValues.rename(shouldRename) + val treeRenamed = treeWithValues.rename(shouldRename) // Result could be derived from arguments, or renamed; take care about that val result = ret.map(_._1).map(_.resolveWith(argsToData)).map { diff --git a/model/src/main/scala/aqua/model/func/raw/FuncOp.scala b/model/src/main/scala/aqua/model/func/raw/FuncOp.scala index c832bb36..8eb099ed 100644 --- a/model/src/main/scala/aqua/model/func/raw/FuncOp.scala +++ b/model/src/main/scala/aqua/model/func/raw/FuncOp.scala @@ -54,22 +54,26 @@ case class FuncOp(tree: Cofree[Chain, RawTag]) extends Model { def resolveValues(vals: Map[String, ValueModel]): FuncOp = FuncOp(tree.map[RawTag](_.mapValues(_.resolveWith(vals)))) - def rename(vals: Map[String, String]): FuncOp = - FuncOp( - tree.map[RawTag](op => - op.mapValues { - case v: VarModel if vals.contains(v.name) => v.copy(name = vals(v.name)) - case v => v - } match { - case c: CallArrowTag => c.copy(call = c.call.mapExport(n => vals.getOrElse(n, n))) - case c: CallServiceTag => c.copy(call = c.call.mapExport(n => vals.getOrElse(n, n))) - case a: AssignmentTag => a.copy(assignTo = vals.getOrElse(a.assignTo, a.assignTo)) - case t: ForTag if vals.contains(t.item) => t.copy(item = vals(t.item)) - case t: NextTag if vals.contains(t.item) => t.copy(item = vals(t.item)) - case t => t - } + def rename(vals: Map[String, String]): FuncOp = { + if (vals.isEmpty) + this + else + FuncOp( + tree.map[RawTag](op => + op.mapValues { + case v: VarModel if vals.contains(v.name) => v.copy(name = vals(v.name)) + case v => v + } match { + case c: CallArrowTag => c.copy(call = c.call.mapExport(n => vals.getOrElse(n, n))) + case c: CallServiceTag => c.copy(call = c.call.mapExport(n => vals.getOrElse(n, n))) + case a: AssignmentTag => a.copy(assignTo = vals.getOrElse(a.assignTo, a.assignTo)) + case t: ForTag if vals.contains(t.item) => t.copy(item = vals(t.item)) + case t: NextTag if vals.contains(t.item) => t.copy(item = vals(t.item)) + case t => t + } + ) ) - ) + } def :+:(prev: FuncOp): FuncOp = FuncOp.RightAssocSemi.combine(prev, this)