diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index ea77e0e50192..e5f9e6377a46 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -75,7 +75,7 @@ class Compiler { new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods new Getters, // Replace non-private vals and vars with getter defs (fields are added later) new ElimByName, // Expand by-name parameter references - new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings + new AugmentScala2Traits, // Expand traits defined in Scala 2.x to simulate old-style rewritings new ResolveSuper, // Implement super accessors and add forwarders to trait methods new Simplify, // Perform local optimizations, simplified versions of what linker does. new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives @@ -87,7 +87,6 @@ class Compiler { new Mixin, // Expand trait fields and trait initializers new LazyVals, // Expand lazy vals new Memoize, // Add private fields to getters and setters - new LinkScala2ImplClasses, // Forward calls to the implementation classes of traits defined by Scala 2.11 new NonLocalReturns, // Expand non-local returns new CapturedVars, // Represent vars captured by closures as heap objects new Constructors, // Collect initialization code in primary constructors @@ -95,7 +94,8 @@ class Compiler { new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions. new GetClass, // Rewrites getClass calls on primitive types. new Simplify), // Perform local optimizations, simplified versions of what linker does. - List(new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments + List(new LinkScala2Impls, // Redirect calls to trait methods defined by Scala 2.x, so that they now go to their implementations + new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments // Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here new ElimStaticThis, // Replace `this` references to static objects by global identifiers new Flatten, // Lift all inner classes to package scope diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 61b057f85b08..343a97c66ada 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -399,9 +399,15 @@ object Flags { /** A module variable (Scala 2.x only) */ final val Scala2ModuleVar = termFlag(57, "") + /** A Scala 2.12 trait that has been augmented with static members */ + final val Scala_2_12_Augmented = typeFlag(57, "") + /** A definition that's initialized before the super call (Scala 2.x only) */ final val Scala2PreSuper = termFlag(58, "") + /** A Scala 2.12 or higher trait */ + final val Scala_2_12_Trait = typeFlag(58, "") + /** A macro (Scala 2.x only) */ final val Macro = commonFlag(59, "") diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 95a4684a4b9c..39e3ca21462d 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -70,26 +70,29 @@ object Mode { /** We are currently unpickling Scala2 info */ val Scala2Unpickling = newMode(13, "Scala2Unpickling") + /** We are currently unpickling from Java 8 or higher */ + val Java8Unpickling = newMode(14, "Java8Unpickling") + /** Use Scala2 scheme for overloading and implicit resolution */ - val OldOverloadingResolution = newMode(14, "OldOverloadingResolution") + val OldOverloadingResolution = newMode(15, "OldOverloadingResolution") /** Allow hk applications of type lambdas to wildcard arguments; * used for checking that such applications do not normally arise */ - val AllowLambdaWildcardApply = newMode(15, "AllowHKApplyToWildcards") + val AllowLambdaWildcardApply = newMode(16, "AllowHKApplyToWildcards") /** Read original positions when unpickling from TASTY */ - val ReadPositions = newMode(16, "ReadPositions") + val ReadPositions = newMode(17, "ReadPositions") /** Don't suppress exceptions thrown during show */ - val PrintShowExceptions = newMode(17, "PrintShowExceptions") + val PrintShowExceptions = newMode(18, "PrintShowExceptions") val PatternOrType = Pattern | Type /** We are elaborating the fully qualified name of a package clause. * In this case, identifiers should never be imported. */ - val InPackageClauseName = newMode(18, "InPackageClauseName") + val InPackageClauseName = newMode(19, "InPackageClauseName") /** We are in the IDE */ val Interactive = newMode(20, "Interactive") diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 543b15fb1460..396ef6f821e7 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -359,6 +359,7 @@ object NameKinds { val ExtMethName = new SuffixNameKind(EXTMETH, "$extension") val ModuleVarName = new SuffixNameKind(OBJECTVAR, "$module") val ModuleClassName = new SuffixNameKind(OBJECTCLASS, "$", optInfoString = "ModuleClass") + val ImplMethName = new SuffixNameKind(IMPLMETH, "$") /** A name together with a signature. Used in Tasty trees. */ object SignedName extends NameKind(63) { diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 6fa4abbcf809..154f6ad90c41 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1407,14 +1407,14 @@ object SymDenotations { baseData._2 def computeBaseData(implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet) = { - val seen = mutable.SortedSet[Int]() + val seen = new BaseClassSetBuilder def addBaseClasses(bcs: List[ClassSymbol], to: List[ClassSymbol]) : List[ClassSymbol] = bcs match { case bc :: bcs1 => val bcs1added = addBaseClasses(bcs1, to) - if (seen contains bc.id) bcs1added + if (seen contains bc) bcs1added else { - seen += bc.id + seen.add(bc) bc :: bcs1added } case nil => @@ -1432,7 +1432,7 @@ object SymDenotations { if (classParents.isEmpty && !emptyParentsExpected) onBehalf.signalProvisional() (classSymbol :: addParentBaseClasses(classParents, Nil), - new BaseClassSet(seen.toArray)) + seen.result) } final override def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean = @@ -2080,17 +2080,44 @@ object SymDenotations { } class BaseClassSet(val classIds: Array[Int]) extends AnyVal { - def contains(sym: Symbol): Boolean = { + def contains(sym: Symbol, limit: Int) = { val id = sym.id - var lo = 0 - var hi = classIds.length - 1 - while (lo <= hi) { - val mid = (lo + hi) / 2 - if (id < classIds(mid)) hi = mid - 1 - else if (id > classIds(mid)) lo = mid + 1 - else return true + var i = 0 + while (i < limit && classIds(i) != id) i += 1 + i < limit && { + if (i > 0) { + val t = classIds(i) + classIds(i) = classIds(i - 1) + classIds(i - 1) = t + } + true } - false + } + def contains(sym: Symbol): Boolean = contains(sym, classIds.length) + } + + private class BaseClassSetBuilder { + private var classIds = new Array[Int](32) + private var length = 0 + + private def resize(size: Int) = { + val classIds1 = new Array[Int](size) + Array.copy(classIds, 0, classIds1, 0, classIds.length min size) + classIds = classIds1 + } + + def contains(sym: Symbol): Boolean = + new BaseClassSet(classIds).contains(sym, length) + + def add(sym: Symbol): Unit = { + if (length == classIds.length) resize(length * 2) + classIds(length) = sym.id + length += 1 + } + + def result = { + if (length != classIds.length) resize(length) + new BaseClassSet(classIds) } } diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala index dd29fa49dd52..9c517d6ef8db 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala @@ -10,6 +10,8 @@ object ClassfileConstants { final val JAVA_MAJOR_VERSION = 45 final val JAVA_MINOR_VERSION = 3 + final val JAVA8_MAJOR_VERSION = 52 + /** (see http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html) * * If the `ACC_INTERFACE` flag is set, the `ACC_ABSTRACT` flag must also diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index ba58393a4c39..4d53a80a9476 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -42,6 +42,8 @@ class ClassfileParser( protected var currentClassName: SimpleName = _ // JVM name of the current class protected var classTParams = Map[Name,Symbol]() + private var Scala2UnpicklingMode = Mode.Scala2Unpickling + classRoot.info = (new NoCompleter).withDecls(instanceScope) moduleRoot.info = (new NoCompleter).withDecls(staticScope).withSourceModule(_ => staticModule) @@ -69,6 +71,8 @@ class ClassfileParser( throw new IOException(s"class file '${in.file}' has wrong magic number 0x${toHexString(magic)}, should be 0x${toHexString(JAVA_MAGIC)}") val minorVersion = in.nextChar.toInt val majorVersion = in.nextChar.toInt + if (majorVersion >= JAVA8_MAJOR_VERSION) + Scala2UnpicklingMode |= Mode.Java8Unpickling if ((majorVersion < JAVA_MAJOR_VERSION) || ((majorVersion == JAVA_MAJOR_VERSION) && (minorVersion < JAVA_MINOR_VERSION))) @@ -714,7 +718,7 @@ class ClassfileParser( def unpickleScala(bytes: Array[Byte]): Some[Embedded] = { val unpickler = new unpickleScala2.Scala2Unpickler(bytes, classRoot, moduleRoot)(ctx) - unpickler.run()(ctx.addMode(Mode.Scala2Unpickling)) + unpickler.run()(ctx.addMode(Scala2UnpicklingMode)) Some(unpickler) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index d5864afd827c..cd0d427f3510 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -256,7 +256,9 @@ object TastyFormat { final val OBJECTCLASS = 40 final val SIGNED = 63 + final val firstInternalTag = 64 + final val IMPLMETH = 64 // AST tags diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 9e2fa4d5562b..9902e8576073 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -466,7 +466,11 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas } def finishSym(sym: Symbol): Symbol = { - if (sym.isClass) sym.setFlag(Scala2x) + if (sym.isClass) { + sym.setFlag(Scala2x) + if (flags.is(Trait) && ctx.mode.is(Mode.Java8Unpickling)) + sym.setFlag(Scala_2_12_Trait) + } if (!(isRefinementClass(sym) || isUnpickleRoot(sym) || (sym is Scala2Existential))) { val owner = sym.owner if (owner.isClass) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index dfd2fda15a02..e57177c355d9 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -362,7 +362,7 @@ class PlainPrinter(_ctx: Context) extends Printer { protected def keyString(sym: Symbol): String = { val flags = sym.flagsUNSAFE if (flags is JavaTrait) "interface" - else if ((flags is Trait) && !(flags is ImplClass)) "trait" + else if (flags is Trait) "trait" else if (sym.isClass) "class" else if (sym.isType) "type" else if (flags is Mutable) "var" diff --git a/compiler/src/dotty/tools/dotc/transform/LinkScala2ImplClasses.scala b/compiler/src/dotty/tools/dotc/transform/LinkScala2ImplClasses.scala deleted file mode 100644 index ca06938dc53a..000000000000 --- a/compiler/src/dotty/tools/dotc/transform/LinkScala2ImplClasses.scala +++ /dev/null @@ -1,62 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import TreeTransforms._ -import Contexts.Context -import Flags._ -import SymUtils._ -import Symbols._ -import SymDenotations._ -import Types._ -import Decorators._ -import DenotTransformers._ -import StdNames._ -import NameOps._ -import Phases._ -import ast.untpd -import ast.Trees._ -import collection.mutable - -/** Rewrite calls - * - * super[M].f(args) - * - * where M is a Scala2 trait implemented by the current class to - * - * M$class.f(this, args) - * - * provided the implementation class M$class defines a corresponding function `f`. - */ -class LinkScala2ImplClasses extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => - import ast.tpd._ - - override def phaseName: String = "linkScala2ImplClasses" - - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Mixin]) - - override def transformApply(app: Apply)(implicit ctx: Context, info: TransformerInfo) = { - def currentClass = ctx.owner.enclosingClass.asClass - app match { - case Apply(sel @ Select(Super(_, _), _), args) - if sel.symbol.owner.is(Scala2xTrait) && currentClass.mixins.contains(sel.symbol.owner) => - val impl = implMethod(sel.symbol) - if (impl.exists) Apply(ref(impl), This(currentClass) :: args).withPos(app.pos) - else app // could have been an abstract method in a trait linked to from a super constructor - case _ => - app - } - } - - private def implMethod(meth: Symbol)(implicit ctx: Context): Symbol = { - val implInfo = meth.owner.implClass.info - if (meth.isConstructor) - implInfo.decl(nme.TRAIT_CONSTRUCTOR).symbol - else - implInfo.decl(meth.name) - .suchThat(c => FullParameterization.memberSignature(c.info) == meth.signature) - .symbol - } - - private val Scala2xTrait = allOf(Scala2x, Trait) -} diff --git a/compiler/src/dotty/tools/dotc/transform/LinkScala2Impls.scala b/compiler/src/dotty/tools/dotc/transform/LinkScala2Impls.scala new file mode 100644 index 000000000000..887f051033b7 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/LinkScala2Impls.scala @@ -0,0 +1,100 @@ +package dotty.tools.dotc +package transform + +import core._ +import TreeTransforms._ +import Contexts.Context +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import StdNames._ +import NameOps._ +import Phases._ +import ast.untpd +import ast.Trees._ +import NameKinds.ImplMethName +import collection.mutable + +/** Rewrite calls + * + * super[M].f(args) + * + * where M is a Scala 2.11 trait implemented by the current class to + * + * M$class.f(this, args) + * + * provided the implementation class M$class defines a corresponding function `f`. + * If M is a Scala 2.12 or newer trait, rewrite to + * + * M.f(this, args) + * + * where f is a static member of M. + */ +class LinkScala2Impls extends MiniPhase with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + override def phaseName: String = "linkScala2Impls" + override def changesMembers = true + val treeTransform = new Transform + + override def runsAfterGroupsOf: Set[Class[_ <: Phase]] = Set(classOf[Mixin]) + // Adds as a side effect static members to traits which can confuse Mixin, + // that's why it is runsAfterGroupOf + + class Transform extends TreeTransform { + def phase = thisTransform + + /** Copy definitions from implementation class to trait itself */ + private def augmentScala_2_12_Trait(mixin: ClassSymbol)(implicit ctx: Context): Unit = { + def newImpl(sym: TermSymbol): Symbol = sym.copy( + owner = mixin, + name = if (sym.isConstructor) sym.name else ImplMethName(sym.name) + ) + for (sym <- mixin.implClass.info.decls) + newImpl(sym.asTerm).enteredAfter(thisTransform) + } + + override def prepareForTemplate(impl: Template)(implicit ctx: Context) = { + val cls = impl.symbol.owner.asClass + for (mixin <- cls.mixins) + if (mixin.is(Scala_2_12_Trait, butNot = Scala_2_12_Augmented)) { + augmentScala_2_12_Trait(mixin) + mixin.setFlag(Scala_2_12_Augmented) + } + this + } + + override def transformApply(app: Apply)(implicit ctx: Context, info: TransformerInfo) = { + def currentClass = ctx.owner.enclosingClass.asClass + app match { + case Apply(sel @ Select(Super(_, _), _), args) + if sel.symbol.owner.is(Scala2xTrait) && currentClass.mixins.contains(sel.symbol.owner) => + val impl = implMethod(sel.symbol) + if (impl.exists) Apply(ref(impl), This(currentClass) :: args).withPos(app.pos) + else app // could have been an abstract method in a trait linked to from a super constructor + case _ => + app + } + } + + private def implMethod(meth: Symbol)(implicit ctx: Context): Symbol = { + val (implInfo, implName) = + if (meth.owner.is(Scala_2_12_Trait)) + (meth.owner.info, ImplMethName(meth.name.asTermName)) + else + (meth.owner.implClass.info, meth.name) + if (meth.isConstructor) + implInfo.decl(nme.TRAIT_CONSTRUCTOR).symbol + else + implInfo.decl(implName) + .suchThat(c => FullParameterization.memberSignature(c.info) == meth.signature) + .symbol + } + } + + private val Scala2xTrait = allOf(Scala2x, Trait) +} diff --git a/compiler/test/dotc/tests.scala b/compiler/test/dotc/tests.scala index 8842840863de..f8ccb89793cf 100644 --- a/compiler/test/dotc/tests.scala +++ b/compiler/test/dotc/tests.scala @@ -342,7 +342,7 @@ class tests extends CompilerTest { "ElimStaticThis.scala", "Erasure.scala", "ExpandPrivate.scala", "ExpandSAMs.scala", "ExplicitOuter.scala", "ExtensionMethods.scala", "FirstTransform.scala", "Flatten.scala", "FullParameterization.scala", "FunctionalInterfaces.scala", "GetClass.scala", - "Getters.scala", "InterceptedMethods.scala", "LambdaLift.scala", "LiftTry.scala", "LinkScala2ImplClasses.scala", + "Getters.scala", "InterceptedMethods.scala", "LambdaLift.scala", "LiftTry.scala", "LinkScala2Impls.scala", "MacroTransform.scala", "Memoize.scala", "Mixin.scala", "MixinOps.scala", "NonLocalReturns.scala", "NormalizeFlags.scala", "OverridingPairs.scala", "ParamForwarding.scala", "Pickler.scala", "PostTyper.scala", "ResolveSuper.scala", "RestoreScopes.scala", "SeqLiterals.scala", "Splitter.scala", "SuperAccessors.scala", diff --git a/tests/run/mixins1/A_1.scala b/tests/run/mixins1/A_1.scala new file mode 100644 index 000000000000..32c369a1b751 --- /dev/null +++ b/tests/run/mixins1/A_1.scala @@ -0,0 +1,11 @@ +trait A { + + var x = 3 + println("hi") + val y = x * x + + def f: Int = x + y + + def f(z: Int): Int = f + z + +} diff --git a/tests/run/mixins1/C_2.scala b/tests/run/mixins1/C_2.scala new file mode 100644 index 000000000000..bd9a571ebaf3 --- /dev/null +++ b/tests/run/mixins1/C_2.scala @@ -0,0 +1,12 @@ +// Intended to be compiled with either 2.11 or 2.12 +class C extends A { + x = 4 + + override def f: Int = super.f + + val z = x + f(x) + y +} + +object Test extends App { + new C().f +}