diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 292bbe6cc70d..94d35d4417d8 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -108,6 +108,20 @@ object RefChecks { case _ => } + /** Disallow using trait parameters as prefix for its parents. + * + * The rationale is to ensure outer-related NPE never happen in Scala. + * Otherwise, outer NPE may happen, see tests/neg/i5083.scala + */ + private def checkParentPrefix(cls: Symbol, parent: Tree)(implicit ctx: Context): Unit = + parent.tpe.typeConstructor match { + case TypeRef(ref: TermRef, _) => + val paramRefs = ref.namedPartsWith(ntp => ntp.symbol.enclosingClass == cls) + if (paramRefs.nonEmpty) + ctx.error("trait parameters cannot be used as parent prefixes", parent.pos) + case _ => + } + /** Check that a class and its companion object to not both define * a class or module with same name */ @@ -961,6 +975,7 @@ class RefChecks extends MiniPhase { thisPhase => val cls = ctx.owner checkOverloadedRestrictions(cls) checkParents(cls) + if (cls.is(Trait)) tree.parents.foreach(checkParentPrefix(cls, _)) checkCompanionNameClashes(cls) checkAllOverrides(cls) tree diff --git a/tests/neg/i5083.scala b/tests/neg/i5083.scala new file mode 100644 index 000000000000..033b5ce8d784 --- /dev/null +++ b/tests/neg/i5083.scala @@ -0,0 +1,25 @@ +class A(a: Int) { + abstract class X { + def f: Int + val x = a + f // NPE: the outer for `Y` is not yet set + } + + trait Y { + val y = a + def f: Int = A.this.a // NPE: the outer for `Y` is not yet set + } + + trait Z(val o: A) extends o.Y { // error + val z = a + } + + class B extends X with Z(new A(4)) +} + + +object Test { + def main(args: Array[String]): Unit = { + val a = new A(3) + new a.B + } +} diff --git a/tests/neg/i5083b.scala b/tests/neg/i5083b.scala new file mode 100644 index 000000000000..c0623d716ebe --- /dev/null +++ b/tests/neg/i5083b.scala @@ -0,0 +1,15 @@ +class A(a: Int) { + class X { + val x = a + } + + trait Y { + val y = a + } + + val z: Z = ??? + + trait Z(val o: A) extends z.o.Y // error: cyclic reference `z` + + class B extends X with Z(new A(4)) +} diff --git a/tests/neg/i5083c.scala b/tests/neg/i5083c.scala new file mode 100644 index 000000000000..5a6cfc1a29ca --- /dev/null +++ b/tests/neg/i5083c.scala @@ -0,0 +1,25 @@ +class A(a: Int) { + trait X { + def f: Int + val x = a + f // NPE: the outer for `Y` is not yet set + } + + trait Y { + val y = a + def f: Int = A.this.a // NPE: the outer for `Y` is not yet set + } + + class Z(o: A) extends m.Y { // error + val m = o + } + + class B extends Z(new A(4)) +} + + +object Test { + def main(args: Array[String]): Unit = { + val a = new A(3) + new a.B + } +}