diff --git a/src/dotty/tools/dotc/core/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala index 9b41eb982a5e..0ef31015c2de 100644 --- a/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/src/dotty/tools/dotc/core/TypeErasure.scala @@ -439,6 +439,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean case ErasedValueType(_, underlying) => sigName(underlying) case tp: TypeRef => + if (!tp.denot.exists) throw new MissingType(tp.prefix, tp.name) val sym = tp.symbol if (!sym.isClass) sigName(tp.info) else if (isDerivedValueClass(sym)) sigName(eraseDerivedValueClassRef(tp)) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d6bb9c3c574e..f4dbfb0e32c0 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -936,6 +936,7 @@ object Types { /** the self type of the underlying classtype */ def givenSelfType(implicit ctx: Context): Type = this match { case tp @ RefinedType(parent, name) => tp.wrapIfMember(parent.givenSelfType) + case tp: ThisType => tp.tref.givenSelfType case tp: TypeProxy => tp.underlying.givenSelfType case _ => NoType } @@ -3181,14 +3182,23 @@ object Types { // ----- Exceptions ------------------------------------------------------------- class TypeError(msg: String) extends Exception(msg) - class FatalTypeError(msg: String) extends TypeError(msg) class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name]) - extends FatalTypeError( - s"""malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}""") + extends TypeError( + s"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}") + + class MissingType(pre: Type, name: Name)(implicit ctx: Context) extends TypeError( + i"""cannot resolve reference to type $pre.$name + |the classfile defining the type might be missing from the classpath${otherReason(pre)}""".stripMargin) + + private def otherReason(pre: Type)(implicit ctx: Context): String = pre match { + case pre: ThisType if pre.givenSelfType.exists => + i"\nor the self type of $pre might not contain all transitive dependencies" + case _ => "" + } class CyclicReference private (val denot: SymDenotation) - extends FatalTypeError(s"cyclic reference involving $denot") { + extends TypeError(s"cyclic reference involving $denot") { def show(implicit ctx: Context) = s"cyclic reference involving ${denot.show}" } @@ -3204,7 +3214,7 @@ object Types { } } - class MergeError(msg: String) extends FatalTypeError(msg) + class MergeError(msg: String) extends TypeError(msg) // ----- Debug --------------------------------------------------------- diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 5d2309e95158..45928af4b7bd 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -120,7 +120,7 @@ class PlainPrinter(_ctx: Context) extends Printer { case tp: TermRef if tp.denot.isOverloaded => "" case tp: SingletonType => - toText(tp.underlying) ~ "(" ~ toTextRef(tp) ~ ")" + toTextLocal(tp.underlying) ~ "(" ~ toTextRef(tp) ~ ")" case tp: TypeRef => toTextPrefix(tp.prefix) ~ selectionString(tp) case tp: RefinedType => diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index bb22e20452af..71fba158871c 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -73,19 +73,25 @@ object RefChecks { /** Check that final and sealed restrictions on class parents * and that self type of this class conforms to self types of parents. + * and required classes. */ - private def checkParents(clazz: Symbol)(implicit ctx: Context): Unit = clazz.info match { + private def checkParents(cls: Symbol)(implicit ctx: Context): Unit = cls.info match { case cinfo: ClassInfo => + def checkSelfConforms(other: TypeRef, category: String, relation: String) = { + val otherSelf = other.givenSelfType.asSeenFrom(cls.thisType, other.classSymbol) + if (otherSelf.exists && !(cinfo.selfType <:< otherSelf)) + ctx.error(d"$category: self type ${cinfo.selfType} of $cls does not conform to self type $otherSelf of $relation ${other.classSymbol}", cls.pos) + } for (parent <- cinfo.classParents) { val pclazz = parent.classSymbol if (pclazz.is(Final)) - ctx.error(d"cannot extend final $pclazz", clazz.pos) - if (pclazz.is(Sealed) && pclazz.associatedFile != clazz.associatedFile) - ctx.error(d"cannot extend sealed $pclazz in different compilation unit", clazz.pos) - val pself = parent.givenSelfType.asSeenFrom(clazz.thisType, parent.classSymbol) - if (pself.exists && !(cinfo.selfType <:< pself)) - ctx.error(d"illegal inheritance: self type ${cinfo.selfType} of $clazz does not conform to self type $pself of parent ${parent.classSymbol}", clazz.pos) + ctx.error(d"cannot extend final $pclazz", cls.pos) + if (pclazz.is(Sealed) && pclazz.associatedFile != cls.associatedFile) + ctx.error(d"cannot extend sealed $pclazz in different compilation unit", cls.pos) + checkSelfConforms(parent, "illegal inheritance", "parent") } + for (reqd <- cinfo.givenSelfType.classSymbols) + checkSelfConforms(reqd.typeRef, "missing requirement", "required") case _ => } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 479eedd380a4..33ec156a1d72 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1087,7 +1087,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit try adapt(typedUnadapted(tree, pt), pt, tree) catch { case ex: CyclicReference => errorTree(tree, cyclicErrorMsg(ex)) - case ex: FatalTypeError => errorTree(tree, ex.getMessage) + case ex: TypeError => errorTree(tree, ex.getMessage) } } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index f222daca4e49..3bce26253025 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -138,7 +138,8 @@ class tests extends CompilerTest { @Test def neg_moduleSubtyping = compileFile(negDir, "moduleSubtyping", xerrors = 4) @Test def neg_escapingRefs = compileFile(negDir, "escapingRefs", xerrors = 2) @Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8) - @Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5) + @Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 6) + @Test def neg_selfreq = compileFile(negDir, "selfreq", xerrors = 4) @Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2) @Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5) @Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2) diff --git a/tests/neg/selfInheritance.scala b/tests/neg/selfInheritance.scala index 5f61c5bbbdb8..993765817dfa 100644 --- a/tests/neg/selfInheritance.scala +++ b/tests/neg/selfInheritance.scala @@ -26,3 +26,7 @@ object Test { object M extends C // error } + +trait X { self: Y => } +trait Y { self: Z => } +trait Z diff --git a/tests/neg/selfreq.scala b/tests/neg/selfreq.scala new file mode 100644 index 000000000000..e75e03c16442 --- /dev/null +++ b/tests/neg/selfreq.scala @@ -0,0 +1,37 @@ +trait X { self: Y => + + type T <: self.U + + def foo(x: T): T + def foo(x: String): String + +} + +trait Y { self: Z => + + type U <: self.V + +} + +trait Z { + + class V + +} + +object O { + val x: X = ??? + x.foo("a") +} + +import scala.tools.nsc.interpreter.IMain + +object Test extends dotty.runtime.LegacyApp { + val engine = new IMain.Factory getScriptEngine() + engine.asInstanceOf[IMain].settings.usejavacp.value = true + val res2 = engine.asInstanceOf[javax.script.Compilable] + res2 compile "8" eval() + val res5 = res2 compile """println("hello") ; 8""" + res5 eval() + res5 eval() +}