interface methods: Scala AST (#11070)

* interface methods: Scala AST

Part of #11006
This PR takes care of the Scala AST and decoder.
It leaves the encoder, type checker, and speedy for later.

changelog_begin
changelog_end

* scalafmt

* AstRewriter
This commit is contained in:
Sofia Faro 2021-09-29 15:11:23 +01:00 committed by GitHub
parent 7dd9c2d3f0
commit c1d1521a14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 268 additions and 40 deletions

View File

@ -591,17 +591,30 @@ private[archive] class DecodeV1(minor: LV.Minor) {
.map(decodeChoice(tpl, _))
.map(ch => (ch.name, ch)),
observers = decodeExpr(lfTempl.getObservers, s"$tpl:observer"),
implements = lfImplements.map(decodeTemplateImplements),
implements = lfImplements.map(decodeTemplateImplements).map(impl => (impl.interface, impl)),
key =
if (lfTempl.hasKey) Some(decodeTemplateKey(tpl, lfTempl.getKey, paramName))
else None,
)
}
// TODO https://github.com/digital-asset/daml/issues/11006
// Decode the rest and store it in the AST
private[this] def decodeTemplateImplements(impl: PLF.DefTemplate.Implements): TypeConName =
decodeTypeConName(impl.getInterface)
private[this] def decodeTemplateImplements(
lfImpl: PLF.DefTemplate.Implements
): TemplateImplements =
TemplateImplements(
interface = decodeTypeConName(lfImpl.getInterface),
methods = lfImpl.getMethodsList.asScala
.map(decodeTemplateImplementsMethod)
.map(method => (method.name, method)),
)
private[this] def decodeTemplateImplementsMethod(
lfMethod: PLF.DefTemplate.ImplementsMethod
): TemplateImplementsMethod =
TemplateImplementsMethod(
name = getInternedName(lfMethod.getMethodInternedName, "TemplateImplementsMethod.name"),
value = decodeExpr(lfMethod.getValue, "TemplateImplementsMethod.value"),
)
private[archive] def decodeChoice(
tpl: DottedName,
@ -654,7 +667,10 @@ private[archive] class DecodeV1(minor: LV.Minor) {
DefInterface(
lfInterface.getChoicesList.asScala.toList
.map(decodeInterfaceChoice)
.map(choice => (choice.name, choice))
.map(choice => (choice.name, choice)),
lfInterface.getMethodsList.asScala.toList
.map(decodeInterfaceMethod)
.map(method => (method.name, method)),
)
private[this] def decodeInterfaceChoice(
@ -667,6 +683,14 @@ private[archive] class DecodeV1(minor: LV.Minor) {
returnType = decodeType(lfChoice.getRetType),
)
private[this] def decodeInterfaceMethod(
lfMethod: PLF.InterfaceMethod
): InterfaceMethod =
InterfaceMethod(
name = getInternedName(lfMethod.getMethodInternedName, "InterfaceMethod.name"),
returnType = decodeType(lfMethod.getType),
)
private[lf] def decodeKind(lfKind: PLF.Kind): Kind =
lfKind.getSumCase match {
case PLF.Kind.SumCase.STAR => KStar

View File

@ -246,6 +246,10 @@ object Ref {
type ChoiceName = Name
val ChoiceName = Name
/* Method name in an interface */
type MethodName = Name
val MethodName = Name
type ModuleName = DottedName
val ModuleName = DottedName

View File

@ -725,6 +725,8 @@ private[daml] class EncodeV1(minor: LV.Minor) {
val builder = PLF.DefInterface.newBuilder()
builder.setTyconInternedDname(dottedNameTable.insert(dottedName))
builder.accumulateLeft(interface.choices.sortByKey)(_ addChoices _)
// TODO https://github.com/digital-asset/daml/issues/11006
// encode interface methods as well
builder.build()
}
@ -814,16 +816,17 @@ private[daml] class EncodeV1(minor: LV.Minor) {
b.accumulateLeft(template.choices.sortByKey)(_ addChoices _)
b.setObservers(template.observers)
template.key.foreach(b.setKey(_))
b.accumulateLeft(template.implements)(_ addImplements encodeTemplateImplements(_))
b.accumulateLeft(template.implements.values)(_ addImplements encodeTemplateImplements(_))
b.build()
}
// TODO https://github.com/digital-asset/daml/issues/11006
// encode rest of TemplateImplements
private implicit def encodeTemplateImplements(
name: Ref.TypeConName
impl: TemplateImplements
): PLF.DefTemplate.Implements = {
val b = PLF.DefTemplate.Implements.newBuilder()
b.setInterface(name)
b.setInterface(impl.interface)
b.build()
}

View File

@ -319,7 +319,11 @@ private[lf] final class Compiler(
addDef(compileKey(identifier, tmpl))
addDef(compileSignatories(identifier, tmpl))
addDef(compileObservers(identifier, tmpl))
tmpl.implements.foreach(x => addDef(compileImplements(identifier, x)))
tmpl.implements.values.foreach { impl =>
addDef(compileImplements(identifier, impl.interface))
// TODO https://github.com/digital-asset/daml/issues/11006
// compile methods also
}
tmpl.choices.values.foreach(x => addDef(compileChoice(identifier, tmpl, x)))
@ -503,6 +507,9 @@ private[lf] final class Compiler(
compile(e) // interfaces have the same representation as underlying template
case EFromInterface(iface @ _, tpl, e) =>
SBFromInterface(tpl)(compile(e))
case ECallInterface(_, _, _) =>
// TODO https://github.com/digital-asset/daml/issues/11006
throw CompilationError("ECallInterface not implemented")
case EExperimental(name, _) =>
SBExperimental(name)

View File

@ -161,6 +161,9 @@ object Ast {
/** Convert interface back to template payload if possible */
final case class EFromInterface(iface: TypeConName, tpl: TypeConName, value: Expr) extends Expr
/** Invoke an interface method */
final case class ECallInterface(iface: TypeConName, method: MethodName, value: Expr) extends Expr
//
// Kinds
//
@ -637,18 +640,27 @@ object Ast {
type TemplateKeySignature = GenTemplateKey[Unit]
object TemplateKeySignature extends GenTemplateKeyCompanion[Unit]
final case class DefInterface(choices: Map[ChoiceName, InterfaceChoice])
final case class DefInterface(
choices: Map[ChoiceName, InterfaceChoice],
methods: Map[MethodName, InterfaceMethod],
)
object DefInterface {
def apply(
choices: Iterable[(ChoiceName, InterfaceChoice)]
choices: Iterable[(ChoiceName, InterfaceChoice)],
methods: Iterable[(MethodName, InterfaceMethod)],
): DefInterface = {
val choiceMap = toMapWithoutDuplicate(
choices,
(name: ChoiceName) =>
throw PackageError(s"collision on interface choice name ${name.toString}"),
)
DefInterface(choiceMap)
val methodMap = toMapWithoutDuplicate(
methods,
(name: MethodName) =>
throw PackageError(s"collision on interface method name ${name.toString}"),
)
DefInterface(choiceMap, methodMap)
}
}
@ -660,6 +672,11 @@ object Ast {
// TODO interfaces Should observers or controllers be part of the interface?
)
case class InterfaceMethod(
name: MethodName,
returnType: Type,
)
case class GenTemplate[E] private[Ast] (
param: ExprVarName, // Binder for template argument.
precond: E, // Template creation precondition.
@ -668,7 +685,7 @@ object Ast {
choices: Map[ChoiceName, GenTemplateChoice[E]], // Choices available in the template.
observers: E, // Observers of the contract.
key: Option[GenTemplateKey[E]],
implements: Set[TypeConName],
implements: Map[TypeConName, GenTemplateImplements[E]],
) extends NoCopy
sealed class GenTemplateCompanion[E] {
@ -681,7 +698,7 @@ object Ast {
choices: Iterable[(ChoiceName, GenTemplateChoice[E])],
observers: E,
key: Option[GenTemplateKey[E]],
implements: Iterable[TypeConName],
implements: Iterable[(TypeConName, GenTemplateImplements[E])],
): GenTemplate[E] = {
val choiceMap = toMapWithoutDuplicate(
@ -690,11 +707,10 @@ object Ast {
throw PackageError(s"collision on choice name ${choiceName.toString}"),
)
val implementsSet = implements.foldLeft(Set.empty[TypeConName])((acc, implement) =>
if (acc.contains(implement))
throw PackageError(s"repeated implementation ${implements.toString}")
else
acc + implement
val implementsMap = toMapWithoutDuplicate(
implements,
(ifaceName: TypeConName) =>
throw PackageError(s"repeated interface implementation ${ifaceName.toString}"),
)
new GenTemplate[E](
@ -705,7 +721,7 @@ object Ast {
choiceMap,
observers,
key,
implementsSet,
implementsMap,
)
}
@ -718,7 +734,7 @@ object Ast {
Map[ChoiceName, GenTemplateChoice[E]],
E,
Option[GenTemplateKey[E]],
Set[TypeConName],
Map[TypeConName, GenTemplateImplements[E]],
)
] = GenTemplate.unapply(arg)
@ -731,7 +747,7 @@ object Ast {
Map[ChoiceName, GenTemplateChoice[E]],
E,
Option[GenTemplateKey[E]],
Set[TypeConName],
Map[TypeConName, GenTemplateImplements[E]],
)
] = Some(
(
@ -809,6 +825,66 @@ object Ast {
type TemplateChoiceSignature = GenTemplateChoice[Unit]
object TemplateChoiceSignature extends GenTemplateChoiceCompanion[Unit]
case class GenTemplateImplements[E] private[Ast] (
interface: TypeConName,
methods: Map[MethodName, GenTemplateImplementsMethod[E]],
)
sealed class GenTemplateImplementsCompanion[E] {
def apply(
interface: TypeConName,
methods: Iterable[(MethodName, GenTemplateImplementsMethod[E])],
): GenTemplateImplements[E] = {
val methodMap = toMapWithoutDuplicate(
methods,
(methodName: MethodName) =>
throw PackageError(s"repeated method implementation ${methodName.toString}"),
)
new GenTemplateImplements[E](interface, methodMap)
}
def apply(
interface: TypeConName,
methods: Map[MethodName, GenTemplateImplementsMethod[E]],
): GenTemplateImplements[E] =
GenTemplateImplements[E](interface, methods)
def unapply(
arg: GenTemplateImplements[E]
): Some[(TypeConName, Map[MethodName, GenTemplateImplementsMethod[E]])] =
Some((arg.interface, arg.methods))
}
type TemplateImplements = GenTemplateImplements[Expr]
object TemplateImplements extends GenTemplateImplementsCompanion[Expr]
type TemplateImplementsSignature = GenTemplateImplements[Unit]
object TemplateImplementsSignature extends GenTemplateImplementsCompanion[Unit]
case class GenTemplateImplementsMethod[E] private[Ast] (
name: MethodName,
value: E,
)
sealed class GenTemplateImplementsMethodCompanion[E] {
def apply(
name: MethodName,
value: E,
): GenTemplateImplementsMethod[E] =
GenTemplateImplementsMethod[E](name, value)
def unapply(
arg: GenTemplateImplementsMethod[E]
): Some[(MethodName, E)] =
Some((arg.name, arg.value))
}
type TemplateImplementsMethod = GenTemplateImplementsMethod[Expr]
object TemplateImplementsMethod extends GenTemplateImplementsMethodCompanion[Expr]
type TemplateImplementsMethodSignature = GenTemplateImplementsMethod[Unit]
object TemplateImplementsMethodSignature extends GenTemplateImplementsMethodCompanion[Unit]
final case class GenDefException[E](message: E)
sealed class GenDefExceptionCompanion[E] {

View File

@ -107,8 +107,12 @@ object Reference {
override def pretty: String = s"choice $choiceName in template $tyCon"
}
final case class Method(tyCon: TypeConName, methodName: MethodName) extends Reference {
override def pretty: String = s"method $methodName in interface $tyCon"
}
final case class Exception(tyCon: TypeConName) extends Reference {
override def pretty: String = s"exception: $tyCon"
override def pretty: String = s"exception $tyCon"
}
}

View File

@ -208,19 +208,36 @@ private[lf] class PackageInterface(signatures: PartialFunction[PackageId, Packag
lookupChoice(tmpName, chName, Reference.Choice(tmpName, chName))
private[this] def lookupInterfaceChoice(
tmpName: TypeConName,
ifaceName: TypeConName,
chName: ChoiceName,
context: => Reference,
): Either[LookupError, InterfaceChoice] =
lookupInterface(tmpName, context).flatMap(
_.choices.get(chName).toRight(LookupError(Reference.Choice(tmpName, chName), context))
lookupInterface(ifaceName, context).flatMap(
_.choices.get(chName).toRight(LookupError(Reference.Choice(ifaceName, chName), context))
)
def lookupInterfaceChoice(
tmpName: TypeConName,
ifaceName: TypeConName,
chName: ChoiceName,
): Either[LookupError, InterfaceChoice] =
lookupInterfaceChoice(tmpName, chName, Reference.Choice(tmpName, chName))
lookupInterfaceChoice(ifaceName, chName, Reference.Choice(ifaceName, chName))
private[this] def lookupInterfaceMethod(
ifaceName: TypeConName,
methodName: MethodName,
context: => Reference,
): Either[LookupError, InterfaceMethod] =
lookupInterface(ifaceName, context).flatMap(
_.methods
.get(methodName)
.toRight(LookupError(Reference.Method(ifaceName, methodName), context))
)
def lookupInterfaceMethod(
ifaceName: TypeConName,
methodName: MethodName,
): Either[LookupError, InterfaceMethod] =
lookupInterfaceMethod(ifaceName, methodName, Reference.Method(ifaceName, methodName))
private[this] def lookupTemplateKey(
name: TypeConName,

View File

@ -201,6 +201,20 @@ object Util {
TemplateKeySignature(typ, (), ())
}
private[this] def toSignature(
implementsMethod: TemplateImplementsMethod
): TemplateImplementsMethodSignature =
implementsMethod match {
case TemplateImplementsMethod(name, _) =>
TemplateImplementsMethodSignature(name, ())
}
private[this] def toSignature(implements: TemplateImplements): TemplateImplementsSignature =
implements match {
case TemplateImplements(name, methods) =>
TemplateImplementsSignature(name, methods.transform((_, v) => toSignature(v)))
}
private[this] def toSignature(template: Template): TemplateSignature =
template match {
case Template(param, _, _, _, choices, _, key, implements) =>
@ -212,7 +226,7 @@ object Util {
choices.transform((_, v) => toSignature(v)),
(),
key.map(toSignature),
implements,
implements.transform((_, v) => toSignature(v)),
)
}

View File

@ -129,6 +129,8 @@ private[daml] class AstRewriter(
EToInterface(apply(iface), apply(tpl), apply(value))
case EFromInterface(iface, tpl, value) =>
EFromInterface(apply(iface), apply(tpl), apply(value))
case ECallInterface(iface, method, value) =>
ECallInterface(apply(iface), method, apply(value))
}
def apply(x: TypeConApp): TypeConApp = x match {
@ -245,7 +247,7 @@ private[daml] class AstRewriter(
},
apply(observers),
key.map(apply),
implements.map(apply),
implements.transform((_, x) => apply(x)),
)
}
@ -273,6 +275,29 @@ private[daml] class AstRewriter(
)
}
def apply(x: TemplateImplements): TemplateImplements =
x match {
case TemplateImplements(
interface,
methods,
) =>
TemplateImplements(
interface,
methods.transform((_, x) => apply(x)),
)
}
def apply(x: TemplateImplementsMethod): TemplateImplementsMethod =
x match {
case TemplateImplementsMethod(
name,
value,
) =>
TemplateImplementsMethod(
name,
apply(value),
)
}
def apply(x: TemplateKey): TemplateKey =
x match {
case TemplateKey(typ, body, maintainers) =>
@ -290,9 +315,19 @@ private[daml] class AstRewriter(
InterfaceChoice(name, consuming, apply(argType), returnType = apply(returnType))
}
def apply(x: InterfaceMethod): InterfaceMethod =
x match {
case InterfaceMethod(name, returnType) =>
InterfaceMethod(name, apply(returnType))
}
def apply(x: DefInterface): DefInterface =
x match {
case DefInterface(choices) => DefInterface(choices.transform((_, x) => apply(x)))
case DefInterface(choices, methods) =>
DefInterface(
choices.transform((_, x) => apply(x)),
methods.transform((_, x) => apply(x)),
)
}
}

View File

@ -454,7 +454,7 @@ private[validation] object Typing {
checkExpr(key.maintainers, TFun(key.typ, TParties))
()
}
implementations.foreach(env.checkIfaceImplementation(tplName, _))
implementations.values.foreach(env.checkIfaceImplementation(tplName, _))
}
def checkIfaceChoice(choice: InterfaceChoice): Unit = {
@ -462,14 +462,15 @@ private[validation] object Typing {
checkType(choice.returnType, KStar)
}
def checkIfaceImplementation(tplTcon: TypeConName, ifaceTcon: TypeConName): Unit = {
val DefInterface(choices) = handleLookup(ctx, interface.lookupInterface(ifaceTcon))
def checkIfaceImplementation(tplTcon: TypeConName, impl: TemplateImplements): Unit = {
val DefInterface(choices, methods @ _) =
handleLookup(ctx, interface.lookupInterface(impl.interface))
choices.values.foreach { case InterfaceChoice(name, consuming, argType, returnType) =>
val tplChoice = handleLookup(ctx, interface.lookupChoice(tplTcon, name))
if (tplChoice.consuming != consuming)
throw EBadInterfaceChoiceImplConsuming(
ctx,
ifaceTcon,
impl.interface,
tplTcon,
name,
consuming,
@ -478,7 +479,7 @@ private[validation] object Typing {
if (!alphaEquiv(tplChoice.argBinder._2, argType))
throw EBadInterfaceChoiceImplArgType(
ctx,
ifaceTcon,
impl.interface,
tplTcon,
name,
argType,
@ -487,13 +488,15 @@ private[validation] object Typing {
if (!alphaEquiv(tplChoice.returnType, returnType))
throw EBadInterfaceChoiceImplRetType(
ctx,
ifaceTcon,
impl.interface,
tplTcon,
name,
returnType,
tplChoice.returnType,
)
}
// TODO https://github.com/digital-asset/daml/issues/11006
// check methods as well
}
def checkDefException(excepName: TypeConName, defException: DefException): Unit = {
@ -1151,6 +1154,10 @@ private[validation] object Typing {
checkImplements(tpl, iface)
checkExpr(value, TTyCon(iface))
TOptional(TTyCon(tpl))
case ECallInterface(iface, methodName, value) =>
val method = handleLookup(ctx, interface.lookupInterfaceMethod(iface, methodName))
checkExpr(value, TTyCon(iface))
method.returnType
case EExperimental(_, typ) =>
typ
}

View File

@ -68,6 +68,8 @@ private[validation] object ExprIterable {
Iterator(value)
case EFromInterface(iface @ _, tpl @ _, value) =>
Iterator(value)
case ECallInterface(iface @ _, method @ _, value) =>
Iterator(value)
}
}
@ -139,7 +141,7 @@ private[validation] object ExprIterable {
choices,
observers,
key,
implements @ _,
implements @ _, // TODO https://github.com/digital-asset/daml/issues/11006
) =>
Iterator(precond, signatories, agreementText) ++
choices.values.iterator.flatMap(iterator(_)) ++

View File

@ -82,6 +82,8 @@ private[validation] object TypeIterable {
Iterator(TTyCon(iface), TTyCon(tpl)) ++ iterator(value)
case EFromInterface(iface, tpl, value) =>
Iterator(TTyCon(iface), TTyCon(tpl)) ++ iterator(value)
case ECallInterface(iface, _, value) =>
Iterator(TTyCon(iface)) ++ iterator(value)
case EVar(_) | EVal(_) | EBuiltin(_) | EPrimCon(_) | EPrimLit(_) | EApp(_, _) | ECase(_, _) |
ELocation(_, _) | EStructCon(_) | EStructProj(_, _) | EStructUpd(_, _, _) | ETyAbs(_, _) |
EExperimental(_, _) =>
@ -191,7 +193,7 @@ private[validation] object TypeIterable {
choices.values.flatMap(iterator(_)) ++
iterator(observers) ++
key.iterator.flatMap(iterator(_)) ++
implements.iterator.map(TTyCon(_))
implements.values.flatMap(iterator(_))
}
private[validation] def iterator(choice: TemplateChoice): Iterator[Type] =
@ -221,6 +223,38 @@ private[validation] object TypeIterable {
iterator(maintainers)
}
private[validation] def iterator(impl: TemplateImplements): Iterator[Type] =
impl match {
case TemplateImplements(interface, methods) =>
Iterator(TTyCon(interface)) ++
methods.values.flatMap(iterator(_))
}
private[validation] def iterator(method: TemplateImplementsMethod): Iterator[Type] =
method match {
case TemplateImplementsMethod(name @ _, value) =>
iterator(value)
}
private[validation] def iterator(interface: DefInterface): Iterator[Type] =
interface match {
case DefInterface(choices, methods) =>
choices.values.iterator.flatMap(iterator(_)) ++
methods.values.iterator.flatMap(iterator(_))
}
private[validation] def iterator(ichoice: InterfaceChoice): Iterator[Type] =
ichoice match {
case InterfaceChoice(name @ _, consuming @ _, argType, retType) =>
Iterator(argType, retType)
}
private[validation] def iterator(imethod: InterfaceMethod): Iterator[Type] =
imethod match {
case InterfaceMethod(name @ _, retType) =>
Iterator(retType)
}
def apply(typ: Type): Iterable[Type] =
new Iterable[Type] {
override def iterator = that.iterator(typ)
@ -235,6 +269,7 @@ private[validation] object TypeIterable {
new Iterable[Type] {
override def iterator: Iterator[Type] =
module.definitions.values.iterator.flatMap(that.iterator(_)) ++
module.interfaces.values.iterator.flatMap(that.iterator(_)) ++
module.templates.values.iterator.flatMap(that.iterator(_))
}
}