summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2016-04-05 22:01:43 +0200
committerLukas Rytz <lukas.rytz@gmail.com>2016-04-12 12:01:31 +0200
commit33e71061d63d08ee23e28d9a43ad601afa74e043 (patch)
tree83c4279524ae3aa6d5a270fc6bd1662102742a26 /src
parentb932419f07c0d747d868ebc3b51ccc54573a5827 (diff)
downloadscala-33e71061d63d08ee23e28d9a43ad601afa74e043.tar.gz
scala-33e71061d63d08ee23e28d9a43ad601afa74e043.tar.bz2
scala-33e71061d63d08ee23e28d9a43ad601afa74e043.zip
SD-98 don't emit unnecessary mixin forwarders
In most cases when a class inherits a concrete method from a trait we don't need to generate a forwarder to the default method in the class. t5148 is moved to pos as it compiles without error now. the error message ("missing or invalid dependency") is still tested by t6440b.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala38
1 files changed, 35 insertions, 3 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index ed7ef0d8fd..03935c3d67 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -47,7 +47,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
sym.isMethod
&& (!sym.hasFlag(DEFERRED | SUPERACCESSOR) || (sym hasFlag lateDEFERRED))
&& sym.owner.isTrait
- && sym.isMethod
&& (!sym.isModule || sym.hasFlag(PRIVATE | LIFTED))
&& (!(sym hasFlag (ACCESSOR | SUPERACCESSOR)) || sym.isLazy)
&& !sym.isPrivate
@@ -240,8 +239,41 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
for (member <- mixinClass.info.decls ; if isImplementedStatically(member)) {
member overridingSymbol clazz match {
case NoSymbol =>
- if (clazz.info.findMember(member.name, 0, 0L, stableOnly = false).alternatives contains member)
- cloneAndAddMixinMember(mixinClass, member).asInstanceOf[TermSymbol] setAlias member
+ val isMemberOfClazz = clazz.info.findMember(member.name, 0, 0L, stableOnly = false).alternatives.contains(member)
+ if (isMemberOfClazz) {
+ val competingMethods = clazz.baseClasses.iterator
+ .filter(_ ne mixinClass)
+ .map(member.overriddenSymbol)
+ .filter(_.exists)
+ .toList
+
+ // `member` is a concrete `method` defined in `mixinClass`, which is a base class of
+ // `clazz`, and the method is not overridden in `clazz`. A forwarder is needed if:
+ //
+ // - A non-trait base class defines matching method. Example:
+ // class C {def f: Int}; trait T extends C {def f = 1}; class D extends T
+ // Even if C.f is abstract, the forwarder in D is needed, otherwise the JVM would
+ // resolve `D.f` to `C.f`, see jvms-6.5.invokevirtual.
+ //
+ // - There exists another concrete, matching method in any of the base classes, and
+ // the `mixinClass` does not itself extend that base class. In this case the
+ // forwarder is needed to disambiguate. Example:
+ // trait T1 {def f = 1}; trait T2 extends T1 {override def f = 2}; class C extends T2
+ // In C we don't need a forwarder for f because T2 extends T1, so the JVM resolves
+ // C.f to T2.f non-ambiguously. See jvms-5.4.3.3, "maximally-specific method".
+ // trait U1 {def f = 1}; trait U2 {self:U1 => override def f = 2}; class D extends U2
+ // In D the forwarder is needed, the interfaces U1 and U2 are unrelated at the JVM
+ // level.
+
+ lazy val mixinSuperInterfaces = mixinClass.ancestors.filter(_.isTraitOrInterface)
+ val needsForwarder = competingMethods.exists(m => {
+ !m.owner.isTraitOrInterface ||
+ (!m.isDeferred && !mixinSuperInterfaces.contains(m.owner))
+ })
+ if (needsForwarder)
+ cloneAndAddMixinMember(mixinClass, member).asInstanceOf[TermSymbol] setAlias member
+ }
+
case _ =>
}
}