From 54ddcc8b6271b1aed4b427dcc74360a0cb212eb4 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Mon, 18 Sep 2023 16:43:22 +0200 Subject: [PATCH] fix(compiler): Do not restrict stream args when ability is present [fixes LNG-233] (#902) Add test --- .../scala/aqua/semantics/ArrowSemSpec.scala | 95 ++++++++++++++++--- types/src/main/scala/aqua/types/Type.scala | 7 +- 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/semantics/src/test/scala/aqua/semantics/ArrowSemSpec.scala b/semantics/src/test/scala/aqua/semantics/ArrowSemSpec.scala index 403545da..1620dd1e 100644 --- a/semantics/src/test/scala/aqua/semantics/ArrowSemSpec.scala +++ b/semantics/src/test/scala/aqua/semantics/ArrowSemSpec.scala @@ -4,17 +4,22 @@ import aqua.parser.expr.func.ArrowExpr import aqua.parser.lexer.{BasicTypeToken, Name} import aqua.raw.Raw import aqua.raw.arrow.ArrowRaw -import aqua.raw.ops.{FuncOp, RawTag, ReturnTag, SeqTag} +import aqua.raw.ops.* import aqua.raw.value.LiteralRaw import aqua.semantics.expr.func.ArrowSem +import aqua.semantics.rules.types.TypesState import aqua.types.* +import aqua.types.ScalarType.* + import cats.Id -import cats.data.{NonEmptyList, State} +import cats.syntax.applicative.* +import cats.data.{NonEmptyList, NonEmptyMap, State} import org.scalatest.EitherValues import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers +import org.scalatest.Inside -class ArrowSemSpec extends AnyFlatSpec with Matchers with EitherValues { +class ArrowSemSpec extends AnyFlatSpec with Matchers with EitherValues with Inside { import Utils.{given, *} @@ -27,21 +32,19 @@ class ArrowSemSpec extends AnyFlatSpec with Matchers with EitherValues { sem.program[State[CompilerState[Id], *]] } - "sem" should "create empty model" in { + "ArrowSem" should "create empty model" in { val model = getModel(program("(a: string, b: u32) -> u8")) model shouldBe (Raw.Empty("Invalid arrow body")) } - "sem" should "create error model" ignore { + it should "create error model" ignore { val model = getModel(RawTag.empty.toFuncOp)(program("(a: string, b: u32) -> u8")) model shouldBe Raw.Empty( "Return type is defined for the arrow, but nothing returned. Use `<- value, ...` as the last expression inside function body." ) } - import aqua.types.ScalarType.* - - "arrow without return type" should "create right model" ignore { + it should "create right model for arrow without return type" ignore { val model = getModel(RawTag.empty.toFuncOp)(program("(a: string, b: u32)")) model shouldBe ArrowRaw( ArrowType(labelled("a", string, labelled("b", u32)), NilType), @@ -50,7 +53,7 @@ class ArrowSemSpec extends AnyFlatSpec with Matchers with EitherValues { ) } - "arrow with return type and correct state" should "create correct model" ignore { + it should "create correct model for arrow with return type and correct state" ignore { val returnValue = LiteralRaw("123", string) val returnTag = FuncOp(ReturnTag(NonEmptyList.one(returnValue)).wrap(RawTag.empty)) val model = getModel(returnTag)(program("(a: string, b: u32) -> string")) @@ -60,7 +63,7 @@ class ArrowSemSpec extends AnyFlatSpec with Matchers with EitherValues { model shouldBe resultModel } - "arrow with return type and seq inside" should "create correct model" ignore { + it should "create correct model for arrow with return type and seq inside" ignore { val returnValue = LiteralRaw("123", string) val seq = FuncOp(SeqTag.wrap(RawTag.empty, ReturnTag(NonEmptyList.one(returnValue)).leaf)) val model = getModel(seq)(program("(a: string, b: u32) -> string")) @@ -70,7 +73,7 @@ class ArrowSemSpec extends AnyFlatSpec with Matchers with EitherValues { model shouldBe resultModel } - "different types in return type and return value" should "create error model" ignore { + it should "create error model for different types in return type and return value" ignore { val returnValue = LiteralRaw("123", string) val seq = FuncOp( SeqTag.wrap( @@ -86,4 +89,74 @@ class ArrowSemSpec extends AnyFlatSpec with Matchers with EitherValues { ) } + + it should "not restrict stream arguments" in { + val body = + PushToStreamTag( + LiteralRaw.quote("a"), + Call.Export("s", StreamType(ScalarType.string)) + ).leaf + + def test( + abilityArgs: List[String], + args: List[String], + initState: CompilerState[Id] + ) = { + val abilityPart = + if (abilityArgs.isEmpty) "" + else abilityArgs.mkString("{", ",", "}") + val argsPart = args.appended("s: *string").mkString("(", ",", ")") + val expr = abilityPart + argsPart + + val (state, raw) = program(expr).apply(body.toFuncOp.pure).run(initState).value + + state.errors shouldBe empty + inside(raw) { case ArrowRaw(_, Nil, bodyRes) => + bodyRes shouldBe body + } + } + + val structType = StructType( + "TestStruct", + NonEmptyMap.one( + "field", + ScalarType.string + ) + ) + + val abilityType = AbilityType( + "TestAbility", + NonEmptyMap.one( + "field", + ScalarType.string + ) + ) + + val state: CompilerState[Id] = CompilerState( + types = TypesState( + strict = Map( + "FirstAbility" -> abilityType, + "SecondAbility" -> abilityType, + "DataStruct" -> structType + ) + ) + ) + + val abilityArgs = List( + "FirstAbility", + "SecondAbility" + ) + + val args = List( + "a: string", + "callback: u32 -> string", + "data: DataStruct" + ) + + for { + abilityArgs <- abilityArgs.toSet.subsets() + args <- args.toSet.subsets() + } test(abilityArgs.toList, args.toList, state) + } + } diff --git a/types/src/main/scala/aqua/types/Type.scala b/types/src/main/scala/aqua/types/Type.scala index 1d816e13..a33cea1b 100644 --- a/types/src/main/scala/aqua/types/Type.scala +++ b/types/src/main/scala/aqua/types/Type.scala @@ -74,9 +74,10 @@ sealed trait ProductType extends Type { } lazy val labelledData: List[(String, DataType)] = this match { - case LabeledConsType(label, t: DataType, pt) => (label -> t) :: pt.labelledData - case LabeledConsType(label, t: ArrowType, pt) => pt.labelledData - case UnlabeledConsType(_, pt) => pt.labelledData + case LabeledConsType(label, t: DataType, pt) => + (label -> t) :: pt.labelledData + case ConsType(_, pt) => + pt.labelledData case _ => Nil } }