Skip to content

infinite recursion in Scala.js using 3.0.0-M3 due to compiler error in dotty. #11064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
scalavision opened this issue Jan 11, 2021 · 1 comment · Fixed by #11069
Closed

infinite recursion in Scala.js using 3.0.0-M3 due to compiler error in dotty. #11064

scalavision opened this issue Jan 11, 2021 · 1 comment · Fixed by #11069
Milestone

Comments

@scalavision
Copy link

Minimized code

In a Scala.js project add the following code snippet :

import scala.scalajs.js
import scala.scalajs.js.typedarray._
import scala.scalajs.js.typedarray.TypedArrayBufferOps._
import java.nio.ByteBuffer

import java.nio._

object ArrayFailure{
  def test() = {
      val tempBuffer = ByteBuffer.allocateDirect(1000)
      tempBuffer.typedArray()
  }
}

Output

[error] -- Error: /../scala-js-dom/src/main/scala/org/scalajs/dom/ext/Extensions.scala:219:6 
[error] 219 |      tempBuffer.typedArray()
[error]     |      ^
[error]     |Recursion limit exceeded.
[error]     |Maybe there is an illegal cyclic reference?
[error]     |If that's not the case, you could also try to increase the stacksize using the -Xss JVM option.
[error]     |A recurring operation is (inner to outer):

[error]     |  subtype scala.scalajs.js.typedarray.TypedArray[?, 
[error]     |  LazyRef(
[error]     |    scala.scalajs.js.typedarray.TypedArrayBufferOps[?
[error]     |       <: scala.scalajs.js.typedarray.TypedArray[?, ?]
[error]     |    ]#TypedArrayType
[error]     |  )
[error]     |] <:< scala.scalajs.js.typedarray.TypedArray[?, ?]
[error]     |  subtype Nothing <:< LazyRef(
[error]     |  scala.scalajs.js.typedarray.TypedArrayBufferOps[?
[error]     |     <: scala.scalajs.js.typedarray.TypedArray[?, ?]
[error]     |  ]#TypedArrayType
[error]     |)
[error]     |  subtype scala.scalajs.js.typedarray.TypedArray[?, 
[error]     |  LazyRef(
[error]     |    scala.scalajs.js.typedarray.TypedArrayBufferOps[?
[error]     |       <: scala.scalajs.js.typedarray.TypedArray[?, ?]
[error]     |    ]#TypedArrayType
[error]     |  )
[error]     |]

Expectation

This should compile. Looking at several Scala.js libraries, building structures like done in the TypedArray seems to be used a lot.

Other comments

I encountered this when trying to update scalajs-dom library to Scala 3.0.0-M3, the working branch is here:

I tried to make a minimized example based on the code in Scala.js, not sure how accurate (or if it is even valid code), but this makes dotty compiler crash, while it compiles in Scala 2.

trait Buffer
trait TypedArray[T, Repr]

case class MyType() extends TypedArray[Int, String]

object TypedArrayBridge {
  def Buffer_typedArray(buffer: Buffer): TypedArray[_,_] = MyType()
}

final class TypedArrayBufferOps[
    TypedArrayType <: TypedArray[_, TypedArrayType]] private (
    private val buffer: Buffer)
    extends AnyVal {

  def typedArray(): TypedArrayType =
    TypedArrayBridge.Buffer_typedArray(buffer).asInstanceOf[TypedArrayType]
}

It is a best effort reimplementation of the code in Scala.js. I also encountered something that looks similar when trying to upgrade the scala-dom-types library. This does not compile:

// Does not compile
trait ErrorEventProps[EP[_ <: DomEvent], DomEvent, DomErrorEvent <: DomEvent] { this: EventPropBuilder[EP, DomEvent] =>
// Defining it like this compiles
trait ErrorEventProps[A, EP[A <: DomEvent], DomEvent, DomErrorEvent <: DomEvent] { this: EventPropBuilder[EP, DomEvent] =>

working on the branch here:

I will try to find time looking more deeply into the language specification, so that I can make these bug reports a bit more precise. Thank you very much for the very supportive feedback so far.

@griggt
Copy link
Contributor

griggt commented Jan 12, 2021

Standalone example:

trait TypedArray[T, Repr]

trait Ops[T <: TypedArray[_, T]] {
  def typedArray(): T
}

object Test {
  def test(ops: Ops[_ <: TypedArray[_, _]]) = ops.typedArray()
}

odersky added a commit to dotty-staging/dotty that referenced this issue Jan 12, 2021
Fixes scala#11064

When comparing a LazyRef with some other type, we need to force the LazyRef
which can potentially lead to a cycle (as observed in scala#11064). However, in the situations
```
Nothing <: LazyRef(...)
LazyRef(...) <: Any
```
we can skip that since the comparison is always true. This scheme fixes scala#11064, but it breaks
down in similar situations if additional bounds are introduced (see neg/i11064.scala).
So it is quite fragile. But it's probably the best we can do.

The example in scala#11064 looks harmless, but type-theoretically it's actually a pretty explosive
mix of F-bounded types and existential types (in Scala 2). In Scala 2, we need to force more
for F-bounded types and we need to approximate existential types by dependent types, so
not everything works the same way.

My long term advice would be: get rid of F-bounds. Model them with intersections at the
use site.
@Kordyjan Kordyjan added this to the 3.0.0 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants