scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
232 stars 21 forks source link

Scala 2.13.6 Compiler Throws AssertionError #12467

Closed jrduncans closed 3 years ago

jrduncans commented 3 years ago

reproduction steps

import akka.stream.scaladsl.Source
import akka.stream.stage._
import akka.stream.{Attributes, Outlet, SourceShape}
import cats.syntax.all._

import scala.concurrent.duration.{Duration, _}
import scala.concurrent.{Await, ExecutionContext, Future}

trait PagedRequest[A, PageIdentifier, Response] {
  def getPage(a: A)(id: PageIdentifier): Future[Response]
  def start(a: A)(implicit pagedResponse: PagedResponse[Response, PageIdentifier]): pagedResponse.NextPage
}

// scalastyle:off structural.type
object PagedResponse {
  type Aux[A0, PageIdentifier0, Item0] = PagedResponse[A0, PageIdentifier0] { type Item = Item0 }
}

trait PagedResponse[A, PageIdentifier] {
  type Item
  sealed trait NextPage
  case class Next(page: PageIdentifier) extends NextPage
  case class NoMorePages() extends NextPage

  def nextPage(a: A): NextPage
  def elements(a: A): List[Item]
}

object PagingSource {
  def apply[Request : ({ type PR[A] = PagedRequest[A, PageIdentifier, Response] })#PR, Response, PageIdentifier, Item](
    paged: Request,
    timeout: Duration = 5.seconds
  )(
    implicit ec: ExecutionContext,
    responseOps: PagedResponse.Aux[Response, PageIdentifier, Item]
  ): Source[Item, akka.NotUsed] =
    Source.fromGraph(new PagingSource(paged, timeout))
}

class PagingSource[
  Request : ({ type PR[A] = PagedRequest[A, PageIdentifier, Response] })#PR,
  Response,
  PageIdentifier,
  Item
](paged: Request, timeout: Duration)(
  implicit ec: ExecutionContext,
  responseOps: PagedResponse.Aux[Response, PageIdentifier, Item]
) extends GraphStage[SourceShape[Item]] {

  val out: Outlet[Item] = Outlet("Output")
  override val shape: SourceShape[Item] = SourceShape(out)

  val pagedOps = implicitly[PagedRequest[Request, PageIdentifier, Response]]

  override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) {
    var nextPage: PagedResponse.Aux[Response, PageIdentifier, Item]#NextPage = pagedOps.start(paged)
    val queue = scala.collection.mutable.Queue.empty[Item]

    setHandler(
      out,
      new OutHandler {
        override def onPull(): Unit =
          if (queue.nonEmpty) {
            push(out, queue.dequeue())
          } else {
            nextPage match {
              case _: PagedResponse.Aux[Response, PageIdentifier, Item]#NoMorePages => completeStage()
              case next: PagedResponse.Aux[Response, PageIdentifier, Item]#Next =>
                (for {
                  returnedPage <- Either.catchNonFatal {
                    Await.result(pagedOps.getPage(paged)(next.page), timeout)
                  }
                } yield {
                  queue ++= responseOps.elements(returnedPage)
                  nextPage = responseOps.nextPage(returnedPage)
                }).bimap(
                  exception => fail(out, exception),
                  _ => if (queue.nonEmpty) push(out, queue.dequeue()) else completeStage()
                )
            }
          }
      }
    )
  }
}

Produces error:

[error] ## Exception when compiling 1 sources to /Users/user/workspace/playground/clone-error-test/target/scala-2.13/classes
[error] java.lang.AssertionError: assertion failed:
[error]   mkAttributedQualifier(example.PagedResponse[Response,PageIdentifier]{type Item = Item}, <none>) parents = List(example.PagedResponse[Response,PageIdentifier])
[error]      while compiling: /Users/user/workspace/playground/clone-error-test/src/main/scala/example/Hello.scala
[error]         during phase: patmat
[error]      library version: version 2.13.6
[error]     compiler version: version 2.13.6
[error]
[error]   last tree to typer: Ident(x)
[error]        tree position: line 25 of /Users/user/workspace/playground/clone-error-test/src/main/scala/example/Hello.scala
[error]             tree tpe: Boolean
[error]               symbol: value x
[error]    symbol definition: x: Boolean (a TermSymbol)
[error]       symbol package: <none>
[error]        symbol owners: value x
[error]            call site: method onPull in package example
[error]
[error] == Source file context for tree position ==
[error]
[error]     22   type Item
[error]     23   sealed trait NextPage
[error]     24   case class Next(page: PageIdentifier) extends NextPage
[error]     25   case class NoMorePages() extends NextPage
[error]     26
[error]     27   def nextPage(a: A): NextPage
[error]     28   def elements(a: A): List[Item]
[error] scala.reflect.internal.SymbolTable.throwAssertionError(SymbolTable.scala:171)
[error] scala.reflect.internal.TreeGen.mkAttributedQualifier(TreeGen.scala:117)
[error] scala.reflect.internal.TreeGen.mkAttributedQualifier(TreeGen.scala:74)
[error] scala.reflect.internal.TreeGen.mkAttributedQualifierIfPossible(TreeGen.scala:152)
[error] scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker$treeCondStrategy$.withOuterTest(MatchTreeMaking.scala:431)
[error] scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker$treeCondStrategy$.withOuterTest(MatchTreeMaking.scala:339)
[error] scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker.addOuterTest$1(MatchTreeMaking.scala:520)
[error] scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker.mkDefault$1(MatchTreeMaking.scala:530)
[error] scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker.renderCondition(MatchTreeMaking.scala:554)

