Skip to content

Fix #1501 - Check trait inheritance condition #1931

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 _))
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 26 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,6 @@ object Checking {
}
stats.foreach(checkValueClassMember)
}

}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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) = ()
}
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1288,13 +1288,15 @@ 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
result
}
else {
val result = typedExpr(tree)(superCtx)
checkTraitInheritance(result.symbol, cls, tree.pos)
checkParentCall(result, cls)
result
}
Expand Down
28 changes: 28 additions & 0 deletions tests/neg/i1501.scala
Original file line number Diff line number Diff line change
@@ -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:
}
2 changes: 1 addition & 1 deletion tests/neg/i1653.scala
Original file line number Diff line number Diff line change
@@ -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
}