Skip to content

Commit 91cf397

Browse files
authored
Merge pull request #2685 from dotty-staging/add-scala12-mixins-v2
Support Scala 2.12 Mixins
2 parents b90c324 + e0057da commit 91cf397

File tree

14 files changed

+157
-74
lines changed

14 files changed

+157
-74
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class Compiler {
7575
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
7676
new Getters, // Replace non-private vals and vars with getter defs (fields are added later)
7777
new ElimByName, // Expand by-name parameter references
78-
new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings
78+
new AugmentScala2Traits, // Expand traits defined in Scala 2.x to simulate old-style rewritings
7979
new ResolveSuper, // Implement super accessors and add forwarders to trait methods
8080
new Simplify, // Perform local optimizations, simplified versions of what linker does.
8181
new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives
@@ -87,15 +87,15 @@ class Compiler {
8787
new Mixin, // Expand trait fields and trait initializers
8888
new LazyVals, // Expand lazy vals
8989
new Memoize, // Add private fields to getters and setters
90-
new LinkScala2ImplClasses, // Forward calls to the implementation classes of traits defined by Scala 2.11
9190
new NonLocalReturns, // Expand non-local returns
9291
new CapturedVars, // Represent vars captured by closures as heap objects
9392
new Constructors, // Collect initialization code in primary constructors
9493
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
9594
new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions.
9695
new GetClass, // Rewrites getClass calls on primitive types.
9796
new Simplify), // Perform local optimizations, simplified versions of what linker does.
98-
List(new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments
97+
List(new LinkScala2Impls, // Redirect calls to trait methods defined by Scala 2.x, so that they now go to their implementations
98+
new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments
9999
// Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
100100
new ElimStaticThis, // Replace `this` references to static objects by global identifiers
101101
new Flatten, // Lift all inner classes to package scope

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,9 +399,15 @@ object Flags {
399399
/** A module variable (Scala 2.x only) */
400400
final val Scala2ModuleVar = termFlag(57, "<modulevar>")
401401

402+
/** A Scala 2.12 trait that has been augmented with static members */
403+
final val Scala_2_12_Augmented = typeFlag(57, "<scala_2_12_augmented>")
404+
402405
/** A definition that's initialized before the super call (Scala 2.x only) */
403406
final val Scala2PreSuper = termFlag(58, "<presuper>")
404407

408+
/** A Scala 2.12 or higher trait */
409+
final val Scala_2_12_Trait = typeFlag(58, "<scala_2_12_trait>")
410+
405411
/** A macro (Scala 2.x only) */
406412
final val Macro = commonFlag(59, "<macro>")
407413

compiler/src/dotty/tools/dotc/core/Mode.scala

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,26 +70,29 @@ object Mode {
7070
/** We are currently unpickling Scala2 info */
7171
val Scala2Unpickling = newMode(13, "Scala2Unpickling")
7272

73+
/** We are currently unpickling from Java 8 or higher */
74+
val Java8Unpickling = newMode(14, "Java8Unpickling")
75+
7376
/** Use Scala2 scheme for overloading and implicit resolution */
74-
val OldOverloadingResolution = newMode(14, "OldOverloadingResolution")
77+
val OldOverloadingResolution = newMode(15, "OldOverloadingResolution")
7578

7679
/** Allow hk applications of type lambdas to wildcard arguments;
7780
* used for checking that such applications do not normally arise
7881
*/
79-
val AllowLambdaWildcardApply = newMode(15, "AllowHKApplyToWildcards")
82+
val AllowLambdaWildcardApply = newMode(16, "AllowHKApplyToWildcards")
8083

8184
/** Read original positions when unpickling from TASTY */
82-
val ReadPositions = newMode(16, "ReadPositions")
85+
val ReadPositions = newMode(17, "ReadPositions")
8386

8487
/** Don't suppress exceptions thrown during show */
85-
val PrintShowExceptions = newMode(17, "PrintShowExceptions")
88+
val PrintShowExceptions = newMode(18, "PrintShowExceptions")
8689

8790
val PatternOrType = Pattern | Type
8891

8992
/** We are elaborating the fully qualified name of a package clause.
9093
* In this case, identifiers should never be imported.
9194
*/
92-
val InPackageClauseName = newMode(18, "InPackageClauseName")
95+
val InPackageClauseName = newMode(19, "InPackageClauseName")
9396

9497
/** We are in the IDE */
9598
val Interactive = newMode(20, "Interactive")

compiler/src/dotty/tools/dotc/core/NameKinds.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ object NameKinds {
359359
val ExtMethName = new SuffixNameKind(EXTMETH, "$extension")
360360
val ModuleVarName = new SuffixNameKind(OBJECTVAR, "$module")
361361
val ModuleClassName = new SuffixNameKind(OBJECTCLASS, "$", optInfoString = "ModuleClass")
362+
val ImplMethName = new SuffixNameKind(IMPLMETH, "$")
362363

363364
/** A name together with a signature. Used in Tasty trees. */
364365
object SignedName extends NameKind(63) {

compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ object ClassfileConstants {
1010
final val JAVA_MAJOR_VERSION = 45
1111
final val JAVA_MINOR_VERSION = 3
1212

13+
final val JAVA8_MAJOR_VERSION = 52
14+
1315
/** (see http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html)
1416
*
1517
* If the `ACC_INTERFACE` flag is set, the `ACC_ABSTRACT` flag must also

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class ClassfileParser(
4242
protected var currentClassName: SimpleName = _ // JVM name of the current class
4343
protected var classTParams = Map[Name,Symbol]()
4444

45+
private var Scala2UnpicklingMode = Mode.Scala2Unpickling
46+
4547
classRoot.info = (new NoCompleter).withDecls(instanceScope)
4648
moduleRoot.info = (new NoCompleter).withDecls(staticScope).withSourceModule(_ => staticModule)
4749

@@ -69,6 +71,8 @@ class ClassfileParser(
6971
throw new IOException(s"class file '${in.file}' has wrong magic number 0x${toHexString(magic)}, should be 0x${toHexString(JAVA_MAGIC)}")
7072
val minorVersion = in.nextChar.toInt
7173
val majorVersion = in.nextChar.toInt
74+
if (majorVersion >= JAVA8_MAJOR_VERSION)
75+
Scala2UnpicklingMode |= Mode.Java8Unpickling
7276
if ((majorVersion < JAVA_MAJOR_VERSION) ||
7377
((majorVersion == JAVA_MAJOR_VERSION) &&
7478
(minorVersion < JAVA_MINOR_VERSION)))
@@ -714,7 +718,7 @@ class ClassfileParser(
714718

715719
def unpickleScala(bytes: Array[Byte]): Some[Embedded] = {
716720
val unpickler = new unpickleScala2.Scala2Unpickler(bytes, classRoot, moduleRoot)(ctx)
717-
unpickler.run()(ctx.addMode(Mode.Scala2Unpickling))
721+
unpickler.run()(ctx.addMode(Scala2UnpicklingMode))
718722
Some(unpickler)
719723
}
720724

compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,9 @@ object TastyFormat {
256256
final val OBJECTCLASS = 40
257257

258258
final val SIGNED = 63
259+
259260
final val firstInternalTag = 64
261+
final val IMPLMETH = 64
260262

261263
// AST tags
262264

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,11 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
466466
}
467467

468468
def finishSym(sym: Symbol): Symbol = {
469-
if (sym.isClass) sym.setFlag(Scala2x)
469+
if (sym.isClass) {
470+
sym.setFlag(Scala2x)
471+
if (flags.is(Trait) && ctx.mode.is(Mode.Java8Unpickling))
472+
sym.setFlag(Scala_2_12_Trait)
473+
}
470474
if (!(isRefinementClass(sym) || isUnpickleRoot(sym) || (sym is Scala2Existential))) {
471475
val owner = sym.owner
472476
if (owner.isClass)

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
362362
protected def keyString(sym: Symbol): String = {
363363
val flags = sym.flagsUNSAFE
364364
if (flags is JavaTrait) "interface"
365-
else if ((flags is Trait) && !(flags is ImplClass)) "trait"
365+
else if (flags is Trait) "trait"
366366
else if (sym.isClass) "class"
367367
else if (sym.isType) "type"
368368
else if (flags is Mutable) "var"

compiler/src/dotty/tools/dotc/transform/LinkScala2ImplClasses.scala

Lines changed: 0 additions & 62 deletions
This file was deleted.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import TreeTransforms._
6+
import Contexts.Context
7+
import Flags._
8+
import SymUtils._
9+
import Symbols._
10+
import SymDenotations._
11+
import Types._
12+
import Decorators._
13+
import DenotTransformers._
14+
import StdNames._
15+
import NameOps._
16+
import Phases._
17+
import ast.untpd
18+
import ast.Trees._
19+
import NameKinds.ImplMethName
20+
import collection.mutable
21+
22+
/** Rewrite calls
23+
*
24+
* super[M].f(args)
25+
*
26+
* where M is a Scala 2.11 trait implemented by the current class to
27+
*
28+
* M$class.f(this, args)
29+
*
30+
* provided the implementation class M$class defines a corresponding function `f`.
31+
* If M is a Scala 2.12 or newer trait, rewrite to
32+
*
33+
* M.f(this, args)
34+
*
35+
* where f is a static member of M.
36+
*/
37+
class LinkScala2Impls extends MiniPhase with IdentityDenotTransformer { thisTransform =>
38+
import ast.tpd._
39+
40+
override def phaseName: String = "linkScala2Impls"
41+
override def changesMembers = true
42+
val treeTransform = new Transform
43+
44+
override def runsAfterGroupsOf: Set[Class[_ <: Phase]] = Set(classOf[Mixin])
45+
// Adds as a side effect static members to traits which can confuse Mixin,
46+
// that's why it is runsAfterGroupOf
47+
48+
class Transform extends TreeTransform {
49+
def phase = thisTransform
50+
51+
/** Copy definitions from implementation class to trait itself */
52+
private def augmentScala_2_12_Trait(mixin: ClassSymbol)(implicit ctx: Context): Unit = {
53+
def newImpl(sym: TermSymbol): Symbol = sym.copy(
54+
owner = mixin,
55+
name = if (sym.isConstructor) sym.name else ImplMethName(sym.name)
56+
)
57+
for (sym <- mixin.implClass.info.decls)
58+
newImpl(sym.asTerm).enteredAfter(thisTransform)
59+
}
60+
61+
override def prepareForTemplate(impl: Template)(implicit ctx: Context) = {
62+
val cls = impl.symbol.owner.asClass
63+
for (mixin <- cls.mixins)
64+
if (mixin.is(Scala_2_12_Trait, butNot = Scala_2_12_Augmented)) {
65+
augmentScala_2_12_Trait(mixin)
66+
mixin.setFlag(Scala_2_12_Augmented)
67+
}
68+
this
69+
}
70+
71+
override def transformApply(app: Apply)(implicit ctx: Context, info: TransformerInfo) = {
72+
def currentClass = ctx.owner.enclosingClass.asClass
73+
app match {
74+
case Apply(sel @ Select(Super(_, _), _), args)
75+
if sel.symbol.owner.is(Scala2xTrait) && currentClass.mixins.contains(sel.symbol.owner) =>
76+
val impl = implMethod(sel.symbol)
77+
if (impl.exists) Apply(ref(impl), This(currentClass) :: args).withPos(app.pos)
78+
else app // could have been an abstract method in a trait linked to from a super constructor
79+
case _ =>
80+
app
81+
}
82+
}
83+
84+
private def implMethod(meth: Symbol)(implicit ctx: Context): Symbol = {
85+
val (implInfo, implName) =
86+
if (meth.owner.is(Scala_2_12_Trait))
87+
(meth.owner.info, ImplMethName(meth.name.asTermName))
88+
else
89+
(meth.owner.implClass.info, meth.name)
90+
if (meth.isConstructor)
91+
implInfo.decl(nme.TRAIT_CONSTRUCTOR).symbol
92+
else
93+
implInfo.decl(implName)
94+
.suchThat(c => FullParameterization.memberSignature(c.info) == meth.signature)
95+
.symbol
96+
}
97+
}
98+
99+
private val Scala2xTrait = allOf(Scala2x, Trait)
100+
}

compiler/test/dotc/tests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ class tests extends CompilerTest {
342342
"ElimStaticThis.scala", "Erasure.scala", "ExpandPrivate.scala", "ExpandSAMs.scala",
343343
"ExplicitOuter.scala", "ExtensionMethods.scala", "FirstTransform.scala",
344344
"Flatten.scala", "FullParameterization.scala", "FunctionalInterfaces.scala", "GetClass.scala",
345-
"Getters.scala", "InterceptedMethods.scala", "LambdaLift.scala", "LiftTry.scala", "LinkScala2ImplClasses.scala",
345+
"Getters.scala", "InterceptedMethods.scala", "LambdaLift.scala", "LiftTry.scala", "LinkScala2Impls.scala",
346346
"MacroTransform.scala", "Memoize.scala", "Mixin.scala", "MixinOps.scala", "NonLocalReturns.scala",
347347
"NormalizeFlags.scala", "OverridingPairs.scala", "ParamForwarding.scala", "Pickler.scala", "PostTyper.scala",
348348
"ResolveSuper.scala", "RestoreScopes.scala", "SeqLiterals.scala", "Splitter.scala", "SuperAccessors.scala",

tests/run/mixins1/A_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
trait A {
2+
3+
var x = 3
4+
println("hi")
5+
val y = x * x
6+
7+
def f: Int = x + y
8+
9+
def f(z: Int): Int = f + z
10+
11+
}

tests/run/mixins1/C_2.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Intended to be compiled with either 2.11 or 2.12
2+
class C extends A {
3+
x = 4
4+
5+
override def f: Int = super.f
6+
7+
val z = x + f(x) + y
8+
}
9+
10+
object Test extends App {
11+
new C().f
12+
}

0 commit comments

Comments
 (0)