LF: Add interface fixed choices in scala AST and type checker (#11146)

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Remy 2021-10-08 08:59:26 +02:00 committed by GitHub
parent 08b9f82614
commit 4336184a79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 101 additions and 41 deletions

View File

@ -284,7 +284,7 @@ private[archive] class DecodeV1(minor: LV.Minor) {
} else {
lfModule.getInterfacesList.asScala.foreach { defn =>
val defName = getInternedDottedName(defn.getTyconInternedDname)
interfaces += (defName -> decodeDefInterface(defn))
interfaces += (defName -> decodeDefInterface(defName, defn))
}
}
@ -662,15 +662,20 @@ private[archive] class DecodeV1(minor: LV.Minor) {
DefException(decodeExpr(lfException.getMessage, s"$exceptionName:message"))
private[this] def decodeDefInterface(
lfInterface: PLF.DefInterface
id: DottedName,
lfInterface: PLF.DefInterface,
): DefInterface =
DefInterface(
lfInterface.getChoicesList.asScala.toList
param = getInternedName(lfInterface.getParamInternedStr, "DefInterface.param"),
virtualChoices = lfInterface.getChoicesList.asScala.view
.map(decodeInterfaceChoice)
.map(choice => (choice.name, choice)),
lfInterface.getMethodsList.asScala.toList
.map(choice => choice.name -> choice),
fixedChoices = lfInterface.getFixedChoicesList.asScala.view
.map(decodeChoice(id, _))
.map(choice => choice.name -> choice),
methods = lfInterface.getMethodsList.asScala.view
.map(decodeInterfaceMethod)
.map(method => (method.name, method)),
.map(method => method.name -> method),
)
private[this] def decodeInterfaceChoice(

View File

@ -724,7 +724,7 @@ private[daml] class EncodeV1(minor: LV.Minor) {
val (dottedName, interface) = nameWithDef
val builder = PLF.DefInterface.newBuilder()
builder.setTyconInternedDname(dottedNameTable.insert(dottedName))
builder.accumulateLeft(interface.choices.sortByKey)(_ addChoices _)
builder.accumulateLeft(interface.virtualChoices.sortByKey)(_ addChoices _)
// TODO https://github.com/digital-asset/daml/issues/11006
// encode interface methods as well
builder.build()

View File

@ -51,7 +51,7 @@ private[lf] final class CommandPreprocessor(
case Left(_) =>
handleLookup(interface.lookupChoice(identifier, choiceId)).argBinder._2
case Right(interfaceChoice) =>
interfaceChoice.argType
interfaceChoice.fold(_.argType, _.argBinder._2)
}
val arg = valueTranslator.unsafeTranslateValue(choice, argument)
speedy.Command.Exercise(identifier, cid, choiceId, arg)

View File

@ -338,7 +338,7 @@ private[lf] final class Compiler(
module.interfaces.foreach { case (ifaceName, iface) =>
val identifier = Identifier(pkgId, QualifiedName(module.name, ifaceName))
addDef(compileFetchInterface(identifier))
iface.choices.values.foreach(
iface.virtualChoices.values.foreach(
builder += compileChoiceInterface(identifier, _)
)
}

View File

@ -637,28 +637,56 @@ object Ast {
type TemplateKeySignature = GenTemplateKey[Unit]
val TemplateKeySignature = new GenTemplateKeyCompanion[Unit]
final case class DefInterface(
choices: Map[ChoiceName, InterfaceChoice],
final case class GenDefInterface[E](
param: ExprVarName, // Binder for template argument.
virtualChoices: Map[ChoiceName, InterfaceChoice],
fixedChoices: Map[ChoiceName, GenTemplateChoice[E]],
methods: Map[MethodName, InterfaceMethod],
)
) {
virtualChoices.keys.foreach(name =>
if (fixedChoices.isDefinedAt(name))
throw PackageError(s"collision on interface choice name $name")
)
}
object DefInterface {
final class GenDefInterfaceCompanion[E] {
def apply(
choices: Iterable[(ChoiceName, InterfaceChoice)],
param: ExprVarName, // Binder for template argument.
virtualChoices: Iterable[(ChoiceName, InterfaceChoice)],
fixedChoices: Iterable[(ChoiceName, GenTemplateChoice[E])],
methods: Iterable[(MethodName, InterfaceMethod)],
): DefInterface = {
val choiceMap = toMapWithoutDuplicate(
choices,
): GenDefInterface[E] = {
val virtualChoiceMap = toMapWithoutDuplicate(
virtualChoices,
(name: ChoiceName) => throw PackageError(s"collision on interface choice name $name"),
)
val fixedChoiceMap = toMapWithoutDuplicate(
fixedChoices,
(name: ChoiceName) => throw PackageError(s"collision on interface choice name $name"),
)
val methodMap = toMapWithoutDuplicate(
methods,
(name: MethodName) => throw PackageError(s"collision on interface method name $name"),
)
DefInterface(choiceMap, methodMap)
GenDefInterface(param, virtualChoiceMap, fixedChoiceMap, methodMap)
}
def unapply(arg: GenDefInterface[E]): Some[
(
ExprVarName,
Map[ChoiceName, InterfaceChoice],
Map[ChoiceName, GenTemplateChoice[E]],
Map[MethodName, InterfaceMethod],
)
] =
Some((arg.param, arg.virtualChoices, arg.fixedChoices, arg.methods))
}
type DefInterface = GenDefInterface[Expr]
val DefInterface = new GenDefInterfaceCompanion[Expr]
type DefInterfaceSignature = GenDefInterface[Unit]
val DefInterfaceSignature = new GenDefInterfaceCompanion[Unit]
final case class InterfaceChoice(
name: ChoiceName,
consuming: Boolean,
@ -925,7 +953,7 @@ object Ast {
definitions: Map[DottedName, GenDefinition[E]],
templates: Map[DottedName, GenTemplate[E]],
exceptions: Map[DottedName, GenDefException[E]],
interfaces: Map[DottedName, DefInterface],
interfaces: Map[DottedName, GenDefInterface[E]],
featureFlags: FeatureFlags,
) {
templates.keysIterator.foreach(name =>
@ -953,7 +981,7 @@ object Ast {
definitions: Iterable[(DottedName, GenDefinition[E])],
templates: Iterable[(DottedName, GenTemplate[E])],
exceptions: Iterable[(DottedName, GenDefException[E])],
interfaces: Iterable[(DottedName, DefInterface)],
interfaces: Iterable[(DottedName, GenDefInterface[E])],
featureFlags: FeatureFlags,
): GenModule[E] = {
@ -990,7 +1018,7 @@ object Ast {
Map[DottedName, GenDefinition[E]],
Map[DottedName, GenTemplate[E]],
Map[DottedName, GenDefException[E]],
Map[DottedName, DefInterface],
Map[DottedName, GenDefInterface[E]],
FeatureFlags,
)
] = Some(

View File

@ -182,14 +182,14 @@ private[lf] class PackageInterface(signatures: PartialFunction[PackageId, Packag
private[this] def lookupInterface(
name: TypeConName,
context: => Reference,
): Either[LookupError, DefInterface] =
): Either[LookupError, DefInterfaceSignature] =
lookupModule(name.packageId, name.qualifiedName.module, context).flatMap(
_.interfaces
.get(name.qualifiedName.name)
.toRight(LookupError(Reference.Interface(name), context))
)
def lookupInterface(name: TypeConName): Either[LookupError, DefInterface] =
def lookupInterface(name: TypeConName): Either[LookupError, DefInterfaceSignature] =
lookupInterface(name, Reference.Interface(name))
private[this] def lookupChoice(
@ -211,15 +211,23 @@ private[lf] class PackageInterface(signatures: PartialFunction[PackageId, Packag
ifaceName: TypeConName,
chName: ChoiceName,
context: => Reference,
): Either[LookupError, InterfaceChoice] =
lookupInterface(ifaceName, context).flatMap(
_.choices.get(chName).toRight(LookupError(Reference.Choice(ifaceName, chName), context))
): Either[LookupError, Either[InterfaceChoice, TemplateChoiceSignature]] =
lookupInterface(ifaceName, context).flatMap(iface =>
iface.virtualChoices.get(chName) match {
case Some(choice) =>
Right(Left(choice))
case None =>
iface.fixedChoices.get(chName) match {
case Some(choice) => Right(Right(choice))
case None => Left(LookupError(Reference.Choice(ifaceName, chName), context))
}
}
)
def lookupInterfaceChoice(
ifaceName: TypeConName,
chName: ChoiceName,
): Either[LookupError, InterfaceChoice] =
): Either[LookupError, Either[InterfaceChoice, TemplateChoiceSignature]] =
lookupInterfaceChoice(ifaceName, chName, Reference.Choice(ifaceName, chName))
private[this] def lookupInterfaceMethod(

View File

@ -230,6 +230,17 @@ object Util {
)
}
private def toSignature(interface: DefInterface): DefInterfaceSignature =
interface match {
case DefInterface(param, virtualChoices, fixedChoices, methods) =>
DefInterfaceSignature(
param,
virtualChoices,
fixedChoices.transform((_, choice) => toSignature(choice)),
methods,
)
}
private[this] def toSignature(module: Module): ModuleSignature =
module match {
case Module(name, definitions, templates, exceptions, interfaces, featureFlags) =>
@ -242,7 +253,7 @@ object Util {
},
templates = templates.transform((_, template) => toSignature(template)),
exceptions = exceptions.transform((_, _) => DefExceptionSignature),
interfaces = interfaces,
interfaces = interfaces.transform((_, iface) => toSignature(iface)),
featureFlags = featureFlags,
)
}

View File

@ -323,10 +323,12 @@ private[daml] class AstRewriter(
def apply(x: DefInterface): DefInterface =
x match {
case DefInterface(choices, methods) =>
case DefInterface(param, virtualChoices, fixedChoices, methods) =>
DefInterface(
choices.transform((_, x) => apply(x)),
methods.transform((_, x) => apply(x)),
param,
virtualChoices.transform((_, v) => apply(v)),
fixedChoices.transform((_, v) => apply(v)),
methods.transform((_, v) => apply(v)),
)
}
}

View File

@ -308,8 +308,8 @@ private[validation] object Typing {
// uniquess of choice names is already checked on construction of the choice map.
val tyConName = TypeConName(pkgId, QualifiedName(mod.name, ifaceName))
val env = Env(langVersion, interface, ContextDefInterface(tyConName), Map.empty)
defInterface.choices.values.foreach { env.checkIfaceChoice(_) }
defInterface.methods.values.foreach { env.checkIfaceMethod(_) }
defInterface.virtualChoices.values.foreach(env.checkIfaceChoice(_))
defInterface.methods.values.foreach(env.checkIfaceMethod(_))
}
}
@ -468,9 +468,9 @@ private[validation] object Typing {
}
def checkIfaceImplementation(tplTcon: TypeConName, impl: TemplateImplements): Unit = {
val DefInterface(choices, methods) =
val DefInterfaceSignature(_, virtualChoices, _, methods) =
handleLookup(ctx, interface.lookupInterface(impl.interface))
choices.values.foreach { case InterfaceChoice(name, consuming, argType, returnType) =>
virtualChoices.values.foreach { case InterfaceChoice(name, consuming, argType, returnType) =>
val tplChoice = handleLookup(ctx, interface.lookupChoice(tplTcon, name))
if (tplChoice.consuming != consuming)
throw EBadInterfaceChoiceImplConsuming(
@ -920,10 +920,15 @@ private[validation] object Typing {
cid: Expr,
arg: Expr,
): Type = {
val choice = handleLookup(ctx, interface.lookupInterfaceChoice(tpl, chName))
checkExpr(cid, TContractId(TTyCon(tpl)))
checkExpr(arg, choice.argType)
TUpdate(choice.returnType)
handleLookup(ctx, interface.lookupInterfaceChoice(tpl, chName)) match {
case Left(virtualChoice) =>
checkExpr(arg, virtualChoice.argType)
TUpdate(virtualChoice.returnType)
case Right(fixedChoice) =>
checkExpr(arg, fixedChoice.argBinder._2)
TUpdate(fixedChoice.returnType)
}
}
private def typeOfExerciseByKey(

View File

@ -238,9 +238,10 @@ private[validation] object TypeIterable {
private[validation] def iterator(interface: DefInterface): Iterator[Type] =
interface match {
case DefInterface(choices, methods) =>
choices.values.iterator.flatMap(iterator(_)) ++
methods.values.iterator.flatMap(iterator(_))
case DefInterface(_, virtualChoices, fixedChoice, methods) =>
virtualChoices.values.iterator.flatMap(iterator) ++
fixedChoice.values.iterator.flatMap(iterator) ++
methods.values.iterator.flatMap(iterator)
}
private[validation] def iterator(ichoice: InterfaceChoice): Iterator[Type] =