mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 14:40:17 +00:00
feat(compiler): Fail on internal error [fixes LNG-229] (#905)
* Refactor utils * Add errors project * logger.error -> internalError * Add comment * Suppress stack trace --------- Co-authored-by: Dima <dmitry.shakhtarin@fluence.ai>
This commit is contained in:
parent
fb75bc267e
commit
8741c910be
16
build.sbt
16
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 sourcecodeV = "0.3.0"
|
||||
val fs2V = "3.9.2"
|
||||
val catsEffectV = "3.6-1f95fd7"
|
||||
val declineV = "2.3.0"
|
||||
@ -163,7 +164,7 @@ lazy val model = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.settings(commons)
|
||||
.dependsOn(types, tree, raw, helpers)
|
||||
.dependsOn(types, tree, raw, helpers, errors)
|
||||
|
||||
lazy val res = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
@ -196,7 +197,7 @@ lazy val semantics = crossProject(JVMPlatform, JSPlatform)
|
||||
"dev.optics" %%% "monocle-macro" % monocleV
|
||||
)
|
||||
)
|
||||
.dependsOn(raw, parser)
|
||||
.dependsOn(raw, parser, errors)
|
||||
|
||||
lazy val compiler = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
@ -266,6 +267,17 @@ lazy val helpers = crossProject(JVMPlatform, JSPlatform)
|
||||
)
|
||||
)
|
||||
|
||||
lazy val errors = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.in(file("utils/errors"))
|
||||
.settings(commons)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"com.lihaoyi" %%% "sourcecode" % sourcecodeV
|
||||
)
|
||||
)
|
||||
|
||||
lazy val `backend-air` = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package aqua.model.inline
|
||||
|
||||
import aqua.errors.Errors.internalError
|
||||
import aqua.model
|
||||
import aqua.model.*
|
||||
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
||||
@ -172,8 +173,7 @@ object ArrowInliner extends Logging {
|
||||
case arrow @ (_, vm: VarModel) =>
|
||||
arrow.some
|
||||
case (_, m) =>
|
||||
logger.error(s"Unexpected: '$m' cannot be an arrow")
|
||||
None
|
||||
internalError(s"($m) cannot be an arrow")
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,8 +205,7 @@ object ArrowInliner extends Logging {
|
||||
case (_, VarModel(name, _, _)) =>
|
||||
arrows.get(name).map(name -> _)
|
||||
case (_, m) =>
|
||||
logger.error(s"Unexpected: '$m' cannot be an arrow")
|
||||
None
|
||||
internalError(s"($m) cannot be an arrow")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package aqua.model.inline
|
||||
|
||||
import aqua.errors.Errors.internalError
|
||||
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
||||
import aqua.model.*
|
||||
import aqua.model.inline.RawValueInliner.collectionToModel
|
||||
@ -277,12 +278,10 @@ object TagInliner extends Logging {
|
||||
n <- Mangler[S].findAndForbidName(item)
|
||||
elementType = iterable.`type` match {
|
||||
case b: BoxType => b.element
|
||||
// TODO: it is unexpected, should we handle this?
|
||||
case _ =>
|
||||
logger.error(
|
||||
s"Unexpected behaviour: non-box type variable '$iterable' in 'for' expression."
|
||||
internalError(
|
||||
s"non-box type variable '$iterable' in 'for' expression."
|
||||
)
|
||||
iterable.`type`
|
||||
}
|
||||
_ <- Exports[S].resolved(item, VarModel(n, elementType))
|
||||
m = mode.map {
|
||||
@ -308,11 +307,10 @@ object TagInliner extends Logging {
|
||||
prefix = p
|
||||
)
|
||||
case (_, (vm, prefix)) =>
|
||||
logger.error(
|
||||
s"Unexpected: stream (${exportTo}) resolved " +
|
||||
internalError(
|
||||
s"stream (${exportTo}) resolved " +
|
||||
s"to ($vm) with prefix ($prefix)"
|
||||
)
|
||||
TagInlined.Empty()
|
||||
}
|
||||
|
||||
case CanonicalizeTag(operand, exportTo) =>
|
||||
|
@ -1,5 +1,6 @@
|
||||
package aqua.model.inline.raw
|
||||
|
||||
import aqua.errors.Errors.internalError
|
||||
import aqua.model.*
|
||||
import aqua.model.ValueModel.Ability
|
||||
import aqua.model.inline.Inline
|
||||
@ -8,6 +9,7 @@ import aqua.model.inline.RawValueInliner.unfold
|
||||
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
||||
import aqua.raw.value.*
|
||||
import aqua.types.*
|
||||
|
||||
import cats.Eval
|
||||
import cats.data.{Chain, IndexedStateT, State}
|
||||
import cats.instances.list.*
|
||||
@ -69,8 +71,9 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
|
||||
at
|
||||
}
|
||||
.getOrElse {
|
||||
logger.error(s"Inlining, cannot find arrow $arrowName in $varModel")
|
||||
ArrowType(NilType, NilType)
|
||||
internalError(
|
||||
s"Inlining, cannot find arrow ($arrowName) in ($varModel)"
|
||||
)
|
||||
}
|
||||
for {
|
||||
callArrow <- CallArrowRawInliner(
|
||||
@ -101,11 +104,10 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
|
||||
flatLiteralWithProperties(lm, Inline.empty, Chain.empty)
|
||||
case _ =>
|
||||
Exports[S].getKeys.flatMap { keys =>
|
||||
logger.error(
|
||||
s"Inlining, cannot find field ${AbilityType
|
||||
.fullName(varModel.name, fieldName)} in ability $varModel. Available: $keys"
|
||||
val field = AbilityType.fullName(varModel.name, fieldName)
|
||||
internalError(
|
||||
s"Inlining, cannot find field ($field) in ability $varModel. Available: $keys"
|
||||
)
|
||||
flatLiteralWithProperties(LiteralModel.quote(""), Inline.empty, Chain.empty)
|
||||
}
|
||||
|
||||
}
|
||||
@ -265,12 +267,14 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
|
||||
}
|
||||
case (v, i) =>
|
||||
// what if pass nil as stream argument?
|
||||
logger.error("Unreachable. Unfolded stream cannot be a literal")
|
||||
State.pure(v -> i)
|
||||
internalError(
|
||||
s"Unfolded stream ($gateRaw) cannot be a literal"
|
||||
)
|
||||
}
|
||||
case l =>
|
||||
logger.error("Unreachable. Unfolded stream cannot be a literal")
|
||||
State.pure(l)
|
||||
internalError(
|
||||
s"Unfolded stream ($vr) cannot be a literal"
|
||||
)
|
||||
}
|
||||
|
||||
case (_, _) =>
|
||||
|
@ -1,5 +1,6 @@
|
||||
package aqua.model.inline.raw
|
||||
|
||||
import aqua.errors.Errors.internalError
|
||||
import aqua.model.inline.Inline.parDesugarPrefixOpt
|
||||
import aqua.model.{CallServiceModel, FuncArrow, MetaModel, SeqModel, ValueModel, VarModel}
|
||||
import aqua.model.inline.{ArrowInliner, Inline, TagInliner}
|
||||
@ -9,6 +10,7 @@ import aqua.raw.ops.Call
|
||||
import aqua.types.ArrowType
|
||||
import aqua.raw.value.CallArrowRaw
|
||||
|
||||
import cats.syntax.traverse.*
|
||||
import cats.data.{Chain, State}
|
||||
import scribe.Logging
|
||||
|
||||
@ -88,14 +90,15 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
|
||||
// if there is no arrow, check if it is stored in Exports as variable and try to resolve it
|
||||
lastArrow.flatMap(arrows.get)
|
||||
)
|
||||
result <- arrow.fold {
|
||||
logger.error(
|
||||
s"Inlining, cannot find arrow $funcName, available: ${arrows.keys
|
||||
.mkString(", ")} and vars: ${exports.keys.mkString(", ")}"
|
||||
)
|
||||
|
||||
State.pure(Nil -> Inline.empty)
|
||||
}(resolveFuncArrow(_, call))
|
||||
result <- arrow
|
||||
.traverse(resolveFuncArrow(_, call))
|
||||
.map(_.getOrElse {
|
||||
val arrs = arrows.keys.mkString(", ")
|
||||
val vars = exports.keys.mkString(", ")
|
||||
internalError(
|
||||
s"Inlining, cannot find arrow ($funcName), available: ($arrs) and vars: ($vars)"
|
||||
)
|
||||
})
|
||||
} yield result
|
||||
|
||||
override def apply[S: Mangler: Exports: Arrows](
|
||||
|
@ -1,5 +1,6 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.errors.Errors.internalError
|
||||
import aqua.raw.value.*
|
||||
import aqua.types.*
|
||||
|
||||
@ -213,11 +214,12 @@ case class VarModel(name: String, baseType: Type, properties: Chain[PropertyMode
|
||||
case nvm: VarModel =>
|
||||
deriveFrom(vv.deriveFrom(nvm))
|
||||
case valueModel =>
|
||||
if (properties.nonEmpty)
|
||||
logger.error(
|
||||
s"Var $name derived from literal $valueModel, but property is lost: $properties"
|
||||
if (properties.isEmpty) valueModel
|
||||
else
|
||||
internalError(
|
||||
s"Var ($name) derived from literal ($valueModel), " +
|
||||
s"but properties ($properties) are lost"
|
||||
)
|
||||
valueModel
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
@ -225,11 +227,13 @@ case class VarModel(name: String, baseType: Type, properties: Chain[PropertyMode
|
||||
}
|
||||
|
||||
case Some(vv) =>
|
||||
if (properties.nonEmpty)
|
||||
logger.error(
|
||||
s"Var $name derived from literal $vv, but property is lost: $properties"
|
||||
if (properties.isEmpty) vv
|
||||
else
|
||||
internalError(
|
||||
s"Var ($name) derived from literal ($vv), " +
|
||||
s"but properties ($properties) are lost: "
|
||||
)
|
||||
vv
|
||||
|
||||
case None =>
|
||||
this // Should not happen
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package aqua.model.transform.topology
|
||||
|
||||
import aqua.errors.Errors.internalError
|
||||
import aqua.model.transform.topology.TopologyPath
|
||||
import aqua.model.transform.cursor.ChainZipper
|
||||
import aqua.model.transform.topology.strategy.*
|
||||
@ -81,12 +82,11 @@ case class Topology private (
|
||||
capturedTopologies.flatMap(
|
||||
_.get(name)
|
||||
.traverse(_.pathOn)
|
||||
.flatTap(p =>
|
||||
Eval.later(
|
||||
if (p.isEmpty) logger.error(s"Captured topology `$name` not found")
|
||||
.map(_.getOrElse {
|
||||
internalError(
|
||||
s"Captured topology ($name) not found"
|
||||
)
|
||||
)
|
||||
.map(_.orEmpty)
|
||||
})
|
||||
)
|
||||
case _ => parent.traverse(_.pathOn).map(_.orEmpty)
|
||||
}
|
||||
@ -337,8 +337,9 @@ object Topology extends Logging {
|
||||
|
||||
resolvedCofree.map(NonEmptyChain.fromChain(_).map(_.uncons)).map {
|
||||
case None =>
|
||||
logger.error("Topology emitted nothing")
|
||||
SeqRes.leaf
|
||||
internalError(
|
||||
s"Topology emitted nothing on (${op.show})"
|
||||
)
|
||||
case Some((el, `nil`)) => el
|
||||
case Some((el, tail)) =>
|
||||
logger.warn("Topology emitted many nodes, that's unusual")
|
||||
|
@ -1,5 +1,6 @@
|
||||
package aqua.semantics
|
||||
|
||||
import aqua.errors.Errors.internalError
|
||||
import aqua.parser.head.{HeadExpr, HeaderExpr, ImportExpr, ImportFromExpr}
|
||||
import aqua.parser.lexer.{LiteralToken, Token}
|
||||
import aqua.parser.{Ast, Expr}
|
||||
@ -361,12 +362,9 @@ object RawSemantics extends Logging {
|
||||
) { case (ctx, p) =>
|
||||
ctx.copy(parts = ctx.parts :+ (ctx -> p))
|
||||
}
|
||||
|
||||
case (state: CompilerState[S], m) =>
|
||||
logger.error("Got unexpected " + m)
|
||||
state.copy(errors = state.errors :+ WrongAST(ast)) -> RawContext.blank.copy(
|
||||
init = Some(init.copy(module = init.module.map(_ + "|init")))
|
||||
.filter(_ != RawContext.blank)
|
||||
case (_, m) =>
|
||||
internalError(
|
||||
s"Unexpected Raw ($m)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
package aqua.constants
|
||||
|
||||
import scala.util.Left
|
||||
import aqua.parser.expr.ConstantExpr
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.raw.value.LiteralRaw
|
||||
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNel}
|
||||
|
||||
object Constants {
|
||||
|
||||
def parse(strs: List[String]): ValidatedNel[String, List[ConstantRaw]] = {
|
||||
val parsed = strs.map(s => ConstantExpr.onlyLiteral.parseAll(s))
|
||||
|
||||
|
22
utils/errors/src/main/scala/aqua/errors/Errors.scala
Normal file
22
utils/errors/src/main/scala/aqua/errors/Errors.scala
Normal file
@ -0,0 +1,22 @@
|
||||
package aqua.errors
|
||||
|
||||
import sourcecode.{Enclosing, FileName, Line}
|
||||
|
||||
import scala.util.control.NoStackTrace
|
||||
|
||||
object Errors {
|
||||
|
||||
/**
|
||||
* Internal error that should never happen.
|
||||
* Use in case of broken invariants.
|
||||
*/
|
||||
def internalError(
|
||||
msg: String
|
||||
)(using file: FileName, line: Line, enclosing: Enclosing): Nothing = {
|
||||
throw new RuntimeException(
|
||||
s"Internal aqua compiler error:\n$msg" +
|
||||
s"\nat ${file.value}:${line.value} in ${enclosing.value}.\n" +
|
||||
s"Please report this issue to https://github.com/fluencelabs/aqua."
|
||||
) with NoStackTrace // Stack trace is rather useless here
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
package aqua.logging
|
||||
|
||||
import cats.syntax.option.*
|
||||
import cats.syntax.either.*
|
||||
import cats.syntax.foldable.*
|
||||
import cats.data.Validated.{invalidNel, validNel}
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNel}
|
||||
import scribe.Level
|
||||
@ -12,20 +15,15 @@ case class LogLevels(
|
||||
|
||||
object LogLevels {
|
||||
|
||||
val logHelpMessage = "Format: '<level> OR <segment>=<level>[,]', where <level> is one of these strings: 'all', 'trace', 'debug', 'info', 'warn', 'error', 'off'. <segment> can be 'compiler', 'fluencejs' or 'aquavm'"
|
||||
val logHelpMessage =
|
||||
"Format: '<level> OR <segment>=<level>[,]', where <level> is one of these strings: 'all', 'trace', 'debug', 'info', 'warn', 'error', 'off'. <segment> can be 'compiler', 'fluencejs' or 'aquavm'"
|
||||
|
||||
def apply(level: Level): LogLevels = LogLevels(level, level, level)
|
||||
|
||||
def levelFromString(s: String): ValidatedNel[String, Level] = {
|
||||
def levelFromString(s: String): ValidatedNel[String, Level] =
|
||||
LogLevel.stringToLogLevel
|
||||
.get(s.toLowerCase)
|
||||
.map(validNel)
|
||||
.getOrElse(
|
||||
invalidNel(
|
||||
s"Invalid log-level '$s'. $logHelpMessage"
|
||||
)
|
||||
)
|
||||
}
|
||||
.get(s.toLowerCase.trim())
|
||||
.toValidNel(s"Invalid log-level '$s'. $logHelpMessage")
|
||||
|
||||
lazy val error =
|
||||
s"Invalid log-level format. $logHelpMessage"
|
||||
@ -36,7 +34,7 @@ object LogLevels {
|
||||
logLevels: LogLevels
|
||||
): Validated[NonEmptyList[String], LogLevels] = {
|
||||
levelFromString(level).andThen { level =>
|
||||
name match {
|
||||
name.trim().toLowerCase() match {
|
||||
case "compiler" =>
|
||||
validNel(logLevels.copy(compiler = level))
|
||||
case "fluencejs" =>
|
||||
@ -44,7 +42,7 @@ object LogLevels {
|
||||
case "aquavm" =>
|
||||
validNel(logLevels.copy(aquavm = level))
|
||||
case s =>
|
||||
invalidNel[String, LogLevels](
|
||||
invalidNel(
|
||||
s"Unknown component '$s' in log-level. Please use one of these: 'aquavm', 'compiler' and 'fluencejs'"
|
||||
)
|
||||
}
|
||||
@ -53,26 +51,15 @@ object LogLevels {
|
||||
|
||||
// Format: '<log-level>' or 'compiler=<log-level>,fluencejs=<log-level>,aquavm=<log-level>',
|
||||
// where <log-level> is one of these strings: 'all', 'trace', 'debug', 'info', 'warn', 'error', 'off'
|
||||
def fromString(s: String): ValidatedNel[String, LogLevels] = {
|
||||
s.split(",").toList match {
|
||||
case l :: Nil =>
|
||||
l.split("=").toList match {
|
||||
case n :: ll :: Nil => fromStrings(n, ll, LogLevels())
|
||||
case ll :: Nil => levelFromString(ll).map(apply)
|
||||
case _ => invalidNel(error)
|
||||
def fromString(s: String): ValidatedNel[String, LogLevels] =
|
||||
s.split(",")
|
||||
.toList
|
||||
.foldLeftM(LogLevels()) { case (levels, level) =>
|
||||
level.split("=").toList match {
|
||||
case n :: l :: Nil => fromStrings(n, l, levels).toEither
|
||||
case l :: Nil => levelFromString(l).map(apply).toEither
|
||||
case _ => invalidNel(error).toEither
|
||||
}
|
||||
|
||||
case arr =>
|
||||
arr.foldLeft(validNel[String, LogLevels](LogLevels())) { case (logLevelV, ss) =>
|
||||
logLevelV.andThen { logLevels =>
|
||||
ss.split("=").toList match {
|
||||
case n :: ll :: Nil => fromStrings(n, ll, logLevels)
|
||||
case n :: Nil => levelFromString(n).map(apply)
|
||||
case _ => invalidNel[String, LogLevels](error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.toValidated
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user