Repository: effekt-lang/effekt
Pull request #485 (2024-06-05 09:26:08)

Use externs in `PolymorphismBoxing`
With this PR, `PolymorphismBoxing` will search for `extern pure def unboxT` and `extern pure def boxT` to box/unbox values.
If those are not found, it will still search for a record `BoxedT`.

It also changes the search s.t. the definitions will also be found if they are in a namespace.

This allows us to, e.g., store `int`s in the tag of a `%Pos` in LLVM.


Patch:
diff --git a/effekt/shared/src/main/scala/effekt/core/PolymorphismBoxing.scala b/effekt/shared/src/main/scala/effekt/core/PolymorphismBoxing.scala
index dabe0081..e4c33c96 100644
--- a/effekt/shared/src/main/scala/effekt/core/PolymorphismBoxing.scala
+++ b/effekt/shared/src/main/scala/effekt/core/PolymorphismBoxing.scala
@@ -8,6 +8,7 @@ import effekt.symbols.{ TmpBlock, TmpValue }
 import effekt.{ CoreTransformed, Phase }
 import effekt.symbols.builtins.{ TBoolean, TDouble, TInt, TChar, TState, TUnit }
 import effekt.symbols.ErrorMessageInterpolator
+import scala.util.boundary
 
 import scala.annotation.targetName
 
@@ -26,11 +27,38 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
 
   /**
    * Describes how to box/unbox values
-   * @param tpe The type of the values to be boxed
-   * @param constructor The constructor to box the values with
-   * @param field The field to access for unboxing
    */
-  case class Boxer(tpe: ValueType.Data, constructor: Id, field: Id)
+  trait Boxer {
+    /** The type of the boxed values */
+    def tpe: ValueType
+    /** Generates a pure expression boxing the parameter */
+    def box(p: Pure): Pure
+    /** Generates a pure expression unboxing the parameter */
+    def unbox(p: Pure): Pure
+  }
+  /**
+   * Describes how to box/unbox values using extern functions
+   * @param tpe The type of the boxed values
+   * @param boxFn The extern function to call to box
+   * @param unboxFn The extern function to call to unbox
+   */
+  case class ExternFnBoxer(val tpe: ValueType, boxFn: Block.BlockVar, unboxFn: Block.BlockVar) extends Boxer {
+    def box(p: Pure) = Pure.PureApp(boxFn, Nil, List(p))
+    def unbox(p: Pure) = Pure.PureApp(unboxFn, Nil, List(p))
+  }
+
+  /**
+   * Describes how to box/unbox values using records
+   *
+   * @param boxTpe       The type BoxedT
+   * @param constructor  The constructor to use for boxing
+   * @param field        The field to access for unboxing
+   */
+  case class RecordBoxer(boxTpe: ValueType.Data, constructor: Constructor, field: Field) extends Boxer {
+    def tpe = boxTpe
+    def box(p: Pure) = Pure.Make(boxTpe, constructor.id, List(p))
+    def unbox(p: Pure) = Pure.Select(p, field.id, field.tpe)
+  }
 
   /**
    * Partial function to describe which values to box and how.
@@ -42,35 +70,37 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
   def box(using PContext): PartialFunction[ValueType, Boxer] = {
     case core.Type.TInt     => PContext.boxer("Int")
     case core.Type.TChar    => PContext.boxer("Char")
-    case core.Type.TBoolean => PContext.boxer("Bool")
     case core.Type.TDouble  => PContext.boxer("Double")
-    case core.Type.TUnit    => PContext.boxer("Unit")
     // Do strings need to be boxed? Really?
-    case core.Type.TString  => PContext.boxer("String")
+    case core.Type.TString  =>
+      val b = PContext.boxer("String")
+      b
   }
 
-  class PContext(val declarations: List[Declaration])(using val Context: Context){
-    def findDeclarations(id: Id): List[Declaration] = {
-      declarations.filter(_.id == id)
-    }
-    def getDataLikeDeclaration(id: Id): Declaration.Data = id match {
-      case x if x == effekt.symbols.builtins.BottomSymbol =>
-        Declaration.Data(id, Nil, Nil)
-      case _ =>
-        val decls: List[Declaration.Data] = declarations.flatMap{
-          case d : Declaration.Data if d.id == id =>
-            Some(d)
-          case _ => None
-        }
-        if (decls.length != 1) {
-          Context.abort(s"No unique declaration for ${id}. Options: \n" +
-            (decls.map(d => PrettyPrinter.pretty(PrettyPrinter.indent(PrettyPrinter.toDoc(d)), 60).layout)).mkString("\n\n"))
-        } else {
-          decls.head
-        }
+  class PContext(declarations: List[Declaration], externs: List[Extern])(using val Context: Context) extends DeclarationContext(declarations, externs){
+    /**
+     * Find a pure extern def with one value parameter named [[name]] in the prelude (or some namespace in the prelude).
+     */
+    def findPureExternFn(name: String): Option[Block.BlockVar] = {
+       this.externs.collectFirst {
+        case d@effekt.core.Extern.Def(id, List(), List(), List(vparam), List(), ret, capts, body)
+          if id.name.name == name && capts.isEmpty => d
+      }.map { definition =>
+         val id = definition.id
+         val tpe = core.BlockType.Function(Nil, Nil, definition.vparams.map(_.tpe), Nil, definition.ret)
+         Block.BlockVar(id, tpe, Set.empty)
+       }
     }
 
