scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.89k stars 1.06k forks source link

Stack overflow when using `classOf[String]` in a Spring Boot `WebTestClient` method as generic param (ExtractDependencies.scala cyclical loop) #12961

Open GavinRay97 opened 3 years ago

GavinRay97 commented 3 years ago

Compiler version

3.0.0

ThisBuild / organization := "org.acme"
ThisBuild / version := "0.0.1-SNAPSHOT"
ThisBuild / scalaVersion := "3.0.0"

val SPRING_VERSION = "2.6.0-SNAPSHOT"

lazy val root = (project in file("."))
  .enablePlugins(RevolverPlugin)
  .settings(
      name := "demo",
      resolvers ++= List(
          "Spring Milestones".at("https://repo.spring.io/milestone"),
          "Spring Snapshots".at("https://repo.spring.io/snapshot")
      ),
      libraryDependencies ++= List(
          ("org.springframework.boot" % "spring-boot-starter-parent" % SPRING_VERSION).pomOnly(),
          "org.springframework.boot" % "spring-boot-starter-actuator" % SPRING_VERSION,
          "org.springframework.boot" % "spring-boot-starter-webflux" % SPRING_VERSION,
          "org.springframework.fu" % "spring-fu-jafu" % "0.4.5-SNAPSHOT",
          "org.springframework.boot" % "spring-boot-starter-test" % SPRING_VERSION % Test,
          "io.projectreactor" % "reactor-test" % "3.4.7" % Test,
          "org.scalameta" %% "munit" % "0.7.26" % Test
      )
  )

lazy val props = new {
  val javaVersion = "8"
}

lazy val libs = new {}

Minimized code

package org.acme

import org.springframework.context.ConfigurableApplicationContext
import org.springframework.test.web.reactive.server.WebTestClient

class IntegrationTests extends munit.FunSuite {

  final private var client: WebTestClient =
    WebTestClient.bindToServer().baseUrl("http://localhost:8181").build()

  final private var context: ConfigurableApplicationContext = _

  override def beforeAll(): Unit =
    context = Application.app.run("test")

  override def afterAll(): Unit =
    context.close()

  test("webclient works") {
    client.get.uri("/").exchange()
      .expectStatus().is2xxSuccessful()
       // Remove "classOf[String]" here and it works
      .expectBody(classOf[String]).isEqualTo("Hello world!")
  }
}

Output (click arrow to expand)

It's the below error, repeated hundreds of times in a cyclical loop.

