diff options
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/core/Definitions.scala | 2 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/typer/Checking.scala | 27 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 | ||||
-rw-r--r-- | tests/neg/i1501.scala | 28 | ||||
-rw-r--r-- | tests/neg/i1653.scala | 2 |
6 files changed, 59 insertions, 4 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 565ec5ce2..ed268fda7 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -260,7 +260,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def AnonClass(parents: List[Type], fns: List[TermSymbol], methNames: List[TermName])(implicit ctx: Context): Block = { val owner = fns.head.owner val parents1 = - if (parents.head.classSymbol.is(Trait)) defn.ObjectType :: parents + if (parents.head.classSymbol.is(Trait)) parents.head.parents.head :: parents else parents val cls = ctx.newNormalizedClassSymbol(owner, tpnme.ANON_FUN, Synthetic, parents1, coord = fns.map(_.pos).reduceLeft(_ union _)) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 866f6e7fa..2797bb8a6 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -476,7 +476,7 @@ class Definitions { lazy val BoxedNumberClass = ctx.requiredClass("java.lang.Number") lazy val ThrowableClass = ctx.requiredClass("java.lang.Throwable") lazy val ClassCastExceptionClass = ctx.requiredClass("java.lang.ClassCastException") - lazy val JavaSerializableClass = ctx.requiredClass("java.lang.Serializable") + lazy val JavaSerializableClass = ctx.requiredClass("java.io.Serializable") lazy val ComparableClass = ctx.requiredClass("java.lang.Comparable") // in scalac modified to have Any as parent diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index ca62ed0a9..27b0f28ca 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -448,7 +448,6 @@ object Checking { } stats.foreach(checkValueClassMember) } - } } @@ -601,6 +600,31 @@ trait Checking { /** Verify classes extending AnyVal meet the requirements */ def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = Checking.checkDerivedValueClass(clazz, stats) + + /** Given a parent `parent` of a class `cls`, if `parent` is a trait check that + * the superclass of `cls` derived from the superclass of `parent`. + * + * An exception is made if `cls` extends `Any`, and `parent` is `java.io.Serializable` + * or `java.lang.Comparable`. These two classes are treated by Scala as universal + * traits. E.g. the following is OK: + * + * ... extends Any with java.io.Serializable + * + * The standard library relies on this idiom. + */ + def checkTraitInheritance(parent: Symbol, cls: ClassSymbol, pos: Position)(implicit ctx: Context): Unit = { + parent match { + case parent: ClassSymbol if parent is Trait => + val psuper = parent.superClass + val csuper = cls.superClass + val ok = csuper.derivesFrom(psuper) || + parent.is(JavaDefined) && csuper == defn.AnyClass && + (parent == defn.JavaSerializableClass || parent == defn.ComparableClass) + if (!ok) + ctx.error(em"illegal trait inheritance: super$csuper does not derive from $parent's super$psuper", pos) + case _ => + } + } } trait NoChecking extends Checking { @@ -617,4 +641,5 @@ trait NoChecking extends Checking { override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt override def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = () + override def checkTraitInheritance(parentSym: Symbol, cls: ClassSymbol, pos: Position)(implicit ctx: Context) = () } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 652c89094..ded8993fb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1288,6 +1288,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (tree.isType) { val result = typedType(tree)(superCtx) val psym = result.tpe.typeSymbol + checkTraitInheritance(psym, cls, tree.pos) if (psym.is(Trait) && !cls.is(Trait) && !cls.superClass.isSubClass(psym)) maybeCall(result, psym, psym.primaryConstructor.info) else @@ -1295,6 +1296,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } else { val result = typedExpr(tree)(superCtx) + checkTraitInheritance(result.symbol, cls, tree.pos) checkParentCall(result, cls) result } diff --git a/tests/neg/i1501.scala b/tests/neg/i1501.scala new file mode 100644 index 000000000..685566403 --- /dev/null +++ b/tests/neg/i1501.scala @@ -0,0 +1,28 @@ +class A { + def foo: Int = 1 +} + +trait B extends A + +abstract class D { + def foo: Int +} + +class C extends D with B // error: illegal trait inheritance +trait E extends D with B // error: illegal trait inheritance + +object Test { + def main(args: Array[String]): Unit = { + println(new C().foo) + } +} + +object Test2 { + class A + class SubA(x: Int) extends A + trait TA extends A + trait TSubA extends SubA(2) // error: trait TSubA may not call constructor of class SubA + + + class Foo extends TA with TSubA // error: missing argument for parameter x of constructor SubA: +} diff --git a/tests/neg/i1653.scala b/tests/neg/i1653.scala index ab5369e5f..f21fc7d54 100644 --- a/tests/neg/i1653.scala +++ b/tests/neg/i1653.scala @@ -1,3 +1,3 @@ trait Foo { - def foo() = new Unit with Foo // error + def foo() = new Unit with Foo // error: cannot extend final class Unit // error: illegal trait inheritance } |