fix(compiler): Fix closure call compilation [fixes LNG-193] (#741)

This commit is contained in:
InversionSpaces 2023-06-13 11:56:17 +02:00 committed by GitHub
parent 8c2240d3b1
commit c5534a964c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 226 additions and 67 deletions

View File

@ -222,9 +222,11 @@ object TagInliner extends Logging {
case v => case v =>
valueToModel(v, false) valueToModel(v, false)
}).flatMap { case (model, prefix) => }).flatMap { case (model, prefix) =>
Exports[S] for {
.resolved(assignTo, model) // NOTE: Name <assignTo> should not exist yet
.as(None -> prefix) _ <- Mangler[S].forbidName(assignTo)
_ <- Exports[S].resolved(assignTo, model)
} yield None -> prefix
} }
case ClosureTag(arrow, detach) => case ClosureTag(arrow, detach) =>

View File

@ -75,38 +75,29 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
private def resolveArrow[S: Mangler: Exports: Arrows]( private def resolveArrow[S: Mangler: Exports: Arrows](
funcName: String, funcName: String,
call: Call call: Call
): State[S, (List[ValueModel], Inline)] = ): State[S, (List[ValueModel], Inline)] = for {
Arrows[S].arrows.flatMap(arrows => arrows <- Arrows[S].arrows
arrows.get(funcName) match { exports <- Exports[S].exports
case Some(fn) => arrow = arrows
resolveFuncArrow(fn, call) .get(funcName)
case None => .orElse(
Exports[S].exports.flatMap { exps => // if there is no arrow, check if it is stored in Exports as variable and try to resolve it
// if there is no arrow, check if it is stored in Exports as variable and try to resolve it exports
exps.get(funcName) match { .get(funcName)
case Some(VarModel(name, ArrowType(_, _), _)) => .collect { case VarModel(name, _: ArrowType, _) =>
Arrows[S].arrows.flatMap(arrows => name
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)
}
} }
} .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]( override def apply[S: Mangler: Exports: Arrows](
raw: CallArrowRaw, raw: CallArrowRaw,

View File

@ -19,6 +19,9 @@ trait Mangler[S] {
def forbid(names: Set[String]): State[S, Unit] 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] = def transformS[R](f: R => S, g: (R, S) => R): Mangler[R] =
new Mangler[R] { new Mangler[R] {

View File

@ -581,21 +581,24 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
} }
/** /**
* func inner(arg: u16) -> u16 -> u16: * func innerName(arg: u16) -> u16 -> u16:
* closure = (x: u16) -> u16: * closureName = (x: u16) -> u16:
* retval = x + arg * retval = x + arg
* <- retval * <- retval
* <- closure * <- closureName
* *
* func outer() -> u16: * func outer() -> u16:
* c <- inner(42) * outterClosureName <- inner(42)
* retval = 37 + c(1) + c(2) * <body(outterClosureName.type)>
* <- retval * <- outterResultName
*/ */
"arrow inliner" should "leave meta after returned closure inlining" in { def closureReturnModel(
val innerName = "inner" innerName: String,
val closureName = "closure" closureName: String,
outterClosureName: String,
outterResultName: String,
body: (ArrowType) => List[RawTag.Tree]
) = {
val closureArg = VarRaw( val closureArg = VarRaw(
"x", "x",
ScalarType.u16 ScalarType.u16
@ -689,33 +692,13 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
) )
).leaf ).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( val outerBody = SeqTag.wrap(
innerCall, innerCall +: body(closureType) :+ ReturnTag(
AssignmentTag(
RawBuilder.add(
RawBuilder.add(
LiteralRaw("37", LiteralType.number),
closureCall("1")
),
closureCall("2")
),
outterRes.name
).leaf,
ReturnTag(
NonEmptyList NonEmptyList
.one( .one(
outterRes outterRes
) )
).leaf ).leaf: _*
) )
val outer = FuncArrow( val outer = FuncArrow(
@ -731,13 +714,63 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
capturedTopology = None capturedTopology = None
) )
val model = ArrowInliner ArrowInliner
.callArrow[InliningState]( .callArrow[InliningState](
outer, outer,
CallModel(Nil, Nil) CallModel(Nil, Nil)
) )
.runA(InliningState()) .runA(InliningState())
.value .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) => val closureCallModel = (x: String, o: VarModel) =>
MetaModel MetaModel
@ -958,6 +991,136 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
model.equalsOrShowDiff(expected) shouldEqual true 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: data Prod:
value: string value: string