[error] ## Exception when compiling 1 sources to C:\Users\rayga\Projects\tmp\polyglot-actions-service\polyglot-faas-scala-spring\maven-version\target\scala-3.0.0\test-classes [error] java.lang.StackOverflowError [error] dotty.tools.dotc.core.Hashable.typeHash(Hashable.scala:47) [error] dotty.tools.dotc.core.Hashable.typeHash$(Hashable.scala:38) [error] dotty.tools.dotc.core.Uniques$AppliedUniques.typeHash(Uniques.scala:56) [error] dotty.tools.dotc.core.Hashable.finishHash(Hashable.scala:78) [error] dotty.tools.dotc.core.Hashable.finishHash$(Hashable.scala:38) [error] dotty.tools.dotc.core.Uniques$AppliedUniques.finishHash(Uniques.scala:56) [error] dotty.tools.dotc.core.Hashable.doHash(Hashable.scala:106) [error] dotty.tools.dotc.core.Hashable.doHash$(Hashable.scala:38) [error] dotty.tools.dotc.core.Uniques$AppliedUniques.doHash(Uniques.scala:56) [error] dotty.tools.dotc.core.Uniques$AppliedUniques.enterIfNew(Uniques.scala:60) [error] dotty.tools.dotc.core.Types$AppliedType$.apply(Types.scala:4195) [error] dotty.tools.dotc.core.TypeApplications$.appliedTo$extension(TypeApplications.scala:372) [error] dotty.tools.dotc.core.Types$AppliedType.derivedAppliedType(Types.scala:4169) [error] dotty.tools.dotc.core.Types$TypeMap.derivedAppliedType(Types.scala:5156) [error] dotty.tools.dotc.core.Types$TypeMap.mapOver(Types.scala:5218) [error] dotty.tools.dotc.core.Substituters$.subst2(Substituters.scala:52) [error] dotty.tools.dotc.core.Substituters$Subst2Map.apply(Substituters.scala:173) [error] dotty.tools.dotc.core.Types$TypeMap.mapOver(Types.scala:5230) [error] dotty.tools.dotc.core.Substituters$.subst2(Substituters.scala:52) [error] dotty.tools.dotc.core.Types$Type.subst(Types.scala:1703) [error] dotty.tools.dotc.core.Types$NamedType.rebase$1(Types.scala:2241) [error] dotty.tools.dotc.core.Types$NamedType.argDenot(Types.scala:2252) [error] dotty.tools.dotc.core.Types$NamedType.fromDesignator$1(Types.scala:2177) [error] dotty.tools.dotc.core.Types$NamedType.computeDenot(Types.scala:2195) [error] dotty.tools.dotc.core.Types$NamedType.denot(Types.scala:2150) [error] dotty.tools.dotc.core.Types$NamedType.info(Types.scala:2138) [error] dotty.tools.dotc.sbt.ExtractDependenciesCollector$TypeDependencyTraverser.traverse(ExtractDependencies.scala:467) [error] dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5726) [error] dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5726) [error] dotty.tools.dotc.core.Types$TypeAccumulator.foldOver(Types.scala:5710) [error] dotty.tools.dotc.core.Types$TypeTraverser.traverseChildren(Types.scala:5727) [error] dotty.tools.dotc.sbt.ExtractDependenciesCollector$TypeDependencyTraverser.traverse(ExtractDependencies.scala:477) [error] dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5726) [error] dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5726) [error] dotty.tools.dotc.core.Types$TypeAccumulator.op$proxy22$1(Types.scala:5640) [error] dotty.tools.dotc.core.Types$TypeAccumulator.foldArgs$1(Types.scala:5640) [error] dotty.tools.dotc.core.Types$TypeAccumulator.foldOver(Types.scala:5644) [error] dotty.tools.dotc.core.Types$TypeTraverser.traverseChildren(Types.scala:5727) [error] dotty.tools.dotc.sbt.ExtractDependenciesCollector$TypeDependencyTraverser.traverse(ExtractDependencies.scala:477) [error] dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5726) [error] dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5726) [error] dotty.tools.dotc.core.Types$TypeAccumulator.foldOver(Types.scala:5671) [error] dotty.tools.dotc.core.Types$TypeTraverser.traverseChildren(Types.scala:5727) [error] dotty.tools.dotc.sbt.ExtractDependenciesCollector$TypeDependencyTraverser.traverse(ExtractDependencies.scala:477) [error] dotty.tools.dotc.sbt.ExtractDependenciesCollector$TypeDependencyTraverser.traverse(ExtractDependencies.scala:467) [error] dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5726) [error] dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5726) [error] dotty.tools.dotc.core.Types$TypeAccumulator.foldOver(Types.scala:5710) [error] dotty.tools.dotc.core.Types$TypeTraverser.traverseChildren(Types.scala:5727) [error] dotty.tools.dotc.sbt.ExtractDependenciesCollector$TypeDependencyTraverser.traverse(ExtractDependencies.scala:477) [error] dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5726) [error] dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5726) [error] dotty.tools.dotc.core.Types$TypeAccumulator.op$proxy22$1(Types.scala:5640) [error] dotty.tools.dotc.core.Types$TypeAccumulator.foldArgs$1(Types.scala:5640) [error] dotty.tools.dotc.core.Types$TypeAccumulator.foldOver(Types.scala:5644) [error] dotty.tools.dotc.core.Types$TypeTraverser.traverseChildren(Types.scala:5727) [error] dotty.tools.dotc.sbt.ExtractDependenciesCollector$TypeDependencyTraverser.traverse(ExtractDependencies.scala:477)
griggt commented 3 years ago

Self-contained minimization:

WebTestClient.java

public interface WebTestClient {
    interface ResponseSpec {
        <B> BodySpec<B, ?> expectBody(Class<B> bodyType);
    }

    interface BodySpec<B, S extends BodySpec<B, S>> {
        <T extends S> T isEqualTo(B expected);
    }
}

Test.scala

object Test {
  def test(r: WebTestClient.ResponseSpec): Unit =
    r.expectBody(classOf[String]).isEqualTo("Hello world!")
}
$ javac -version && dotc -version
javac 1.8.0_292
Scala compiler version 3.0.2-RC1-bin-SNAPSHOT-git-0e533b0 -- Copyright 2002-2021, LAMP/EPFL

$ javac -d out WebTestClient.java 
$ dotc -Yforce-sbt-phases -classpath out Test.scala

