Skip to content

Commit b297832

Browse files
authored
Merge pull request #1931 from dotty-staging/fix-#1501
Fix #1501 - Check trait inheritance condition
2 parents 07b67a8 + 36e91d4 commit b297832

File tree

6 files changed

+59
-4
lines changed

6 files changed

+59
-4
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
260260
def AnonClass(parents: List[Type], fns: List[TermSymbol], methNames: List[TermName])(implicit ctx: Context): Block = {
261261
val owner = fns.head.owner
262262
val parents1 =
263-
if (parents.head.classSymbol.is(Trait)) defn.ObjectType :: parents
263+
if (parents.head.classSymbol.is(Trait)) parents.head.parents.head :: parents
264264
else parents
265265
val cls = ctx.newNormalizedClassSymbol(owner, tpnme.ANON_FUN, Synthetic, parents1,
266266
coord = fns.map(_.pos).reduceLeft(_ union _))

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ class Definitions {
476476
lazy val BoxedNumberClass = ctx.requiredClass("java.lang.Number")
477477
lazy val ThrowableClass = ctx.requiredClass("java.lang.Throwable")
478478
lazy val ClassCastExceptionClass = ctx.requiredClass("java.lang.ClassCastException")
479-
lazy val JavaSerializableClass = ctx.requiredClass("java.lang.Serializable")
479+
lazy val JavaSerializableClass = ctx.requiredClass("java.io.Serializable")
480480
lazy val ComparableClass = ctx.requiredClass("java.lang.Comparable")
481481

482482
// in scalac modified to have Any as parent

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,6 @@ object Checking {
448448
}
449449
stats.foreach(checkValueClassMember)
450450
}
451-
452451
}
453452
}
454453

@@ -601,6 +600,31 @@ trait Checking {
601600
/** Verify classes extending AnyVal meet the requirements */
602601
def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) =
603602
Checking.checkDerivedValueClass(clazz, stats)
603+
604+
/** Given a parent `parent` of a class `cls`, if `parent` is a trait check that
605+
* the superclass of `cls` derived from the superclass of `parent`.
606+
*
607+
* An exception is made if `cls` extends `Any`, and `parent` is `java.io.Serializable`
608+
* or `java.lang.Comparable`. These two classes are treated by Scala as universal
609+
* traits. E.g. the following is OK:
610+
*
611+
* ... extends Any with java.io.Serializable
612+
*
613+
* The standard library relies on this idiom.
614+
*/
615+
def checkTraitInheritance(parent: Symbol, cls: ClassSymbol, pos: Position)(implicit ctx: Context): Unit = {
616+
parent match {
617+
case parent: ClassSymbol if parent is Trait =>
618+
val psuper = parent.superClass
619+
val csuper = cls.superClass
620+
val ok = csuper.derivesFrom(psuper) ||
621+
parent.is(JavaDefined) && csuper == defn.AnyClass &&
622+
(parent == defn.JavaSerializableClass || parent == defn.ComparableClass)
623+
if (!ok)
624+
ctx.error(em"illegal trait inheritance: super$csuper does not derive from $parent's super$psuper", pos)
625+
case _ =>
626+
}
627+
}
604628
}
605629

606630
trait NoChecking extends Checking {
@@ -617,4 +641,5 @@ trait NoChecking extends Checking {
617641
override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt
618642
override def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt
619643
override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = ()
644+
override def checkTraitInheritance(parentSym: Symbol, cls: ClassSymbol, pos: Position)(implicit ctx: Context) = ()
620645
}

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,13 +1288,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
12881288
if (tree.isType) {
12891289
val result = typedType(tree)(superCtx)
12901290
val psym = result.tpe.typeSymbol
1291+
checkTraitInheritance(psym, cls, tree.pos)
12911292
if (psym.is(Trait) && !cls.is(Trait) && !cls.superClass.isSubClass(psym))
12921293
maybeCall(result, psym, psym.primaryConstructor.info)
12931294
else
12941295
result
12951296
}
12961297
else {
12971298
val result = typedExpr(tree)(superCtx)
1299+
checkTraitInheritance(result.symbol, cls, tree.pos)
12981300
checkParentCall(result, cls)
12991301
result
13001302
}

tests/neg/i1501.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class A {
2+
def foo: Int = 1
3+
}
4+
5+
trait B extends A
6+
7+
abstract class D {
8+
def foo: Int
9+
}
10+
11+
class C extends D with B // error: illegal trait inheritance
12+
trait E extends D with B // error: illegal trait inheritance
13+
14+
object Test {
15+
def main(args: Array[String]): Unit = {
16+
println(new C().foo)
17+
}
18+
}
19+
20+
object Test2 {
21+
class A
22+
class SubA(x: Int) extends A
23+
trait TA extends A
24+
trait TSubA extends SubA(2) // error: trait TSubA may not call constructor of class SubA
25+
26+
27+
class Foo extends TA with TSubA // error: missing argument for parameter x of constructor SubA:
28+
}

tests/neg/i1653.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
trait Foo {
2-
def foo() = new Unit with Foo // error
2+
def foo() = new Unit with Foo // error: cannot extend final class Unit // error: illegal trait inheritance
33
}

0 commit comments

Comments
 (0)