BadSetAxis exception on the same element being added with and without an activation (InconsistentSet error in compile-time check)
neko-kai opened this issue · comments
From Telegram [RU], reproduction:
import distage._
import cats.effect.IO
import izumi.distage.framework.CoreCheckableAppSimple
trait PonvScenario[F[_]]
trait LogMessages[F[_]] extends PonvScenario[F]
object App extends CoreCheckableAppSimple[IO] with App {
def a[F[_]: TagK] = new ModuleDef {
make[LogMessages[F]]
many[PonvScenario[F]].ref[LogMessages[F]]
}
def b[F[_]: TagK] = new ModuleDef {
make[LogMessages[F]].tagged(Mode.Test)
many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Test)
}
def program[F[_]: TagK] = a[F] ++ b[F]
def module: ModuleBase = program[IO]
def roots: Roots = Roots.Everything
// BadSetAxis
Injector[IO]().produce(program[IO], Roots.Everything, Activation(Mode -> Mode.Test)).use {
_ => IO.unit
}.unsafeRunSync()
}
object Test {
import izumi.distage.framework.PlanCheck
// InconsistentSetMembers
object WiringCheck extends PlanCheck.Main(App)
}
Scastie: https://scastie.scala-lang.org/GA5bSD3xRmORHidCmeTyNQ
Another instance:
import distage._
import cats.effect.IO
trait PonvScenario[F[_]]
trait LogMessages[F[_]] extends PonvScenario[F]
trait F[A]
object App extends App {
val a = new ModuleDef {
make[LogMessages[F]]
many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Prod)
}
val b = new ModuleDef {
many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Test)
}
val program = a ++ b
Injector[IO]().produceRun(program, Activation(Mode -> Mode.Test)) {
x: Set[PonvScenario[IO]] => IO.unit
}.unsafeRunSync()
}
https://scastie.scala-lang.org/G1coAud3SRKTKxtaQPA7RA
Note that here both elements are tagged instead of one, so they should probably be mutually exclusive...
First example is not a bug. make[LogMessages[F]]
should be erased in favour of many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Test)
thus .ref[LogMessages[F]]
gets invalidated.
It looks weird but it's a sound behavior. We may try to improve UX for such cases by either improving diagnostics or changing our approach to set element filtering. Latter can't be done without a very detailed proposal considering a lot of different cornercases.
Second example indeed demonstrates unsoundness though I believe that the real problem is that both definitions
many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Prod)
many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Test)
are merged into
many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Prod, Mode.Test)
We may either improve merging logic (e.g. never merge conflicting axis) or remove merging completely. Seems like we don't need it anymore with our new solver.