Skip to content

exception caught when loading class C: Cyclic reference involving class C #10888

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
kostaskougios opened this issue Dec 22, 2020 · 2 comments · Fixed by #11006
Closed

exception caught when loading class C: Cyclic reference involving class C #10888

kostaskougios opened this issue Dec 22, 2020 · 2 comments · Fixed by #11006
Assignees
Milestone

Comments

@kostaskougios
Copy link

kostaskougios commented Dec 22, 2020

Minimized code

scala file failing to compile:

import com.badlogic.gdx.graphics.g2d.ParticleEffectPool

case class DrawParticleEffect(effect: ParticleEffectPool)

mill build file build.sc, use this to fetch the ParticleEffectPool class which causes the issue :

import Deps._
import mill._
import mill.scalalib._
import mill.scalalib.scalafmt._

object `gdx-lib` extends Common {
  override def ivyDeps = Agg(
    Gdx.Core,
    Gdx.Backend,
    Gdx.Native,
    Gdx.Controllers
  )
}

trait Common extends SbtModule with ScalafmtModule {
  override def scalaVersion = Deps.ScalaVersion

  override def scalacOptions = Seq("-deprecation", "-feature", "-unchecked")
}

object Deps {
  val ScalaVersion = "3.0.0-M3"

  object Gdx {
    private val Version = "1.9.12"
    val Core = ivy"com.badlogicgames.gdx:gdx:$Version"
    val Backend = ivy"com.badlogicgames.gdx:gdx-backend-lwjgl3:$Version"
    val BackendHeadless = ivy"com.badlogicgames.gdx:gdx-backend-headless:$Version"
    val Native = ivy"com.badlogicgames.gdx:gdx-platform:$Version;classifier=natives-desktop"
    val Controllers = ivy"com.badlogicgames.gdx:gdx-controllers-lwjgl3:$Version"
  }
}

use mill __.compile to compile and get the error.

Output

[info] Compiling 1 Scala source to /home/ariskk/temp/scala3issue/out/gdx-lib/compile/dest/classes ...
exception caught when loading class ParticleEffectPool: Cyclic reference involving class ParticleEffectPool
[error] -- [E046] Cyclic Error: /home/ariskk/temp/scala3issue/gdx-lib/src/main/scala/games/lib/draw/DrawParticleEffect.scala:8:81 
[error] 8 |case class DrawParticleEffect(renderPriority: Float, x: Float, y: Float, effect: ParticleEffectPool, scaleFactor: Float = 1) {}
[error]   |                                                                                 ^
[error]   |                       Cyclic reference involving class ParticleEffectPool
[error] one error found

Expectation

This should compile. It compiles ok with scala 2.13.4 but it could be a class file issue from the gdx library.

@smarter
Copy link
Member

smarter commented Dec 22, 2020

Reproducible without involving a build tool by using coursier to set the classpath:

scalac -classpath "$(cs fetch -p "com.badlogicgames.gdx:gdx:1.9.12")" i10888.scala

The loop and stacktrace is:

completing type DrawParticleEffect
  completing val <init>
    completing val effect
      completing type ParticleEffectPool
        completing val utils
          completing type utils
          completed utils in package com.badlogic.gdx
        completed utils in package com.badlogic.gdx
        completing type ParticleEffectPool
