Skip to content
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

Support Scala 2.12 Mixins #2685

Merged
merged 10 commits into from
Jun 14, 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
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -87,15 +87,15 @@ 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
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
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
Expand Down
6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -399,9 +399,15 @@ object Flags {
/** A module variable (Scala 2.x only) */
final val Scala2ModuleVar = termFlag(57, "<modulevar>")

/** A Scala 2.12 trait that has been augmented with static members */
final val Scala_2_12_Augmented = typeFlag(57, "<scala_2_12_augmented>")

/** A definition that's initialized before the super call (Scala 2.x only) */
final val Scala2PreSuper = termFlag(58, "<presuper>")

/** A Scala 2.12 or higher trait */
final val Scala_2_12_Trait = typeFlag(58, "<scala_2_12_trait>")

/** A macro (Scala 2.x only) */
final val Macro = commonFlag(59, "<macro>")

Expand Down
13 changes: 8 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be nice to leave a note that it is used as heuristic to find scala 2.12


/** 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")
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/NameKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
53 changes: 40 additions & 13 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand All @@ -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 =
Expand Down Expand Up @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -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)
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,9 @@ object TastyFormat {
final val OBJECTCLASS = 40

final val SIGNED = 63

final val firstInternalTag = 64
final val IMPLMETH = 64

// AST tags

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

This file was deleted.

100 changes: 100 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/LinkScala2Impls.scala
Original file line number Diff line number Diff line change
@@ -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)
}
Loading