Skip to content

Commit c3e6c20

Browse files
committed
Support trait fields in the post-implclass world
``` ⚡ cat sandbox/test.scala trait T { def m = v val v = 42 } class C extends T object Test { def main(args: Array[String]): Unit = { assert(new C().m == 42) } } /code/scala on topic/nuke-impl-classes* ⚡ qscalac -Xprint:fields,constructors,erasure,mixin sandbox/test.scala && qscala Test [[syntax trees at end of fields]] // test.scala package <empty> { abstract <sub_synth> trait T extends scala.AnyRef { <accessor> <sub_synth> def T$_setter_$v_=(x$1: Int): Unit; def /*T*/$init$(): Unit = { () }; def m: Int = T.this.v; <stable> <accessor> <sub_synth> def v: Int = 42 }; <sub_synth> class C extends AnyRef with T { override <stable> <accessor> def v: Int = C.this.v; private[this] val v: Int = _; override <accessor> def T$_setter_$v_=(x$1: Int): Unit = C.this.v = x$1; def <init>(): C = { C.super.<init>(); () } }; object Test extends scala.AnyRef { def <init>(): Test.type = { Test.super.<init>(); () }; def main(args: Array[String]): Unit = scala.Predef.assert(new C().m.==(42)) } } [[syntax trees at end of erasure]] // test.scala package <empty> { abstract <sub_synth> trait T extends Object { <accessor> <defaultmethod> <sub_synth> def T$_setter_$v_=(x$1: Int): Unit; def /*T*/$init$(): Unit = { () }; <defaultmethod> def m(): Int = T.this.v(); <stable> <accessor> <defaultmethod> <sub_synth> def v(): Int = 42 }; <sub_synth> class C extends Object with T { override <stable> <accessor> def v(): Int = C.this.v; private[this] val v: Int = _; override <accessor> def T$_setter_$v_=(x$1: Int): Unit = C.this.v = x$1; def <init>(): C = { C.super.<init>(); C.super./*T*/$init$(); () } }; <sub_synth> object Test extends Object { def <init>(): Test.type = { Test.super.<init>(); () }; def main(args: Array[String]): Unit = scala.Predef.assert(new C().m().==(42)) } } [[syntax trees at end of constructors]] // test.scala package <empty> { abstract <sub_synth> trait T extends Object { <accessor> <defaultmethod> <sub_synth> def T$_setter_$v_=(x$1: Int): Unit; <defaultmethod> def m(): Int = T.this.v(); <stable> <accessor> <defaultmethod> <sub_synth> def v(): Int; def /*T*/$init$(): Unit = { T.this.T$_setter_$v_=(42); () } }; <sub_synth> class C extends Object with T { override <stable> <accessor> def v(): Int = C.this.v; private[this] val v: Int = _; override <accessor> def T$_setter_$v_=(x$1: Int): Unit = C.this.v = x$1; def <init>(): C = { C.super.<init>(); C.super./*T*/$init$(); () } }; <sub_synth> object Test extends Object { def main(args: Array[String]): Unit = scala.Predef.assert(new C().m().==(42)); def <init>(): Test.type = { Test.super.<init>(); () } } } [[syntax trees at end of mixin]] // test.scala: tree is unchanged since constructors /code/scala on topic/nuke-impl-classes* ⚡ javap -c -classpath . T C Compiled from "test.scala" public interface T { public abstract void T$_setter_$v_$eq(int); public int m(); Code: 0: aload_0 1: invokeinterface #15, 1 // InterfaceMethod v:()I 6: ireturn public abstract int v(); public void $init$(); Code: 0: aload_0 1: bipush 42 3: invokeinterface #21, 2 // InterfaceMethod T$_setter_$v_$eq:(I)V 8: return } Compiled from "test.scala" public class C implements T { public int v(); Code: 0: aload_0 1: getfield #15 // Field v:I 4: ireturn public void T$_setter_$v_$eq(int); Code: 0: aload_0 1: iload_1 2: putfield #15 // Field v:I 5: return public C(); Code: 0: aload_0 1: invokespecial #24 // Method java/lang/Object."<init>":()V 4: aload_0 5: invokespecial #27 // Method T.$init$:()V 8: return } ```
1 parent 24d24c8 commit c3e6c20

