From 3b733441a8f3657aedda4bf657b2c8660419bb41 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 15 Nov 2023 11:10:25 +0000 Subject: [PATCH 01/23] Refactor --- .../semantics/expr/func/CallArrowSem.scala | 9 +++-- .../aqua/semantics/expr/func/ReturnSem.scala | 37 ++++++++++--------- .../rules/types/TypesInterpreter.scala | 19 +++++++--- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/semantics/src/main/scala/aqua/semantics/expr/func/CallArrowSem.scala b/semantics/src/main/scala/aqua/semantics/expr/func/CallArrowSem.scala index 44f4ad1f4..6d291d130 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/func/CallArrowSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/func/CallArrowSem.scala @@ -10,14 +10,15 @@ import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.types.TypesAlgebra import aqua.types.{ProductType, StreamType, Type} + import cats.Monad -import cats.syntax.flatMap.* -import cats.syntax.functor.* -import cats.syntax.traverse.* -import cats.syntax.option.* import cats.syntax.applicative.* import cats.syntax.apply.* import cats.syntax.comonad.* +import cats.syntax.flatMap.* +import cats.syntax.functor.* +import cats.syntax.option.* +import cats.syntax.traverse.* class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal { diff --git a/semantics/src/main/scala/aqua/semantics/expr/func/ReturnSem.scala b/semantics/src/main/scala/aqua/semantics/expr/func/ReturnSem.scala index 4a42283ee..13705eea0 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/func/ReturnSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/func/ReturnSem.scala @@ -1,34 +1,37 @@ package aqua.semantics.expr.func -import aqua.raw.ops.ReturnTag +import aqua.helpers.syntax.optiont.* import aqua.parser.expr.func.ReturnExpr import aqua.raw.Raw +import aqua.raw.ops.ReturnTag import aqua.semantics.Prog import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.types.TypesAlgebra + +import cats.Monad +import cats.data.{NonEmptyList, OptionT} import cats.syntax.applicative.* +import cats.syntax.flatMap.* import cats.syntax.functor.* import cats.syntax.traverse.* -import cats.data.NonEmptyList -import cats.syntax.flatMap.* -import cats.Monad class ReturnSem[S[_]](val expr: ReturnExpr[S]) extends AnyVal { def program[Alg[_]: Monad](implicit V: ValuesAlgebra[S, Alg], T: TypesAlgebra[S, Alg] - ): Prog[Alg, Raw] = - expr.values - .traverse(v => V.valueToRaw(v).map(_.map(v -> _))) - .map(_.sequence) - .flatMap { - case Some(vals) => - T.checkArrowReturn(vals).map[Raw] { - case true => ReturnTag(vals.map(_._2)).leaf.toFuncOp - case false => Raw.error("Return types validation failed") - } - case None => - Raw.error("Return types resolution failed").pure[Alg] - } + ): Prog[Alg, Raw] = ( + for { + vals <- expr.values.traverse(v => + OptionT( + V.valueToRaw(v) + ).map(v -> _) + ) + _ <- OptionT.withFilterF( + T.checkArrowReturn(vals) + ) + } yield ReturnTag(vals.map(_._2)).leaf.toFuncOp + ).getOrElse( + Raw.error("Return values validation failed") + ) } diff --git a/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala b/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala index 00fd16784..9b312c182 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala @@ -348,7 +348,8 @@ class TypesInterpreter[S[_], X](using report .error( token, - s"Number of fields doesn't match the data type, expected: $expected, given: $givenType" + "Number of fields doesn't match, " + + s"expected: $expected, given: $givenType" ) .as(false) } else { @@ -544,34 +545,42 @@ class TypesInterpreter[S[_], X](using report .error( values.head._1, - "Return expression was already used in scope; you can use only one Return in an arrow declaration, use conditional return pattern if you need to return based on condition" + "Return expression was already used in scope; " + + "you can use only one Return in an arrow declaration, " + + "use conditional return pattern if you need to return based on condition" ) .as(frame -> false) else if (frame.token.res.isEmpty) report .error( values.head._1, - "No return type declared for this arrow, please remove `<- ...` expression or add `-> ...` return type(s) declaration to the arrow" + "No return type declared for this arrow, " + + "please remove `<- ...` expression " + + "or add `-> ...` return type(s) declaration to the arrow" ) .as(frame -> false) else if (frame.token.res.length > values.length) report .error( values.last._1, - s"Expected ${frame.token.res.length - values.length} more values to be returned, see return type declaration" + s"Expected ${frame.token.res.length - values.length} more " + + s"values to be returned, see return type declaration" ) .as(frame -> false) else if (frame.token.res.length < values.length) report .error( values.toList.drop(frame.token.res.length).headOption.getOrElse(values.last)._1, - s"Too many values are returned from this arrow, this one is unexpected. Defined return type: ${frame.arrowType.codomain}" + s"Too many values are returned from this arrow, " + + s"this is unexpected. Defined return type: ${frame.arrowType.codomain}" ) .as(frame -> false) else frame.arrowType.codomain.toList .zip(values.toList) .traverse { case (returnType, (token, returnValue)) => + println(s"returnType: $returnType, returnValue: $returnValue") + println(s"acceptsValueOf: ${returnType.acceptsValueOf(returnValue.`type`)}") if (!returnType.acceptsValueOf(returnValue.`type`)) report .error( From 5ae93415429aaa570a90e3d9a051d6f862393bb8 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 15 Nov 2023 11:34:52 +0000 Subject: [PATCH 02/23] Refactor type system --- .../scala/aqua/model/inline/ArrowInliner.scala | 16 +++++++++------- model/src/main/scala/aqua/model/CallModel.scala | 9 ++++----- model/src/main/scala/aqua/model/ValueModel.scala | 4 ++-- .../aqua/semantics/rules/ValuesAlgebra.scala | 2 +- .../src/main/scala/aqua/types/CompareTypes.scala | 3 ++- types/src/main/scala/aqua/types/Type.scala | 16 ++++++++++++++-- 6 files changed, 32 insertions(+), 18 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index 0be4c3bb2..c6b97e0d3 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -6,7 +6,7 @@ import aqua.model.* import aqua.model.inline.state.{Arrows, Exports, Mangler} import aqua.raw.ops.RawTag import aqua.raw.value.{ValueRaw, VarRaw} -import aqua.types.{AbilityType, ArrowType, CollectionType, NamedType, StreamType, Type} +import aqua.types.* import cats.data.StateT import cats.data.{Chain, IndexedStateT, State} @@ -105,7 +105,9 @@ object ArrowInliner extends Logging { exports <- Exports[S].exports arrows <- Arrows[S].arrows // gather all arrows and variables from abilities - returnedAbilities = rets.collect { case VarModel(name, at: AbilityType, _) => name -> at } + returnedAbilities = rets.collect { case VarModel(name, at: GeneralAbilityType, _) => + name -> at + } varsFromAbilities = returnedAbilities.flatMap { case (name, at) => getAbilityVars(name, None, at, exports) }.toMap @@ -138,9 +140,9 @@ object ArrowInliner extends Logging { private def getAbilityFields[T <: Type]( name: String, newName: Option[String], - `type`: NamedType, + `type`: GeneralAbilityType, exports: Map[String, ValueModel] - )(fields: NamedType => Map[String, T]): Map[String, ValueModel] = + )(fields: GeneralAbilityType => Map[String, T]): Map[String, ValueModel] = fields(`type`).flatMap { case (fName, _) => val fullName = AbilityType.fullName(name, fName) val newFullName = AbilityType.fullName(newName.getOrElse(name), fName) @@ -162,7 +164,7 @@ object ArrowInliner extends Logging { private def getAbilityVars( abilityName: String, abilityNewName: Option[String], - abilityType: AbilityType, + abilityType: GeneralAbilityType, exports: Map[String, ValueModel] ): Map[String, ValueModel] = { val get = getAbilityFields( @@ -193,7 +195,7 @@ object ArrowInliner extends Logging { private def getAbilityArrows( name: String, newName: Option[String], - `type`: NamedType, + `type`: GeneralAbilityType, exports: Map[String, ValueModel], arrows: Map[String, FuncArrow] ): Map[String, FuncArrow] = { @@ -214,7 +216,7 @@ object ArrowInliner extends Logging { private def getAbilityArrows[S: Arrows: Exports]( name: String, - `type`: NamedType + `type`: GeneralAbilityType ): State[S, Map[String, FuncArrow]] = for { exports <- Exports[S].exports arrows <- Arrows[S].arrows diff --git a/model/src/main/scala/aqua/model/CallModel.scala b/model/src/main/scala/aqua/model/CallModel.scala index 7efb92491..d2e9c066b 100644 --- a/model/src/main/scala/aqua/model/CallModel.scala +++ b/model/src/main/scala/aqua/model/CallModel.scala @@ -1,8 +1,8 @@ package aqua.model -import aqua.raw.ops.Call -import aqua.types.{ArrowType, NamedType, Type} import aqua.model.ValueModel.{Ability, Arrow} +import aqua.raw.ops.Call +import aqua.types.* // TODO docs case class CallModel(args: List[ValueModel], exportTo: List[CallModel.Export]) { @@ -12,9 +12,8 @@ case class CallModel(args: List[ValueModel], exportTo: List[CallModel.Export]) { m }.toSet - def abilityArgs: List[(String, NamedType)] = args.collect { case Ability(m, t, _) => - (m, t) - } + def abilityArgs: List[(String, GeneralAbilityType)] = + args.collect { case Ability(m, t, _) => m -> t } def usesVarNames: Set[String] = args.flatMap(_.usesVarNames).toSet } diff --git a/model/src/main/scala/aqua/model/ValueModel.scala b/model/src/main/scala/aqua/model/ValueModel.scala index 79a601ed1..fb2cb9946 100644 --- a/model/src/main/scala/aqua/model/ValueModel.scala +++ b/model/src/main/scala/aqua/model/ValueModel.scala @@ -66,9 +66,9 @@ object ValueModel { object Ability { - def unapply(vm: VarModel): Option[(String, NamedType, Chain[PropertyModel])] = + def unapply(vm: VarModel): Option[(String, GeneralAbilityType, Chain[PropertyModel])] = vm match { - case VarModel(name, t: (AbilityType | ServiceType), properties) => + case VarModel(name, t: GeneralAbilityType, properties) => (name, t, properties).some case _ => none } diff --git a/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala b/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala index 30429a31d..a8ed5a571 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala @@ -384,7 +384,7 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](using ): OptionT[Alg, CallArrowRaw] = { lazy val nameTypeFromAbility = OptionT( N.read(ab.asName, mustBeDefined = false) - ).collect { case nt: (AbilityType | ServiceType) => ab.asName -> nt } + ).collect { case nt: GeneralAbilityType => ab.asName -> nt } lazy val nameTypeFromService = for { st <- OptionT( diff --git a/types/src/main/scala/aqua/types/CompareTypes.scala b/types/src/main/scala/aqua/types/CompareTypes.scala index 3dc276b72..1b7a293a0 100644 --- a/types/src/main/scala/aqua/types/CompareTypes.scala +++ b/types/src/main/scala/aqua/types/CompareTypes.scala @@ -124,7 +124,8 @@ object CompareTypes { case (x: OptionType, y: StreamType) => apply(x.element, y.element) case (x: OptionType, y: ArrayType) => apply(x.element, y.element) case (x: StreamType, y: StreamType) => apply(x.element, y.element) - case (lnt: AbilityType, rnt: AbilityType) => compareNamed(lnt.fields, rnt.fields) + case (lnt: GeneralAbilityType, rnt: GeneralAbilityType) => + compareNamed(lnt.fields, rnt.fields) case (lnt: StructType, rnt: StructType) => compareNamed(lnt.fields, rnt.fields) // Literals and scalars diff --git a/types/src/main/scala/aqua/types/Type.scala b/types/src/main/scala/aqua/types/Type.scala index 5df10c500..021fe143d 100644 --- a/types/src/main/scala/aqua/types/Type.scala +++ b/types/src/main/scala/aqua/types/Type.scala @@ -443,7 +443,16 @@ case class StreamType(override val element: DataType) extends MutableStreamType override def withElement(t: DataType): CollectionType = copy(element = t) } -case class ServiceType(name: String, fields: NonEmptyMap[String, ArrowType]) extends NamedType { +/** + * This type unites types that work as abilities, + * namely `ServiceType` and `AbilityType` + */ +sealed trait GeneralAbilityType extends NamedType + +case class ServiceType( + name: String, + fields: NonEmptyMap[String, ArrowType] +) extends GeneralAbilityType { override val specifier: String = "service" @@ -452,7 +461,10 @@ case class ServiceType(name: String, fields: NonEmptyMap[String, ArrowType]) ext } // Ability is an unordered collection of labelled types and arrows -case class AbilityType(name: String, fields: NonEmptyMap[String, Type]) extends NamedType { +case class AbilityType( + name: String, + fields: NonEmptyMap[String, Type] +) extends GeneralAbilityType { override val specifier: String = "ability" From 6607eeb2c123922d0304414757e7e2ec3f6c2463 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 15 Nov 2023 11:37:40 +0000 Subject: [PATCH 03/23] Remove println --- .../src/main/scala/aqua/semantics/expr/func/ReturnSem.scala | 2 +- .../scala/aqua/semantics/rules/types/TypesInterpreter.scala | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/semantics/src/main/scala/aqua/semantics/expr/func/ReturnSem.scala b/semantics/src/main/scala/aqua/semantics/expr/func/ReturnSem.scala index 13705eea0..9f233b1b1 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/func/ReturnSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/func/ReturnSem.scala @@ -17,7 +17,7 @@ import cats.syntax.traverse.* class ReturnSem[S[_]](val expr: ReturnExpr[S]) extends AnyVal { - def program[Alg[_]: Monad](implicit + def program[Alg[_]: Monad](using V: ValuesAlgebra[S, Alg], T: TypesAlgebra[S, Alg] ): Prog[Alg, Raw] = ( diff --git a/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala b/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala index 9b312c182..2131c8e8a 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala @@ -579,8 +579,6 @@ class TypesInterpreter[S[_], X](using frame.arrowType.codomain.toList .zip(values.toList) .traverse { case (returnType, (token, returnValue)) => - println(s"returnType: $returnType, returnValue: $returnValue") - println(s"acceptsValueOf: ${returnType.acceptsValueOf(returnValue.`type`)}") if (!returnType.acceptsValueOf(returnValue.`type`)) report .error( From dfabaf61a42c06ed3b809f1be7fc504d839ae452 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 15 Nov 2023 11:56:07 +0000 Subject: [PATCH 04/23] Fix renaming --- model/raw/src/main/scala/aqua/raw/ops/RawTag.scala | 3 +++ types/src/main/scala/aqua/types/Type.scala | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/model/raw/src/main/scala/aqua/raw/ops/RawTag.scala b/model/raw/src/main/scala/aqua/raw/ops/RawTag.scala index 8d4cc2a9c..347a74987 100644 --- a/model/raw/src/main/scala/aqua/raw/ops/RawTag.scala +++ b/model/raw/src/main/scala/aqua/raw/ops/RawTag.scala @@ -345,6 +345,9 @@ case class ServiceIdTag( override def exportsVarNames: Set[String] = Set(name) + override def renameExports(map: Map[String, String]): RawTag = + copy(name = map.getOrElse(name, name)) + override def mapValues(f: ValueRaw => ValueRaw): RawTag = ServiceIdTag(value.map(f), serviceType, name) } diff --git a/types/src/main/scala/aqua/types/Type.scala b/types/src/main/scala/aqua/types/Type.scala index 021fe143d..3118062f6 100644 --- a/types/src/main/scala/aqua/types/Type.scala +++ b/types/src/main/scala/aqua/types/Type.scala @@ -348,7 +348,7 @@ sealed trait NamedType extends Type { def getArrowsEval(path: Option[String], nt: NamedType): Eval[List[(String, ArrowType)]] = nt.fields.toNel.toList.flatTraverse { // sub-arrows could be in abilities or services - case (innerName, innerType: (ServiceType | AbilityType)) => + case (innerName, innerType: GeneralAbilityType) => val newPath = path.fold(innerName)(AbilityType.fullName(_, innerName)) getArrowsEval(newPath.some, innerType) case (aName, aType: ArrowType) => From a801580f9c307a55b531a1e714c212120e95afe0 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 15 Nov 2023 15:09:49 +0000 Subject: [PATCH 05/23] Add unit tests --- build.sbt | 6 +- .../aqua/compiler/AquaCompilerSpec.scala | 80 +++++++++++-- .../scala/aqua/types/TypeVarianceSpec.scala | 37 ++++++ types/src/test/scala/aqua/types/package.scala | 112 ++++++++++++++++++ 4 files changed, 222 insertions(+), 13 deletions(-) create mode 100644 types/src/test/scala/aqua/types/TypeVarianceSpec.scala create mode 100644 types/src/test/scala/aqua/types/package.scala diff --git a/build.sbt b/build.sbt index 07f1a8ba3..1607b5238 100644 --- a/build.sbt +++ b/build.sbt @@ -7,6 +7,7 @@ val catsV = "2.10.0" val catsParseV = "0.3.10" val monocleV = "3.1.0" val scalaTestV = "3.2.17" +val scalaTestScalaCheckV = "3.2.17.0" val sourcecodeV = "0.3.0" val fs2V = "3.9.3" val catsEffectV = "3.6-1f95fd7" @@ -23,8 +24,9 @@ val commons = Seq( }, scalaVersion := scalaV, libraryDependencies ++= Seq( - "com.outr" %%% "scribe" % scribeV, - "org.scalatest" %%% "scalatest" % scalaTestV % Test + "com.outr" %%% "scribe" % scribeV, + "org.scalatest" %%% "scalatest" % scalaTestV % Test, + "org.scalatestplus" %%% "scalacheck-1-17" % scalaTestScalaCheckV % Test ), scalacOptions ++= { Seq( diff --git a/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala b/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala index 0f68680ec..50731b53e 100644 --- a/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala +++ b/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala @@ -1,12 +1,15 @@ package aqua.compiler -import aqua.model.{CallModel, ForModel, FunctorModel, LiteralModel, ValueModel, VarModel} +import aqua.model.AquaContext +import aqua.model.CallServiceModel +import aqua.model.FlattenModel import aqua.model.transform.ModelBuilder -import aqua.model.transform.TransformConfig import aqua.model.transform.Transform -import aqua.parser.ParserError +import aqua.model.transform.TransformConfig +import aqua.model.{CallModel, ForModel, FunctorModel, LiteralModel, ValueModel, VarModel} import aqua.parser.Ast import aqua.parser.Parser +import aqua.parser.ParserError import aqua.parser.lift.Span import aqua.parser.lift.Span.S import aqua.raw.ConstantRaw @@ -18,15 +21,12 @@ import aqua.types.{ArrayType, CanonStreamType, LiteralType, ScalarType, StreamTy import cats.Id import cats.data.{Chain, NonEmptyChain, NonEmptyMap, Validated, ValidatedNec} import cats.instances.string.* -import cats.syntax.show.* -import cats.syntax.option.* import cats.syntax.either.* +import cats.syntax.option.* +import cats.syntax.show.* +import org.scalatest.Inside import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -import org.scalatest.Inside -import aqua.model.AquaContext -import aqua.model.FlattenModel -import aqua.model.CallServiceModel class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside { import ModelBuilder.* @@ -120,7 +120,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside { def getDataSrv(name: String, varName: String, t: Type) = { CallServiceRes( - LiteralModel.fromRaw(LiteralRaw.quote("getDataSrv")), + LiteralModel.quote("getDataSrv"), name, CallRes(Nil, Some(CallModel.Export(varName, t))), LiteralModel.fromRaw(ValueRaw.InitPeerId) @@ -363,7 +363,65 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside { errorCall(transformCfg, 0, initPeer) ) - insideRes(src, transformCfg = transformCfg)("main") { case main :: _ => + insideRes(src, transformCfg = transformCfg)("main") { case main :: Nil => + main.body.equalsOrShowDiff(expected) should be(true) + } + } + + it should "allow returning and passing services as abilities" in { + val src = Map( + "main.aqua" -> """ + |aqua Test + | + |export test + | + |ability Ab: + | log(log: string) + | + |service Srv("default-id"): + | log(log: string) + | + |func useAb{Ab}(): + | Ab.log("test") + | + |func genDefault() -> Ab: + | <- Srv + | + |func genResolved() -> Ab: + | Srv "resolved-id" + | <- Srv + | + |func test(): + | resolved <- genResolved() + | useAb{resolved}() + | default <- genDefault() + | useAb{default}() + |""".stripMargin + ) + + val transformCfg = TransformConfig() + + insideRes(src, transformCfg = transformCfg)("test") { case main :: Nil => + def srvCall(id: String) = + CallServiceRes( + serviceId = LiteralModel.quote(id), + funcName = "log", + call = CallRes( + List(LiteralModel.quote("test")), + None + ), + initPeer + ).leaf + + val expected = XorRes.wrap( + SeqRes.wrap( + getDataSrv("-relay-", "-relay-", ScalarType.string), + srvCall("resolved-id"), + srvCall("default-id") + ), + errorCall(transformCfg, 0, initPeer) + ) + main.body.equalsOrShowDiff(expected) should be(true) } } diff --git a/types/src/test/scala/aqua/types/TypeVarianceSpec.scala b/types/src/test/scala/aqua/types/TypeVarianceSpec.scala new file mode 100644 index 000000000..2aee2d5fb --- /dev/null +++ b/types/src/test/scala/aqua/types/TypeVarianceSpec.scala @@ -0,0 +1,37 @@ +package aqua.types + +import aqua.types.* + +import cats.data.NonEmptyMap +import org.scalacheck.* +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import scala.collection.immutable.SortedMap + +class TypeVarianceSpec extends AnyFlatSpec with ScalaCheckPropertyChecks with Matchers { + + "ServiceType" should "be subtype of AbilityType" in { + given Arbitrary[NonEmptyMap[String, ArrowType]] = Arbitrary( + Gen + .nonEmptyListOf( + for { + name <- anyName + typ <- arrowOf(Arbitrary.arbitrary[DataType]) + } yield name -> typ + ) + .map(m => NonEmptyMap.fromMapUnsafe(SortedMap.from(m))) + ) + + forAll { + ( + arrows: NonEmptyMap[String, ArrowType], + abName: String, + srvName: String + ) => + AbilityType(abName, arrows).acceptsValueOf( + ServiceType(srvName, arrows) + ) shouldBe true + } + } +} diff --git a/types/src/test/scala/aqua/types/package.scala b/types/src/test/scala/aqua/types/package.scala new file mode 100644 index 000000000..fba26edda --- /dev/null +++ b/types/src/test/scala/aqua/types/package.scala @@ -0,0 +1,112 @@ +package aqua + +import cats.data.NonEmptyMap +import org.scalacheck.* +import scala.collection.immutable.SortedMap + +package object types { + + val anyName: Gen[String] = for { + first <- Gen.alphaChar + other <- Gen.alphaStr + } yield first + other + + def productOf[T <: Type](gen: Gen[T]): Gen[ProductType] = + Gen.sized(size => Gen.resize(size / 2, Gen.listOf(gen).map(ProductType.apply))) + + def labeledProductOf[T <: Type](gen: Gen[T]): Gen[ProductType] = + Gen.sized(size => + Gen + .resize( + size / 2, + Gen.listOf(for { + name <- anyName + typ <- gen + } yield name -> typ) + ) + .map(ProductType.labelled) + ) + + def arrowOf[T <: Type](gen: Gen[T]): Gen[ArrowType] = + Gen.sized(size => + for { + input <- Gen.resize(size / 2, labeledProductOf(gen)) + output <- Gen.resize(size / 2, productOf(gen)) + } yield ArrowType(input, output) + ) + + given Arbitrary[ScalarType] = Arbitrary(Gen.oneOf(ScalarType.all)) + + given Arbitrary[LiteralType] = Arbitrary( + Gen.oneOf( + LiteralType.bool, + LiteralType.unsigned, + LiteralType.signed, + LiteralType.float, + LiteralType.number, + LiteralType.string + ) + ) + + private def fromData[T](f: DataType => T): Arbitrary[T] = + Arbitrary( + Gen.sized(size => + Gen + .resize( + size / 2, + Arbitrary.arbitrary[DataType] + ) + .map(f) + ) + ) + + given Arbitrary[OptionType] = + fromData(OptionType.apply) + + given Arbitrary[ArrayType] = + fromData(ArrayType.apply) + + given Arbitrary[CanonStreamType] = + fromData(CanonStreamType.apply) + + given Arbitrary[StructType] = Arbitrary( + Gen.sized(size => + for { + name <- anyName + fields <- Gen + .nonEmptyMap( + for { + name <- anyName + typ <- Gen.resize( + size / 2, + Arbitrary.arbitrary[DataType] + ) + } yield name -> typ + ) + .map(m => NonEmptyMap.fromMapUnsafe(SortedMap.from(m))) + } yield StructType(name, fields) + ) + ) + + given Arbitrary[DataType] = Arbitrary( + Gen.sized(size => + if (size <= 0) + Gen.oneOf( + Arbitrary.arbitrary[ScalarType], + Arbitrary.arbitrary[LiteralType] + ) + else + Gen.resize( + size / 2, + Gen.oneOf( + Arbitrary.arbitrary[ScalarType], + Arbitrary.arbitrary[LiteralType], + Arbitrary.arbitrary[OptionType], + Arbitrary.arbitrary[ArrayType], + Arbitrary.arbitrary[CanonStreamType], + Arbitrary.arbitrary[StructType] + ) + ) + ) + ) +} From 0ff10081eaac90e86bde4269bab37f6499ca2f0e Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Fri, 17 Nov 2023 15:18:11 +0000 Subject: [PATCH 06/23] Do not convert to call arrow --- .../scala/aqua/parser/lexer/ValueToken.scala | 48 ++++--------------- .../rules/types/TypesInterpreter.scala | 37 +++++++------- 2 files changed, 29 insertions(+), 56 deletions(-) diff --git a/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala b/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala index a4a818ad7..7ced80ded 100644 --- a/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala +++ b/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala @@ -2,23 +2,23 @@ package aqua.parser.lexer import aqua.parser.Expr import aqua.parser.head.FilenameExpr -import aqua.parser.lexer.Token.* import aqua.parser.lexer.NamedArg.namedArgs +import aqua.parser.lexer.Token.* import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser.* -import aqua.types.LiteralType import aqua.parser.lift.Span import aqua.parser.lift.Span.{P0ToSpan, PToSpan, S} +import aqua.types.LiteralType +import cats.arrow.FunctionK +import cats.data.{NonEmptyList, NonEmptyMap} import cats.parse.{Numbers, Parser as P, Parser0 as P0} import cats.syntax.comonad.* -import cats.syntax.functor.* -import cats.{~>, Comonad, Functor} -import cats.data.{NonEmptyList, NonEmptyMap} import cats.syntax.foldable.* -import cats.arrow.FunctionK -import cats.syntax.traverse.* +import cats.syntax.functor.* import cats.syntax.option.* +import cats.syntax.traverse.* +import cats.{Comonad, Functor, ~>} sealed trait ValueToken[F[_]] extends Token[F] { def mapK[K[_]: Comonad](fk: F ~> K): ValueToken[K] @@ -42,38 +42,6 @@ case class PropertyToken[F[_]: Comonad]( private def isConst(name: String): Boolean = name.forall(c => !c.isLetter || c.isUpper) - /** - * This method tries to convert property token to - * call arrow token. - * - * Next properties pattern is transformed: - * (Class)+ arrow() - * ^^^^^^^ - * this part is transformed to ability name. - */ - private def toCallArrow: Option[CallArrowToken[F]] = value match { - case VarToken(name) => - val ability = properties.init.traverse { - case f @ IntoField(_) => f.value.some - case _ => none - }.map( - name.value +: _ - ).filter( - _.forall(isClass) - ).map(props => name.rename(props.mkString("."))) - - (properties.last, ability) match { - case (IntoArrow(funcName, args), Some(ability)) => - CallArrowToken( - ability.asTypeToken.some, - funcName, - args - ).some - case _ => none - } - case _ => none - } - /** * This method tries to convert property token to * property token with dotted var name inside value token. @@ -144,7 +112,7 @@ case class PropertyToken[F[_]: Comonad]( * @return Some(token) if token was adjusted, None otherwise */ def adjust: Option[ValueToken[F]] = - toCallArrow.orElse(toDottedName) + toDottedName } object PropertyToken { diff --git a/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala b/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala index 2131c8e8a..7df3cddd5 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala @@ -209,27 +209,32 @@ class TypesInterpreter[S[_], X](using arguments: List[ValueRaw] ): State[X, Option[PropertyRaw]] = { rootT match { - case AbilityType(name, fieldsAndArrows) => - fieldsAndArrows(op.name.value).fold( - report - .error( - op, - s"Arrow `${op.name.value}` not found in type `$name`, available: ${fieldsAndArrows.toNel.toList.map(_._1).mkString(", ")}" - ) - .as(None) - ) { - case at @ ArrowType(_, _) => - locations - .pointFieldLocation(name, op.name.value, op) - .as(Some(IntoArrowRaw(op.name.value, at, arguments))) - case _ => + case ab: GeneralAbilityType => + val name = ab.name + val fields = ab.fields + lazy val fieldNames = fields.toNel.toList.map(_._1).mkString(", ") + fields(op.name.value) + .fold( report .error( op, - s"Unexpected. `${op.name.value}` must be an arrow." + s"Arrow `${op.name.value}` not found in type `$name`, " + + s"available: $fieldNames" ) .as(None) - } + ) { + case at @ ArrowType(_, _) => + locations + .pointFieldLocation(name, op.name.value, op) + .as(Some(IntoArrowRaw(op.name.value, at, arguments))) + case _ => + report + .error( + op, + s"Unexpected. `${op.name.value}` must be an arrow." + ) + .as(None) + } case t => t.properties .get(op.name.value) From 94d5aee4fe4143d7f37d0791bd14b1554001e666 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Fri, 17 Nov 2023 16:42:14 +0000 Subject: [PATCH 07/23] Check ability --- .../scala/aqua/compiler/AquaCompiler.scala | 14 +++---- .../aqua/model/inline/RawValueInliner.scala | 16 ++++---- .../main/scala/aqua/model/AquaContext.scala | 12 +++--- .../scala/aqua/parser/lexer/ValueToken.scala | 40 ++++++++++++++++++- .../scala/aqua/semantics/CompilerState.scala | 4 +- .../scala/aqua/semantics/RawSemantics.scala | 22 +++++----- .../aqua/semantics/header/HeaderHandler.scala | 6 +-- .../aqua/semantics/rules/ValuesAlgebra.scala | 33 +++++++++------ .../rules/abilities/AbilitiesAlgebra.scala | 2 + .../abilities/AbilitiesInterpreter.scala | 15 ++++--- .../semantics/rules/names/NamesState.scala | 2 + 11 files changed, 109 insertions(+), 57 deletions(-) diff --git a/compiler/src/main/scala/aqua/compiler/AquaCompiler.scala b/compiler/src/main/scala/aqua/compiler/AquaCompiler.scala index 5913e3d32..66b7c7446 100644 --- a/compiler/src/main/scala/aqua/compiler/AquaCompiler.scala +++ b/compiler/src/main/scala/aqua/compiler/AquaCompiler.scala @@ -1,7 +1,7 @@ package aqua.compiler -import aqua.compiler.AquaError.{ParserError as AquaParserError, *} import aqua.backend.Backend +import aqua.compiler.AquaError.{ParserError as AquaParserError, *} import aqua.linker.{AquaModule, Linker, Modules} import aqua.model.AquaContext import aqua.parser.lift.{LiftParser, Span} @@ -9,22 +9,22 @@ import aqua.parser.{Ast, ParserError} import aqua.raw.RawPart.Parts import aqua.raw.{RawContext, RawPart} import aqua.res.AquaRes -import aqua.semantics.{CompilerState, Semantics} import aqua.semantics.header.{HeaderHandler, HeaderSem, Picker} +import aqua.semantics.{CompilerState, Semantics} import aqua.semantics.{SemanticError, SemanticWarning} +import cats.arrow.FunctionK import cats.data.* -import cats.data.Validated.{validNec, Invalid, Valid} +import cats.data.Validated.{Invalid, Valid, validNec} import cats.parse.Parser0 import cats.syntax.applicative.* +import cats.syntax.either.* import cats.syntax.flatMap.* import cats.syntax.functor.* import cats.syntax.monoid.* -import cats.syntax.traverse.* import cats.syntax.semigroup.* -import cats.syntax.either.* -import cats.{~>, Comonad, Functor, Monad, Monoid, Order} -import cats.arrow.FunctionK +import cats.syntax.traverse.* +import cats.{Comonad, Functor, Monad, Monoid, Order, ~>} import scribe.Logging class AquaCompiler[F[_]: Monad, E, I: Order, S[_]: Comonad, C: Monoid: Picker]( diff --git a/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala b/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala index 3caba68fc..f677af973 100644 --- a/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala @@ -1,22 +1,22 @@ package aqua.model.inline -import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler} -import aqua.model.inline.Inline.MergeMode.* import aqua.model.* +import aqua.model.inline.Inline.MergeMode.* import aqua.model.inline.raw.* +import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler} import aqua.raw.ops.* import aqua.raw.value.* import aqua.types.{ArrayType, LiteralType, OptionType, StreamType} import cats.Eval -import cats.syntax.traverse.* -import cats.syntax.monoid.* -import cats.syntax.functor.* -import cats.syntax.flatMap.* -import cats.syntax.apply.* -import cats.instances.list.* import cats.data.{Chain, State, StateT} +import cats.instances.list.* import cats.syntax.applicative.* +import cats.syntax.apply.* +import cats.syntax.flatMap.* +import cats.syntax.functor.* +import cats.syntax.monoid.* +import cats.syntax.traverse.* import scribe.Logging object RawValueInliner extends Logging { diff --git a/model/src/main/scala/aqua/model/AquaContext.scala b/model/src/main/scala/aqua/model/AquaContext.scala index e057c36fa..9be943798 100644 --- a/model/src/main/scala/aqua/model/AquaContext.scala +++ b/model/src/main/scala/aqua/model/AquaContext.scala @@ -2,23 +2,23 @@ package aqua.model import aqua.raw.arrow.FuncRaw import aqua.raw.ops.CallArrowRawTag -import aqua.raw.value.ValueRaw import aqua.raw.value.CallArrowRaw +import aqua.raw.value.ValueRaw import aqua.raw.{ConstantRaw, RawContext, RawPart, ServiceRaw, TypeRaw} import aqua.types.{AbilityType, StructType, Type} import cats.Monoid -import cats.data.NonEmptyMap import cats.data.Chain +import cats.data.NonEmptyMap import cats.kernel.Semigroup -import cats.syntax.functor.* -import cats.syntax.foldable.* -import cats.syntax.traverse.* import cats.syntax.bifunctor.* +import cats.syntax.foldable.* +import cats.syntax.functor.* import cats.syntax.monoid.* import cats.syntax.option.* -import scribe.Logging +import cats.syntax.traverse.* import scala.collection.immutable.SortedMap +import scribe.Logging case class AquaContext( module: Option[String], diff --git a/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala b/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala index 7ced80ded..98152a097 100644 --- a/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala +++ b/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala @@ -102,6 +102,38 @@ case class PropertyToken[F[_]: Comonad]( case _ => none } + /** + * This method tries to convert property token to + * call arrow token. + * + * Next properties pattern is transformed: + * (Class)+ arrow() + * ^^^^^^^ + * this part is transformed to ability name. + */ + private def toCallArrow: Option[CallArrowToken[F]] = value match { + case VarToken(name) => + val ability = properties.init.traverse { + case f @ IntoField(_) => f.value.some + case _ => none + }.map( + name.value +: _ + ).filter( + _.forall(isClass) + ).map(props => name.rename(props.mkString("."))) + + (properties.last, ability) match { + case (IntoArrow(funcName, args), Some(ability)) => + CallArrowToken( + ability.asTypeToken.some, + funcName, + args + ).some + case _ => none + } + case _ => none + } + /** * This is a hacky method to adjust parsing result * to format that was used previously. @@ -112,7 +144,13 @@ case class PropertyToken[F[_]: Comonad]( * @return Some(token) if token was adjusted, None otherwise */ def adjust: Option[ValueToken[F]] = - toDottedName + toCallArrow.orElse(toDottedName) + + lazy val leadingName: Option[NamedTypeToken[F]] = + value match { + case VarToken(name) => name.asTypeToken.some + case _ => none + } } object PropertyToken { diff --git a/semantics/src/main/scala/aqua/semantics/CompilerState.scala b/semantics/src/main/scala/aqua/semantics/CompilerState.scala index 84c57a4dd..1ea492121 100644 --- a/semantics/src/main/scala/aqua/semantics/CompilerState.scala +++ b/semantics/src/main/scala/aqua/semantics/CompilerState.scala @@ -6,10 +6,10 @@ import aqua.raw.RawContext import aqua.semantics.rules.abilities.AbilitiesState import aqua.semantics.rules.definitions.DefinitionsState import aqua.semantics.rules.locations.LocationsState -import aqua.semantics.rules.names.NamesState -import aqua.semantics.rules.types.TypesState import aqua.semantics.rules.mangler.ManglerState +import aqua.semantics.rules.names.NamesState import aqua.semantics.rules.report.ReportState +import aqua.semantics.rules.types.TypesState import cats.Semigroup import cats.data.{Chain, State} diff --git a/semantics/src/main/scala/aqua/semantics/RawSemantics.scala b/semantics/src/main/scala/aqua/semantics/RawSemantics.scala index 2d2ea61bf..922823dba 100644 --- a/semantics/src/main/scala/aqua/semantics/RawSemantics.scala +++ b/semantics/src/main/scala/aqua/semantics/RawSemantics.scala @@ -1,31 +1,31 @@ package aqua.semantics import aqua.errors.Errors.internalError +import aqua.parser.lexer.{LiteralToken, Token} +import aqua.parser.{Ast, Expr} import aqua.raw.ops.* +import aqua.raw.{Raw, RawContext, RawPart} +import aqua.semantics.header.Picker +import aqua.semantics.header.Picker.* import aqua.semantics.rules.abilities.{AbilitiesAlgebra, AbilitiesInterpreter, AbilitiesState} import aqua.semantics.rules.definitions.{DefinitionsAlgebra, DefinitionsInterpreter} import aqua.semantics.rules.locations.{DummyLocationsInterpreter, LocationsAlgebra} -import aqua.semantics.rules.names.{NamesAlgebra, NamesInterpreter} import aqua.semantics.rules.mangler.{ManglerAlgebra, ManglerInterpreter} -import aqua.semantics.rules.types.{TypesAlgebra, TypesInterpreter} +import aqua.semantics.rules.names.{NamesAlgebra, NamesInterpreter} import aqua.semantics.rules.report.{ReportAlgebra, ReportInterpreter} -import aqua.semantics.header.Picker -import aqua.semantics.header.Picker.* -import aqua.raw.{Raw, RawContext, RawPart} -import aqua.parser.{Ast, Expr} -import aqua.parser.lexer.{LiteralToken, Token} +import aqua.semantics.rules.types.{TypesAlgebra, TypesInterpreter} -import cats.{Eval, Monad} import cats.data.{Chain, EitherT, NonEmptyChain, State, StateT, ValidatedNec, Writer} import cats.syntax.applicative.* -import cats.syntax.option.* import cats.syntax.apply.* import cats.syntax.flatMap.* -import cats.syntax.functor.* import cats.syntax.foldable.* +import cats.syntax.functor.* +import cats.syntax.option.* import cats.syntax.reducible.* -import cats.syntax.traverse.* import cats.syntax.semigroup.* +import cats.syntax.traverse.* +import cats.{Eval, Monad} import scribe.Logging class RawSemantics[S[_]](using diff --git a/semantics/src/main/scala/aqua/semantics/header/HeaderHandler.scala b/semantics/src/main/scala/aqua/semantics/header/HeaderHandler.scala index 68a2672fb..4cadd52bf 100644 --- a/semantics/src/main/scala/aqua/semantics/header/HeaderHandler.scala +++ b/semantics/src/main/scala/aqua/semantics/header/HeaderHandler.scala @@ -12,13 +12,13 @@ import cats.free.Cofree import cats.instances.list.* import cats.instances.option.* import cats.kernel.Semigroup -import cats.syntax.option.* +import cats.syntax.apply.* +import cats.syntax.bifunctor.* import cats.syntax.foldable.* import cats.syntax.functor.* +import cats.syntax.option.* import cats.syntax.semigroup.* import cats.syntax.validated.* -import cats.syntax.bifunctor.* -import cats.syntax.apply.* import cats.{Comonad, Eval, Monoid} class HeaderHandler[S[_]: Comonad, C](using diff --git a/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala b/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala index a8ed5a571..edc01553a 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala @@ -100,20 +100,27 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](using } case prop @ PropertyToken(value, properties) => - prop.adjust.fold( - for { - valueRaw <- valueToRaw(value) - result <- valueRaw.flatTraverse(raw => - properties - .foldLeftM(raw) { case (prev, op) => - OptionT( - resolveSingleProperty(prev.`type`, op) - ).map(prop => ApplyPropertyRaw(prev, prop)) - } - .value + lazy val default = for { + valueRaw <- valueToRaw(value) + result <- valueRaw.flatTraverse(raw => + properties + .foldLeftM(raw) { case (prev, op) => + OptionT( + resolveSingleProperty(prev.`type`, op) + ).map(prop => ApplyPropertyRaw(prev, prop)) + } + .value + ) + } yield result + + prop.leadingName.fold(default)(name => + A.isDefinedAbility(name) + .flatMap(isDefined => + prop.adjust + .filter(_ => isDefined) + .fold(default)(valueToRaw) ) - } yield result - )(valueToRaw) + ) case dvt @ NamedValueToken(typeName, fields) => (for { diff --git a/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesAlgebra.scala b/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesAlgebra.scala index 59996224d..175ba69c6 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesAlgebra.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesAlgebra.scala @@ -15,6 +15,8 @@ trait AbilitiesAlgebra[S[_], Alg[_]] { defaultId: Option[ValueRaw] ): Alg[Boolean] + def isDefinedAbility(name: NamedTypeToken[S]): Alg[Boolean] + def getArrow(name: NamedTypeToken[S], arrow: Name[S]): Alg[Option[ArrowType]] def renameService(name: NamedTypeToken[S]): Alg[Option[String]] diff --git a/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesInterpreter.scala b/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesInterpreter.scala index e5c89c664..78d078b83 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesInterpreter.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/abilities/AbilitiesInterpreter.scala @@ -4,19 +4,19 @@ import aqua.parser.lexer.{Name, NamedTypeToken, Token, ValueToken} import aqua.raw.value.ValueRaw import aqua.raw.{RawContext, ServiceRaw} import aqua.semantics.Levenshtein -import aqua.semantics.rules.report.ReportAlgebra -import aqua.semantics.rules.mangler.ManglerAlgebra import aqua.semantics.rules.locations.LocationsAlgebra -import aqua.semantics.rules.{abilities, StackInterpreter} +import aqua.semantics.rules.mangler.ManglerAlgebra +import aqua.semantics.rules.report.ReportAlgebra +import aqua.semantics.rules.{StackInterpreter, abilities} import aqua.types.{ArrowType, ServiceType} import cats.data.{NonEmptyMap, State} -import cats.syntax.functor.* +import cats.syntax.applicative.* import cats.syntax.apply.* import cats.syntax.foldable.* -import cats.syntax.traverse.* -import cats.syntax.applicative.* +import cats.syntax.functor.* import cats.syntax.option.* +import cats.syntax.traverse.* import monocle.Lens import monocle.macros.GenLens @@ -70,6 +70,9 @@ class AbilitiesInterpreter[S[_], X](using locations.pointTokenWithFieldLocation(name.value, name, arrow.value, arrow) } + override def isDefinedAbility(name: NamedTypeToken[S]): State[X, Boolean] = + getState.map(_.abilities.contains(name.value)) + override def getArrow(name: NamedTypeToken[S], arrow: Name[S]): SX[Option[ArrowType]] = getAbility(name.value).flatMap { case Some(abCtx) => diff --git a/semantics/src/main/scala/aqua/semantics/rules/names/NamesState.scala b/semantics/src/main/scala/aqua/semantics/rules/names/NamesState.scala index 873c6b107..3d62cbcf1 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/names/NamesState.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/names/NamesState.scala @@ -3,6 +3,7 @@ package aqua.semantics.rules.names import aqua.parser.lexer.{Name, Token} import aqua.raw.RawContext import aqua.types.{ArrowType, Type} + import cats.kernel.Monoid import cats.syntax.functor.* @@ -64,4 +65,5 @@ object NamesState { }, constants = context.allValues.map { case (s, vm) => (s, vm.`type`) } ) + } From fdb0e6aea3e01bf455eef83d355486cc1f6d029a Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Tue, 21 Nov 2023 10:55:11 +0000 Subject: [PATCH 08/23] Refactor captured values resolution --- .../aqua/model/inline/ArrowInliner.scala | 94 ++++++++++++---- .../aqua/model/inline/state/Arrows.scala | 10 +- .../aqua/model/inline/state/Exports.scala | 33 +++++- .../scala/aqua/raw/ops/RawTagGivens.scala | 8 +- .../src/main/scala/aqua/model/ArgsCall.scala | 11 +- types/src/main/scala/aqua/types/Type.scala | 105 +++++++++--------- 6 files changed, 171 insertions(+), 90 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index c6b97e0d3..23e27c06e 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -10,11 +10,13 @@ import aqua.types.* import cats.data.StateT import cats.data.{Chain, IndexedStateT, State} +import cats.kernel.Semigroup import cats.syntax.applicative.* import cats.syntax.bifunctor.* import cats.syntax.foldable.* import cats.syntax.functor.* import cats.syntax.option.* +import cats.syntax.semigroup.* import cats.syntax.show.* import cats.syntax.traverse.* import cats.{Eval, Monoid} @@ -227,6 +229,16 @@ object ArrowInliner extends Logging { renamed: Map[String, T] ) + given [T]: Monoid[Renamed[T]] with { + override def empty: Renamed[T] = Renamed(Map.empty, Map.empty) + + override def combine(x: Renamed[T], y: Renamed[T]): Renamed[T] = + Renamed( + x.renames ++ y.renames, + x.renamed ++ y.renamed + ) + } + // TODO: Make this extension private somehow? extension [T](vals: Map[String, T]) { @@ -252,6 +264,64 @@ object ArrowInliner extends Logging { ) } + def renamedCaptured[S: Mangler]( + fn: FuncArrow, + exports: Map[String, ValueModel], + arrows: Map[String, FuncArrow] + ): State[S, (Renamed[ValueModel], Renamed[FuncArrow])] = { + // Gather abilities related values + val abilitiesValues = fn.capturedValues.collect { + // Gather only top level abilities + case (name, vm @ ValueModel.Ability(_, at, Chain.nil)) => + name -> (at, Exports.gatherFrom( + List(name), + fn.capturedValues + )) + } + val abilitiesValuesKeys = abilitiesValues.flatMap { case (_, (_, values)) => + values.keySet + } + + // Gather abilities related arrows + val abilitiesArrows = abilitiesValues.toList.foldMap { case (_, (_, values)) => + Arrows.arrowsByValues(fn.capturedArrows, values).toList + }.toMap + + val otherValues = fn.capturedValues -- abilitiesValuesKeys + val otherArrows = fn.capturedArrows -- abilitiesArrows.keySet + + for { + otherValuesRenamed <- findNewNames(otherValues) + otherArrowsRenamed <- findNewNames(otherArrows) + + // Calculate renaming based on abilities + valuesRenamed <- abilitiesValues.toList.traverse { case (name, (at, values)) => + Mangler[S] + .findAndForbidName(name) + .map(rename => + AbilityType + .renames(at)(name, rename) + .updated(name, rename) + ) + .map(renames => + val valuesRenamed = values.renamed(renames).map { + case (name, vm: VarModel) => name -> vm.copy(name = name) + case v => v + } + Renamed(renames, valuesRenamed) + ) + }.map(_.combineAll) + + arrowsRenamed = Renamed( + valuesRenamed.renames.filterKeys(abilitiesArrows.keySet).toMap, + abilitiesArrows.renamed(valuesRenamed.renames) + ) + + values = otherValuesRenamed |+| valuesRenamed + arrows = otherArrowsRenamed |+| arrowsRenamed + } yield values -> arrows + } + /** * Prepare the function and the context for inlining * @@ -285,26 +355,8 @@ object ArrowInliner extends Logging { arrowRenames = args.arrowArgsRenames abRenames = args.abilityArgsRenames - /** - * Find new names for captured values and arrows - * to avoid collisions, then resolve them in context. - */ - capturedValues <- findNewNames(fn.capturedValues) - /** - * If arrow correspond to a value, - * rename in accordingly to the value - */ - capturedArrowValues = Arrows.arrowsByValues( - fn.capturedArrows, - fn.capturedValues - ) - capturedArrowValuesRenamed = capturedArrowValues.renamed( - capturedValues.renames - ) - /** - * Rename arrows that are not values - */ - capturedArrows <- findNewNames(fn.capturedArrows -- capturedArrowValues.keySet) + captured <- renamedCaptured(fn, exports, arrows) + (capturedValues, capturedArrows) = captured /** * Function defines variables inside its body. @@ -333,7 +385,7 @@ object ArrowInliner extends Logging { * It seems that resolving whole `exports` * and `arrows` is not necessary. */ - arrowsResolved = arrows ++ capturedArrowValuesRenamed ++ capturedArrows.renamed + arrowsResolved = arrows ++ capturedArrows.renamed exportsResolved = exports ++ data.renamed ++ capturedValues.renamed tree = fn.body.rename(renaming) diff --git a/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala b/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala index 1946b2550..ddf0e501c 100644 --- a/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala +++ b/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala @@ -1,14 +1,16 @@ package aqua.model.inline.state +import aqua.model.ValueModel import aqua.model.{ArgsCall, FuncArrow} import aqua.raw.arrow.FuncRaw -import aqua.model.ValueModel +import aqua.types.* import cats.data.State import cats.instances.list.* import cats.syntax.functor.* -import cats.syntax.traverse.* +import cats.syntax.option.* import cats.syntax.show.* +import cats.syntax.traverse.* /** * State algebra for resolved arrows @@ -31,9 +33,9 @@ trait Arrows[S] extends Scoped[S] { topology: Option[String] )(using Exports[S]): State[S, Unit] = for { - exps <- Exports[S].exports arrs <- arrows - capturedVars = exps.filterKeys(arrow.capturedVars).toMap + exps <- Exports[S].exports + capturedVars <- Exports[S].gather(arrow.capturedVars.toSeq) capturedArrows = arrs.filterKeys(arrow.capturedVars).toMap ++ Arrows.arrowsByValues(arrs, capturedVars) funcArrow = FuncArrow.fromRaw(arrow, capturedArrows, capturedVars, topology) diff --git a/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala b/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala index 489626060..3be3c70eb 100644 --- a/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala +++ b/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala @@ -1,8 +1,8 @@ package aqua.model.inline.state -import aqua.model.{LiteralModel, ValueModel, VarModel} import aqua.model.ValueModel.Ability -import aqua.types.{AbilityType, NamedType} +import aqua.model.{LiteralModel, ValueModel, VarModel} +import aqua.types.{AbilityType, GeneralAbilityType, NamedType} import cats.data.{NonEmptyList, State} @@ -78,6 +78,9 @@ trait Exports[S] extends Scoped[S] { */ val exports: State[S, Map[String, ValueModel]] + final def gather(names: Seq[String]): State[S, Map[String, ValueModel]] = + exports.map(Exports.gatherFrom(names, _)) + /** * Change [[S]] to [[R]] */ @@ -125,6 +128,32 @@ trait Exports[S] extends Scoped[S] { object Exports { def apply[S](using exports: Exports[S]): Exports[S] = exports + /** + * Gather all the values that are related to the given names + * (ability fields) + * + * @param names names of variables + * @param state exports state + */ + def gatherFrom( + names: Seq[String], + state: Map[String, ValueModel] + ): Map[String, ValueModel] = { + val related = for { + variable <- names + exp <- state.get(variable).toList + _ = println(s"var: $variable, exp: $exp") + at <- exp.`type` match { + case at: GeneralAbilityType => at :: Nil + case _ => Nil + } + field <- at.allFields.toNel.toList + (fieldName, _) = field + } yield AbilityType.fullName(variable, fieldName) + + state.filterKeys(names.toSet ++ related).toMap + } + // Get last linked VarModel def getLastValue(name: String, state: Map[String, ValueModel]): Option[ValueModel] = { state.get(name) match { diff --git a/model/raw/src/main/scala/aqua/raw/ops/RawTagGivens.scala b/model/raw/src/main/scala/aqua/raw/ops/RawTagGivens.scala index 2738adb59..b0ff46f3d 100644 --- a/model/raw/src/main/scala/aqua/raw/ops/RawTagGivens.scala +++ b/model/raw/src/main/scala/aqua/raw/ops/RawTagGivens.scala @@ -2,13 +2,13 @@ package aqua.raw.ops import aqua.raw.value.{LiteralRaw, ValueRaw} -import cats.free.Cofree import cats.data.Chain -import cats.{Eval, Semigroup} +import cats.free.Cofree +import cats.syntax.all.* import cats.syntax.apply.* -import cats.syntax.semigroup.* import cats.syntax.foldable.* -import cats.syntax.all.* +import cats.syntax.semigroup.* +import cats.{Eval, Semigroup} trait RawTagGivens { diff --git a/model/src/main/scala/aqua/model/ArgsCall.scala b/model/src/main/scala/aqua/model/ArgsCall.scala index 130c06d9b..da34193e7 100644 --- a/model/src/main/scala/aqua/model/ArgsCall.scala +++ b/model/src/main/scala/aqua/model/ArgsCall.scala @@ -1,7 +1,7 @@ package aqua.model -import aqua.model.{ValueModel, VarModel} import aqua.model.ValueModel.Ability +import aqua.model.{ValueModel, VarModel} import aqua.raw.ops.Call import aqua.raw.value.{ValueRaw, VarRaw} import aqua.types.* @@ -54,14 +54,7 @@ case class ArgsCall(args: ProductType, callWith: List[ValueModel]) { */ lazy val abilityArgsRenames: Map[String, String] = abilityArgs.toList.foldMap { case (name, (vm, at)) => - at.arrows.keys - .map(arrowPath => - val fullName = AbilityType.fullName(name, arrowPath) - val newFullName = AbilityType.fullName(vm.name, arrowPath) - fullName -> newFullName - ) - .toMap - .updated(name, vm.name) + AbilityType.renames(at)(name, vm.name) } /** diff --git a/types/src/main/scala/aqua/types/Type.scala b/types/src/main/scala/aqua/types/Type.scala index 3118062f6..25525303b 100644 --- a/types/src/main/scala/aqua/types/Type.scala +++ b/types/src/main/scala/aqua/types/Type.scala @@ -1,6 +1,7 @@ package aqua.types import aqua.errors.Errors.internalError +import aqua.types.* import aqua.types.Type.* import cats.Eval @@ -8,9 +9,11 @@ import cats.PartialOrder import cats.data.NonEmptyList import cats.data.NonEmptyMap import cats.syntax.applicative.* +import cats.syntax.foldable.* import cats.syntax.option.* import cats.syntax.partialOrder.* import cats.syntax.traverse.* +import scala.collection.immutable.SortedMap sealed trait Type { @@ -339,73 +342,62 @@ sealed trait NamedType extends Type { def fields: NonEmptyMap[String, Type] + /** + * Get all fields defined in this type and its fields of named type. + * Paths to fields are returned **without** type name + * to allow renaming on call site. + */ + final def allFields: NonEmptyMap[String, Type] = { + def allEval(path: Option[String], nt: NamedType): Eval[List[(String, Type)]] = { + val fieldsList = nt.fields.toNel.toList + fieldsList.flatTraverse { + case (name, t: NamedType) => + val newPath = path.fold(name)(AbilityType.fullName(_, name)) + allEval(newPath.some, t) + case _ => Eval.now(Nil) + }.map(fieldsList ++ _) + } + + allEval(none, this) + .map(l => + /** + * As fields are `NonEmptyMap`, this + * operation should be safe + */ + NonEmptyMap.fromMapUnsafe(SortedMap.from(l)) + ) + .value + } + /** * Get all arrows defined in this type and its sub-abilities. * Paths to arrows are returned **without** type name * to allow renaming on call site. */ - lazy val arrows: Map[String, ArrowType] = { - def getArrowsEval(path: Option[String], nt: NamedType): Eval[List[(String, ArrowType)]] = - nt.fields.toNel.toList.flatTraverse { - // sub-arrows could be in abilities or services - case (innerName, innerType: GeneralAbilityType) => - val newPath = path.fold(innerName)(AbilityType.fullName(_, innerName)) - getArrowsEval(newPath.some, innerType) - case (aName, aType: ArrowType) => - val newPath = path.fold(aName)(AbilityType.fullName(_, aName)) - List(newPath -> aType).pure - case _ => Nil.pure - } - - getArrowsEval(None, this).value.toMap - } + lazy val arrows: Map[String, ArrowType] = + allFields.toSortedMap.toMap.collect { case (name, at: ArrowType) => + name -> at + } /** * Get all abilities defined in this type and its sub-abilities. * Paths to abilities are returned **without** type name * to allow renaming on call site. */ - lazy val abilities: Map[String, AbilityType] = { - def getAbilitiesEval( - path: Option[String], - nt: NamedType - ): Eval[List[(String, AbilityType)]] = - nt.fields.toNel.toList.flatTraverse { - // sub-abilities could be only in abilities - case (abName, abType: AbilityType) => - val fullName = path.fold(abName)(AbilityType.fullName(_, abName)) - getAbilitiesEval(fullName.some, abType).map( - (fullName -> abType) :: _ - ) - case _ => Nil.pure - } - - getAbilitiesEval(None, this).value.toMap - } + lazy val abilities: Map[String, AbilityType] = + allFields.toSortedMap.toMap.collect { case (name, at: AbilityType) => + name -> at + } /** * Get all variables defined in this type and its sub-abilities. * Paths to variables are returned **without** type name * to allow renaming on call site. */ - lazy val variables: Map[String, DataType] = { - def getVariablesEval( - path: Option[String], - nt: NamedType - ): Eval[List[(String, DataType)]] = - nt.fields.toNel.toList.flatTraverse { - // sub-variables could be only in abilities - case (abName, abType: AbilityType) => - val newPath = path.fold(abName)(AbilityType.fullName(_, abName)) - getVariablesEval(newPath.some, abType) - case (dName, dType: DataType) => - val newPath = path.fold(dName)(AbilityType.fullName(_, dName)) - List(newPath -> dType).pure - case _ => Nil.pure - } - - getVariablesEval(None, this).value.toMap - } + lazy val variables: Map[String, DataType] = + allFields.toSortedMap.toMap.collect { case (name, at: DataType) => + name -> at + } } // Struct is an unordered collection of labelled types @@ -474,6 +466,19 @@ case class AbilityType( object AbilityType { def fullName(name: String, field: String) = s"$name.$field" + + def renames(at: NamedType)( + name: String, + newName: String + ): Map[String, String] = + at.allFields.keys.toList + .map(path => + val fullName = AbilityType.fullName(name, path) + val newFullName = AbilityType.fullName(newName, path) + fullName -> newFullName + ) + .toMap + .updated(name, newName) } /** From a827dfbed4524e4cfb9dc675bd7827758db19eab Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Tue, 21 Nov 2023 10:59:58 +0000 Subject: [PATCH 09/23] Remove println --- .../inline/src/main/scala/aqua/model/inline/state/Exports.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala b/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala index 3be3c70eb..7f09b6a36 100644 --- a/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala +++ b/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala @@ -142,7 +142,6 @@ object Exports { val related = for { variable <- names exp <- state.get(variable).toList - _ = println(s"var: $variable, exp: $exp") at <- exp.`type` match { case at: GeneralAbilityType => at :: Nil case _ => Nil From d2300bbc84d56c2a46c0ed22560b05790fcc60d7 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Tue, 21 Nov 2023 11:25:53 +0000 Subject: [PATCH 10/23] Fix fields gathering --- .../main/scala/aqua/model/inline/ArrowInliner.scala | 10 +++++++--- types/src/main/scala/aqua/types/Type.scala | 9 ++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index 23e27c06e..4cde38a1d 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -107,7 +107,7 @@ object ArrowInliner extends Logging { exports <- Exports[S].exports arrows <- Arrows[S].arrows // gather all arrows and variables from abilities - returnedAbilities = rets.collect { case VarModel(name, at: GeneralAbilityType, _) => + returnedAbilities = rets.collect { case ValueModel.Ability(name, at, _) => name -> at } varsFromAbilities = returnedAbilities.flatMap { case (name, at) => @@ -149,6 +149,8 @@ object ArrowInliner extends Logging { val fullName = AbilityType.fullName(name, fName) val newFullName = AbilityType.fullName(newName.getOrElse(name), fName) + println(s"getAbilityFields: $fullName, $newFullName") + Exports .getLastValue(fullName, exports) .map(newFullName -> _) @@ -177,7 +179,7 @@ object ArrowInliner extends Logging { ) get(_.variables) ++ get(_.arrows).flatMap { - case arrow @ (_, vm: VarModel) => + case arrow @ (_, vm @ ValueModel.Arrow(_, _)) => arrow.some case (_, m) => internalError(s"($m) cannot be an arrow") @@ -209,7 +211,7 @@ object ArrowInliner extends Logging { ) get(_.arrows).flatMap { - case (_, VarModel(name, _, _)) => + case (_, ValueModel.Arrow(name, _)) => arrows.get(name).map(name -> _) case (_, m) => internalError(s"($m) cannot be an arrow") @@ -414,6 +416,8 @@ object ArrowInliner extends Logging { // Process renamings, prepare environment fn <- ArrowInliner.prelude(arrow, call, exports, arrows) inlineResult <- ArrowInliner.inline(fn, call, streams) + _ = println(s"Inline result: $inlineResult") + _ = println(s"exports to save: ${inlineResult.exportsToSave}") } yield inlineResult ) ) diff --git a/types/src/main/scala/aqua/types/Type.scala b/types/src/main/scala/aqua/types/Type.scala index 25525303b..c9b0bfc63 100644 --- a/types/src/main/scala/aqua/types/Type.scala +++ b/types/src/main/scala/aqua/types/Type.scala @@ -349,13 +349,16 @@ sealed trait NamedType extends Type { */ final def allFields: NonEmptyMap[String, Type] = { def allEval(path: Option[String], nt: NamedType): Eval[List[(String, Type)]] = { + val qualified = (name: String) => path.fold(name)(AbilityType.fullName(_, name)) val fieldsList = nt.fields.toNel.toList + val currentFields = fieldsList.map { case (name, t) => + qualified(name) -> t + } fieldsList.flatTraverse { case (name, t: NamedType) => - val newPath = path.fold(name)(AbilityType.fullName(_, name)) - allEval(newPath.some, t) + allEval(qualified(name).some, t) case _ => Eval.now(Nil) - }.map(fieldsList ++ _) + }.map(currentFields ++ _) } allEval(none, this) From f0dc246c34d7adb2331e5e2cc76489c14289b7bc Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Tue, 21 Nov 2023 11:26:20 +0000 Subject: [PATCH 11/23] Remove println --- .../inline/src/main/scala/aqua/model/inline/ArrowInliner.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index 4cde38a1d..9fdd86f3e 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -149,8 +149,6 @@ object ArrowInliner extends Logging { val fullName = AbilityType.fullName(name, fName) val newFullName = AbilityType.fullName(newName.getOrElse(name), fName) - println(s"getAbilityFields: $fullName, $newFullName") - Exports .getLastValue(fullName, exports) .map(newFullName -> _) From cab90bfc368a1ac0732f4e8e77f5f071d0d8a5b3 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Tue, 21 Nov 2023 11:31:09 +0000 Subject: [PATCH 12/23] Remove println --- .../inline/src/main/scala/aqua/model/inline/ArrowInliner.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index 9fdd86f3e..ddc76677e 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -414,8 +414,6 @@ object ArrowInliner extends Logging { // Process renamings, prepare environment fn <- ArrowInliner.prelude(arrow, call, exports, arrows) inlineResult <- ArrowInliner.inline(fn, call, streams) - _ = println(s"Inline result: $inlineResult") - _ = println(s"exports to save: ${inlineResult.exportsToSave}") } yield inlineResult ) ) From 3204ddba4fbb9347537018fa126cca7b2edee6a5 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Tue, 21 Nov 2023 16:04:03 +0000 Subject: [PATCH 13/23] Fix renaming, export ability --- .../src/main/scala/aqua/model/inline/ArrowInliner.scala | 3 ++- .../scala/aqua/model/inline/raw/MakeAbilityRawInliner.scala | 5 +++-- .../src/main/scala/aqua/model/inline/state/Exports.scala | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index ddc76677e..ca0ba1b5c 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -305,7 +305,8 @@ object ArrowInliner extends Logging { ) .map(renames => val valuesRenamed = values.renamed(renames).map { - case (name, vm: VarModel) => name -> vm.copy(name = name) + case (name, vm: VarModel) if renames.contains(vm.name) => + name -> vm.copy(name = name) case v => v } Renamed(renames, valuesRenamed) diff --git a/model/inline/src/main/scala/aqua/model/inline/raw/MakeAbilityRawInliner.scala b/model/inline/src/main/scala/aqua/model/inline/raw/MakeAbilityRawInliner.scala index a880bef9b..b4999c521 100644 --- a/model/inline/src/main/scala/aqua/model/inline/raw/MakeAbilityRawInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/raw/MakeAbilityRawInliner.scala @@ -1,12 +1,13 @@ package aqua.model.inline.raw +import aqua.model.ValueModel.Ability import aqua.model.inline.Inline import aqua.model.inline.RawValueInliner.unfold import aqua.model.inline.state.{Arrows, Exports, Mangler} import aqua.model.{SeqModel, ValueModel, VarModel} import aqua.raw.value.AbilityRaw import aqua.types.AbilityType -import aqua.model.ValueModel.Ability + import cats.Eval import cats.data.{Chain, IndexedStateT, NonEmptyMap, State} import cats.syntax.foldable.* @@ -31,7 +32,7 @@ object MakeAbilityRawInliner extends RawInliner[AbilityRaw] { propertiesAllowed: Boolean ): State[S, (ValueModel, Inline)] = { for { - name <- Mangler[S].findAndForbidName(raw.abilityType.name + "_ab") + name <- Mangler[S].findAndForbidName(raw.abilityType.name + "_anon") foldedFields <- raw.fieldsAndArrows.nonEmptyTraverse(unfold(_)) varModel = VarModel(name, raw.baseType) valsInline = foldedFields.toList.foldMap { case (_, inline) => inline }.desugar diff --git a/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala b/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala index 7f09b6a36..528249cf5 100644 --- a/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala +++ b/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala @@ -194,9 +194,9 @@ object Exports { value: ValueModel ): State[Map[String, ValueModel], Unit] = State.modify { state => value match { - case Ability(name, at, property) if property.isEmpty => + case Ability(name, at, properties) if properties.isEmpty => val pairs = getAbilityPairs(name, exportName, at, state) - state ++ pairs.toList.toMap + state ++ pairs.toList.toMap + (exportName -> value) case _ => state + (exportName -> value) } } From 4c27ee2a61472388861631a837ef2cf1d40a54db Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Tue, 21 Nov 2023 16:25:51 +0000 Subject: [PATCH 14/23] Rename only abilities --- .../inline/src/main/scala/aqua/model/inline/ArrowInliner.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index ca0ba1b5c..15d06b8d8 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -307,6 +307,8 @@ object ArrowInliner extends Logging { val valuesRenamed = values.renamed(renames).map { case (name, vm: VarModel) if renames.contains(vm.name) => name -> vm.copy(name = name) + case (name, vm @ ValueModel.Ability(_, _, _)) => + name -> vm.copy(name = name) case v => v } Renamed(renames, valuesRenamed) From e9052e27bc38f341857f706e855c7d0c6cdf421c Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Tue, 21 Nov 2023 16:57:38 +0000 Subject: [PATCH 15/23] Fix unit tests --- .../aqua/compiler/AquaCompilerSpec.scala | 3 +- .../aqua/model/inline/ArrowInlinerSpec.scala | 21 +++--- .../scala/aqua/semantics/SemanticsSpec.scala | 71 ++++++++++++------- 3 files changed, 57 insertions(+), 38 deletions(-) diff --git a/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala b/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala index 6aba7fc79..a0f05157d 100644 --- a/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala +++ b/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala @@ -418,7 +418,8 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside { SeqRes.wrap( getDataSrv("-relay-", "-relay-", ScalarType.string), srvCall("resolved-id"), - srvCall("default-id") + srvCall("default-id"), + emptyRespCall(transformCfg, initPeer) ), errorCall(transformCfg, 0, initPeer) ) diff --git a/model/inline/src/test/scala/aqua/model/inline/ArrowInlinerSpec.scala b/model/inline/src/test/scala/aqua/model/inline/ArrowInlinerSpec.scala index 124c07c85..786f479cc 100644 --- a/model/inline/src/test/scala/aqua/model/inline/ArrowInlinerSpec.scala +++ b/model/inline/src/test/scala/aqua/model/inline/ArrowInlinerSpec.scala @@ -3,20 +3,21 @@ package aqua.model.inline import aqua.model.* import aqua.model.MetaModel.CallArrowModel import aqua.model.inline.state.InliningState +import aqua.raw.arrow.{ArrowRaw, FuncRaw} import aqua.raw.ops.* import aqua.raw.value.* -import aqua.types.* import aqua.raw.value.{CallArrowRaw, ValueRaw} -import aqua.raw.arrow.{ArrowRaw, FuncRaw} +import aqua.types.* + import cats.Eval -import cats.syntax.show.* -import cats.syntax.option.* -import cats.syntax.flatMap.* -import cats.free.Cofree import cats.data.{Chain, NonEmptyList, NonEmptyMap} +import cats.free.Cofree +import cats.syntax.flatMap.* +import cats.syntax.option.* +import cats.syntax.show.* +import org.scalatest.Inside import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -import org.scalatest.Inside class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside { @@ -266,8 +267,8 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside { val model = callFuncModel(newFunc) - val restrictionName = model.collect { - case RestrictionModel(name, _) => name + val restrictionName = model.collect { case RestrictionModel(name, _) => + name }.headOption restrictionName shouldBe Some(someStr.name) @@ -2610,8 +2611,6 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside { .runA(InliningState()) .value - // TODO: Don't know for what to test here - // inliner will just log an error in case of failure model.head should not equal EmptyModel } diff --git a/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala b/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala index 6d7b01339..2da479bb9 100644 --- a/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala +++ b/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala @@ -13,7 +13,7 @@ import aqua.types.* import cats.Eval import cats.data.State import cats.data.Validated -import cats.data.{Chain, EitherNec, NonEmptyChain} +import cats.data.{Chain, EitherNec, NonEmptyChain, NonEmptyMap} import cats.free.Cofree import cats.syntax.foldable.* import cats.syntax.option.* @@ -102,18 +102,29 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside { | """.stripMargin - def testServiceCallStr(str: String) = - CallArrowRawTag - .ability( - abilityName = "Test", - funcName = "testCallStr", - call = Call(LiteralRaw.quote(str) :: Nil, Nil), - arrowType = ArrowType( - ProductType.labelled(("s" -> ScalarType.string) :: Nil), - ProductType(ScalarType.string :: Nil) - ) + def testServiceCallStr(str: String) = { + val arrowType = ArrowType( + ProductType.labelled(("s" -> ScalarType.string) :: Nil), + ProductType(ScalarType.string :: Nil) + ) + + CallArrowRawTag( + Nil, + ApplyPropertyRaw( + VarRaw( + "Test", + ServiceType( + "Test", + NonEmptyMap.of( + "testCallStr" -> arrowType, + "testCall" -> ArrowType(NilType, NilType) + ) + ) + ), + IntoArrowRaw("testCallStr", arrowType, LiteralRaw.quote(str) :: Nil) ) - .leaf + ).leaf + } def equ(left: ValueRaw, right: ValueRaw): ApplyBinaryOpRaw = ApplyBinaryOpRaw(ApplyBinaryOpRaw.Op.Eq, left, right, ScalarType.bool) @@ -152,14 +163,21 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside { insideBody(script) { body => val arrowType = ArrowType(NilType, ConsType.cons(ScalarType.string, NilType)) - val serviceCall = CallArrowRawTag - .ability( - abilityName = "A", - funcName = "fn1", - call = emptyCall, - arrowType = arrowType + val serviceCall = CallArrowRawTag( + Nil, + ApplyPropertyRaw( + VarRaw( + "A", + ServiceType( + "A", + NonEmptyMap.of( + "fn1" -> arrowType + ) + ) + ), + IntoArrowRaw("fn1", arrowType, Nil) ) - .leaf + ).leaf val expected = ParTag.wrap( @@ -880,13 +898,14 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside { |""".stripMargin insideBody(script) { body => - matchSubtree(body) { case (CallArrowRawTag(_, ca: CallArrowRaw), _) => - inside(ca.arguments) { case (c: CollectionRaw) :: Nil => - c.values.exists { - case VarRaw(name, _) => name == "stream" - case _ => false - } should be(true) - } + matchSubtree(body) { + case (CallArrowRawTag(_, ApplyPropertyRaw(_, IntoArrowRaw("consume", _, args))), _) => + inside(args) { case (c: CollectionRaw) :: Nil => + c.values.exists { + case VarRaw(name, _) => name == "stream" + case _ => false + } should be(true) + } } } } From bbe16f1323c9921ef9a43d6450a5a20fbb2a0851 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Tue, 21 Nov 2023 17:04:40 +0000 Subject: [PATCH 16/23] Fix captured arrows renaming --- .../aqua/model/inline/ArrowInliner.scala | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index 15d06b8d8..4b5194daa 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -291,9 +291,6 @@ object ArrowInliner extends Logging { val otherArrows = fn.capturedArrows -- abilitiesArrows.keySet for { - otherValuesRenamed <- findNewNames(otherValues) - otherArrowsRenamed <- findNewNames(otherArrows) - // Calculate renaming based on abilities valuesRenamed <- abilitiesValues.toList.traverse { case (name, (at, values)) => Mangler[S] @@ -320,8 +317,20 @@ object ArrowInliner extends Logging { abilitiesArrows.renamed(valuesRenamed.renames) ) - values = otherValuesRenamed |+| valuesRenamed - arrows = otherArrowsRenamed |+| arrowsRenamed + otherValuesRenamed <- findNewNames(otherValues) + otherArrowsValues = Arrows.arrowsByValues( + otherArrows, + otherValues + ) + otherArrowsValuesRenamed = Renamed( + otherValuesRenamed.renames.filterKeys(otherArrowsValues.keySet).toMap, + otherArrowsValues.renamed(otherValuesRenamed.renames) + ) + + otherArrowsRenamed <- findNewNames(otherArrows) + + values = valuesRenamed |+| otherValuesRenamed + arrows = arrowsRenamed |+| otherArrowsValuesRenamed |+| otherArrowsRenamed } yield values -> arrows } From 127a88a05101dce4e926eeaf15f29a2630bc5e0b Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 22 Nov 2023 10:19:33 +0000 Subject: [PATCH 17/23] Add comments --- .../aqua/model/inline/ArrowInliner.scala | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index 4b5194daa..e30839fff 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -264,6 +264,14 @@ object ArrowInliner extends Logging { ) } + /** + * Correctly rename captured values and arrows of a function + * + * @param fn Function + * @param exports Exports state before calling/inlining + * @param arrows Arrows state before calling/inlining + * @return Renamed values and arrows + */ def renamedCaptured[S: Mangler]( fn: FuncArrow, exports: Map[String, ValueModel], @@ -273,11 +281,16 @@ object ArrowInliner extends Logging { val abilitiesValues = fn.capturedValues.collect { // Gather only top level abilities case (name, vm @ ValueModel.Ability(_, at, Chain.nil)) => - name -> (at, Exports.gatherFrom( - List(name), - fn.capturedValues - )) + name -> ( + at, + // Gather all values related to `name` + Exports.gatherFrom( + name :: Nil, + fn.capturedValues + ) + ) } + // Gather all abilities related names val abilitiesValuesKeys = abilitiesValues.flatMap { case (_, (_, values)) => values.keySet } @@ -287,6 +300,7 @@ object ArrowInliner extends Logging { Arrows.arrowsByValues(fn.capturedArrows, values).toList }.toMap + // Gather all other values and arrows that are not related to abilities val otherValues = fn.capturedValues -- abilitiesValuesKeys val otherArrows = fn.capturedArrows -- abilitiesArrows.keySet @@ -296,8 +310,10 @@ object ArrowInliner extends Logging { Mangler[S] .findAndForbidName(name) .map(rename => + // Get renaming map for this ability AbilityType .renames(at)(name, rename) + // Add ability rename too .updated(name, rename) ) .map(renames => From 6412df119344799cf6eaba8734ddc02a0657c98c Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 22 Nov 2023 10:39:19 +0000 Subject: [PATCH 18/23] Refactor --- .../main/scala/aqua/model/inline/ArrowInliner.scala | 12 ++++++------ .../model/inline/raw/ApplyPropertiesRawInliner.scala | 2 +- .../model/inline/raw/MakeAbilityRawInliner.scala | 4 ++-- .../main/scala/aqua/model/inline/state/Arrows.scala | 4 ++-- .../main/scala/aqua/model/inline/state/Exports.scala | 4 ++-- model/src/main/scala/aqua/model/ArgsCall.scala | 2 +- model/src/main/scala/aqua/model/CallModel.scala | 4 ++-- model/src/main/scala/aqua/model/ValueModel.scala | 12 ++++++------ 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index e30839fff..4d2b65335 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -107,8 +107,8 @@ object ArrowInliner extends Logging { exports <- Exports[S].exports arrows <- Arrows[S].arrows // gather all arrows and variables from abilities - returnedAbilities = rets.collect { case ValueModel.Ability(name, at, _) => - name -> at + returnedAbilities = rets.collect { case ValueModel.Ability(vm, at) => + vm.name -> at } varsFromAbilities = returnedAbilities.flatMap { case (name, at) => getAbilityVars(name, None, at, exports) @@ -209,8 +209,8 @@ object ArrowInliner extends Logging { ) get(_.arrows).flatMap { - case (_, ValueModel.Arrow(name, _)) => - arrows.get(name).map(name -> _) + case (_, ValueModel.Arrow(vm, _)) => + arrows.get(vm.name).map(vm.name -> _) case (_, m) => internalError(s"($m) cannot be an arrow") } @@ -280,7 +280,7 @@ object ArrowInliner extends Logging { // Gather abilities related values val abilitiesValues = fn.capturedValues.collect { // Gather only top level abilities - case (name, vm @ ValueModel.Ability(_, at, Chain.nil)) => + case (name, ValueModel.Ability(vm, at)) if vm.properties.isEmpty => name -> ( at, // Gather all values related to `name` @@ -320,7 +320,7 @@ object ArrowInliner extends Logging { val valuesRenamed = values.renamed(renames).map { case (name, vm: VarModel) if renames.contains(vm.name) => name -> vm.copy(name = name) - case (name, vm @ ValueModel.Ability(_, _, _)) => + case (name, ValueModel.Ability(vm, _)) => name -> vm.copy(name = name) case v => v } diff --git a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala b/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala index 43df1e0fd..7327efc82 100644 --- a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala @@ -223,7 +223,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi State.pure((vm, prevInline.mergeWith(optimizationInline, SeqMode))) ) { case (state, property) => state.flatMap { - case (vm @ Ability(_, at, _), leftInline) => + case (vm @ Ability(_, at), leftInline) => unfoldAbilityProperty(vm, at, property.raw).map { case (vm, inl) => ( vm, diff --git a/model/inline/src/main/scala/aqua/model/inline/raw/MakeAbilityRawInliner.scala b/model/inline/src/main/scala/aqua/model/inline/raw/MakeAbilityRawInliner.scala index b4999c521..0eae5c105 100644 --- a/model/inline/src/main/scala/aqua/model/inline/raw/MakeAbilityRawInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/raw/MakeAbilityRawInliner.scala @@ -20,9 +20,9 @@ object MakeAbilityRawInliner extends RawInliner[AbilityRaw] { fields: NonEmptyMap[String, (ValueModel, Inline)] ): State[S, Unit] = fields.toNel.traverse { - case (n, (Ability(abilityName, _, _), _)) => + case (n, (Ability(vm, _), _)) => val leftName = AbilityType.fullName(name, n) - Exports[S].copyWithAbilityPrefix(abilityName, leftName) + Exports[S].copyWithAbilityPrefix(vm.name, leftName) case (n, (vm, _)) => Exports[S].resolveAbilityField(name, n, vm) }.as(()) diff --git a/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala b/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala index ddf0e501c..b5ce7b56e 100644 --- a/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala +++ b/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala @@ -110,8 +110,8 @@ object Arrows { values: Map[String, ValueModel] ): Map[String, FuncArrow] = { val arrowKeys = arrows.keySet ++ arrows.values.map(_.funcName) - val varsKeys = values.keySet ++ values.values.collect { case ValueModel.Arrow(name, _) => - name + val varsKeys = values.keySet ++ values.values.collect { case ValueModel.Arrow(vm, _) => + vm.name } val keys = arrowKeys.intersect(varsKeys) diff --git a/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala b/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala index 528249cf5..b5d98d703 100644 --- a/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala +++ b/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala @@ -194,8 +194,8 @@ object Exports { value: ValueModel ): State[Map[String, ValueModel], Unit] = State.modify { state => value match { - case Ability(name, at, properties) if properties.isEmpty => - val pairs = getAbilityPairs(name, exportName, at, state) + case Ability(vm, at) if vm.properties.isEmpty => + val pairs = getAbilityPairs(vm.name, exportName, at, state) state ++ pairs.toList.toMap + (exportName -> value) case _ => state + (exportName -> value) } diff --git a/model/src/main/scala/aqua/model/ArgsCall.scala b/model/src/main/scala/aqua/model/ArgsCall.scala index da34193e7..3a6d15236 100644 --- a/model/src/main/scala/aqua/model/ArgsCall.scala +++ b/model/src/main/scala/aqua/model/ArgsCall.scala @@ -44,7 +44,7 @@ case class ArgsCall(args: ProductType, callWith: List[ValueModel]) { * Name of argument -> (variable passed in the call, type) */ lazy val abilityArgs: Map[String, (VarModel, NamedType)] = - zipped.collect { case ((name, _), vr @ Ability(_, t, _)) => + zipped.collect { case ((name, _), Ability(vr, t)) => name -> (vr, t) }.toMap diff --git a/model/src/main/scala/aqua/model/CallModel.scala b/model/src/main/scala/aqua/model/CallModel.scala index d2e9c066b..8912037bc 100644 --- a/model/src/main/scala/aqua/model/CallModel.scala +++ b/model/src/main/scala/aqua/model/CallModel.scala @@ -9,11 +9,11 @@ case class CallModel(args: List[ValueModel], exportTo: List[CallModel.Export]) { override def toString: String = s"[${args.mkString(" ")}] ${exportTo.mkString(" ")}" def arrowArgNames: Set[String] = args.collect { case Arrow(m, _) => - m + m.name }.toSet def abilityArgs: List[(String, GeneralAbilityType)] = - args.collect { case Ability(m, t, _) => m -> t } + args.collect { case Ability(m, t) => m.name -> t } def usesVarNames: Set[String] = args.flatMap(_.usesVarNames).toSet } diff --git a/model/src/main/scala/aqua/model/ValueModel.scala b/model/src/main/scala/aqua/model/ValueModel.scala index fb2cb9946..636ef06dd 100644 --- a/model/src/main/scala/aqua/model/ValueModel.scala +++ b/model/src/main/scala/aqua/model/ValueModel.scala @@ -56,20 +56,20 @@ object ValueModel { object Arrow { - def unapply(vm: ValueModel): Option[(String, ArrowType)] = + def unapply(vm: ValueModel): Option[(VarModel, ArrowType)] = vm match { - case VarModel(name, t: ArrowType, _) => - (name, t).some + case vm @ VarModel(_, t: ArrowType, _) => + (vm, t).some case _ => none } } object Ability { - def unapply(vm: VarModel): Option[(String, GeneralAbilityType, Chain[PropertyModel])] = + def unapply(vm: VarModel): Option[(VarModel, GeneralAbilityType)] = vm match { - case VarModel(name, t: GeneralAbilityType, properties) => - (name, t, properties).some + case vm @ VarModel(_, t: GeneralAbilityType, _) => + (vm, t).some case _ => none } } From c0178e6b9f177df436d4ff067497a9ca35a5ca70 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 22 Nov 2023 10:54:59 +0000 Subject: [PATCH 19/23] Rename only arrows --- .../inline/src/main/scala/aqua/model/inline/ArrowInliner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index 4d2b65335..60b3c6f2a 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -318,7 +318,7 @@ object ArrowInliner extends Logging { ) .map(renames => val valuesRenamed = values.renamed(renames).map { - case (name, vm: VarModel) if renames.contains(vm.name) => + case (name, ValueModel.Arrow(vm, _)) if renames.contains(vm.name) => name -> vm.copy(name = name) case (name, ValueModel.Ability(vm, _)) => name -> vm.copy(name = name) From dade5df820c59de2238f677de67c89b76acb9643 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 22 Nov 2023 13:53:22 +0000 Subject: [PATCH 20/23] Add comments, refactor --- .../aqua/model/inline/ArrowInliner.scala | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index 60b3c6f2a..acbf1c0e7 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -317,9 +317,25 @@ object ArrowInliner extends Logging { .updated(name, rename) ) .map(renames => + // This code is HACKERY!!! val valuesRenamed = values.renamed(renames).map { + /** + * `VarModel` is sometimes used to point to an arrow. + * So if it is renamed, we should rename the `VarModel` too. + * Otherwise renamed value will be resolved + * to previous name when trying to resolve the arrow. + * But this should be done only if the name in model + * is the same as the name of the export, + * because export could point to another arrow. + */ case (name, ValueModel.Arrow(vm, _)) if renames.contains(vm.name) => name -> vm.copy(name = name) + /** + * `VarModel` is used to point to an ability. + * So if it is renamed, we should rename the `VarModel` too. + * Otherwise renamed value will be resolved + * to previous name when trying to resolve the ability. + */ case (name, ValueModel.Ability(vm, _)) => name -> vm.copy(name = name) case v => v @@ -328,11 +344,13 @@ object ArrowInliner extends Logging { ) }.map(_.combineAll) + // Rename arrows according to values arrowsRenamed = Renamed( valuesRenamed.renames.filterKeys(abilitiesArrows.keySet).toMap, abilitiesArrows.renamed(valuesRenamed.renames) ) + // Rename values and arrows unrelated to abilities otherValuesRenamed <- findNewNames(otherValues) otherArrowsValues = Arrows.arrowsByValues( otherArrows, @@ -343,7 +361,7 @@ object ArrowInliner extends Logging { otherArrowsValues.renamed(otherValuesRenamed.renames) ) - otherArrowsRenamed <- findNewNames(otherArrows) + otherArrowsRenamed <- findNewNames(otherArrows -- otherArrowsValues.keySet) values = valuesRenamed |+| otherValuesRenamed arrows = arrowsRenamed |+| otherArrowsValuesRenamed |+| otherArrowsRenamed From 969d75ee28ace527c1df7b7781844ec96125ea3f Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Wed, 22 Nov 2023 14:04:42 +0000 Subject: [PATCH 21/23] Add comments --- .../src/main/scala/aqua/model/inline/ArrowInliner.scala | 7 ++++++- .../src/main/scala/aqua/model/inline/state/Arrows.scala | 1 - .../main/scala/aqua/semantics/rules/ValuesAlgebra.scala | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index acbf1c0e7..0a3395c31 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -283,7 +283,12 @@ object ArrowInliner extends Logging { case (name, ValueModel.Ability(vm, at)) if vm.properties.isEmpty => name -> ( at, - // Gather all values related to `name` + /** + * Gather all values related to `name` + * NOTE: It is important that `capturedValues` are + * populated by all values related to ability `name` + * on creation of `FuncArrow`. + */ Exports.gatherFrom( name :: Nil, fn.capturedValues diff --git a/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala b/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala index b5ce7b56e..e6f53efc3 100644 --- a/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala +++ b/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala @@ -34,7 +34,6 @@ trait Arrows[S] extends Scoped[S] { )(using Exports[S]): State[S, Unit] = for { arrs <- arrows - exps <- Exports[S].exports capturedVars <- Exports[S].gather(arrow.capturedVars.toSeq) capturedArrows = arrs.filterKeys(arrow.capturedVars).toMap ++ Arrows.arrowsByValues(arrs, capturedVars) diff --git a/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala b/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala index edc01553a..3753c3b1f 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala @@ -113,6 +113,12 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](using ) } yield result + /** + * This is a HACKERY!!! + * Imports have very different mechanism of resolving, + * so here we try to differentiate them and adjust property + * token accordingly. + */ prop.leadingName.fold(default)(name => A.isDefinedAbility(name) .flatMap(isDefined => From 5b54dd1c47c59b47883df9e307611a85f50ea2ae Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Thu, 23 Nov 2023 09:44:47 +0000 Subject: [PATCH 22/23] Rename method --- .../src/main/scala/aqua/model/inline/ArrowInliner.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala index 0a3395c31..70cbf9c3f 100644 --- a/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/ArrowInliner.scala @@ -272,7 +272,7 @@ object ArrowInliner extends Logging { * @param arrows Arrows state before calling/inlining * @return Renamed values and arrows */ - def renamedCaptured[S: Mangler]( + def renameCaptured[S: Mangler]( fn: FuncArrow, exports: Map[String, ValueModel], arrows: Map[String, FuncArrow] @@ -406,7 +406,7 @@ object ArrowInliner extends Logging { arrowRenames = args.arrowArgsRenames abRenames = args.abilityArgsRenames - captured <- renamedCaptured(fn, exports, arrows) + captured <- renameCaptured(fn, exports, arrows) (capturedValues, capturedArrows) = captured /** From fefcfd84f70ce7ddc661e738c66544f3002effc0 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Thu, 23 Nov 2023 09:56:09 +0000 Subject: [PATCH 23/23] Add integration test --- .../aqua/examples/abilities.aqua | 26 ++++++++++++++++++- .../src/__test__/examples.spec.ts | 6 +++++ integration-tests/src/examples/abilityCall.ts | 25 +++++++++++++++--- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/integration-tests/aqua/examples/abilities.aqua b/integration-tests/aqua/examples/abilities.aqua index 78a18ff6f..9a02a2e79 100644 --- a/integration-tests/aqua/examples/abilities.aqua +++ b/integration-tests/aqua/examples/abilities.aqua @@ -2,7 +2,7 @@ aqua Main use DECLARE_CONST, decl_bar from "imports_exports/declare.aqua" as Declare -export handleAb, SomeService, bug214, checkAbCalls, bugLNG258_1, bugLNG258_2, bugLNG258_3, multipleAbilityWithClosure +export handleAb, SomeService, bug214, checkAbCalls, bugLNG258_1, bugLNG258_2, bugLNG258_3, multipleAbilityWithClosure, MySrv, returnSrvAsAbility service SomeService("wed"): getStr(s: string) -> string @@ -128,4 +128,28 @@ func multipleAbilityWithClosure() -> i8, i8: ab2 <- createAb(2) <- ab.arrow(), ab2.arrow() +ability MyAb: + call() -> string + +service MySrv("default-id"): + call() -> string + +func mySrvDefault() -> MyAb: + <- MySrv + +func mySrvResolved() -> MyAb: + MySrv "resolved-id" + <- MySrv + +func useMyAb{MyAb}() -> string: + <- MyAb.call() + +func returnSrvAsAbility() -> []string: + result: *string + MySrvDefault <- mySrvDefault() + MySrvResolved <- mySrvResolved() + result <- useMyAb{MySrvDefault}() + result <- useMyAb{MySrvResolved}() + <- result + diff --git a/integration-tests/src/__test__/examples.spec.ts b/integration-tests/src/__test__/examples.spec.ts index 5050bc571..23b0204df 100644 --- a/integration-tests/src/__test__/examples.spec.ts +++ b/integration-tests/src/__test__/examples.spec.ts @@ -38,6 +38,7 @@ import { bugLNG258Call2, bugLNG258Call3, multipleAbilityWithClosureCall, + returnSrvAsAbilityCall, } from "../examples/abilityCall.js"; import { nilLengthCall, @@ -579,6 +580,11 @@ describe("Testing examples", () => { expect(result1).toStrictEqual([1, 2]); }); + it("abilities.aqua return service as ability", async () => { + let result = await returnSrvAsAbilityCall(); + expect(result).toStrictEqual(["default-id", "resolved-id"]); + }); + it("functors.aqua LNG-119 bug", async () => { let result = await bugLng119Call(); expect(result).toEqual([1]); diff --git a/integration-tests/src/examples/abilityCall.ts b/integration-tests/src/examples/abilityCall.ts index 32b81e960..843cbb770 100644 --- a/integration-tests/src/examples/abilityCall.ts +++ b/integration-tests/src/examples/abilityCall.ts @@ -6,7 +6,9 @@ import { bugLNG258_1, bugLNG258_2, bugLNG258_3, - multipleAbilityWithClosure + multipleAbilityWithClosure, + registerMySrv, + returnSrvAsAbility, } from "../compiled/examples/abilities"; export async function abilityCall(): Promise<[string, string, string, number]> { @@ -39,6 +41,23 @@ export async function bugLNG258Call3(): Promise<[number, number]> { return await bugLNG258_3(); } -export async function multipleAbilityWithClosureCall(): Promise<[number, number]> { - return await multipleAbilityWithClosure() +export async function multipleAbilityWithClosureCall(): Promise< + [number, number] +> { + return await multipleAbilityWithClosure(); +} + +export async function returnSrvAsAbilityCall(): Promise { + const srv = (id: string) => { + return { + call: () => { + return id; + }, + }; + }; + + registerMySrv("default-id", srv("default-id")); + registerMySrv("resolved-id", srv("resolved-id")); + + return await returnSrvAsAbility(); }