Skip to content

crash on compilable code: cyclic reference involving class Visitor #1750

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
helloqirun opened this issue Nov 28, 2016 · 2 comments
Closed

crash on compilable code: cyclic reference involving class Visitor #1750

helloqirun opened this issue Nov 28, 2016 · 2 comments

Comments

@helloqirun
Copy link

scalac can compile but dotc crashes.

$ cat abc.scala

trait Lang1 {
  trait Exp
  trait Visitor { def f(left: Exp): Unit }
  class Eval1 extends Visitor { self =>
    def f(left: Exp) = ()
  }
}
trait Lang2 extends Lang1 {
  class Visitor extends Eval1 { Visitor =>
  }
}

$ dotc abc.scala

exception while transforming () extends Lang2.this.Eval1() {} of class class dotty.tools.dotc.ast.Trees$Template # 174
exception while transforming class Visitor() extends Lang2.this.Eval1() {} of class class dotty.tools.dotc.ast.Trees$TypeDef # 175
exception while transforming () extends Object with Lang1 {
  class Visitor() extends Lang2.this.Eval1() {}
} of class class dotty.tools.dotc.ast.Trees$Template # 176
exception while transforming @scala.annotation.internal.SourceFile("abc.scala") <trait> trait Lang2() extends

Object with Lang1 {
  class Visitor() extends Lang2.this.Eval1() {}
} of class class dotty.tools.dotc.ast.Trees$TypeDef # 177
exception while transforming package <empty> {
  @scala.annotation.internal.SourceFile("abc.scala") <trait> trait Lang1()
     extends
   Object {
    <trait> interface trait Exp() extends Object {}
    <trait> interface trait Visitor() extends Object {
      def f(left: Lang1.this.Exp): Unit
    }
    class Eval1() extends Object() with Lang1.this.Visitor {
      def f(left: Lang1.this.Exp): Unit = ()
    }
  }
  @scala.annotation.internal.SourceFile("abc.scala") <trait> trait Lang2()
     extends
   Object with Lang1 {
    class Visitor() extends Lang2.this.Eval1() {}
  }
} of class class dotty.tools.dotc.ast.Trees$PackageDef # 178

exception occurred while compiling abc.scala
Exception in thread "main" dotty.tools.dotc.core.Types$CyclicReference: cyclic reference involving class Visitor
	at dotty.tools.dotc.core.Types$CyclicReference$.apply(Types.scala:3838)
	at dotty.tools.dotc.core.SymDenotations$ClassDenotation$$anonfun$baseTypeRefOf$2.apply(SymDenotations.scala:1720)
	at dotty.tools.dotc.core.SymDenotations$ClassDenotation$$anonfun$baseTypeRefOf$2.apply(SymDenotations.scala:1709)
	at dotty.tools.dotc.reporting.Reporting$class.conditionalTraceIndented(Reporter.scala:132)
	at dotty.tools.dotc.core.Contexts$Context.conditionalTraceIndented(Contexts.scala:57)
	at dotty.tools.dotc.reporting.Reporting$class.debugTraceIndented(Reporter.scala:127)
	at dotty.tools.dotc.core.Contexts$Context.debugTraceIndented(Contexts.scala:57)
<snipped>
@liufengyun
Copy link
Contributor

The exception happens in the phase RefChecks.

@liufengyun
Copy link
Contributor

I've been debugging this a little bit last night, this test case is very tricky, it doesn't appear if either:

  1. we change def f(left: Exp): Unit to def f(x: Int): Unit, or
  2. we rename Visitor to a different name in lang2.

The combination of def f(left: Exp) and duplicate class name will trigger a very deep asSeenFrom in ref-checking overriding of def f, which results in a cyclic loop in baseTypeRefOf. This is because baseTypeRefOf(lang2.this.Visitor) will cause the parent call baseTypeRefOf(Lang2.this.Eval1), which in turn triggers the parent call baseTypeRefOf (Lang2.this.Visitor) again (NOT Lang1, because asSeenFrom), thus a cyclic reference is detected.

There is also a specification different from Scala here: Scalac allows overriding of Visitor, while Dotty disallows overriding of classes.

liufengyun added a commit to dotty-staging/dotty that referenced this issue Dec 22, 2016
odersky added a commit to dotty-staging/dotty that referenced this issue Jan 29, 2017
Illegal class overrides are fundamentally at odds with the way dotty
represents types and therefore can cause lots of low-level problems.

Two measures in this commit

First, we detect direct illegal class overrides on completion instead of
during RefChecks. Break the override by making the previously
overriding type private.

This fixes i1750.scala, but still fails for indirect overrides between
two unrelated outer traits/classes that are inherited by the same class or trait.
We fix this by catching the previously thrown ClassCastException.
Test case for indirect overrides is in i1750a.scala.
odersky added a commit to dotty-staging/dotty that referenced this issue Jan 29, 2017
Illegal class overrides are fundamentally at odds with the way dotty
represents types and therefore can cause lots of low-level problems.

Two measures in this commit

First, we detect direct illegal class overrides on completion instead of
during RefChecks. Break the override by making the previously
overriding type private.

This fixes i1750.scala, but still fails for indirect overrides between
two unrelated outer traits/classes that are inherited by the same class or trait.
We fix this by catching the previously thrown ClassCastException
in both ExtractAPI and RefChecks.

Test case for indirect overrides is in i1750a.scala.
odersky added a commit to dotty-staging/dotty that referenced this issue Jan 29, 2017
Illegal class overrides are fundamentally at odds with the way dotty
represents types and therefore can cause lots of low-level problems.

Two measures in this commit

First, we detect direct illegal class overrides on completion instead of
during RefChecks. Break the override by making the previously
overriding type private.

This fixes i1750.scala, but still fails for indirect overrides between
two unrelated outer traits/classes that are inherited by the same class or trait.
We fix this by catching the previously thrown ClassCastException
in both ExtractAPI and RefChecks.

Test case for indirect overrides is in i1750a.scala.
odersky added a commit that referenced this issue Jan 30, 2017
Fix #1750: Alternative fix for cyclic references due to illegal class overrides
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