From 49729ac40aba56d303e42fc8023170ae8b5f7fd8 Mon Sep 17 00:00:00 2001 From: Dima Date: Mon, 8 Apr 2024 12:24:55 +0300 Subject: [PATCH] fix(compiler): Ordering in option inliner [LNG-351] (#1114) --- aqua-src/antithesis.aqua | 10 +- integration-tests/aqua/examples/option.aqua | 14 ++- .../src/__test__/examples.spec.ts | 10 ++ .../src/examples/useOptionalCall.ts | 10 ++ .../inline/raw/CollectionRawInliner.scala | 15 +-- .../inline/CollectionRawInlinerSpec.scala | 97 ++++++++++++++++--- 6 files changed, 128 insertions(+), 28 deletions(-) diff --git a/aqua-src/antithesis.aqua b/aqua-src/antithesis.aqua index fd2d33ec..2f7021fc 100644 --- a/aqua-src/antithesis.aqua +++ b/aqua-src/antithesis.aqua @@ -1,8 +1,10 @@ aqua Main -import someFunc from "import.aqua" +export main -export someFunc +func reward(amount: ?u32) -> u32: + result = ?[amount! / 10, 0] + <- result! -func a() -> string: - <- "srrt" +func main(a: u32) -> u32: + <- reward(?[a]) \ No newline at end of file diff --git a/integration-tests/aqua/examples/option.aqua b/integration-tests/aqua/examples/option.aqua index d63960fb..63e9fecf 100644 --- a/integration-tests/aqua/examples/option.aqua +++ b/integration-tests/aqua/examples/option.aqua @@ -1,6 +1,6 @@ aqua Option -export SomeS, useOptional, returnOptional, returnNone +export SomeS, useOptional, returnOptional, returnNone, getDefault, getArg import "@fluencelabs/aqua-lib/builtin.aqua" @@ -26,4 +26,14 @@ func returnOptional() -> ?string: func returnNone() -> ?string: result: *string Op.noop() - <- result \ No newline at end of file + <- result + +func reward(amount: ?u32) -> u32: + result = ?[amount! / 10, 42] + <- result! + +func getArg(a: u32) -> u32: + <- reward(?[a]) + +func getDefault() -> u32: + <- reward(?[]) \ No newline at end of file diff --git a/integration-tests/src/__test__/examples.spec.ts b/integration-tests/src/__test__/examples.spec.ts index 3dc2cdb2..324bde3f 100644 --- a/integration-tests/src/__test__/examples.spec.ts +++ b/integration-tests/src/__test__/examples.spec.ts @@ -68,6 +68,8 @@ import { returnNull, returnOptionalCall, useOptionalCall, + getDefaultCall, + getArgCall } from "../examples/useOptionalCall.js"; import { viaArrCall, @@ -607,6 +609,14 @@ describe("Testing examples", () => { expect(noneResult).toBe(null); }); + it("option.aqua default values LNG-351", async () => { + registerHandlers(); + let defaultResult = await getDefaultCall(); + let argResult = await getArgCall(1000); + expect(defaultResult).toBe(42); + expect(argResult).toBe(100); + }); + it("nestedFuncs.aqua", async () => { let nestedFuncsResult = await nestedFuncsCall(); expect(nestedFuncsResult).toBe("some-str"); diff --git a/integration-tests/src/examples/useOptionalCall.ts b/integration-tests/src/examples/useOptionalCall.ts index 0861a4dd..c5369b28 100644 --- a/integration-tests/src/examples/useOptionalCall.ts +++ b/integration-tests/src/examples/useOptionalCall.ts @@ -3,6 +3,8 @@ import { returnOptional, useOptional, registerSomeS, + getDefault, + getArg } from "../compiled/examples/option.js"; export function registerHandlers(): void { @@ -31,3 +33,11 @@ export async function returnOptionalCall(): Promise { export async function returnNull(): Promise { return await returnNone(); } + +export async function getDefaultCall(): Promise { + return await getDefault(); +} + +export async function getArgCall(num: number): Promise { + return await getArg(num); +} diff --git a/model/inline/src/main/scala/aqua/model/inline/raw/CollectionRawInliner.scala b/model/inline/src/main/scala/aqua/model/inline/raw/CollectionRawInliner.scala index 59a7881c..538f674c 100644 --- a/model/inline/src/main/scala/aqua/model/inline/raw/CollectionRawInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/raw/CollectionRawInliner.scala @@ -34,13 +34,9 @@ object CollectionRawInliner extends RawInliner[CollectionRaw] { .map(Chain.fromSeq) // push values to the stream, that is gathering the collection - vals = valsWithInlines.map { case (v, _) => - PushToStreamModel(v, streamExp).leaf - } - - // all inlines will be added before pushing values to the stream - inlines = valsWithInlines.flatMap { case (_, t) => - Chain.fromOption(t) + vals = valsWithInlines.map { case (v, tOp) => + val pts = PushToStreamModel(v, streamExp).leaf + tOp.map(t => SeqModel.wrap(Chain(t, pts))).getOrElse(pts) } canonName <- @@ -52,18 +48,17 @@ object CollectionRawInliner extends RawInliner[CollectionRaw] { raw.collectionType match { case ArrayType(_) => RestrictionModel(streamName, streamType).wrap( - SeqModel.wrap(inlines ++ vals :+ CanonicalizeModel(stream, canon).leaf) + SeqModel.wrap(vals :+ CanonicalizeModel(stream, canon).leaf) ) case OptionType(_) => RestrictionModel(streamName, streamType).wrap( SeqModel.wrap( - SeqModel.wrap(inlines), XorModel.wrap(vals :+ NullModel.leaf), CanonicalizeModel(stream, canon).leaf ) ) case _ => - SeqModel.wrap(inlines ++ vals) + SeqModel.wrap(vals) } ) } diff --git a/model/inline/src/test/scala/aqua/model/inline/CollectionRawInlinerSpec.scala b/model/inline/src/test/scala/aqua/model/inline/CollectionRawInlinerSpec.scala index 5af8b5fb..55340d16 100644 --- a/model/inline/src/test/scala/aqua/model/inline/CollectionRawInlinerSpec.scala +++ b/model/inline/src/test/scala/aqua/model/inline/CollectionRawInlinerSpec.scala @@ -4,8 +4,27 @@ import aqua.model.* import aqua.model.inline.raw.{ApplyIntoCopyRawInliner, CollectionRawInliner} import aqua.model.inline.state.InliningState import aqua.raw.ops.* -import aqua.raw.value.{CollectionRaw, LiteralRaw, MakeStructRaw, VarRaw} -import aqua.types.{CanonStreamType, OptionType, ScalarType, StreamMapType, StreamType, StructType} +import aqua.raw.value.{ + CallArrowRaw, + CallServiceRaw, + CollectionRaw, + LiteralRaw, + MakeStructRaw, + VarRaw +} +import aqua.types.{ + ArrowType, + CanonStreamType, + LiteralType, + NilType, + OptionType, + ProductType, + ScalarType, + StreamMapType, + StreamType, + StructType +} + import cats.data.{NonEmptyList, NonEmptyMap} import cats.syntax.show.* import org.scalatest.flatspec.AnyFlatSpec @@ -38,18 +57,28 @@ class CollectionRawInlinerSpec extends AnyFlatSpec with Matchers { val expected = RestrictionModel("option-inline", StreamType(nestedType)).wrap( // create a stream SeqModel.wrap( - // create an object - RestrictionModel(streamMapVar.name, streamMapType).wrap( - InsertKeyValueModel(LiteralModel.quote("field1"), LiteralModel.number(3), streamMapVar.name, streamMapType).leaf, - CanonicalizeModel(streamMapVar, CallModel.Export("nested_type_obj", nestedType)).leaf - ), XorModel.wrap( SeqModel.wrap( - // push object to the stream - PushToStreamModel( - VarModel("nested_type_obj", nestedType), - CallModel.Export("option-inline", StreamType(nestedType)) - ).leaf + // create an object + RestrictionModel(streamMapVar.name, streamMapType).wrap( + InsertKeyValueModel( + LiteralModel.quote("field1"), + LiteralModel.number(3), + streamMapVar.name, + streamMapType + ).leaf, + CanonicalizeModel( + streamMapVar, + CallModel.Export("nested_type_obj", nestedType) + ).leaf + ), + SeqModel.wrap( + // push object to the stream + PushToStreamModel( + VarModel("nested_type_obj", nestedType), + CallModel.Export("option-inline", StreamType(nestedType)) + ).leaf + ) ), NullModel.leaf ), @@ -64,4 +93,48 @@ class CollectionRawInlinerSpec extends AnyFlatSpec with Matchers { tree.get.equalsOrShowDiff(expected) shouldBe true } + "collection inliner" should "unfold option with calculations inside xor (bug LNG-351)" in { + + val zero = LiteralRaw("0", ScalarType.u32) + val call = CallServiceRaw( + LiteralRaw.quote("srv"), + "someCall", + ArrowType(NilType, ProductType(ScalarType.u32 :: Nil)), + Nil + ) + + val raw = CollectionRaw(NonEmptyList.of(call, zero), OptionType(ScalarType.u32)) + + val (v, tree) = + RawValueInliner.valueToModel[InliningState](raw, false).runA(InliningState()).value + + val streamValue = VarModel("option-inline", StreamType(LiteralType.unsigned)) + val resultValue = VarModel("option-inline-0", CanonStreamType(LiteralType.unsigned)) + + v shouldBe resultValue + + val expected = RestrictionModel(streamValue.name, streamValue.`type`).wrap( + SeqModel.wrap( + XorModel.wrap( + SeqModel.wrap( + CallServiceModel("srv", "someCall", Nil, VarModel("someCall", ScalarType.u32)).leaf, + PushToStreamModel( + VarModel("someCall", ScalarType.u32), + CallModel.Export(streamValue.name, streamValue.`type`) + ).leaf + ), + PushToStreamModel( + LiteralModel.number(0), + CallModel.Export(streamValue.name, streamValue.`type`) + ).leaf, + NullModel.leaf + ), + CanonicalizeModel(streamValue, CallModel.Export(resultValue.name, resultValue.`type`)).leaf + ) + ) + + + tree.get.equalsOrShowDiff(expected) shouldBe true + } + }