File tree

2 files changed

+16
-8
lines changed

2 files changed

+16
-8
lines changed

src/compiler/scala/tools/nsc/transform/Constructors.scala

+15-7
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
7373

7474
override def transform(tree: Tree): Tree = {
7575
tree match {
76-
case cd @ ClassDef(mods0, name0, tparams0, impl0) if !cd.symbol.isInterface && !isPrimitiveValueClass(cd.symbol) =>
76+
case cd @ ClassDef(mods0, name0, tparams0, impl0) if !isPrimitiveValueClass(cd.symbol) =>
7777
if(cd.symbol eq AnyValClass) {
7878
cd
7979
}
@@ -456,7 +456,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
456456

457457
// find and dissect primary constructor
458458
private val (primaryConstr, _primaryConstrParams, primaryConstrBody) = stats collectFirst {
459-
case dd@DefDef(_, _, _, vps :: Nil, _, rhs: Block) if dd.symbol.isPrimaryConstructor => (dd, vps map (_.symbol), rhs)
459+
case dd@DefDef(_, _, _, vps :: Nil, _, rhs: Block) if dd.symbol.isPrimaryConstructor || dd.symbol.isMixinConstructor => (dd, vps map (_.symbol), rhs)
460460
} getOrElse {
461461
abort("no constructor in template: impl = " + impl)
462462
}
@@ -545,10 +545,14 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
545545
}
546546

547547
// Create an assignment to class field `to` with rhs `from`
548-
def mkAssign(to: Symbol, from: Tree): Tree =
549-
localTyper.typedPos(to.pos) {
550-
Assign(Select(This(clazz), to), from)
551-
}
548+
def mkAssign(to: Symbol, from: Tree): Tree = {
549+
val tree = if (to.owner.isTrait) {
550+
val setter = to.setterIn(to.owner)
551+
gen.mkMethodCall(This(to.owner), setter, Nil, from :: Nil)
552+
} else Assign(Select(This(clazz), to), from)
553+
554+
localTyper.typedPos(to.pos)(tree)
555+
}
552556

553557
// Create code to copy parameter to parameter accessor field.
554558
// If parameter is $outer, check that it is not null so that we NPE
@@ -629,8 +633,12 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
629633
// Triage methods -- they all end up in the template --
630634
// regular ones go to `defBuf`, secondary contructors go to `auxConstructorBuf`.
631635
// The primary constructor is dealt with separately (we're massaging it here).
632-
case _: DefDef if statSym.isPrimaryConstructor => ()
636+
case _: DefDef if statSym.isPrimaryConstructor || statSym.isMixinConstructor => ()
633637
case _: DefDef if statSym.isConstructor => auxConstructorBuf += stat
638+
case stat: DefDef if stat.symbol.owner.isTrait && stat.symbol.hasAllFlags(STABLE | ACCESSOR) && stat.rhs != EmptyTree =>
639+
val emitField = memoizeValue(statSym)
640+
moveEffectToCtor(stat.mods, stat.rhs, if (emitField) statSym else NoSymbol) // TODO: ever NoSymbol?
641+
defBuf += deriveDefDef(stat)(_ => EmptyTree)
634642
case stat: DefDef =>
635643
defBuf += stat
636644

src/compiler/scala/tools/nsc/transform/Fields.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
433433

434434

435435
override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] =
436-
if (!currentOwner.isClass || currentOwner.isPackageClass || currentOwner.isInterface) super.transformStats(stats, exprOwner)
436+
if (!currentOwner.isClass || currentOwner.isPackageClass || currentOwner.hasFlag(INTERFACE)) super.transformStats(stats, exprOwner)
437437
else afterOwnPhase {
438438
fieldsAndAccessors(exprOwner) ++ (stats flatMap transformStat(exprOwner)) // TODO use thicket encoding of multi-tree transformStat?
439439
}

0 commit comments

Comments
 (0)