mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
LF: ad-hoc data struct to store LF Struct like data (#7220)
A `Struct[+X]` is a list of pair `(FieldName, X)` ordered by first component. We use this data-structure to represent TStruct LF type. It will be used in upcomming PRs to sorted fields in Struct values. CHANGELOG_BEGIN CHANGELOG_END
This commit is contained in:
parent
48e05e25e2
commit
ada8ba033b
@ -8,7 +8,7 @@ import java.util
|
||||
|
||||
import com.daml.lf.archive.Decode.ParseError
|
||||
import com.daml.lf.data.Ref._
|
||||
import com.daml.lf.data.{Decimal, ImmArray, Numeric, Time}
|
||||
import com.daml.lf.data.{Decimal, ImmArray, Numeric, Struct, Time}
|
||||
import ImmArray.ImmArraySeq
|
||||
import com.daml.lf.language.Ast._
|
||||
import com.daml.lf.language.Util._
|
||||
@ -639,8 +639,7 @@ private[archive] class DecodeV1(minor: LV.Minor) extends Decode.OfPackage[PLF.Pa
|
||||
val struct = lfType.getStruct
|
||||
val fields = struct.getFieldsList.asScala
|
||||
assertNonEmpty(fields, "fields")
|
||||
TStruct(fields.map(decodeFieldWithType)(breakOut))
|
||||
|
||||
TStruct(Struct(fields.map(decodeFieldWithType): _*))
|
||||
case PLF.Type.SumCase.SUM_NOT_SET =>
|
||||
throw ParseError("Type.SUM_NOT_SET")
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.lf
|
||||
package data
|
||||
|
||||
import com.daml.lf.data.Ref.Name
|
||||
|
||||
final case class Struct[+X] private (sortedFields: ImmArray[(Ref.Name, X)]) extends NoCopy {
|
||||
|
||||
def lookup(name: Ref.Name): Option[X] =
|
||||
sortedFields.find(_._1 == name).map(_._2)
|
||||
|
||||
def mapValue[Y](f: X => Y) = new Struct(sortedFields.map { case (k, v) => k -> f(v) })
|
||||
|
||||
def toImmArray: ImmArray[(Ref.Name, X)] = sortedFields
|
||||
|
||||
def names: Iterator[Ref.Name] = iterator.map(_._1)
|
||||
|
||||
def values: Iterator[X] = iterator.map(_._2)
|
||||
|
||||
def iterator: Iterator[(Ref.Name, X)] = sortedFields.iterator
|
||||
|
||||
def foreach(f: ((Ref.Name, X)) => Unit): Unit = sortedFields.foreach(f)
|
||||
|
||||
}
|
||||
|
||||
object Struct {
|
||||
|
||||
def apply[X](fields: (Name, X)*): Struct[X] =
|
||||
new Struct(fields.sortBy(_._1: String).to[ImmArray])
|
||||
|
||||
def apply[X](fields: ImmArray[(Name, X)]): Struct[X] = apply(fields.toSeq: _*)
|
||||
|
||||
def fromSortedImmArray[X](fields: ImmArray[(Ref.Name, X)]): Either[String, Struct[X]] = {
|
||||
val struct = new Struct(fields)
|
||||
Either.cond(
|
||||
(struct.names zip struct.names.drop(1)).forall { case (x, y) => (x compare y) <= 0 },
|
||||
struct,
|
||||
s"the list $fields is not sorted by name"
|
||||
)
|
||||
}
|
||||
|
||||
private[this] val Emtpy = new Struct(ImmArray.empty)
|
||||
|
||||
def empty[X]: Struct[X] = Emtpy.asInstanceOf[Struct[X]]
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.lf.data
|
||||
|
||||
import org.scalatest.prop.PropertyChecks
|
||||
import org.scalatest.{Matchers, WordSpec}
|
||||
|
||||
class StructSpec extends WordSpec with Matchers with PropertyChecks {
|
||||
|
||||
private[this] val List(f1, f2, f3) = List("f1", "f2", "f3").map(Ref.Name.assertFromString)
|
||||
|
||||
"SortMap.fromSortedImmArray" should {
|
||||
|
||||
"fail if the input list is not sorted" in {
|
||||
|
||||
val negativeTestCases =
|
||||
Table(
|
||||
"list",
|
||||
ImmArray.empty,
|
||||
ImmArray(f1 -> 1),
|
||||
ImmArray(f1 -> 1, f1 -> 2),
|
||||
ImmArray(f1 -> 1, f2 -> 2, f3 -> 3),
|
||||
)
|
||||
|
||||
val positiveTestCases = Table(
|
||||
"list",
|
||||
ImmArray(f1 -> 1, f2 -> 2, f3 -> 3, f1 -> 2),
|
||||
ImmArray(f2 -> 2, f3 -> 3, f1 -> 1),
|
||||
)
|
||||
|
||||
forEvery(negativeTestCases)(l => Struct.fromSortedImmArray(l) shouldBe 'right)
|
||||
|
||||
forEvery(positiveTestCases)(l => Struct.fromSortedImmArray(l) shouldBe 'left)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
"SortMap.apply" should {
|
||||
"sorted fields" in {
|
||||
|
||||
val testCases =
|
||||
Table(
|
||||
"list",
|
||||
Struct(),
|
||||
Struct(f1 -> 1),
|
||||
Struct(f1 -> 1, f1 -> 2),
|
||||
Struct(f1 -> 1, f2 -> 2, f3 -> 3),
|
||||
Struct(f1 -> 1, f2 -> 2, f3 -> 3, f1 -> 2),
|
||||
Struct(f2 -> 2, f3 -> 3, f1 -> 1),
|
||||
)
|
||||
|
||||
forEvery(testCases) { s =>
|
||||
s.names.toSeq shouldBe s.names.toSeq.sorted[String]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -262,7 +262,8 @@ private[daml] class EncodeV1(val minor: LV.Minor) {
|
||||
PLF.Type.Forall.newBuilder().accumulateLeft(binders)(_ addVars _).setBody(body))
|
||||
case TStruct(fields) =>
|
||||
expect(args.isEmpty)
|
||||
builder.setStruct(PLF.Type.Struct.newBuilder().accumulateLeft(fields)(_ addFields _))
|
||||
builder.setStruct(
|
||||
PLF.Type.Struct.newBuilder().accumulateLeft(fields.toImmArray)(_ addFields _))
|
||||
case TSynApp(name, args) =>
|
||||
val b = PLF.Type.Syn.newBuilder()
|
||||
b.setTysyn(name)
|
||||
|
@ -6,7 +6,7 @@ package svalue
|
||||
|
||||
import java.util
|
||||
|
||||
import com.daml.lf.data.{FrontStack, ImmArray, Numeric, Ref, Time}
|
||||
import com.daml.lf.data.{FrontStack, ImmArray, Numeric, Ref, Struct, Time}
|
||||
import com.daml.lf.language.{Ast, Util => AstUtil}
|
||||
import com.daml.lf.speedy.SResult._
|
||||
import com.daml.lf.speedy.SValue._
|
||||
@ -168,23 +168,23 @@ class OrderingSpec
|
||||
}
|
||||
|
||||
private val typeStructReps = List(
|
||||
ImmArray.empty,
|
||||
ImmArray(Ref.Name.assertFromString("field0") -> AstUtil.TUnit),
|
||||
ImmArray(Ref.Name.assertFromString("field0") -> AstUtil.TInt64),
|
||||
ImmArray(Ref.Name.assertFromString("field1") -> AstUtil.TUnit),
|
||||
ImmArray(
|
||||
Struct.empty,
|
||||
Struct(Ref.Name.assertFromString("field0") -> AstUtil.TUnit),
|
||||
Struct(Ref.Name.assertFromString("field0") -> AstUtil.TInt64),
|
||||
Struct(Ref.Name.assertFromString("field1") -> AstUtil.TUnit),
|
||||
Struct(
|
||||
Ref.Name.assertFromString("field1") -> AstUtil.TUnit,
|
||||
Ref.Name.assertFromString("field2") -> AstUtil.TUnit,
|
||||
),
|
||||
ImmArray(
|
||||
Struct(
|
||||
Ref.Name.assertFromString("field1") -> AstUtil.TUnit,
|
||||
Ref.Name.assertFromString("field2") -> AstUtil.TInt64,
|
||||
),
|
||||
ImmArray(
|
||||
Struct(
|
||||
Ref.Name.assertFromString("field1") -> AstUtil.TInt64,
|
||||
Ref.Name.assertFromString("field2") -> AstUtil.TUnit,
|
||||
),
|
||||
ImmArray(
|
||||
Struct(
|
||||
Ref.Name.assertFromString("field1") -> AstUtil.TUnit,
|
||||
Ref.Name.assertFromString("field3") -> AstUtil.TUnit,
|
||||
),
|
||||
@ -206,8 +206,8 @@ class OrderingSpec
|
||||
Ast.TTyCon(VariantTypeCon),
|
||||
Ast.TNat(Numeric.Scale.MinValue),
|
||||
Ast.TNat(Numeric.Scale.MaxValue),
|
||||
Ast.TStruct(ImmArray.empty),
|
||||
Ast.TStruct(ImmArray(Ref.Name.assertFromString("field") -> AstUtil.TUnit)),
|
||||
Ast.TStruct(Struct.empty),
|
||||
Ast.TStruct(Struct(Ref.Name.assertFromString("field") -> AstUtil.TUnit)),
|
||||
Ast.TApp(Ast.TBuiltin(Ast.BTArrow), Ast.TBuiltin(Ast.BTUnit)),
|
||||
Ast.TApp(
|
||||
Ast.TApp(Ast.TBuiltin(Ast.BTArrow), Ast.TBuiltin(Ast.BTUnit)),
|
||||
|
@ -219,7 +219,7 @@ object Ast {
|
||||
case TForall((v, _), body) =>
|
||||
maybeParens(prec > precTForall, "∀" + v + prettyForAll(body))
|
||||
case TStruct(fields) =>
|
||||
"(" + fields
|
||||
"(" + fields.iterator
|
||||
.map { case (n, t) => n + ": " + prettyType(t, precTForall) }
|
||||
.toSeq
|
||||
.mkString(", ") + ")"
|
||||
@ -264,13 +264,7 @@ object Ast {
|
||||
final case class TForall(binder: (TypeVarName, Kind), body: Type) extends Type
|
||||
|
||||
/** Structs */
|
||||
final case class TStruct private (sortedFields: ImmArray[(FieldName, Type)]) extends Type
|
||||
|
||||
object TStruct extends (ImmArray[(FieldName, Type)] => TStruct) {
|
||||
// should be dropped once the compiler sort fields.
|
||||
def apply(fields: ImmArray[(FieldName, Type)]): TStruct =
|
||||
new TStruct(ImmArray(fields.toSeq.sortBy(_._1: String)))
|
||||
}
|
||||
final case class TStruct(fields: Struct[Type]) extends Type
|
||||
|
||||
sealed abstract class BuiltinType extends Product with Serializable
|
||||
|
||||
|
@ -67,9 +67,7 @@ object TypeOrdering extends Ordering[Ast.Type] {
|
||||
compareType(n1 compareTo n2, stack)
|
||||
case (Ast.TStruct(fields1), Ast.TStruct(fields2)) =>
|
||||
compareType(
|
||||
math.Ordering
|
||||
.Iterable[String]
|
||||
.compare(fields1.toSeq.map(_._1), fields2.toSeq.map(_._1)),
|
||||
Ordering.Iterable[String].compare(fields1.names.toSeq, fields2.names.toSeq),
|
||||
zipAndPush(fields1.iterator.map(_._2), fields2.iterator.map(_._2), stack)
|
||||
)
|
||||
case (Ast.TApp(t11, t12), Ast.TApp(t21, t22)) =>
|
||||
|
@ -36,5 +36,6 @@ da_scala_test(
|
||||
":parser",
|
||||
"//daml-lf/data",
|
||||
"//daml-lf/language",
|
||||
"@maven//:org_scalaz_scalaz_core_2_12",
|
||||
],
|
||||
)
|
||||
|
@ -42,7 +42,7 @@ private[daml] class AstRewriter(
|
||||
case TForall(binder, body) =>
|
||||
TForall(binder, apply(body))
|
||||
case TStruct(fields) =>
|
||||
TStruct(fields.map(apply))
|
||||
TStruct(fields.mapValue(apply))
|
||||
}
|
||||
|
||||
def apply(nameWithType: (Name, Type)): (Name, Type) = nameWithType match {
|
||||
|
@ -4,7 +4,7 @@
|
||||
package com.daml.lf.testing.parser
|
||||
|
||||
import com.daml.lf.data
|
||||
import com.daml.lf.data.{ImmArray, Ref}
|
||||
import com.daml.lf.data.{ImmArray, Ref, Struct}
|
||||
import com.daml.lf.language.Ast._
|
||||
import com.daml.lf.language.Util._
|
||||
import com.daml.lf.testing.parser.Parsers._
|
||||
@ -57,7 +57,7 @@ private[parser] class TypeParser[P](parameters: ParserParameters[P]) {
|
||||
id ~ `:` ~ typ ^^ { case name ~ _ ~ t => name -> t }
|
||||
|
||||
private lazy val tStruct: Parser[Type] =
|
||||
`<` ~>! rep1sep(fieldType, `,`) <~ `>` ^^ (fs => TStruct(ImmArray(fs)))
|
||||
`<` ~>! rep1sep(fieldType, `,`) <~ `>` ^^ (fs => TStruct(Struct(fs: _*)))
|
||||
|
||||
private lazy val tTypeSynApp: Parser[Type] =
|
||||
`|` ~> fullIdentifier ~ rep(typ0) <~ `|` ^^ { case id ~ tys => TSynApp(id, ImmArray(tys)) }
|
||||
|
@ -6,7 +6,7 @@ package com.daml.lf.testing.parser
|
||||
import java.math.BigDecimal
|
||||
|
||||
import com.daml.lf.data.Ref._
|
||||
import com.daml.lf.data.{ImmArray, Numeric, Time}
|
||||
import com.daml.lf.data.{ImmArray, Numeric, Struct, Time}
|
||||
import com.daml.lf.language.Ast._
|
||||
import com.daml.lf.language.Util._
|
||||
import com.daml.lf.testing.parser.Implicits._
|
||||
@ -97,8 +97,8 @@ class ParsersSpec extends WordSpec with TableDrivenPropertyChecks with Matchers
|
||||
"a -> b" -> TApp(TApp(TBuiltin(BTArrow), α), β),
|
||||
"a -> b -> a" -> TApp(TApp(TBuiltin(BTArrow), α), TApp(TApp(TBuiltin(BTArrow), β), α)),
|
||||
"forall (a: *). Mod:T a" -> TForall((α.name, KStar), TApp(T, α)),
|
||||
"<f1: a, f2: Bool, f3:Mod:T>" -> TStruct(
|
||||
ImmArray[(FieldName, Type)](n"f1" -> α, n"f2" -> TBuiltin(BTBool), n"f3" -> T))
|
||||
"<f1: a, f2: Bool, f3:Mod:T>" ->
|
||||
TStruct(Struct(n"f1" -> α, n"f2" -> TBuiltin(BTBool), n"f3" -> T))
|
||||
)
|
||||
|
||||
forEvery(testCases)((stringToParse, expectedType) =>
|
||||
|
@ -366,7 +366,7 @@ object Repl {
|
||||
case TForall((v, _), body) =>
|
||||
maybeParens(prec > precTForall, "∀" + v + prettyForAll(body))
|
||||
case TStruct(fields) =>
|
||||
"(" + fields
|
||||
"(" + fields.iterator
|
||||
.map { case (n, t) => n + ": " + prettyType(t, precTForall) }
|
||||
.toSeq
|
||||
.mkString(", ") + ")"
|
||||
|
@ -4,7 +4,6 @@
|
||||
package com.daml.lf.validation
|
||||
|
||||
import com.daml.lf.language.Ast._
|
||||
import com.daml.lf.validation.Util._
|
||||
|
||||
private[validation] object AlphaEquiv {
|
||||
|
||||
@ -31,7 +30,7 @@ private[validation] object AlphaEquiv {
|
||||
binderDepthRhs + (varName2 -> currentDepth)
|
||||
).alphaEquiv(b1, b2)
|
||||
case (TStruct(fs1), TStruct(fs2)) =>
|
||||
(fs1.keys sameElements fs1.keys) &&
|
||||
(fs1.names sameElements fs2.names) &&
|
||||
(fs1.values zip fs2.values).forall((alphaEquiv _).tupled)
|
||||
case _ => false
|
||||
}
|
||||
|
@ -28,9 +28,7 @@ private[validation] object TypeSubst {
|
||||
} else
|
||||
TForall(v0 -> k, go(fv0 + v0, subst0 - v0, t))
|
||||
case TStruct(ts) =>
|
||||
TStruct(ts.transform { (_, x) =>
|
||||
go(fv0, subst0, x)
|
||||
})
|
||||
TStruct(ts.mapValue(go(fv0, subst0, _)))
|
||||
}
|
||||
|
||||
private def freshTypeVarName(fv: Set[TypeVarName]): TypeVarName =
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.lf.validation
|
||||
|
||||
import com.daml.lf.data.{ImmArray, Numeric}
|
||||
import com.daml.lf.data.{ImmArray, Numeric, Struct}
|
||||
import com.daml.lf.data.Ref._
|
||||
import com.daml.lf.language.Ast._
|
||||
import com.daml.lf.language.Util._
|
||||
@ -119,8 +119,7 @@ private[validation] object Typing {
|
||||
BTextMapToList ->
|
||||
TForall(
|
||||
alpha.name -> KStar,
|
||||
TTextMap(alpha) ->: TList(
|
||||
TStruct(ImmArray(keyFieldName -> TText, valueFieldName -> alpha)))
|
||||
TTextMap(alpha) ->: TList(TStruct(Struct(keyFieldName -> TText, valueFieldName -> alpha)))
|
||||
),
|
||||
BTextMapSize ->
|
||||
TForall(
|
||||
@ -431,8 +430,8 @@ private[validation] object Typing {
|
||||
case TForall((v, k), b) =>
|
||||
introTypeVar(v, k).checkType(b, KStar)
|
||||
KStar
|
||||
case TStruct(recordType) =>
|
||||
checkRecordType(recordType)
|
||||
case TStruct(fields) =>
|
||||
checkRecordType(fields.toImmArray)
|
||||
KStar
|
||||
}
|
||||
|
||||
@ -453,9 +452,7 @@ private[validation] object Typing {
|
||||
case TForall((v, k), b) =>
|
||||
TForall((v, k), introTypeVar(v, k).expandTypeSynonyms(b))
|
||||
case TStruct(recordType) =>
|
||||
TStruct(recordType.transform { (_, x) =>
|
||||
expandTypeSynonyms(x)
|
||||
})
|
||||
TStruct(recordType.mapValue(expandTypeSynonyms(_)))
|
||||
}
|
||||
|
||||
private def expandSynApp(syn: TypeSynName, tArgs: ImmArray[Type]): Type = {
|
||||
@ -518,14 +515,12 @@ private[validation] object Typing {
|
||||
|
||||
private def typeOfStructCon(fields: ImmArray[(FieldName, Expr)]): Type = {
|
||||
checkUniq[FieldName](fields.keys, EDuplicateField(ctx, _))
|
||||
TStruct(fields.transform { (_, x) =>
|
||||
typeOf(x)
|
||||
})
|
||||
TStruct(Struct(fields.iterator.map { case (f, x) => f -> typeOf(x) }.toSeq: _*))
|
||||
}
|
||||
|
||||
private def typeOfStructProj(field: FieldName, expr: Expr): Type = typeOf(expr) match {
|
||||
case TStruct(structType) =>
|
||||
structType.lookup(field, EUnknownField(ctx, field))
|
||||
structType.lookup(field).getOrElse(throw EUnknownField(ctx, field))
|
||||
case typ =>
|
||||
throw EExpectedStructType(ctx, typ)
|
||||
}
|
||||
@ -533,7 +528,7 @@ private[validation] object Typing {
|
||||
private def typeOfStructUpd(field: FieldName, struct: Expr, update: Expr): Type =
|
||||
typeOf(struct) match {
|
||||
case typ @ TStruct(structType) =>
|
||||
checkExpr(update, structType.lookup(field, EUnknownField(ctx, field)))
|
||||
checkExpr(update, structType.lookup(field).getOrElse(throw EUnknownField(ctx, field)))
|
||||
typ
|
||||
case typ =>
|
||||
throw EExpectedStructType(ctx, typ)
|
||||
@ -763,7 +758,7 @@ private[validation] object Typing {
|
||||
// fetches return the contract id and the contract itself
|
||||
TUpdate(
|
||||
TStruct(
|
||||
ImmArray(
|
||||
Struct(
|
||||
(contractIdFieldName, TContractId(TTyCon(retrieveByKey.templateId))),
|
||||
(contractFieldName, TTyCon(retrieveByKey.templateId)))))
|
||||
case UpdateLookupByKey(retrieveByKey) =>
|
||||
|
Loading…
Reference in New Issue
Block a user