// A test for the case where the outer field of class B#J should be eliminated.

import reflect.ClassTag

abstract class A {
  abstract class I

  val foo = this
}

class B extends A {
  class J extends I {
    val bar = foo
  }

  type II = I
  class K extends II {
    val bar = foo
  }

  class L extends (I @annotation.tailrec) {
    val bar = foo
  }
}


class C extends A {
  val c: C = this

  class M extends c.I {
    val bar = foo
  }
}


object Test extends App {
  val b = new B
  val c0 = new C
  val c = new { override val c = c0 } with C

  assert((new b.J).bar eq b)
  assert((new b.K).bar eq b)
  assert((new b.L).bar eq b)
  assert((new c.M).bar eq c)

  def checkOuterFields[C: ClassTag](expected: Int): Unit = {
    val cls = implicitly[ClassTag[C]].runtimeClass
    val outerFields = cls.getDeclaredFields().filter(_.getName.contains("$outer"))
    assert(outerFields.size == expected, outerFields.map(_.getName))
  }

  checkOuterFields[A#I](1) // the base class must have the $outer pointer
  checkOuterFields[B#J](0) // reuse parent class' $outer pointer
  checkOuterFields[B#K](0) // ... through an alias
  checkOuterFields[B#L](0) // ... through the annotated type
  checkOuterFields[C#M](1) // different prefix, can't share.
}
