Revert "remove support of retroactie interface instance in api-type-s… (#18856)

* Revert "remove support of retroactie interface instance in api-type-signature (#18382)"
This commit is contained in:
Remy 2024-03-25 17:21:05 +01:00 committed by GitHub
parent 179183441a
commit d786b53522
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 185 additions and 6 deletions

View File

@ -12,7 +12,17 @@ import scalaz.syntax.semigroup._
import scalaz.syntax.traverse._
import scalaz.syntax.std.map._
import scalaz.syntax.std.option._
import scalaz.{Applicative, Bifunctor, Bitraverse, Bifoldable, Foldable, Functor, Monoid, Traverse}
import scalaz.{
Applicative,
Bifunctor,
Bitraverse,
Bifoldable,
Foldable,
Functor,
Monoid,
Semigroup,
Traverse,
}
import scalaz.Tags.FirstVal
import java.{util => j}
@ -203,6 +213,29 @@ final case class DefTemplate[+Ty](
}
def getKey: j.Optional[_ <: Ty] = key.toJava
private[typesig] def extendWithInterface[OTy >: Ty](
ifaceName: Ref.TypeConName,
ifc: DefInterface[OTy],
): DefTemplate[OTy] = {
import TemplateChoices.{Resolved, Unresolved}
copy(
implementedInterfaces = implementedInterfaces :+ ifaceName,
tChoices = tChoices match {
case unr @ Unresolved(_, sources) =>
unr.copy(unresolvedChoiceSources = sources incl ifaceName)
// If unresolved, we need only add ifc as a future interface to resolve;
// otherwise, we must self-resolve and add to preexisting resolutions
case r @ Resolved(rc) =>
type K[C] = Semigroup[Resolved.Choices[C]]
r.copy(resolvedChoices =
FirstVal
.unsubst[K, TemplateChoice[OTy]](Semigroup.apply)
.append(rc, ifc choicesAsResolved ifaceName)
)
},
)
}
}
object DefTemplate {
@ -282,16 +315,14 @@ sealed abstract class TemplateChoices[+Ty] extends Product with Serializable {
): Either[ResolveError[Resolved[O]], Resolved[O]] = this match {
case Unresolved(direct, unresolved) =>
val getAstInterface = astInterfaces.lift
type ResolutionResult[C] =
(Set[Ref.TypeConName], Map[Ref.ChoiceName, NonEmpty[Map[Option[Ref.TypeConName], C]]])
type ResolutionResult[C] = (Set[Ref.TypeConName], Resolved.Choices[C])
val (missing, resolved): ResolutionResult[TemplateChoice[O]] =
FirstVal.unsubst[ResolutionResult, TemplateChoice[O]](
unresolved.forgetNE
.foldMap { tcn =>
getAstInterface(tcn).cata(
{ astIf =>
val tcnResolved =
astIf.choices.transform((_, tc) => NonEmpty(Map, some(tcn) -> tc))
val tcnResolved = astIf choicesAsResolved tcn
FirstVal.subst[ResolutionResult, TemplateChoice[O]](
Set.empty[Ref.TypeConName],
tcnResolved,
@ -350,6 +381,11 @@ object TemplateChoices {
object Resolved {
private[daml] def fromDirect[Ty](directChoices: Map[Ref.ChoiceName, TemplateChoice[Ty]]) =
Resolved(directAsResolved(directChoices))
// choice type abstracted over the TemplateChoice, for specifying
// aggregation of choices (typically with tags, foldMap, semigroup)
private[typesig] type Choices[C] =
Map[Ref.ChoiceName, NonEmpty[Map[Option[Ref.TypeConName], C]]]
}
implicit val `TemplateChoices traverse`: Traverse[TemplateChoices] = new Traverse[TemplateChoices]
@ -388,13 +424,38 @@ object TemplateChoice {
}
/** @param choices Choices of this interface, indexed by name
* @param retroImplements IDs of templates that implement this interface, upon
* introduction of this interface into the environment
*/
final case class DefInterface[+Ty](
choices: Map[Ref.ChoiceName, TemplateChoice[Ty]],
viewType: Option[Ref.TypeConName],
// retroImplements are used only by LF 1.x
retroImplements: Set[Ref.TypeConName] = Set.empty,
) {
def getChoices: j.Map[Ref.ChoiceName, _ <: TemplateChoice[Ty]] =
choices.asJava
// Restructure `choices` in the resolved-choices data structure format,
// for aggregation with [[TemplateChoices.Resolved]].
private[typesig] def choicesAsResolved[Name](
selfName: Name
): Map[Ref.ChoiceName, NonEmpty[Map[Option[Name], TemplateChoice[Ty]]]] =
choices transform ((_, tc) => NonEmpty(Map, some(selfName) -> tc))
private[typesig] def resolveRetroImplements[S, OTy >: Ty](selfName: Ref.TypeConName, s: S)(
setTemplate: SetterAt[Ref.TypeConName, S, DefTemplate[OTy]]
): (S, DefInterface[OTy]) = {
def addMySelf(dt: DefTemplate[OTy]) =
dt.extendWithInterface(selfName, this)
retroImplements
.foldLeft((s, retroImplements)) { (sr, tplName) =>
val (s, remaining) = sr
setTemplate(s, tplName).cata(setter => (setter(addMySelf), remaining - tplName), sr)
}
.map(remaining => copy(retroImplements = remaining))
}
}
object DefInterface extends FWTLike[DefInterface] {

View File

@ -8,6 +8,8 @@ import com.daml.lf.archive.Dar
import data.Ref, Ref.{Identifier, PackageId}
import scala.collection.immutable.Map
import scalaz.std.tuple._
import scalaz.syntax.functor._
import scalaz.syntax.std.map._
import scalaz.Semigroup
@ -45,6 +47,21 @@ final case class EnvironmentSignature(
}
})
def resolveRetroImplements: EnvironmentSignature = {
import PackageSignature.findTemplate
val (newTypeDecls, newInterfaces) = interfaces.foldLeft((typeDecls, interfaces)) {
case ((typeDecls, interfaces), (ifTc, defIf)) =>
defIf
.resolveRetroImplements(ifTc, typeDecls) { case (typeDecls, tplName) =>
findTemplate(typeDecls, tplName) map { itt => f =>
typeDecls.updated(tplName, itt.copy(template = f(itt.template)))
}
}
.map(defIf => interfaces.updated(ifTc, defIf))
}
copy(typeDecls = newTypeDecls, interfaces = newInterfaces)
}
def resolveInterfaceViewType(tcn: Ref.TypeConName): Option[DefInterface.ViewTypeFWT] =
typeDecls get tcn flatMap (_.asInterfaceViewType)
}

View File

@ -11,9 +11,11 @@ import reader.Errors
import com.daml.daml_lf_dev.DamlLf
import com.daml.lf.archive.ArchivePayload
import scalaz.std.either._
import scalaz.std.tuple._
import scalaz.syntax.bifunctor._
import scalaz.syntax.std.boolean._
import scala.collection.immutable.Map
import scala.collection.immutable.{Map, SeqOps}
import scala.jdk.CollectionConverters._
// Duplicate of the one in com.daml.lf.language to separate Ast and Iface
@ -98,6 +100,41 @@ final case class PackageSignature(
findInterface: PartialFunction[Ref.TypeConName, DefInterface.FWT]
): PackageSignature = resolveChoices(findInterface, failIfUnresolvedChoicesLeft = false)
/** Update internal templates, as well as external templates via `setTemplates`,
* with retroactive interface implementations. Note retroactive interfaces are
* available only on LF 1.x
*
* @param setTemplate Used to look up templates that can't be found in this
* interface
*/
private def resolveRetroImplements[S](
s: S
)(setTemplate: SetterAt[Ref.TypeConName, S, DefTemplate.FWT]): (S, PackageSignature) = {
type SandTpls = (S, Map[QualifiedName, TypeDecl.Template])
def setTpl(
sm: SandTpls,
tcn: Ref.TypeConName,
): Option[(DefTemplate.FWT => DefTemplate.FWT) => SandTpls] = {
import PackageSignature.findTemplate
val (s, tplsM) = sm
if (tcn.packageId == packageId)
findTemplate(tplsM orElse typeDecls, tcn.qualifiedName).map {
case itt @ TypeDecl.Template(_, dt) =>
f => (s, tplsM.updated(tcn.qualifiedName, itt.copy(template = f(dt))))
}
else setTemplate(s, tcn) map (_ andThen ((_, tplsM)))
}
val ((sEnd, newTpls), newIfcs) = interfaces.foldLeft(
((s, Map.empty): SandTpls, Map.empty[QualifiedName, DefInterface.FWT])
) { case ((s, astIfs), (ifcName, astIf)) =>
astIf
.resolveRetroImplements(Ref.TypeConName(packageId, ifcName), s)(setTpl)
.rightMap(newIf => astIfs.updated(ifcName, newIf))
}
(sEnd, copy(typeDecls = typeDecls ++ newTpls, interfaces = newIfcs))
}
private def resolveInterfaceViewType(n: Ref.QualifiedName): Option[Record.FWT] =
typeDecls get n flatMap (_.asInterfaceViewType)
}
@ -156,6 +193,55 @@ object PackageSignature {
): Option[TypeDecl.Template] =
m.lift(k) collect { case itt: TypeDecl.Template => itt }
// Given a lookup function for package state setters, produce a lookup function
// for setters on specific templates in that set of packages.
private[this] def setPackageTemplates[S](
findPackage: GetterSetterAt[PackageId, S, PackageSignature]
): SetterAt[Ref.TypeConName, S, DefTemplate.FWT] = {
def go(s: S, tcn: Ref.TypeConName): Option[(DefTemplate.FWT => DefTemplate.FWT) => S] = for {
foundPkg <- findPackage(s, tcn.packageId)
(ifc, sIfc) = foundPkg
itt <- findTemplate(ifc.typeDecls, tcn.qualifiedName)
} yield f =>
sIfc(
ifc.copy(typeDecls =
ifc.typeDecls.updated(tcn.qualifiedName, itt.copy(template = f(itt.template)))
)
)
go
}
/** Extend the set of interfaces represented by `s` and `findPackage` with
* `newSignatures`. Produce the resulting `S` and a replacement copy of
* `newSignatures` with templates and interfaces therein resolved.
*
* Does not search members of `s` for fresh interfaces.
*/
def resolveRetroImplements[S, CC[B] <: Seq[B] with SeqOps[B, CC, CC[B]]](
s: S,
newSignatures: CC[PackageSignature],
)(
findPackage: GetterSetterAt[PackageId, S, PackageSignature]
): (S, CC[PackageSignature]) = {
type St = (S, CC[PackageSignature])
val findTpl = setPackageTemplates[St] { case ((s, newSignatures), pkgId) =>
findPackage(s, pkgId).map(_.rightMap(_ andThen ((_, newSignatures)))).orElse {
val ix = newSignatures indexWhere (_.packageId == pkgId)
(ix >= 0) option ((newSignatures(ix), newSig => (s, newSignatures.updated(ix, newSig))))
}
}
(0 until newSignatures.size).foldLeft((s, newSignatures)) {
case (st @ (_, newSignatures), ifcK) =>
val ((s2, newSignatures2), newAtIfcK) =
newSignatures(ifcK).resolveRetroImplements(st)(findTpl)
// the tricky part here: newSignatures2 is guaranteed not to have altered
// the value at ifcK, and to have made all "self" changes in newAtIfcK.
// So there is no conflict, we can discard the value in the seq
(s2, newSignatures2.updated(ifcK, newAtIfcK))
}
}
/** An argument for [[PackageSignature#resolveChoices]] given a package database,
* such as json-api's `LedgerReader.PackageStore`.
*/

View File

@ -201,6 +201,14 @@ class SignatureReaderSpec extends AnyWordSpec with Matchers with Inside {
}
lazy val itpES = EnvironmentSignature.fromPackageSignatures(itp).resolveChoices
lazy val itpWithoutRetroImplements = itp.copy(
main = itp.main.copy(
interfaces = itp.main.interfaces - qn("RetroInterface:RetroIf")
)
)
lazy val itpESWithoutRetroImplements =
EnvironmentSignature.fromPackageSignatures(itpWithoutRetroImplements).resolveChoices
"load without errors" in {
itp shouldBe itp
}
@ -321,6 +329,13 @@ class SignatureReaderSpec extends AnyWordSpec with Matchers with Inside {
Bar -> Set(None),
)
}
"resolve retro implements harmlessly when there are none" in {
PackageSignature.resolveRetroImplements((), itpWithoutRetroImplements.all)((_, _) =>
None
) should ===((), itpWithoutRetroImplements.all)
itpESWithoutRetroImplements.resolveRetroImplements should ===(itpESWithoutRetroImplements)
}
}
private def wrappInModule(dataName: DottedName, dfn: Ast.DDataType) =