exception caught when loading class ParticleEffectPool: Cyclic reference involving class ParticleEffectPool
dotty.tools.dotc.core.CyclicReference: 
        at dotty.tools.dotc.core.CyclicReference$.apply(TypeErrors.scala:155)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:148)
        at dotty.tools.dotc.core.Denotations$Denotation.completeInfo$1(Denotations.scala:188)
        at dotty.tools.dotc.core.Denotations$Denotation.info(Denotations.scala:190)
        at dotty.tools.dotc.core.Types$ThisType.underlying(Types.scala:2668)
        at dotty.tools.dotc.core.Types$Type.widen(Types.scala:1160)
        at dotty.tools.dotc.core.classfile.ClassfileParser.processInner$1(ClassfileParser.scala:354)
        at dotty.tools.dotc.core.classfile.ClassfileParser.sig2type$1(ClassfileParser.scala:392)
        at dotty.tools.dotc.core.classfile.ClassfileParser.processClassType$1(ClassfileParser.scala:377)
        at dotty.tools.dotc.core.classfile.ClassfileParser.sig2type$1(ClassfileParser.scala:392)
        at dotty.tools.dotc.core.classfile.ClassfileParser.dotty$tools$dotc$core$classfile$ClassfileParser$$sigToType(ClassfileParser.scala:516)
        at dotty.tools.dotc.core.classfile.ClassfileParser$AttributeCompleter.complete(ClassfileParser.scala:658)
        at dotty.tools.dotc.core.classfile.ClassfileParser.parseClass(ClassfileParser.scala:190)
        at dotty.tools.dotc.core.classfile.ClassfileParser.run$$anonfun$1(ClassfileParser.scala:87)
        at dotty.tools.dotc.core.classfile.ClassfileParser.run(ClassfileParser.scala:82)
        at dotty.tools.dotc.core.ClassfileLoader.load(SymbolLoaders.scala:416)
        at dotty.tools.dotc.core.ClassfileLoader.doComplete(SymbolLoaders.scala:411)
        at dotty.tools.dotc.core.SymbolLoader.complete(SymbolLoaders.scala:343)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:152)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeOnce(SymDenotations.scala:362)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.isAbsent(SymDenotations.scala:571)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.isAccessibleFrom(SymDenotations.scala:864)
        at dotty.tools.dotc.core.Denotations$SingleDenotation.accessibleFrom(Denotations.scala:613)
        at dotty.tools.dotc.typer.Typer.selection$3(Typer.scala:212)
        at dotty.tools.dotc.typer.Typer.recur$1(Typer.scala:246)
        at dotty.tools.dotc.typer.Typer.namedImportRef$1(Typer.scala:253)
        at dotty.tools.dotc.typer.Typer.loop$1(Typer.scala:389)
        at dotty.tools.dotc.typer.Typer.findRefRecur$1(Typer.scala:411)
        at dotty.tools.dotc.typer.Typer.findRef(Typer.scala:414)
        at dotty.tools.dotc.typer.Typer.typedIdent(Typer.scala:469)
        at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:2497)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2589)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2663)
        at dotty.tools.dotc.typer.Namer.typedAheadType$$anonfun$1(Namer.scala:1251)
        at dotty.tools.dotc.typer.Namer.typedAhead(Namer.scala:1243)
        at dotty.tools.dotc.typer.Namer.typedAheadType(Namer.scala:1251)
        at dotty.tools.dotc.typer.Namer.valOrDefDefSig(Namer.scala:1455)
        at dotty.tools.dotc.typer.Namer$Completer.typeSig(Namer.scala:700)
        at dotty.tools.dotc.typer.Namer$Completer.completeInCreationContext(Namer.scala:821)
        at dotty.tools.dotc.typer.Namer$Completer.complete(Namer.scala:732)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:152)
        at dotty.tools.dotc.core.Denotations$Denotation.completeInfo$1(Denotations.scala:188)
        at dotty.tools.dotc.core.Denotations$Denotation.info(Denotations.scala:190)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.ensureCompleted(SymDenotations.scala:370)
        at dotty.tools.dotc.typer.Typer.retrieveSym(Typer.scala:2470)
        at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:2495)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2589)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2663)
        at dotty.tools.dotc.typer.Namer.typedAheadExpr$$anonfun$1(Namer.scala:1256)
        at dotty.tools.dotc.typer.Namer.typedAhead(Namer.scala:1243)
        at dotty.tools.dotc.typer.Namer.typedAheadExpr(Namer.scala:1256)
        at dotty.tools.dotc.typer.Namer.completeParams$$anonfun$1(Namer.scala:1272)
        at scala.collection.immutable.List.foreach(List.scala:333)
        at dotty.tools.dotc.typer.Namer.completeParams(Namer.scala:1272)
        at dotty.tools.dotc.typer.Namer.defDefSig$$anonfun$3(Namer.scala:1494)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
        at scala.collection.immutable.List.foreach(List.scala:333)
        at dotty.tools.dotc.typer.Namer.defDefSig(Namer.scala:1494)
        at dotty.tools.dotc.typer.Namer$Completer.typeSig(Namer.scala:704)
        at dotty.tools.dotc.typer.Namer$Completer.completeInCreationContext(Namer.scala:821)
        at dotty.tools.dotc.typer.Namer$Completer.complete(Namer.scala:732)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:152)
        at dotty.tools.dotc.core.Denotations$Denotation.completeInfo$1(Denotations.scala:188)
        at dotty.tools.dotc.core.Denotations$Denotation.info(Denotations.scala:190)
        at dotty.tools.dotc.typer.Namer$ClassCompleter.completeConstructor(Namer.scala:1112)
        at dotty.tools.dotc.typer.Namer$ClassCompleter.completeInCreationContext(Namer.scala:1192)
        at dotty.tools.dotc.typer.Namer$Completer.complete(Namer.scala:732)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:152)
        at dotty.tools.dotc.core.Denotations$Denotation.completeInfo$1(Denotations.scala:188)
        at dotty.tools.dotc.core.Denotations$Denotation.info(Denotations.scala:190)
        at dotty.tools.dotc.core.SymDenotations$SymDenotation.ensureCompleted(SymDenotations.scala:370)
        at dotty.tools.dotc.typer.Typer.retrieveSym(Typer.scala:2470)
        at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:2495)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2589)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2663)
        at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:2685)
        at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:2735)
        at dotty.tools.dotc.typer.Typer.typedPackageDef(Typer.scala:2313)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2561)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2590)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2663)
        at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:2779)
        at dotty.tools.dotc.typer.FrontEnd.liftedTree1$1(FrontEnd.scala:79)
        at dotty.tools.dotc.typer.FrontEnd.typeCheck$$anonfun$1(FrontEnd.scala:84)
        at dotty.tools.dotc.typer.FrontEnd.monitor(FrontEnd.scala:43)
        at dotty.tools.dotc.typer.FrontEnd.typeCheck(FrontEnd.scala:85)
        at dotty.tools.dotc.typer.FrontEnd.runOn$$anonfun$3(FrontEnd.scala:120)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
        at scala.collection.immutable.List.foreach(List.scala:333)
        at dotty.tools.dotc.typer.FrontEnd.runOn(FrontEnd.scala:120)
        at dotty.tools.dotc.Run.runPhases$4$$anonfun$4(Run.scala:185)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
        at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323)
        at dotty.tools.dotc.Run.runPhases$5(Run.scala:195)
        at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:203)
        at scala.runtime.function.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
        at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:67)
        at dotty.tools.dotc.Run.compileUnits(Run.scala:210)
        at dotty.tools.dotc.Run.compileSources(Run.scala:146)
        at dotty.tools.dotc.Run.compile(Run.scala:130)
        at dotty.tools.dotc.Driver.doCompile(Driver.scala:39)
        at dotty.tools.dotc.Driver.process(Driver.scala:186)
        at dotty.tools.dotc.Driver.process(Driver.scala:155)
        at dotty.tools.dotc.Driver.process(Driver.scala:167)
        at dotty.tools.dotc.Driver.main(Driver.scala:194)
        at dotty.tools.dotc.Main.main(Main.scala)