-    lazy val prelude = Context.module.findPrelude
+    /**
+     * Find a record declaration named [[name]] with one field in the prelude (or some namespace in the prelude).
+     */
+    def findRecord(name: String): Option[Declaration.Data] = {
+      this.declarations.collectFirst {
+        case d@effekt.core.Declaration.Data(tpe, List(), List(Constructor(cns, List(fld))))
+          if tpe.name.name == name => d
+      }
+    }
 
     /**
      * Finds the corresponding boxer for a primitive type.
@@ -78,17 +108,37 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
      * @param name The name of the [[ValueType]]
      * @return a [[Boxer]] that describes how to box values of that type
      */
-    def boxer(name: String): Boxer = prelude.types.get("Boxed" + name) match {
-      case Some(value) => value match {
-        case tpeCns @ symbols.TypeConstructor.Record(_, List(), cns @ symbols.Constructor(_, List(), List(field), _)) =>
-          Boxer(ValueType.Data(tpeCns, List()), cns, field)
-        case tpeCns @ symbols.TypeConstructor.DataType(_, List(), List(cns @ symbols.Constructor(_, List(), List(field), _))) =>
-          Boxer(ValueType.Data(tpeCns, List()), cns, field)
-        case _ =>
-          Context.abort(s"No appropriate Boxed${name} type, but ${name} used as type parameter.")
+    def boxer(name: String): Boxer = {
+      /** Try to find `boxT` and `unboxT` externs */
+      def findExternFnBoxer() = boundary {
+        val box = findPureExternFn("box" ++ name).getOrElse { boundary.break(None) }
+        val unbox = findPureExternFn("unbox" ++ name).getOrElse { boundary.break(None) }
+        val boxRet = box.annotatedTpe match {
+          case BlockType.Function(_, _, _, _, result) => result
+          case _ => Context.abort(pp"${box} is not of function type.")
+        }
+        unbox.annotatedTpe match {
+          case BlockType.Function(_, _, List(unboxArg), List(), _) =>
+            if (unboxArg != boxRet) {
+              Context.abort(pp"Argument type of ${unbox} and return type of ${box} do not match up.")
+            }
+          case _ => Context.abort(pp"${unbox} is not of function type.")
+        }
+        Some(ExternFnBoxer(boxRet, box, unbox))
+      }
+      /** Try to find a `BoxedT` type */
+      def findRecordBoxer() = boundary {
+        findRecord("Boxed" ++ name) match {
+          case Some(Declaration.Data(tpe, List(), List(cns@Constructor(id, List(field))))) =>
+            Some(RecordBoxer(ValueType.Data(tpe, Nil), cns, field))
+          case _ => None
+        }
+      }
+      findExternFnBoxer() orElse findRecordBoxer() getOrElse {
+        Context.abort(s"Type ${name}, which needs to be boxed, is used as a type argument but no " +
+          s"corresponding pure externs box${name} and unbox${name} were defined in the prelude, " +
+          s"and also no record type Boxed${name}.")
       }
-      case None =>
-        Context.abort(s"Primitive ${name} is used as type parameter but Boxed${name} is not defined.")
     }
   }
   def PContext(using ctx: PContext): PContext = ctx
@@ -96,7 +146,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
 
   override def run(input: CoreTransformed)(using Context): Option[CoreTransformed] = input match {
     case CoreTransformed(source, tree, mod, core) => {
-      implicit val pctx: PContext = new PContext(core.declarations)
+      implicit val pctx: PContext = new PContext(core.declarations, core.externs)
       Context.module = mod
       val transformed = Context.timed(phaseName, source.name) { transform(core) }
       Some(CoreTransformed(source, tree, mod, transformed))
@@ -226,7 +276,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
     case Stmt.Match(scrutinee, clauses, default) =>
       scrutinee.tpe match {
         case ValueType.Data(symbol, targs) =>
-          val Declaration.Data(tpeId, tparams, constructors) = PContext.getDataLikeDeclaration(symbol)
+          val Declaration.Data(tpeId, tparams, constructors) = PContext.getData(symbol)
           Stmt.Match(transform(scrutinee), clauses.map {
             case (id, clause: Block.BlockLit) =>
               val constructor = constructors.find(_.id == id).get
@@ -280,7 +330,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
       val fcoercer = coercer[Block](tpe, itpe, targs)
       fcoercer.callPure(b, (vcoercers zip tVargs).map(_(_)))
     case Pure.Make(data, tag, vargs) =>
-      val dataDecl = PContext.getDataLikeDeclaration(data.name)
+      val dataDecl = PContext.getData(data.name)
       val ctorDecl = dataDecl.constructors.find(_.id == tag).getOrElse {
         Context.panic(pp"No constructor found for tag ${tag} in data type: ${data}")
       }
@@ -298,7 +348,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
         case ValueType.Data(symbol, targs) => (symbol, targs)
         case t => Context.abort(s"Select on value of type ${PrettyPrinter.format(t)} is not supported.")
       }
-      PContext.getDataLikeDeclaration(symbol) match {
+      PContext.getData(symbol) match {
         case Declaration.Data(id, tparams, List(Constructor(cns, fields))) =>
           val f = fields.find(_.id == field).getOrElse{
             Context.abort(s"${id} has no field ${field}.")
@@ -365,7 +415,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
 
     override def apply(t: Pure): Pure = {
       val boxer = box(valueType)
-      Pure.Make(boxer.tpe, boxer.constructor, List(t))
+      boxer.box(t)
     }
   }
   case class UnboxCoercer(valueType: ValueType)(using PContext) extends Coercer[ValueType, Pure] {
@@ -374,7 +424,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
 
     override def apply(t: Pure): Pure = {
       val boxer = box(valueType)
-      Pure.Select(t, boxer.field, to)
+      boxer.unbox(t)
     }
   }
   case class BottomCoercer(valueType: ValueType)(using PContext) extends Coercer[ValueType, Pure] {
@@ -384,9 +434,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
     override def apply(t: Pure): Pure = {
       to match {
         case core.Type.TInt => Pure.Literal(1337L, core.Type.TInt)
-        case core.Type.TBoolean => Pure.Literal(false, core.Type.TBoolean)
         case core.Type.TDouble  => Pure.Literal(13.37, core.Type.TDouble)
-        case core.Type.TUnit    => Pure.Literal((), core.Type.TUnit)
         // Do strings need to be boxed? Really?
         case core.Type.TString  => Pure.Literal("<?nothing>", core.Type.TString)
         case t if box.isDefinedAt(t) => sys error s"No default value defined for ${t}"
diff --git a/libraries/common/effekt.effekt b/libraries/common/effekt.effekt
index b4985fa2..e8af313c 100644
--- a/libraries/common/effekt.effekt
+++ b/libraries/common/effekt.effekt
@@ -604,11 +604,58 @@ def each(start: Int, end: Int) { action: (Int) => Unit / Control } = {
 
 def repeat(n: Int) { action: () => Unit / Control } = each(0, n) { n => action() }
 
-
-// For boxing polymorphic values
-record BoxedUnit(unboxUnit: Unit)
-record BoxedInt(unboxInt: Int)
-record BoxedChar(unboxChar: Char)
-record BoxedBool(unboxBool: Bool)
-record BoxedDouble(unboxDouble: Double)
-record BoxedString(unboxString: String)
\ No newline at end of file
+namespace internal {
+  namespace boxing {
+    // For boxing polymorphic values
+    extern type BoxedUnit
+    extern pure def boxUnit(u: Unit): Unit =
+    extern pure def unboxUnit(u: Unit): Unit =
+
+    extern type BoxedInt
+    extern pure def boxInt(n: Int): BoxedInt =
+      llvm """
+        %boxed1 = insertvalue %Pos zeroinitializer, i64 %n, 0
+        %boxed2 = insertvalue %Pos %boxed1, %Obj null, 1
+        ret %Pos %boxed2
+      """
+    extern pure def unboxInt(b: BoxedInt): Int =
+      llvm """
+        %unboxed = extractvalue %Pos %b, 0
+        ret %Int %unboxed
+      """
+
+    extern type BoxedChar
+    extern pure def boxChar(c: Char): BoxedChar =
+      llvm """
+        %boxed1 = insertvalue %Pos zeroinitializer, i64 %c, 0
+        %boxed2 = insertvalue %Pos %boxed1, %Obj null, 1
+        ret %Pos %boxed2
+      """
+    extern pure def unboxChar(b: BoxedChar): Char =
+      llvm """
+        %unboxed = extractvalue %Pos %b, 0
+        ret %Int %unboxed
+      """
+
+    extern type BoxedBool
+    extern pure def boxBool(b: Bool): BoxedBool =
+    extern pure def unboxBool(b: BoxedBool): Bool =
+
+    extern type BoxedDouble
+    extern pure def boxDouble(d: Double): BoxedDouble =
+      llvm """
+        %n = bitcast double %d to i64
+        %boxed1 = insertvalue %Pos zeroinitializer, i64 %n, 0
+        %boxed2 = insertvalue %Pos %boxed1, %Obj null, 1
+        ret %Pos %boxed2
+      """
+    extern pure def unboxDouble(b: BoxedDouble): Double =
+      llvm """
+        %unboxed = extractvalue %Pos %b, 0
+        %d = bitcast i64 %unboxed to double
+        ret %Double %d
+      """
+
+    record BoxedString(unboxString: String)
+  }
+}
\ No newline at end of file


Test patch:
diff --git a/effekt/jvm/src/test/scala/effekt/core/CoreTests.scala b/effekt/jvm/src/test/scala/effekt/core/CoreTests.scala
index 24feb1ff..192652ed 100644
--- a/effekt/jvm/src/test/scala/effekt/core/CoreTests.scala
+++ b/effekt/jvm/src/test/scala/effekt/core/CoreTests.scala
@@ -107,7 +107,6 @@ trait CorePhaseTests[P <: Phase[CoreTransformed, CoreTransformed]](phase: P) ext
   }
   protected val theSourceModuleDecl: effekt.source.ModuleDecl = effekt.source.ModuleDecl("(core test)", Nil, Nil) // FIXME sentinel value
 
-  // TODO Maybe fix this up so we can do findPrelude. Cmp. PolymorphismBoxingTests for a hacky way to do this
   protected val theSourceModule: effekt.symbols.Module = effekt.symbols.Module(theSourceModuleDecl, theSource)
 
   override def transform(input: ModuleDecl): ModuleDecl = {
diff --git a/effekt/jvm/src/test/scala/effekt/core/PolymorphismBoxingTests.scala b/effekt/jvm/src/test/scala/effekt/core/PolymorphismBoxingTests.scala
index f51195ac..bf3030db 100644
--- a/effekt/jvm/src/test/scala/effekt/core/PolymorphismBoxingTests.scala
+++ b/effekt/jvm/src/test/scala/effekt/core/PolymorphismBoxingTests.scala
@@ -1,46 +1,46 @@
-package effekt.core
+package effekt
+package core
 
 import effekt.{core, source, symbols}
 import effekt.context.Context
 import effekt.core.{Block, Definition, DirectApp, PolymorphismBoxing, Pure, Run, Stmt}
 import effekt.source.{IdDef, Include}
-import effekt.symbols.{Module, Name, TypeConstructor, TypeSymbol, ValueSymbol, ValueType}
 import effekt.util.messages
 import effekt.util.messages.DebugMessaging
 import kiama.parsing.{Failure, NoSuccess, Success}
 
 abstract class AbstractPolymorphismBoxingTests extends CorePhaseTests(PolymorphismBoxing) {
 
-  def boxDef(tpe: ValueType.ValueTypeApp): List[symbols.Symbol] = {
-    val tpeCns: symbols.TypeConstructor.Record =
-      symbols.TypeConstructor.Record(symbols.LocalName("Boxed" ++ tpe.constructor.name.name), List(), null)
-    tpeCns.constructor =
-      symbols.Constructor(symbols.LocalName("MkBoxed" ++ tpe.constructor.name.name), List(), null, tpeCns)
-    tpeCns.constructor.fields = List(
-      symbols.Field(symbols.LocalName("unbox" ++ tpe.constructor.name.name),
-        symbols.ValueParam(symbols.LocalName("value"), Some(tpe)), tpeCns.constructor))
-    List(tpeCns, tpeCns.constructor, tpeCns.constructor.fields.head)
-  }
-
-  val boxtpes = symbols.builtins.rootTypes.values.flatMap {
-    case t: TypeConstructor.ExternType => boxDef(ValueType.ValueTypeApp(t, List()))
-    case _ => Nil
-  }.map { c => (c.name.name, c) }.toMap
-
-  /** Make sure that the stdlib module is found, with the appropriate `Boxed`... definitions */
-  val pseudoStdLib = Module(effekt.source.ModuleDecl("effekt", List(), List()), kiama.util.StringSource("", "effekt"))
-
-  pseudoStdLib.exports(Nil, symbols.Bindings(Map.empty, boxtpes.collect[String, symbols.TypeSymbol]{
-    case (k,t: symbols.TypeSymbol) => (k,t)
-  }, Map.empty, Map.empty))
+  override protected val defaultNames: Map[String, _root_.effekt.symbols.Symbol] = super.defaultNames ++ Map(
+    "BoxedInt" -> Id("BoxedInt"),
+    "BoxedString" -> Id("BoxedString"),
+    "MkBoxedString" -> Id("MkBoxedString"),
+    "boxInt" -> Id("boxInt"),
+    "unboxInt" -> Id("unboxInt"),
+    "unboxString" -> Id("unboxInt"),
+  )
+  val boxDecls = List(
+    Declaration.Data(defaultNames("BoxedString"), List(),
+      List(Constructor(defaultNames("MkBoxedString"),
+        List(Field(defaultNames("unboxString"), ValueType.Data(defaultNames("String"), Nil))))))
+  )
+  val boxExterns = List(
+    Extern.Def(defaultNames("boxInt"), Nil, Nil, List(ValueParam(Id("i"), ValueType.Data(defaultNames("Int"), Nil))), Nil,
+      ValueType.Data(defaultNames("BoxedInt"), Nil), Set.empty,
+      ExternBody.StringExternBody(source.FeatureFlag.Default, Template(List("<box int>"), Nil))),
+    Extern.Def(defaultNames("unboxInt"), Nil, Nil, List(ValueParam(Id("i"), ValueType.Data(defaultNames("BoxedInt"), Nil))), Nil,
+      ValueType.Data(defaultNames("Int"), Nil), Set.empty,
+      ExternBody.StringExternBody(source.FeatureFlag.Default, Template(List("<unbox int>"), Nil)))
+  )
 
-  override val theSourceModule = Module(source.ModuleDecl("test", List(Include("effekt")), List()), kiama.util.StringSource("", "test"))
-  theSourceModule.exports(List(pseudoStdLib), symbols.Bindings.empty)
+  override def transform(input: ModuleDecl): ModuleDecl = input match {
+    case ModuleDecl(path, includes, declarations, externs, definitions, exports) =>
+      super.transform(ModuleDecl(path, includes, boxDecls ++ declarations, boxExterns ++ externs, definitions, exports)) match {
+        case ModuleDecl(path, includes, declarations, externs, definitions, exports) =>
+          ModuleDecl(path, includes, declarations.filterNot(boxDecls.contains), externs.filterNot(boxExterns.contains), definitions, exports)
+      }
+  }
 
-  override protected val defaultNames = boxtpes ++
-    symbols.builtins.rootTypes ++ Map(
-    // TODO maybe add used names
-  )
 }
 class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests {
   test("simple non-polymorphic code should stay the same"){
@@ -87,7 +87,7 @@ class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests {
       """module main
         |
         |def id = { ['A](a: 'A) => return a: 'A }
-        |def idInt = { (x: Int) => return (id: ['A]('A) => 'A @ {})[BoxedInt](make BoxedInt MkBoxedInt(x: Int)).unboxInt: Int }
+        |def idInt = { (x: Int) => return (unboxInt: (BoxedInt) => Int @ {})((id: ['A]('A) => 'A @ {})[BoxedInt]((boxInt: (Int) => BoxedInt @ {})(x: Int))) }
         |""".stripMargin
     assertTransformsTo(from, to)
   }
@@ -103,7 +103,7 @@ class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests {
       """module main
         |
         |def id = { ['A](a: 'A) => return a: 'A }
-        |def idInt = { (x: Int) => val tmp = (id: ['A]('A) => 'A @ {})[BoxedInt](make BoxedInt MkBoxedInt(x: Int)) ; return tmp:BoxedInt.unboxInt: Int }
+        |def idInt = { (x: Int) => val tmp = (id: ['A]('A) => 'A @ {})[BoxedInt]((boxInt: (Int) => BoxedInt @ {})(x: Int)) ; return (unboxInt: (BoxedInt) => Int @ {})(tmp:BoxedInt) }
         |""".stripMargin
     assertTransformsTo(from, to)
   }
@@ -127,8 +127,8 @@ class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests {
         |def idInt = { (x: Int) =>
         |    {
         |        let res = run {
-        |            let boxedRes = !(id: ['A]('A) => 'A @ {})[BoxedInt](make BoxedInt MkBoxedInt(x: Int))
-        |            return boxedRes:BoxedInt.unboxInt: Int
+        |            let boxedRes = !(id: ['A]('A) => 'A @ {})[BoxedInt]((boxInt: (Int) => BoxedInt @ {})(x: Int))
+        |            return (unboxInt: (BoxedInt) => Int @ {})(boxedRes:BoxedInt)
         |        }
         |        return res: Int
         |    }
@@ -150,11 +150,11 @@ class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests {
         |    val r = (hof: ['A](){ b : ('A) => 'A } => 'A @ {} )[BoxedInt](){ (boxedX: BoxedInt) =>
         |      {
         |         def originalFn = { (x: Int) => return x: Int }
-        |         val result = (originalFn: (Int) => Int @ {})(boxedX: BoxedInt.unboxInt: Int);
-        |         return make BoxedInt MkBoxedInt(result: Int)
+        |         val result = (originalFn: (Int) => Int @ {})((unboxInt: (BoxedInt) => Int @ {})(boxedX: BoxedInt));
+        |         return (boxInt: (Int) => BoxedInt @ {})(result: Int)
         |      }
         |    };
-        |    return r:BoxedInt.unboxInt: Int
+        |    return (unboxInt: (BoxedInt) => Int @ {})(r:BoxedInt)
         |}
         |""".stripMargin
     assertTransformsTo(from, to)
@@ -182,14 +182,14 @@ class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests {
         |                  (hhofargarg: Int) =>
         |                      {
         |                        def tmp = hhofargB: ('A) => 'A @ {}
-        |                        val rres:BoxedInt = (tmp: ('A) => 'A @ {})(make BoxedInt MkBoxedInt(hhofargarg: Int));
-        |                        return rres:BoxedInt.unboxInt: Int
+        |                        val rres:BoxedInt = (tmp: ('A) => 'A @ {})((boxInt: (Int) => BoxedInt @ {})(hhofargarg: Int));
+        |                        return (unboxInt: (BoxedInt) => Int @ {})(rres:BoxedInt)
         |                      }
         |              };
-        |              return make BoxedInt MkBoxedInt(res:Int)
+        |              return (boxInt: (Int) => BoxedInt @ {})(res:Int)
         |          }
         |    };
-        |    return result:BoxedInt.unboxInt: Int
+        |    return (unboxInt: (BoxedInt) => Int @ {})(result:BoxedInt)
         |}
         |""".stripMargin
     assertTransformsTo(from, to)