diff --git a/daml-lf/interface/src/main/scala/com/digitalasset/daml/lf/iface/reader/InterfaceReader.scala b/daml-lf/interface/src/main/scala/com/digitalasset/daml/lf/iface/reader/InterfaceReader.scala index 6ffa60e52c23..953712b63624 100644 --- a/daml-lf/interface/src/main/scala/com/digitalasset/daml/lf/iface/reader/InterfaceReader.scala +++ b/daml-lf/interface/src/main/scala/com/digitalasset/daml/lf/iface/reader/InterfaceReader.scala @@ -240,7 +240,7 @@ object InterfaceReader { unserializableDataType( ctx, s"Unserializable primitive type: $a must be applied to one and only one TNat") - case Ast.BTUpdate | Ast.BTScenario | Ast.BTArrow => + case Ast.BTUpdate | Ast.BTScenario | Ast.BTArrow | Ast.BTAnyTemplate => unserializableDataType(ctx, s"Unserializable primitive type: $a") } (arity, primType) = ab diff --git a/daml-lf/interface/src/test/scala/com/digitalasset/daml/lf/iface/TypeSpec.scala b/daml-lf/interface/src/test/scala/com/digitalasset/daml/lf/iface/TypeSpec.scala index aafd7a007f3b..569f684939b7 100644 --- a/daml-lf/interface/src/test/scala/com/digitalasset/daml/lf/iface/TypeSpec.scala +++ b/daml-lf/interface/src/test/scala/com/digitalasset/daml/lf/iface/TypeSpec.scala @@ -70,6 +70,7 @@ class TypeSpec extends WordSpec with Matchers { TypePrim(PrimTypeBool, ImmArraySeq(assertOneArg(args))) case Pkg.BTOptional => TypePrim(PrimTypeOptional, ImmArraySeq(assertOneArg(args))) case Pkg.BTArrow => sys.error("cannot use arrow in interface type") + case Pkg.BTAnyTemplate => sys.error("cannot use anytemplate in interface type") } case Pkg.TTyCon(tycon) => TypeCon(TypeConName(tycon), args.toImmArray.toSeq) case Pkg.TNat(_) => sys.error("cannot use nat type in interface type") diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala index ac0cd20138b2..96adb99a32d3 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala @@ -586,6 +586,12 @@ final case class Compiler(packages: PackageId PartialFunction Package) { case ELocation(loc, e) => SELocation(loc, translate(e)) + + case EToAnyTemplate(e) => + SEApp(SEBuiltin(SBToAnyTemplate), Array(translate(e))) + + case EFromAnyTemplate(tmplId, e) => + SEApp(SEBuiltin(SBFromAnyTemplate(tmplId)), Array(translate(e))) } @tailrec @@ -1030,6 +1036,7 @@ final case class Compiler(packages: PackageId PartialFunction Package) { case SRecord(_, _, args) => args.forEach(goV) case SVariant(_, _, value) => goV(value) case SEnum(_, _) => () + case SAnyTemplate(SRecord(_, _, args)) => args.forEach(goV) case _: SPAP | SToken | STuple(_, _) => throw CompileError("validate: unexpected SEValue") } diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index f5f3ac3aae51..d8432ea50da6 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -1203,6 +1203,33 @@ object SBuiltin { throw DamlEUserError(args.get(0).asInstanceOf[SText].value) } + /** $to_any_template + * :: arg (template argument) + * -> AnyTemplate + */ + final case object SBToAnyTemplate extends SBuiltin(1) { + def execute(args: util.ArrayList[SValue], machine: Machine): Unit = { + machine.ctrl = CtrlValue(args.get(0) match { + case r @ SRecord(_, _, _) => SAnyTemplate(r) + case v => crash(s"ToAnyTemplate on non-record: $v") + }) + } + } + + /** $from_any_template + * :: AnyTemplate + * -> Optional t (where t = TTyCon(expectedTemplateId)) + */ + final case class SBFromAnyTemplate(expectedTemplateId: TypeConName) extends SBuiltin(1) { + def execute(args: util.ArrayList[SValue], machine: Machine): Unit = { + machine.ctrl = CtrlValue(args.get(0) match { + case SAnyTemplate(r @ SRecord(actualTemplateId, _, _)) => + SOptional(if (actualTemplateId == expectedTemplateId) Some(r) else None) + case v => crash(s"FromAnyTemplate applied to non-AnyTemplate: $v") + }) + } + } + // Helpers // diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SValue.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SValue.scala index a711607bc2e4..e83434b4adde 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SValue.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SValue.scala @@ -66,6 +66,8 @@ sealed trait SValue { V.ValueMap(SortedLookupList(mVal).mapValue(_.toValue)) case SContractId(coid) => V.ValueContractId(coid) + case SAnyTemplate(_) => + throw SErrorCrash("SValue.toValue: unexpected SAnyTemplate") case STNat(_) => throw SErrorCrash("SValue.toValue: unexpected STNat") case _: SPAP => @@ -109,7 +111,8 @@ sealed trait SValue { case SContractId(coid) => SContractId(f(coid)) case SEnum(_, _) | _: SPrimLit | SToken | STNat(_) => this - + case SAnyTemplate(SRecord(tycon, fields, values)) => + SAnyTemplate(SRecord(tycon, fields, mapArrayList(values, v => v.mapContractId(f)))) } def equalTo(v2: SValue): Boolean = { @@ -181,6 +184,8 @@ object SValue { final case class SMap(value: HashMap[String, SValue]) extends SValue + final case class SAnyTemplate(t: SRecord) extends SValue + // Corresponds to a DAML-LF Nat type reified as a Speedy value. // It is currently used to track at runtime the scale of the // Numeric builtin's arguments/output. Should never be translated diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala index 54c703ba96a8..7fc8174a09a3 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala @@ -506,8 +506,8 @@ object Speedy { } } case SContractId(_) | SDate(_) | SNumeric(_) | SInt64(_) | SParty(_) | SText(_) | - STimestamp(_) | STuple(_, _) | SMap(_) | SRecord(_, _, _) | STNat(_) | _: SPAP | - SToken => + STimestamp(_) | STuple(_, _) | SMap(_) | SRecord(_, _, _) | SAnyTemplate(_) | STNat(_) | + _: SPAP | SToken => crash("Match on non-matchable value") } diff --git a/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/SpeedyTest.scala b/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/SpeedyTest.scala index 02bf0a8fbb49..3f816036a6a0 100644 --- a/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/SpeedyTest.scala +++ b/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/SpeedyTest.scala @@ -5,6 +5,7 @@ package com.digitalasset.daml.lf.speedy import java.util +import com.digitalasset.daml.lf.data.Ref._ import com.digitalasset.daml.lf.PureCompiledPackages import com.digitalasset.daml.lf.data.{FrontStack, Ref} import com.digitalasset.daml.lf.language.Ast @@ -19,6 +20,7 @@ import org.scalatest.{Matchers, WordSpec} class SpeedyTest extends WordSpec with Matchers { import SpeedyTest._ + import defaultParserParameters.{defaultPackageId => pkgId} "pattern matching" should { @@ -91,6 +93,74 @@ class SpeedyTest extends WordSpec with Matchers { } + val anyTemplatePkg = + p""" + module Test { + record @serializable T1 = { party: Party } ; + template (record : T1) = { + precondition True, + signatories Cons @Party [(Test:T1 {party} record)] (Nil @Party), + observers Nil @Party, + agreement "Agreement", + choices { + } + } ; + record @serializable T2 = { party: Party } ; + template (record : T2) = { + precondition True, + signatories Cons @Party [(Test:T2 {party} record)] (Nil @Party), + observers Nil @Party, + agreement "Agreement", + choices { + } + } ; + } + """ + + val anyTemplatePkgs = typeAndCompile(anyTemplatePkg) + + "to_any_template" should { + + "throw an exception on Int64" in { + eval(e"""to_any_template 1""", anyTemplatePkgs) shouldBe 'left + } + "succeed on template type" in { + eval(e"""to_any_template (Test:T1 {party = 'Alice'})""", anyTemplatePkgs) shouldBe + Right( + SAnyTemplate(SRecord( + Identifier(pkgId, QualifiedName.assertFromString("Test:T1")), + Name.Array(Name.assertFromString("party")), + ArrayList(SParty(Party.assertFromString("Alice"))) + ))) + } + + } + + "from_any_template" should { + + "throw an exception on Int64" in { + eval(e"""from_any_template @Test:T1 1""", anyTemplatePkgs) shouldBe 'left + } + + "return Some(tpl) if template id matches" in { + eval( + e"""from_any_template @Test:T1 (to_any_template (Test:T1 {party = 'Alice'}))""", + anyTemplatePkgs) shouldBe + Right( + SOptional(Some(SRecord( + Identifier(pkgId, QualifiedName.assertFromString("Test:T1")), + Name.Array(Name.assertFromString("party")), + ArrayList(SParty(Party.assertFromString("Alice"))) + )))) + } + + "return None if template id does not match" in { + eval( + e"""from_any_template @Test:T2 (to_any_template (Test:T1 {party = 'Alice'}))""", + anyTemplatePkgs) shouldBe Right(SOptional(None)) + } + } + } object SpeedyTest { diff --git a/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Ast.scala b/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Ast.scala index 033a65d74cf4..8a5ed3d5083c 100644 --- a/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Ast.scala +++ b/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Ast.scala @@ -145,6 +145,12 @@ object Ast { final case class ESome(typ: Type, body: Expr) extends Expr + /** AnyTemplate constructor **/ + final case class EToAnyTemplate(body: Expr) extends Expr + + /** Extract the underlying template if it matches the tmplId **/ + final case class EFromAnyTemplate(tmplId: TypeConName, body: Expr) extends Expr + // // Kinds // @@ -269,6 +275,7 @@ object Ast { case object BTDate extends BuiltinType case object BTContractId extends BuiltinType case object BTArrow extends BuiltinType + case object BTAnyTemplate extends BuiltinType // // Primitive literals diff --git a/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Util.scala b/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Util.scala index a81b7f5e60dc..c534674b6928 100644 --- a/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Util.scala +++ b/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Util.scala @@ -48,6 +48,7 @@ object Util { val TTimestamp = TBuiltin(BTTimestamp) val TDate = TBuiltin(BTDate) val TParty = TBuiltin(BTParty) + val TAnyTemplate = TBuiltin(BTAnyTemplate) val TNumeric = new ParametricType1(BTNumeric) val TList = new ParametricType1(BTList) diff --git a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/AstRewriter.scala b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/AstRewriter.scala index b5dffa69fc31..75e2cf1d19fb 100644 --- a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/AstRewriter.scala +++ b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/AstRewriter.scala @@ -121,6 +121,10 @@ private[digitalasset] class AstRewriter( ENone(apply(typ)) case ESome(typ, body) => ESome(apply(typ), apply(body)) + case EToAnyTemplate(body) => + EToAnyTemplate(apply(body)) + case EFromAnyTemplate(tmplId, body) => + EFromAnyTemplate(tmplId, apply(body)) } def apply(x: TypeConApp): TypeConApp = x match { diff --git a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/ExprParser.scala b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/ExprParser.scala index ea6ab1c28038..72f8ba6872c5 100644 --- a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/ExprParser.scala +++ b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/ExprParser.scala @@ -32,6 +32,8 @@ private[parser] class ExprParser[P](parserParameters: ParserParameters[P]) { eAbs | eTyAbs | eLet | + eToAnyTemplate | + eFromAnyTemplate | contractId | fullIdentifier ^^ EVal | (id ^? builtinFunctions) ^^ EBuiltin | @@ -174,6 +176,14 @@ private[parser] class ExprParser[P](parserParameters: ParserParameters[P]) { case b ~ body => ELet(b, body) } + private lazy val eToAnyTemplate: Parser[Expr] = + `to_any_template` ~>! expr0 ^^ EToAnyTemplate + + private lazy val eFromAnyTemplate: Parser[Expr] = + `from_any_template` ~>! `@` ~> fullIdentifier ~ expr0 ^^ { + case tyCon ~ e => EFromAnyTemplate(tyCon, e) + } + private lazy val pattern: Parser[CasePat] = primCon ^^ CPPrimCon | `nil` ^^^ CPNil | diff --git a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/Lexer.scala b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/Lexer.scala index cf86ace68421..472be579af2c 100644 --- a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/Lexer.scala +++ b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/Lexer.scala @@ -39,7 +39,9 @@ private[parser] object Lexer extends RegexParsers { "fetch_by_key" -> `fetch_by_key`, "lookup_by_key" -> `lookup_by_key`, "by" -> `by`, - "to" -> `to` + "to" -> `to`, + "to_any_template" -> `to_any_template`, + "from_any_template" -> `from_any_template` ) val token: Parser[Token] = diff --git a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/Token.scala b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/Token.scala index c05c6ebd80ee..e8078ad51060 100644 --- a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/Token.scala +++ b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/Token.scala @@ -51,6 +51,8 @@ private[parser] object Token { case object `lookup_by_key` extends Token case object `by` extends Token case object `to` extends Token + case object `to_any_template` extends Token + case object `from_any_template` extends Token final case class Id(s: String) extends Token final case class ContractId(s: String) extends Token diff --git a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/TypeParser.scala b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/TypeParser.scala index b740fc8c5332..328753bdd6e7 100644 --- a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/TypeParser.scala +++ b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/TypeParser.scala @@ -27,6 +27,7 @@ private[parser] class TypeParser[P](parameters: ParserParameters[P]) { "ContractId" -> BTContractId, "Arrow" -> BTArrow, "Map" -> BTMap, + "AnyTemplate" -> BTAnyTemplate, ) private[parser] def fullIdentifier: Parser[Ref.Identifier] = diff --git a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Serializability.scala b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Serializability.scala index 47868d306aed..fddd93283f75 100644 --- a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Serializability.scala +++ b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Serializability.scala @@ -95,6 +95,8 @@ private[validation] object Serializability { unserializable(URContractId) case BTArrow => unserializable(URFunction) + case BTAnyTemplate => + unserializable(URAnyTemplate) } case TForall(_, _) => unserializable(URForall) diff --git a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/TypeSubst.scala b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/TypeSubst.scala index d37846a30924..ee551162ef78 100644 --- a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/TypeSubst.scala +++ b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/TypeSubst.scala @@ -159,6 +159,11 @@ private[validation] case class TypeSubst(map: Map[TypeVarName, Type], private va ENone(apply(typ)) case ESome(typ, body) => ESome(apply(typ), apply(body)) + case EToAnyTemplate(body) => + EToAnyTemplate(apply(body)) + case EFromAnyTemplate(tmplId, body) => + EFromAnyTemplate(tmplId, apply(body)) + } def apply(choice: TemplateChoice): TemplateChoice = choice match { diff --git a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Typing.scala b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Typing.scala index 22164d1aac91..39df87d56f4c 100644 --- a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Typing.scala +++ b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Typing.scala @@ -23,7 +23,8 @@ private[validation] object Typing { } private def kindOfBuiltin(bType: BuiltinType): Kind = bType match { - case BTInt64 | BTText | BTTimestamp | BTParty | BTBool | BTDate | BTUnit => KStar + case BTInt64 | BTText | BTTimestamp | BTParty | BTBool | BTDate | BTUnit | BTAnyTemplate => + KStar case BTNumeric => KArrow(KNat, KStar) case BTList | BTUpdate | BTScenario | BTContractId | BTOptional | BTMap => KArrow(KStar, KStar) case BTArrow => KArrow(KStar, KArrow(KStar, KStar)) @@ -714,6 +715,21 @@ private[validation] object Typing { checkExpr(exp, TScenario(typ)) } + private def typeOfToAnyTemplate(body: Expr): Type = + typeOf(body) match { + case TTyCon(tmplId) => + lookupTemplate(ctx, tmplId) + TAnyTemplate + case typ => + throw EExpectedTemplateType(ctx, typ) + } + + private def typeOfFromAnyTemplate(tpl: TypeConName, body: Expr): Type = { + lookupTemplate(ctx, tpl) + checkExpr(body, TAnyTemplate) + TOptional(TTyCon(tpl)) + } + def typeOf(expr0: Expr): Type = expr0 match { case EVar(name) => lookupExpVar(name) @@ -777,6 +793,10 @@ private[validation] object Typing { checkType(typ, KStar) val _ = checkExpr(body, typ) TOptional(typ) + case EToAnyTemplate(body) => + typeOfToAnyTemplate(body) + case EFromAnyTemplate(tmplId, body) => + typeOfFromAnyTemplate(tmplId, body) } def checkExpr(expr: Expr, typ: Type): Type = { diff --git a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/ValidationError.scala b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/ValidationError.scala index f9bfed84eb0b..05bc2a0a5660 100644 --- a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/ValidationError.scala +++ b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/ValidationError.scala @@ -138,6 +138,9 @@ final case class URHigherKinded(varName: TypeVarName, kind: Kind) extends Unseri case object URUninhabitatedType extends UnserializabilityReason { def pretty: String = "variant type without constructors" } +case object URAnyTemplate extends UnserializabilityReason { + def pretty: String = "AnyTemplate" +} abstract class ValidationError extends java.lang.RuntimeException with Product with Serializable { def context: Context @@ -285,6 +288,11 @@ final case class EExpectedTemplatableType(context: Context, conName: TypeConName protected def prettyInternal: String = s"expected monomorphic record type in template definition, but found: ${conName.qualifiedName}" +} +final case class EExpectedTemplateType(context: Context, typ: Type) extends ValidationError { + protected def prettyInternal: String = + s"expected template type as argument to toAnyTemplate, but found: ${typ.pretty}" + } final case class EImportCycle(context: Context, modName: List[ModuleName]) extends ValidationError { protected def prettyInternal: String = s"cycle in module dependency ${modName.mkString(" -> ")}" diff --git a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/traversable/ExprTraversable.scala b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/traversable/ExprTraversable.scala index 9f42f1a04c22..676f68c6abf8 100644 --- a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/traversable/ExprTraversable.scala +++ b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/traversable/ExprTraversable.scala @@ -58,6 +58,10 @@ private[validation] object ExprTraversable { case ENone(typ @ _) => case ESome(typ @ _, body) => f(body) + case EToAnyTemplate(body) => + f(body) + case EFromAnyTemplate(tmplId @ _, body) => + f(body) } () } diff --git a/daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala b/daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala index 601d5242092a..890fb1289e8f 100644 --- a/daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala +++ b/daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/TypingSpec.scala @@ -32,6 +32,7 @@ class TypingSpec extends WordSpec with TableDrivenPropertyChecks with Matchers { BTDate -> k"*", BTContractId -> k"* -> *", BTArrow -> k"* -> * -> *", + BTAnyTemplate -> k"*", ) forEvery(testCases) { (bType: BuiltinType, expectedKind: Kind) => @@ -65,6 +66,9 @@ class TypingSpec extends WordSpec with TableDrivenPropertyChecks with Matchers { "Checker.typeOf" should { "infers the proper type for expression" in { + // The part of the expression that corresponds to the expression + // defined by the given rule should be wrapped in double + // parentheses. val testCases = Table( "expression" -> "expected type", @@ -161,6 +165,12 @@ class TypingSpec extends WordSpec with TableDrivenPropertyChecks with Matchers { // ExpDefault E"Λ (τ : ⋆) (σ : ⋆). λ (e₁ : τ) (e₂: σ) → (( case e₁ of _ → e₂ ))" -> T"∀ (τ : ⋆) (σ : ⋆). τ → σ → (( σ ))", + // ExpToAnyTemplate + E"""λ (t : Mod:T) -> (( to_any_template t ))""" -> + T"Mod:T -> AnyTemplate", + // ExpFromAnyTemplate + E"""λ (t: AnyTemplate) -> (( from_any_template @Mod:T t ))""" -> + T"AnyTemplate → Option Mod:T", ) forEvery(testCases) { (exp: Expr, expectedType: Type) => @@ -329,6 +339,15 @@ class TypingSpec extends WordSpec with TableDrivenPropertyChecks with Matchers { E"Λ (τ : ⋆). λ (e : τ) → (( case e of () → () ))", // ExpCaseOr E"Λ (τ : ⋆). λ (e : τ) → (( case e of ))", + // ExpToAnyTemplate + E"Λ (τ : *). λ (r: Mod:R τ) -> to_any_template r", + E"Λ (τ : *). λ (t: Mod:Tree τ) -> to_any_template t", + E"λ (c: Color) -> to_any_template c", + // ExpFromAnyTemplate + E"λ (t: AnyTemplate) -> from_any_template @Mod:R t", + E"λ (t: AnyTemplate) -> from_any_template @Mod:Tree t", + E"λ (t: AnyTemplate) -> from_any_template @Mod:Color t", + E"λ (t: Mod:T) -> from_any_template @Mod:T t", // ScnPure E"Λ (τ : ⋆ → ⋆). λ (e: τ) → (( spure @τ e ))", E"Λ (τ : ⋆) (σ : ⋆). λ (e: τ) → (( spure @σ e ))",