Skip to content

Commit

Permalink
fix(compiler): Fix closure call compilation [fixes LNG-193] (#741)
Browse files Browse the repository at this point in the history
  • Loading branch information
InversionSpaces committed Jun 13, 2023
1 parent 8c2240d commit c5534a9
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,11 @@ object TagInliner extends Logging {
case v =>
valueToModel(v, false)
}).flatMap { case (model, prefix) =>
Exports[S]
.resolved(assignTo, model)
.as(None -> prefix)
for {
// NOTE: Name <assignTo> should not exist yet
_ <- Mangler[S].forbidName(assignTo)
_ <- Exports[S].resolved(assignTo, model)
} yield None -> prefix
}

case ClosureTag(arrow, detach) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,38 +75,29 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
private def resolveArrow[S: Mangler: Exports: Arrows](
funcName: String,
call: Call
): State[S, (List[ValueModel], Inline)] =
Arrows[S].arrows.flatMap(arrows =>
arrows.get(funcName) match {
case Some(fn) =>
resolveFuncArrow(fn, call)
case None =>
Exports[S].exports.flatMap { exps =>
// if there is no arrow, check if it is stored in Exports as variable and try to resolve it
exps.get(funcName) match {
case Some(VarModel(name, ArrowType(_, _), _)) =>
Arrows[S].arrows.flatMap(arrows =>
arrows.get(name) match {
case Some(fn) =>
resolveFuncArrow(fn, call)
case _ =>
logger.error(
s"Inlining, cannot find arrow $funcName, available: ${arrows.keys
.mkString(", ")}"
)
State.pure(Nil -> Inline.empty)
}
)
case _ =>
logger.error(
s"Inlining, cannot find arrow $funcName, available: ${arrows.keys
.mkString(", ")}"
)
State.pure(Nil -> Inline.empty)
}
): State[S, (List[ValueModel], Inline)] = for {
arrows <- Arrows[S].arrows
exports <- Exports[S].exports
arrow = arrows
.get(funcName)
.orElse(
// if there is no arrow, check if it is stored in Exports as variable and try to resolve it
exports
.get(funcName)
.collect { case VarModel(name, _: ArrowType, _) =>
name
}
}
)
.flatMap(arrows.get)
)
result <- arrow.fold {
logger.error(
s"Inlining, cannot find arrow $funcName, available: ${arrows.keys
.mkString(", ")}"
)

State.pure(Nil -> Inline.empty)
}(resolveFuncArrow(_, call))
} yield result

