From ba66bd47f08e1adf3d44f578452a5113948cb988 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 2 Feb 2017 14:55:06 +1100 Subject: [PATCH 1/7] Fix #1501 - Check trait inheritance condition We need to check a coherence condition between the superclass of a trait and the superclass of an inheriting class or trait. --- .../src/dotty/tools/dotc/typer/Checking.scala | 12 +++++++++++- .../src/dotty/tools/dotc/typer/Typer.scala | 2 ++ tests/neg/i1501.scala | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i1501.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index ca62ed0a9a8d..f4625cbfd479 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,16 @@ 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`. + */ + def checkTraitInheritance(parent: Symbol, cls: ClassSymbol, pos: Position)(implicit ctx: Context): Unit = + parent match { + case parent: ClassSymbol if parent.is(Trait) && !cls.superClass.derivesFrom(parent.superClass) => + ctx.error(em"illegal trait inheritance: super${cls.superClass} does not derive from $parent's super${parent.superClass}", pos) + case _ => + } } trait NoChecking extends Checking { @@ -617,4 +626,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 18ae790b7bda..4f9d96c70f0d 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 000000000000..045f2be1de2f --- /dev/null +++ b/tests/neg/i1501.scala @@ -0,0 +1,18 @@ +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) + } +} From 97b0a084f4490a0e5cc3c7a02d18647e7fa89330 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 2 Feb 2017 16:36:37 +1100 Subject: [PATCH 2/7] Refine checkTraitInheritance condition Need to take account of situations like extends Any with java.io.Serializable which occur in stdlib. --- .../src/dotty/tools/dotc/typer/Checking.scala | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index f4625cbfd479..74f1311bf51f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -603,12 +603,25 @@ trait Checking { /** 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 a Java class + * that extends `Object`. For instance, we accept code like + * + * ... 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 = + def checkTraitInheritance(parent: Symbol, cls: ClassSymbol, pos: Position)(implicit ctx: Context): Unit = { parent match { - case parent: ClassSymbol if parent.is(Trait) && !cls.superClass.derivesFrom(parent.superClass) => - ctx.error(em"illegal trait inheritance: super${cls.superClass} does not derive from $parent's super${parent.superClass}", pos) + 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 && psuper == defn.ObjectClass + if (!ok) + ctx.error(em"illegal trait inheritance: super$csuper does not derive from $parent's super$psuper", pos) case _ => + } } } From 0f396544ced68ca2548b204527e2325c3fc65135 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 2 Feb 2017 16:38:27 +1100 Subject: [PATCH 3/7] Refine AnonClass generation The leading class should be the superclass of the first trait (which is not always Object). We could think of a more refined condition, (i.e. taking the least common superclass of all extended traits), but I think it's not worth it, as one can always spell out the right superclass manually. --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 565ec5ce2750..ed268fda7326 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 _)) From ff0921c2e588eeb6590efea057862c14f2154e99 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 2 Feb 2017 16:38:41 +1100 Subject: [PATCH 4/7] Update test case --- tests/neg/i1653.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neg/i1653.scala b/tests/neg/i1653.scala index ab5369e5f361..f21fc7d54d96 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 } From 0828a5275ace05ac3bcc89c3fbbced389fab7107 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 3 Feb 2017 10:07:14 +1100 Subject: [PATCH 5/7] Add test scenarios --- tests/neg/i1501.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/neg/i1501.scala b/tests/neg/i1501.scala index 045f2be1de2f..68556640383a 100644 --- a/tests/neg/i1501.scala +++ b/tests/neg/i1501.scala @@ -16,3 +16,13 @@ object Test { 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: +} From 54cd06526acf1cc45cbf3480ece4885f8853ab18 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 3 Feb 2017 10:11:42 +1100 Subject: [PATCH 6/7] Narrow Java exception to inheritance rule Excepted are only Serializable and Comparable. This follows scalac's behavior. --- compiler/src/dotty/tools/dotc/typer/Checking.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 74f1311bf51f..27b0f28ca914 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -604,8 +604,9 @@ trait Checking { /** 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 a Java class - * that extends `Object`. For instance, we accept code like + * 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 * @@ -617,7 +618,8 @@ trait Checking { val psuper = parent.superClass val csuper = cls.superClass val ok = csuper.derivesFrom(psuper) || - parent.is(JavaDefined) && csuper == defn.AnyClass && psuper == defn.ObjectClass + 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 _ => From 36e91d4ed0a293943d66f409f7515953e961067f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 10 Feb 2017 12:58:53 +1100 Subject: [PATCH 7/7] Fix package name of Java's Serializable class It's java.io, not java.lang. --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 7fe6505ff624..759cf89bb0d4 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