Looks like the problem goes away if I remove the .widen on this line: https://github.com/lampepfl/dotty/blob/12ff02fd169ccc0066ec9a4a62e4864b22d5b075/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala#L354, I don't know what that code is trying to achieve, but it looks like this is directly ported from Scala 2 which has similar code (but doesn't run into a loop): https://github.com/scala/scala/blob/1a250cb4c14f7999fb610dd6f8d163ab65aa5aa7/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala#L669

The problematic Java class looks like:

public class ParticleEffectPool extends Pool<PooledEffect> {
  ...
  public class PooledEffect extends ParticleEffect { ... }
}

So a class extends another class parameterized by a non-static inner class, I don't really know what that means.

@smarter
Copy link
Member

smarter commented Dec 22, 2020

I don't know what that code is trying to achieve

It's needed for https://github.com/lampepfl/dotty/tree/master/tests/pos/t4737 to compile, it translates A.this.B into A#B, the problem is that calling widen on a ThisType forces the info of the class, which leads to cycles here, but we don't need to do that to strip the ThisType:

diff --git compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
index 05d2b29c5f9..9ae1bce5ac7 100644
--- compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
+++ compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
@@ -349,9 +349,15 @@ class ClassfileParser(
       val tag = sig(index); index += 1
       (tag: @switch) match {
         case 'L' =>
+          /** Inner class references of A.this.B into A#B, do the same transformation recursively on `A` */
           def processInner(tp: Type): Type = tp match {
             case tp: TypeRef if !tp.symbol.owner.is(Flags.ModuleClass) =>
-              TypeRef(processInner(tp.prefix), tp.symbol.asType)
+              val prefix = tp.prefix match
+                case ThisType(prefix) =>
+                  println("tp.prefix: " + tp.prefix + " " + tp.prefix.hashCode)
+                  prefix
+                case prefix => prefix
+              TypeRef(processInner(prefix), tp.symbol.asType)
             case _ =>
               tp
           }

But why do we have ThisType to strip in the first place? It looks like they come from calls to typeRef, in which case instead of fixing things afterwards, maybe we should replace typeRef by the reachableTypeRef currently being worked on in #10785

@liufengyun liufengyun self-assigned this Jan 6, 2021
liufengyun added a commit to dotty-staging/dotty that referenced this issue Jan 6, 2021
For Java inner classes, we should use `A#B` instead of `A.this.B`.

Co-authored-by: Guillaume Martres <[email protected]>
liufengyun added a commit to dotty-staging/dotty that referenced this issue Jan 6, 2021
For Java inner classes, we should use `A#B` instead of `A.this.B`.

Co-authored-by: Guillaume Martres <[email protected]>
liufengyun added a commit to dotty-staging/dotty that referenced this issue Jan 6, 2021
For Java inner classes, we should use `A#B` instead of `A.this.B`.

Co-authored-by: Guillaume Martres <[email protected]>
liufengyun added a commit to dotty-staging/dotty that referenced this issue Jan 7, 2021
For Java inner classes, we should use `A#B` instead of `A.this.B`.

Co-authored-by: Guillaume Martres <[email protected]>
liufengyun added a commit to dotty-staging/dotty that referenced this issue Jan 7, 2021
For Java inner classes, we should use `A#B` instead of `A.this.B`.

Co-authored-by: Guillaume Martres <[email protected]>
liufengyun added a commit to dotty-staging/dotty that referenced this issue Feb 8, 2021
For Java inner classes, we should use `A#B` instead of `A.this.B`.

Co-authored-by: Guillaume Martres <[email protected]>
liufengyun added a commit to dotty-staging/dotty that referenced this issue Feb 15, 2021
For Java inner classes, we should use `A#B` instead of `A.this.B`.

Co-authored-by: Guillaume Martres <[email protected]>
liufengyun added a commit to dotty-staging/dotty that referenced this issue Feb 15, 2021
For Java inner classes, we should use `A#B` instead of `A.this.B`.

Co-authored-by: Guillaume Martres <[email protected]>
liufengyun added a commit that referenced this issue Feb 15, 2021
Fix #10888: Avoid A.this.B for Java inner classes
@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.

4 participants