problem

This code compiled under numerous previous version of Scala, including Scala 2.13.5.

SethTisue commented 3 years ago

That's a lot of code — have you attempted to minimize it?

A proper minimization would 1) have less code in it, probably a lot less, and 2) if at all possible, not involve an external dependency.

(Regardless, thanks for the report, it is already valuable.)

jrduncans commented 3 years ago

Not having a clue what the error means, I didn't have any idea where to start minimizing. I will update if I find the time to trial and error into anything simpler.

griggt commented 3 years ago

Minimized to:

object PagedResponse {
  type Aux[Item0] = PagedResponse { type Item = Item0 }
}

trait PagedResponse {
  type Item
  sealed trait NextPage
  case class NoMorePages() extends NextPage
}

object Test {
  def foo[A](next: PagedResponse.Aux[A]#NextPage): Unit = next match {
    case _: PagedResponse.Aux[A]#NoMorePages => ???
  }
}
$ scalac -2.12.15 t12467.scala     // ok
$ scalac -2.13.5  t12467.scala     // ok
$ scalac -3.0.2   t12467.scala     // ok
$ scalac -2.13.6  t12467.scala
error: java.lang.AssertionError: assertion failed:
  mkAttributedQualifier(PagedResponse{type Item = A}, <none>) parents = List(PagedResponse)
     while compiling: t12467.scala
        during phase: patmat
     library version: version 2.13.6
    compiler version: version 2.13.6
  reconstructed args:

  last tree to typer: Ident(x)
       tree position: line 8 of t12467.scala
            tree tpe: Boolean
              symbol: value x
   symbol definition: x: Boolean (a TermSymbol)
      symbol package: <none>
       symbol owners: value x
           call site: method foo in object Test in package <empty>

== Source file context for tree position ==

     5 trait PagedResponse {
     6   type Item
     7   sealed trait NextPage
     8   case class NoMorePages() extends NextPage
     9 }
    10
    11 object Test {
    at scala.reflect.internal.SymbolTable.throwAssertionError(SymbolTable.scala:171)
    at scala.reflect.internal.TreeGen.mkAttributedQualifier(TreeGen.scala:117)
    at scala.reflect.internal.TreeGen.mkAttributedQualifier(TreeGen.scala:74)
    at scala.reflect.internal.TreeGen.mkAttributedQualifierIfPossible(TreeGen.scala:152)
    at scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker$treeCondStrategy$.withOuterTest(MatchTreeMaking.scala:431)
    at scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker$treeCondStrategy$.withOuterTest(MatchTreeMaking.scala:339)
    at scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker.addOuterTest$1(MatchTreeMaking.scala:520)
    at scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker.mkDefault$1(MatchTreeMaking.scala:530)
    at scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker.renderCondition(MatchTreeMaking.scala:554)
    at scala.tools.nsc.transform.patmat.MatchTreeMaking$TreeMakers$TypeTestTreeMaker.<init>(MatchTreeMaking.scala:558)
SethTisue commented 3 years ago

Thanks, Tom!

It isn't obvious to me what PR might be responsible. @lrytz @dwijnand do you have a guess? if not, I guess the next step would be bisecting it

dwijnand commented 3 years ago

Thanks for the minimisation, Tom!

https://github.com/scala/scala/pull/9504. That's @retronym's code, that looks to improving how we validate that the prefixes align. But here the prefixes are not stable values, they're just refined types (behind the aux type alias) so there's no outer test to emit. I'll send a patch.