From 410f136126698e4f95ca3ecb594b4f9153afb9a5 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 2 Sep 2024 19:21:29 +0300 Subject: [PATCH] SFunc support in SigmaPrint, LSV6 test --- .../src/main/scala/sigma/ast/TypeCodes.scala | 2 - .../sigmastate/helpers/SigmaPPrint.scala | 2 + .../scala/sigma/LanguageSpecificationV5.scala | 24 ------- .../scala/sigma/LanguageSpecificationV6.scala | 72 +++++++++++++++++++ .../test/scala/sigma/SigmaDslTesting.scala | 29 ++++++-- 5 files changed, 97 insertions(+), 32 deletions(-) diff --git a/core/shared/src/main/scala/sigma/ast/TypeCodes.scala b/core/shared/src/main/scala/sigma/ast/TypeCodes.scala index 62a257adb4..68670449db 100644 --- a/core/shared/src/main/scala/sigma/ast/TypeCodes.scala +++ b/core/shared/src/main/scala/sigma/ast/TypeCodes.scala @@ -17,8 +17,6 @@ object TypeCodes { /** SFunc type */ val FuncType: TypeCode = TypeCode @@ (LastDataType + 1).toByte - // val LastFuncType : TypeCode = TypeCode @@ 255.toByte - /** We use optimized encoding of constant values to save space in serialization. * Since Box registers are stored as Constant nodes we save 1 byte for each register. * This is due to convention that Value.opCode falling in [1..LastDataType] region is a constant. diff --git a/parsers/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala b/parsers/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala index 24aaeddefd..11381e1ac3 100644 --- a/parsers/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala +++ b/parsers/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala @@ -52,6 +52,8 @@ object SigmaPPrint extends PPrinter { s"SOption[${typeName(ot.elemType)}]" case _: STuple => "STuple" + case _: SFunc => + s"SFunc" case _ => sys.error(s"Cannot get typeName($tpe)") } diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index af4f93d861..987c436ff8 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -9827,30 +9827,6 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => } } - property("higher order lambdas") { - val f = existingFeature( - { (xs: Coll[Int]) => - val inc = { (x: Int) => x + 1 } - - def apply(in: (Int => Int, Int)) = in._1(in._2) - - xs.map { (x: Int) => apply((inc, x)) } - }, - """{(xs: Coll[Int]) => - | val inc = { (x: Int) => x + 1 } - | def apply(in: (Int => Int, Int)) = in._1(in._2) - | xs.map { (x: Int) => apply((inc, x)) } - | } - |""".stripMargin - ) - - // TODO v6.0: Add support of SFunc in TypeSerializer (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/847) - assertExceptionThrown( - f.verifyCase(Coll[Int](), Expected(Success(Coll[Int]()), 0)), - exceptionLike[MatchError]("(SInt$) => SInt$ (of class sigma.ast.SFunc)") - ) - } - override protected def afterAll(): Unit = { printDebug(CErgoTreeEvaluator.DefaultProfiler.generateReport) printDebug("==========================================================") diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 382c47403c..af31279f11 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -6,6 +6,7 @@ import sigma.ast.syntax.TrueSigmaProp import sigma.ast._ import sigma.data.{CBigInt, ExactNumeric} import sigma.eval.{CostDetails, SigmaDsl, TracedCost} +import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound import sigmastate.utils.Helpers @@ -463,4 +464,75 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => tree.root shouldBe t2.root } + property("higher order lambdas") { + val f = newFeature[Coll[Int], Coll[Int]]( + { (xs: Coll[Int]) => + val inc = { (x: Int) => x + 1 } + + def apply(in: (Int => Int, Int)) = in._1(in._2) + + xs.map { (x: Int) => apply((inc, x)) } + }, + """{(xs: Coll[Int]) => + | val inc = { (x: Int) => x + 1 } + | def apply(in: (Int => Int, Int)) = in._1(in._2) + | xs.map { (x: Int) => apply((inc, x)) } + | } + |""".stripMargin, + FuncValue( + Array((1, SCollectionType(SInt))), + MapCollection( + ValUse(1, SCollectionType(SInt)), + FuncValue( + Array((3, SInt)), + Apply( + FuncValue( + Array((5, SPair(SFunc(Array(SInt), SInt, List()), SInt))), + Apply( + SelectField.typed[Value[SFunc]]( + ValUse(5, SPair(SFunc(Array(SInt), SInt, List()), SInt)), + 1.toByte + ), + Array( + SelectField.typed[Value[SInt.type]]( + ValUse(5, SPair(SFunc(Array(SInt), SInt, List()), SInt)), + 2.toByte + ) + ) + ) + ), + Array( + Tuple( + Vector( + FuncValue( + Array((5, SInt)), + ArithOp(ValUse(5, SInt), IntConstant(1), OpCode @@ (-102.toByte)) + ), + ValUse(3, SInt) + ) + ) + ) + ) + ) + ) + ), + sinceVersion = VersionContext.V6SoftForkVersion + ) + + verifyCases( + Seq( + Coll(1, 2) -> Expected( + Success(Coll(2, 3)), + cost = 1793, + expectedDetails = CostDetails.ZeroCost + ) + ), + f, + preGeneratedSamples = Some(Seq( + Coll(Int.MinValue, Int.MaxValue - 1), + Coll(0, 1, 2, 3, 100, 1000) + )) + ) + } + } diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 58873449b4..d334ac4653 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -261,6 +261,7 @@ class SigmaDslTesting extends AnyPropSpec s"""Should succeed with the same value or fail with the same exception, but was: |First result: $b1 |Second result: $b2 + |Input: $x |Root cause: $cause |""".stripMargin) } @@ -715,11 +716,17 @@ class SigmaDslTesting extends AnyPropSpec override def checkEquality(input: A, logInputOutput: Boolean = false): Try[(B, CostDetails)] = { // check the old implementation against Scala semantic function var oldRes: Try[(B, CostDetails)] = null - if (ergoTreeVersionInTests < VersionContext.JitActivationVersion) oldRes = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { try checkEq(scalaFunc)(oldF)(input) catch { - case e: TestFailedException => throw e + case e: TestFailedException => + if(activatedVersionInTests < changedInVersion) { + throw e + } else { + // old ergoscript may succeed in new version while old scalafunc may fail, + // see e.g. "Option.getOrElse with lazy default" test + Failure(e) + } case t: Throwable => Failure(t) } @@ -872,10 +879,14 @@ class SigmaDslTesting extends AnyPropSpec extends Feature[A, B] { override def isSupportedIn(vc: VersionContext): Boolean = - vc.activatedVersion >= sinceVersion && vc.ergoTreeVersion >= sinceVersion + vc.activatedVersion >= sinceVersion override def scalaFunc: A => B = { x => - sys.error(s"Semantic Scala function is not defined for old implementation: $this") + if (isSupportedIn(VersionContext.current)) { + scalaFuncNew(x) + } else { + sys.error(s"Semantic Scala function is not defined for old implementation: $this") + } } implicit val cs = compilerSettingsInTests @@ -925,8 +936,14 @@ class SigmaDslTesting extends AnyPropSpec printTestCases: Boolean, failOnTestVectors: Boolean): Unit = { val funcRes = checkEquality(input, printTestCases) - funcRes.isFailure shouldBe true - Try(scalaFunc(input)) shouldBe expected.value + if(!isSupportedIn(VersionContext.current)) { + funcRes.isFailure shouldBe true + } + if(isSupportedIn(VersionContext.current)) { + Try(scalaFunc(input)) shouldBe expected.value + } else { + Try(scalaFunc(input)).isFailure shouldBe true + } } }