override def apply[S: Mangler: Exports: Arrows](
raw: CallArrowRaw,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ trait Mangler[S] {

def forbid(names: Set[String]): State[S, Unit]

def forbidName(name: String): State[S, Unit] =
forbid(Set(name))

def transformS[R](f: R => S, g: (R, S) => R): Mangler[R] =
new Mangler[R] {

Expand Down
229 changes: 196 additions & 33 deletions model/inline/src/test/scala/aqua/model/inline/ArrowInlinerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -581,21 +581,24 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
}

/**
* func inner(arg: u16) -> u16 -> u16:
* closure = (x: u16) -> u16:
* func innerName(arg: u16) -> u16 -> u16:
* closureName = (x: u16) -> u16:
* retval = x + arg
* <- retval
* <- closure
* <- closureName
*
* func outer() -> u16:
* c <- inner(42)
* retval = 37 + c(1) + c(2)
* <- retval
* outterClosureName <- inner(42)
* <body(outterClosureName.type)>
* <- outterResultName
*/
"arrow inliner" should "leave meta after returned closure inlining" in {
val innerName = "inner"
val closureName = "closure"

def closureReturnModel(
innerName: String,
closureName: String,
outterClosureName: String,
outterResultName: String,
body: (ArrowType) => List[RawTag.Tree]
) = {
val closureArg = VarRaw(
"x",
ScalarType.u16
Expand Down Expand Up @@ -689,33 +692,13 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
)
).leaf

val closureCall = (i: String) =>
CallArrowRaw(
ability = None,
name = outterClosure.name,
arguments = List(LiteralRaw(i, LiteralType.number)),
baseType = closureType,
serviceId = None
)

val outerBody = SeqTag.wrap(
innerCall,
AssignmentTag(
RawBuilder.add(
RawBuilder.add(
LiteralRaw("37", LiteralType.number),
closureCall("1")
),
closureCall("2")
),
outterRes.name
).leaf,
ReturnTag(
innerCall +: body(closureType) :+ ReturnTag(
NonEmptyList
.one(
outterRes
)
).leaf
).leaf: _*
)

val outer = FuncArrow(
Expand All @@ -731,13 +714,63 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
capturedTopology = None
)

val model = ArrowInliner
ArrowInliner
.callArrow[InliningState](
outer,
CallModel(Nil, Nil)
)
.runA(InliningState())
.value
}

/**
* func inner(arg: u16) -> u16 -> u16:
* closure = (x: u16) -> u16:
* retval = x + arg
* <- retval
* <- closure
*
* func outer() -> u16:
* c <- inner(42)
* retval = 37 + c(1) + c(2)
* <- retval
*/
"arrow inliner" should "leave meta after returned closure inlining" in {
val innerName = "inner"
val closureName = "closure"
val outterClosureName = "c"
val outterResultName = "retval"

val closureCall = (closureType: ArrowType, i: String) =>
CallArrowRaw(
ability = None,
name = outterClosureName,
arguments = List(LiteralRaw(i, LiteralType.number)),
baseType = closureType,
serviceId = None
)

val body = (closureType: ArrowType) =>
List(
AssignmentTag(
RawBuilder.add(
RawBuilder.add(
LiteralRaw("37", LiteralType.number),
closureCall(closureType, "1")
),
closureCall(closureType, "2")
),
outterResultName
).leaf
)

val model = closureReturnModel(
innerName = innerName,
closureName = closureName,
outterClosureName = outterClosureName,
outterResultName = outterResultName,
body = body
)

val closureCallModel = (x: String, o: VarModel) =>
MetaModel
Expand Down Expand Up @@ -958,6 +991,136 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff(expected) shouldEqual true
}

/**
* func inner(arg: u16) -> u16 -> u16:
* closure = (x: u16) -> u16:
* retval = x + arg
* <- retval
* <- closure
*
* func outer() -> u16:
* c <- inner(42)
* b = c
* a = b
* retval = 37 + a(1) + b(2) + c{3}
* <- retval
*/
"arrow inliner" should "correctly inline renamed closure [bug LNG-193]" in {
val innerName = "inner"
val closureName = "closure"
val outterClosureName = "c"
val outterResultName = "retval"
val firstRename = "b"
val secondRename = "a"

val closureCall = (name: String, closureType: ArrowType, i: String) =>
CallArrowRaw(
ability = None,
name = name,
arguments = List(LiteralRaw(i, LiteralType.number)),
baseType = closureType,
serviceId = None
)

val body = (closureType: ArrowType) =>
List(
AssignmentTag(
VarRaw(outterClosureName, closureType),
firstRename
).leaf,
AssignmentTag(
VarRaw(firstRename, closureType),
secondRename
).leaf,
AssignmentTag(
RawBuilder
.add(
RawBuilder.add(
RawBuilder.add(
LiteralRaw("37", LiteralType.number),
closureCall(secondRename, closureType, "1")
),
closureCall(firstRename, closureType, "2")
),
closureCall(outterClosureName, closureType, "3")
),
outterResultName
).leaf
)

val model = closureReturnModel(
innerName = innerName,
closureName = closureName,
outterClosureName = outterClosureName,
outterResultName = outterResultName,
body = body
)

val closureCallModel = (x: String, o: VarModel) =>
MetaModel
.CallArrowModel(closureName)
.wrap(
ApplyTopologyModel(closureName)
.wrap(
ModelBuilder
.add(
LiteralModel(x, LiteralType.number),
LiteralModel("42", LiteralType.number)
)(o)
.leaf
)
)

/* WARNING: This naming is unstable */
val tempAdd0 = VarModel("add-0", ScalarType.u16)
val tempAdd1 = VarModel("add-1", ScalarType.u16)
val tempAdd2 = VarModel("add-2", ScalarType.u16)
val tempAdd3 = VarModel("add-3", ScalarType.u16)
val tempAdd4 = VarModel("add-4", ScalarType.u16)
val tempAdd = VarModel("add", ScalarType.u16)

val expected = SeqModel.wrap(
MetaModel
.CallArrowModel(innerName)
.wrap(
CaptureTopologyModel(closureName).leaf
),
SeqModel.wrap(
ParModel.wrap(
SeqModel.wrap(
ParModel.wrap(
SeqModel.wrap(
closureCallModel("1", tempAdd2),
ModelBuilder
.add(
LiteralModel("37", LiteralType.number),
tempAdd2
)(tempAdd1)
.leaf
),
closureCallModel("2", tempAdd3)
),
ModelBuilder
.add(
tempAdd1,
tempAdd3
)(tempAdd0)
.leaf
),
closureCallModel("3", tempAdd4)
),
ModelBuilder
.add(
tempAdd0,
tempAdd4
)(tempAdd)
.leaf
)
)

model.equalsOrShowDiff(expected) shouldEqual true
}

/*
data Prod:
value: string
Expand Down

0 comments on commit c5534a9

Please sign in to comment.