java.lang.StackOverflowError while compiling Test.scala
Exception in thread "main" java.lang.StackOverflowError
    at dotty.tools.dotc.core.Hashable.typeHash(Hashable.scala:47)
    at dotty.tools.dotc.core.Hashable.typeHash$(Hashable.scala:38)
    at dotty.tools.dotc.core.Uniques$AppliedUniques.typeHash(Uniques.scala:56)
    at dotty.tools.dotc.core.Hashable.finishHash(Hashable.scala:78)
    at dotty.tools.dotc.core.Hashable.finishHash$(Hashable.scala:38)
    at dotty.tools.dotc.core.Uniques$AppliedUniques.finishHash(Uniques.scala:56)
    at dotty.tools.dotc.core.Hashable.doHash(Hashable.scala:106)
    at dotty.tools.dotc.core.Hashable.doHash$(Hashable.scala:38)
    at dotty.tools.dotc.core.Uniques$AppliedUniques.doHash(Uniques.scala:56)
    at dotty.tools.dotc.core.Uniques$AppliedUniques.enterIfNew(Uniques.scala:60)
    at dotty.tools.dotc.core.Types$AppliedType$.apply(Types.scala:4254)
    at dotty.tools.dotc.core.TypeApplications$.appliedTo$extension(TypeApplications.scala:372)
    at dotty.tools.dotc.core.Types$AppliedType.derivedAppliedType(Types.scala:4228)
    at dotty.tools.dotc.core.Types$TypeMap.derivedAppliedType(Types.scala:5275)
    at dotty.tools.dotc.core.Types$TypeMap.mapOver(Types.scala:5337)
    at dotty.tools.dotc.core.Substituters$.subst2(Substituters.scala:52)
    at dotty.tools.dotc.core.Substituters$Subst2Map.apply(Substituters.scala:173)
    at dotty.tools.dotc.core.Types$TypeMap.mapOver(Types.scala:5349)
    at dotty.tools.dotc.core.Substituters$.subst2(Substituters.scala:52)
    at dotty.tools.dotc.core.Types$Type.subst(Types.scala:1728)
    at dotty.tools.dotc.core.Types$NamedType.rebase$1(Types.scala:2274)
    at dotty.tools.dotc.core.Types$NamedType.argDenot(Types.scala:2285)
    at dotty.tools.dotc.core.Types$NamedType.fromDesignator$1(Types.scala:2210)
    at dotty.tools.dotc.core.Types$NamedType.computeDenot(Types.scala:2228)
    at dotty.tools.dotc.core.Types$NamedType.denot(Types.scala:2183)
    at dotty.tools.dotc.core.Types$NamedType.info(Types.scala:2171)
    at dotty.tools.dotc.sbt.ExtractDependenciesCollector$TypeDependencyTraverser.traverse(ExtractDependencies.scala:459)
    at dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5859)
    at dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5859)
    at dotty.tools.dotc.core.Types$TypeAccumulator.foldOver(Types.scala:5843)
    at dotty.tools.dotc.core.Types$TypeTraverser.traverseChildren(Types.scala:5860)
    at dotty.tools.dotc.sbt.ExtractDependenciesCollector$TypeDependencyTraverser.traverse(ExtractDependencies.scala:469)
    at dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5859)
    at dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5859)
    at dotty.tools.dotc.core.Types$TypeAccumulator.op$proxy23$1(Types.scala:5773)
    at dotty.tools.dotc.core.Types$TypeAccumulator.foldArgs$1(Types.scala:5773)
    at dotty.tools.dotc.core.Types$TypeAccumulator.foldOver(Types.scala:5777)
    at dotty.tools.dotc.core.Types$TypeTraverser.traverseChildren(Types.scala:5860)
    at dotty.tools.dotc.sbt.ExtractDependenciesCollector$TypeDependencyTraverser.traverse(ExtractDependencies.scala:469)
    at dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5859)
    at dotty.tools.dotc.core.Types$TypeTraverser.apply(Types.scala:5859)
    at dotty.tools.dotc.core.Types$TypeAccumulator.foldOver(Types.scala:5804)
    at dotty.tools.dotc.core.Types$TypeTraverser.traverseChildren(Types.scala:5860)
     ... truncated ...
dwijnand commented 3 years ago

I'm going to guess it's the f-bounds that causing the cycle.

wjoel commented 2 months ago

The minimized example seems to compile as it should using Scala 3.5.0.

(Found this issue while trying to track down another Java-related issue with some weird bounds, was hoping it might be related, but doesn't seem like it).