diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.scala index bbfe6c1e505a..6ea2c2b4f7f7 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.scala @@ -145,7 +145,8 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] { NoMatchingOverloadID, StableIdentPatternID, StaticFieldsShouldPrecedeNonStaticID, - IllegalSuperAccessorID + IllegalSuperAccessorID, + TraitParameterUsedAsParentPrefixID def errorNumber = ordinal - 2 } diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 1e75b2106244..a523ee8ef4e1 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -2362,4 +2362,20 @@ object messages { } val explanation: String = "" } + + case class TraitParameterUsedAsParentPrefix(cls: Symbol)(implicit val ctx: Context) + extends Message(TraitParameterUsedAsParentPrefixID) { + val kind: String = "Reference" + val msg: String = + s"${cls.show} cannot extend from a parent that is derived via its own parameters" + val explanation: String = + ex""" + |The parent class/trait that ${cls.show} extends from is obtained from + |the parameter of ${cls.show}. This is disallowed in order to prevent + |outer-related Null Pointer Exceptions in Scala. + | + |In order to fix this issue consider directly extending from the parent rather + |than obtaining it from the parameters of ${cls.show}. + |""".stripMargin + } } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index c8e76126870b..d050b5da351d 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -116,7 +116,7 @@ object RefChecks { 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.sourcePos) + ctx.error(TraitParameterUsedAsParentPrefix(cls), parent.sourcePos) case _ => } diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index d285444e297c..01b53e5374db 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -1638,4 +1638,23 @@ class ErrorMessagesTests extends ErrorMessagesTest { message.msg ) } + + @Test def traitParametersUsedAsParentPrefix() = + checkMessagesAfter(RefChecks.name) { + """ + |class Outer { + | trait Inner + | trait Test(val outer: Outer) extends outer.Inner + |} + |""".stripMargin + }.expect { + (ictx, messages) => + implicit val ctx: Context = ictx + val TraitParameterUsedAsParentPrefix(cls) :: Nil = messages + assertEquals("trait Test", cls.show) + assertEquals( + s"${cls.show} cannot extend from a parent that is derived via its own parameters", + messages.head.msg + ) + } }