Skip to content

Commit 968fc30

Browse files
committed
Fields phase
Constructors: simplify transform loop Also encapsulate mixin ctor creation in `newMixinConstructor` Constructors: emit mixin-super constructor calls This was previously done by AddInterfaces, whose days are numbered. wip: trait fields 1 wip: drop stuff from constructors lazyGetterBody wippythewipwip constructors transforminfo wip for unit-typed getters, delay trait setters until constructors, mix in accessors and fields there as well bare bones info transform is "working" ``` trait OneConcreteVal[T] { val x = 1 // : T = ??? } trait OneOtherConcreteVal[T] { var y: T = ??? } class C extends OneConcreteVal[Int] with OneOtherConcreteVal[String] ``` /* old decls for trait trait OneOtherConcreteVal: Scope{ def y(): Object; def y_=(x$1: Object): Unit } new decls for trait trait OneOtherConcreteVal: Scope{ def y(): Object; def y_=(x$1: Object): Unit; def $init$(): Unit } old decls for trait trait OneConcreteVal: Scope{ val x(): Int } new decls for trait trait OneConcreteVal: Scope{ val x(): Int; def $init$(): Unit; private[this] def x_=(x$1: Int): Unit } old decls for class C: Scope{ def <init>(): C } mixing in List(method y, method y_=, value x, method x_=) from List(trait OneOtherConcreteVal, trait OneConcreteVal) new decls for class C: Scope{ def <init>(): C; def y(): Object; private[this] var y: Object; def y_=(x$1: Object): Unit; val x(): Int; private[this] val x: Int; private[this] def x_=(x$1: Int): Unit } */ getting there: need to avoid MIXEDIN/lateDEFERRED so as not to confuse mixins? temporarily using ARTIFACT instead of MIXEDIN to not confuse mixin kinda working... impl classes get accessors until mixin, so that constructors acts on them there, and produced the init method in the required spot (the impl class) the info-transformed of constructors: - traits receive trait-setters for vals - classes receive fields & accessors for mixed in traits Constructors tree transformer - makes trees for decls added during above info transform (TODO: decide on the right flag to communicate this) - adds mixin super calls to the primary constructor Later, mixins will drop all accessors in the impl class, retaining only the init method. Selects of setters/getters (in that init method) are rewritten to refer to the getters/setters in the interface (TODO) TODO (among many other things) - no constructor in trait - trait setter for val isn't found when rewriting accessors in impl classes - annotations - privacy ``` trait OneConcreteVal[T] { var x = 1 // : T = ??? def foo = x } trait OneOtherConcreteVal[T] { var y: T = ??? } class C extends OneConcreteVal[Int] with OneOtherConcreteVal[String] ``` ``` [[syntax trees at end of constructors]] // fields.scala package <empty> { abstract trait OneConcreteVal extends Object { <empty>; // <-- TODO <accessor> def x(): Int; @scala.runtime.TraitSetter <accessor> def x_=(x$1: Int): Unit; def foo(): Int }; abstract trait OneOtherConcreteVal extends Object { <empty>; // <-- TODO <accessor> def y(): Object; @scala.runtime.TraitSetter <accessor> def y_=(x$1: Object): Unit }; class C extends Object with OneConcreteVal with OneOtherConcreteVal { def <init>(): C = { C.super.<init>(); C.this./*OneConcreteVal$class*/$init$(); C.this./*OneOtherConcreteVal$class*/$init$(); () }; <accessor> <artifact> def y(): Object = C.this.y; <artifact> private[this] var y: Object = _; @scala.runtime.TraitSetter <accessor> <artifact> def y_=(x$1: Object): Unit = C.this.y = x$1; <accessor> <artifact> def x(): Int = C.this.x; <artifact> private[this] var x: Int = _; @scala.runtime.TraitSetter <accessor> <artifact> def x_=(x$1: Int): Unit = C.this.x = x$1 }; abstract trait OneConcreteVal$class extends Object with OneConcreteVal { def /*OneConcreteVal$class*/$init$(): Unit = { OneConcreteVal$class.this.x_=(1); () }; <accessor> def x(): Int; @scala.runtime.TraitSetter <accessor> def x_=(x$1: Int): Unit; def foo(): Int = OneConcreteVal$class.this.x() }; abstract trait OneOtherConcreteVal$class extends Object with OneOtherConcreteVal { def /*OneOtherConcreteVal$class*/$init$(): Unit = { OneOtherConcreteVal$class.this.y_=(scala.this.Predef.???()); () }; <accessor> def y(): Object; @scala.runtime.TraitSetter <accessor> def y_=(x$1: Object): Unit } } ``` ``` [[syntax trees at end of mixin]] // fields.scala package <empty> { abstract trait OneConcreteVal extends Object { <accessor> def x(): Int; @scala.runtime.TraitSetter <accessor> def x_=(x$1: Int): Unit; def foo(): Int }; abstract trait OneOtherConcreteVal extends Object { <accessor> def y(): Object; @scala.runtime.TraitSetter <accessor> def y_=(x$1: Object): Unit }; class C extends Object with OneConcreteVal with OneOtherConcreteVal { def foo(): Int = OneConcreteVal$class.foo(C.this); def <init>(): C = { C.super.<init>(); OneConcreteVal$class./*OneConcreteVal$class*/$init$(C.this); OneOtherConcreteVal$class./*OneOtherConcreteVal$class*/$init$(C.this); () }; <accessor> <artifact> def y(): Object = C.this.y; <artifact> private[this] var y: Object = _; @scala.runtime.TraitSetter <accessor> <artifact> def y_=(x$1: Object): Unit = C.this.y = x$1; <accessor> <artifact> def x(): Int = C.this.x; <artifact> private[this] var x: Int = _; @scala.runtime.TraitSetter <accessor> <artifact> def x_=(x$1: Int): Unit = C.this.x = x$1 }; abstract trait OneConcreteVal$class extends { def /*OneConcreteVal$class*/$init$($this: OneConcreteVal): Unit = { $this.x_=(1); () }; def foo($this: OneConcreteVal): Int = $this.x() }; abstract trait OneOtherConcreteVal$class extends { def /*OneOtherConcreteVal$class*/$init$($this: OneOtherConcreteVal): Unit = { $this.y_=(scala.this.Predef.???()); () } } } ``` emit trait setters, target interface for setter don't emit trait constructors yet, don't mess with mixin class ctors hack to explore where to keep annotations on trait fields we don't have a field -- only a getter -- so, where will we keep non-getter but-field annotations? restore mixins reduce OOification re: annotation derivation - replace virtual dispatch on sealed traits by matches - rename `keepClean` to `defaultRetention` - allow multiple categories in annotationFilter (for trait fields) mixin only deals with lazy accessors/vals do not used referenced to correlate getter/setter it messes with paramaccessor's usage, and we don't really need it annotations typo poking around overriding and unit-typed vars detect unit correctly setter should be considered deferred, just like getter and val fixup constructor triaging introduce constructor's very own flag stop using referenced entirely refactor add Fields phase meh mehmeh meeeeeh Fields phase is kind of working. Yay. emit synthetic setter for trait val make sure to clone info's, so we don't share symbols for method args this manifests itself as an exception in lambdalift, finding proxies ``` trait OneAbstractVar[T] { val x: Int = 123 } class ConcVar extends OneAbstractVar[Int] ``` compiles to the following (as desired!): ``` ➜ scala git:(traits-late-fields) ✗ cfr /tmp/ConcVar.class /* * Decompiled with CFR 0_102. * * Could not load the following classes: * scala.reflect.ScalaSignature */ import OneAbstractVar; import OneAbstractVar$class; import scala.reflect.ScalaSignature; @ScalaSignature(bytes="\u0006\u0001\u00112A!\u0001\u0002\u0001\u000b\t91i\u001c8d-\u0006\u0014(\"A\u0002\u0002\u000fq*W\u000e\u001d;z}\r\u00011c\u0001\u0001\u0007\u0019A\u0011qAC\u0007\u0002\u0011)\t\u0011\"A\u0003tG\u0006d\u0017-\u0003\u0002\f\u0011\t1\u0011I\\=SK\u001a\u00042!\u0004\b\u0011\u001b\u0005\u0011\u0011BA\b\u0003\u00059ye.Z!cgR\u0014\u0018m\u0019;WCJ\u0004\"aB\t\n\u0005IA!aA%oi\")A\u0003\u0001C\u0001+\u00051A(\u001b8jiz\"\u0012A\u0006\t\u0003\u001b\u0001A\u0011\u0002\u0007\u0001A\u0002\u000b\u0007I\u0011A\r\u0002\u0003a,\u0012\u0001\u0005\u0005\n7\u0001\u0001\r\u0011!Q\u0001\nA\t!\u0001\u001f\u0011\t\u0013u\u0001\u0001\u0019aa\u0001J\u0003q\u0012!H(oK\u0006\u00137\u000f\u001e:bGR4\u0016M\u001d\u0013`g\u0016$H/\u001a:`Ia|F%Z9\u0015\u0005}\u0011\u0003CA\u0004!\u0013\t\t\u0003B\u0001\u0003V]&$\bbB\u0012\u001d\u0003\u0003\u0005\r\u0001E\u0001\u0004q\u0012\n\u0004") public class ConcVar implements OneAbstractVar<Object> { private final int x; public ConcVar() { OneAbstractVar$class.$init$(this); } @OverRide public int x() { return this.x; } @OverRide public void OneAbstractVar$_setter_$x_$eq(int x$1) { this.x = x$1; } } ➜ scala git:(traits-late-fields) ✗ cfr /tmp/OneAbstractVar\$class.class /* * Decompiled with CFR 0_102. */ import OneAbstractVar; public abstract class OneAbstractVar$class { public static void $init$(OneAbstractVar $this) { $this.OneAbstractVar$_setter_$x_$eq(123); } } ➜ scala git:(traits-late-fields) ✗ cfr /tmp/OneAbstractVar.class /* * Decompiled with CFR 0_102. * * Could not load the following classes: * scala.reflect.ScalaSignature */ import scala.reflect.ScalaSignature; @ScalaSignature(bytes="\u0006\u0001\u001d2q!\u0001\u0002\u0011\u0002\u0007\u0005QA\u0001\bP]\u0016\f%m\u001d;sC\u000e$h+\u0019:\u000b\u0003\r\tq\u0001P3naRLhh\u0001\u0001\u0016\u0005\u0019q2C\u0001\u0001\b!\tA1\"D\u0001\n\u0015\u0005Q\u0011!B:dC2\f\u0017B\u0001\u0007\n\u0005\u0019\te.\u001f*fM\")a\u0002\u0001C\u0001\u001f\u00051A%\u001b8ji\u0012\"\u0012\u0001\u0005\t\u0003\u0011EI!AE\u0005\u0003\tUs\u0017\u000e\u001e\u0005\b)\u0001\u0011\rQ\"\u0001\u0016\u0003\u0005AX#\u0001\f\u0011\u0005!9\u0012B\u0001\r\n\u0005\rIe\u000e\u001e\u0005\n5\u0001\u0001\r11A'\u0002m\tQd\u00148f\u0003\n\u001cHO]1diZ\u000b'\u000fJ0tKR$XM]0%q~#S-\u001d\u000b\u0003!qAq!H\r\u0002\u0002\u0003\u0007a#A\u0002yIE\"Qa\b\u0001C\u0002\u0001\u0012\u0011\u0001V\t\u0003C\u0011\u0002\"\u0001\u0003\u0012\n\u0005\rJ!a\u0002(pi\"Lgn\u001a\t\u0003\u0011\u0015J!AJ\u0005\u0003\u0007\u0005s\u0017\u0010") public interface OneAbstractVar<T> { public void OneAbstractVar$_setter_$x_$eq(int var1); public int x(); } ``` cleanup TODO: overriding accessors don't mix in accessors if conflicting member exists getting closer to bootstrap exclude lazy vals, don't makeNotPrivate lazy accessors and presupers require old treatment constructors should leave strict valdefs alone restore constructors to minimal diff compared to scala#4723 revert more gratuitous stuff revert more in mixins trying to get to bootstrap.. currently failing in scala.util.Properties reduction of bootstrap failures on a bed of TODOs, with a hint of minimalism use primary constructor as owner for stats in template? fix pattern match in infotransform, remove mutation deferred setter does need impl in subclass remove SYNTHESIZE_IMPL_IN_SUBCLASS Use NEEDS_TREES for all comms between InfoTransform and tree transform We can now compile the library again, though the bytecode diff is not empty yet :-) TODO: - Weirdly, scala signatures are not emitted? - Fields are no longer emitted for constants (an improvement, but should not do this until next milestone) - the volatile annotation is not preserved - App, Enumeration compiled differently - Owner chain changes due to lifting of rhs affects: --> anonymous function in Iterator, PartialFunction, scala/collection/immutable/HashMap - traitsetter name mangling differences - abortflag accessors duplicated in scala/collection/generic/VolatileAbort.class - ... yep, need SYNTHESIZE_IMPL_IN_SUBCLASS distinguish accessors that should be mixed into subclass, and those that simply need to be implemented in tree transform, after info transform added the decl emit const vals for now to minimize bytecode diff TODO: still some bytecode diffs with constants remain (Enumeration) correct owner for stats in template closing in on boostrap strap.done: [mkdir] Created dir: /Users/adriaan/git/scala/build/strap/classes/library [strap.library] Compiling 584 files to /Users/adriaan/git/scala/build/strap/classes/library [strap.library] /Users/adriaan/git/scala/src/library/scala/collection/convert/WrapAsScala.scala:173: warning: abstract type A in type pattern scala.collection.convert.Wrappers.ConcurrentMapWrapper[A,B] is unchecked since it is eliminated by erasure [strap.library] case cmw: ConcurrentMapWrapper[A, B] => cmw.underlying [strap.library] ^ [strap.library] warning: there were 111 deprecation warnings; re-run with -deprecation for details [strap.library] two warnings found [javac] Compiling 165 source files to /Users/adriaan/git/scala/build/strap/classes/library [javac] Note: Some input files use unchecked or unsafe operations. [javac] Note: Recompile with -Xlint:unchecked for details. [propertyfile] Creating new property file: /Users/adriaan/git/scala/build/strap/classes/library/library.properties [copy] Copying 1 file to /Users/adriaan/git/scala/build/strap/classes/library [stopwatch] [strap.library.timer: 1:31.668 sec] [mkdir] Created dir: /Users/adriaan/git/scala/build/strap/classes/reflect [strap.reflect] Compiling 157 files to /Users/adriaan/git/scala/build/strap/classes/reflect [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/JavaUniverse.scala:16: error: overriding variable uniques in class SymbolTable of type scala.reflect.internal.util.WeakHashSet[JavaUniverse.this.Type]; [strap.reflect] value uniques needs `override' modifier [strap.reflect] class JavaUniverse extends InternalSymbolTable with JavaUniverseForce with ReflectSetup with RuntimeSymbolTable { self => [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedOps.scala:6: error: overriding variable uniques in trait SynchronizedTypes of type scala.collection.mutable.WeakHashMap[SynchronizedOps.this.Type,java.lang.ref.WeakReference[SynchronizedOps.this.Type]]; [strap.reflect] variable uniques in class SymbolTable of type scala.reflect.internal.util.WeakHashSet[SynchronizedOps.this.Type] needs to be a stable, immutable value [strap.reflect] private[reflect] trait SynchronizedOps extends internal.SymbolTable [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala:16: error: overriding variable _recursionTable in trait Symbols of type scala.collection.immutable.Map[SynchronizedSymbols.this.Symbol,Int]; [strap.reflect] lazy value _recursionTable has weaker access privileges; it should not be private [strap.reflect] private lazy val _recursionTable = mkThreadLocalStorage(immutable.Map.empty[Symbol, Int]) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:43: error: overriding variable _skolemizationLevel in trait Types of type Int; [strap.reflect] lazy value _skolemizationLevel has weaker access privileges; it should not be private [strap.reflect] private lazy val _skolemizationLevel = mkThreadLocalStorage(0) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:50: error: overriding variable _intersectionWitness in trait Types of type scala.collection.mutable.WeakHashMap[List[SynchronizedTypes.this.Type],scala.ref.WeakReference[SynchronizedTypes.this.Type]]; [strap.reflect] lazy value _intersectionWitness has weaker access privileges; it should not be private [strap.reflect] private lazy val _intersectionWitness = mkThreadLocalStorage(perRunCaches.newWeakMap[List[Type], sWeakRef[Type]]()) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:53: error: overriding variable _subsametypeRecursions in trait TypeComparers of type Int; [strap.reflect] lazy value _subsametypeRecursions has weaker access privileges; it should not be private [strap.reflect] private lazy val _subsametypeRecursions = mkThreadLocalStorage(0) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:57: error: overriding variable _pendingSubTypes in trait TypeComparers of type scala.collection.mutable.HashSet[SynchronizedTypes.this.SubTypePair]; [strap.reflect] lazy value _pendingSubTypes has weaker access privileges; it should not be private [strap.reflect] private lazy val _pendingSubTypes = mkThreadLocalStorage(new mutable.HashSet[SubTypePair]) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:60: error: overriding variable _basetypeRecursions in trait Types of type Int; [strap.reflect] lazy value _basetypeRecursions has weaker access privileges; it should not be private [strap.reflect] private lazy val _basetypeRecursions = mkThreadLocalStorage(0) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:64: error: overriding variable _pendingBaseTypes in trait Types of type scala.collection.mutable.HashSet[SynchronizedTypes.this.Type]; [strap.reflect] lazy value _pendingBaseTypes has weaker access privileges; it should not be private [strap.reflect] private lazy val _pendingBaseTypes = mkThreadLocalStorage(new mutable.HashSet[Type]) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:67: error: overriding variable _lubResults in trait GlbLubs of type scala.collection.mutable.HashMap[(scala.reflect.internal.Depth, List[SynchronizedTypes.this.Type]),SynchronizedTypes.this.Type]; [strap.reflect] lazy value _lubResults has weaker access privileges; it should not be private [strap.reflect] private lazy val _lubResults = mkThreadLocalStorage(new mutable.HashMap[(Depth, List[Type]), Type]) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:70: error: overriding variable _glbResults in trait GlbLubs of type scala.collection.mutable.HashMap[(scala.reflect.internal.Depth, List[SynchronizedTypes.this.Type]),SynchronizedTypes.this.Type]; [strap.reflect] lazy value _glbResults has weaker access privileges; it should not be private [strap.reflect] private lazy val _glbResults = mkThreadLocalStorage(new mutable.HashMap[(Depth, List[Type]), Type]) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:73: error: overriding variable _indent in trait Types of type String; [strap.reflect] lazy value _indent has weaker access privileges; it should not be private [strap.reflect] private lazy val _indent = mkThreadLocalStorage("") [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:77: error: overriding variable _toStringRecursions in trait TypeToStrings of type Int; [strap.reflect] lazy value _toStringRecursions has weaker access privileges; it should not be private [strap.reflect] private lazy val _toStringRecursions = mkThreadLocalStorage(0) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:81: error: overriding variable _toStringSubjects in trait TypeToStrings of type scala.collection.mutable.HashSet[SynchronizedTypes.this.Type]; [strap.reflect] lazy value _toStringSubjects has weaker access privileges; it should not be private [strap.reflect] private lazy val _toStringSubjects = mkThreadLocalStorage(new mutable.HashSet[Type]) [strap.reflect] ^ [strap.reflect] /Users/adriaan/git/scala/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala:21: error: overriding variable uniques in trait Types of type scala.reflect.internal.util.WeakHashSet[SynchronizedTypes.this.Type]; [strap.reflect] variable uniques has incompatible type [strap.reflect] private val uniques = mutable.WeakHashMap[Type, jWeakRef[Type]]() [strap.reflect] ^ [strap.reflect] 15 errors found make private vals in traits not-private TODO: refine only look at strict accessors don't widen type for getter -- wtf current TODO: overriding vals mor compact accessor synth major cleanup compiles library & reflect, but not compiler: [strap.compiler] /Users/adriaan/git/scala/src/compiler/scala/tools/nsc/CompileServer.scala:40: error: object creation impossible, since: [strap.compiler] it has 151 unimplemented members. last error flag pickling issue? info mangling, traitsetter remove hacks from other files trait fields stuff
1 parent 537d2ea commit 968fc30

15 files changed

+732
-31
lines changed

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
409409
private val isRefChecked = prev.name == "refchecks" || prev.refChecked
410410
override def refChecked: Boolean = isRefChecked
411411

412+
private val isAssigningFields = name == "fields" || prev.assignsFields
413+
override def assignsFields: Boolean = isAssigningFields // allow assigning to val
414+
412415
/** Is current phase cancelled on this unit? */
413416
def cancelled(unit: CompilationUnit) = {
414417
// run the typer only if in `createJavadoc` mode
@@ -486,10 +489,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
486489
val runsRightAfter = None
487490
} with ExtensionMethods
488491

492+
// phaseName = "fields"
493+
object fields extends {
494+
val global: Global.this.type = Global.this
495+
val runsAfter = List("extmethods") // not sure where to put it yet, but should be before erasure
496+
val runsRightAfter = None
497+
} with Fields
498+
489499
// phaseName = "pickler"
490500
object pickler extends {
491501
val global: Global.this.type = Global.this
492-
val runsAfter = List("extmethods")
502+
val runsAfter = List("fields") // should probably pickle the info for synthetic accessors
493503
val runsRightAfter = None
494504
} with Pickler
495505

@@ -689,7 +699,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
689699
* This implementation creates a description map at the same time.
690700
*/
691701
protected def computeInternalPhases(): Unit = {
692-
// Note: this fits -Xshow-phases into 80 column width, which it is
702+
// Note: this fits -Xshow-phases into 80 column width, which is
693703
// desirable to preserve.
694704
val phs = List(
695705
syntaxAnalyzer -> "parse source into ASTs, perform simple desugaring",
@@ -699,6 +709,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
699709
patmat -> "translate match expressions",
700710
superAccessors -> "add super accessors in traits and nested classes",
701711
extensionMethods -> "add extension methods for inline classes",
712+
fields -> "synthesize accessors and fields",
702713
pickler -> "serialize symbol tables",
703714
refChecks -> "reference/override checking, translate nested objects",
704715
uncurry -> "uncurry, translate function values to anonymous classes",

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
5858
def needsImplMethod(sym: Symbol) = (
5959
sym.isMethod
6060
&& isInterfaceMember(sym)
61+
&& (!sym.isAccessor || sym.isLazy) // We no longer do the implclass-dance for accessors -- they are mixed in directly into their implementing classes during the fields phase
62+
// so that constructors populates the init method in the impl class
6163
&& (!sym.hasFlag(DEFERRED | SUPERACCESSOR) || (sym hasFlag lateDEFERRED))
6264
)
6365

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
135135
* and thus may only be accessed from value or method definitions owned by the current class
136136
* (ie there's no point drilling down into nested classes).
137137
*
138-
* (d) regarding candidates in (b), they are accessible from all places listed in (c) and in addition
138+
* (d) regarding candidates in (b), they are accesible from all places listed in (c) and in addition
139139
* from nested classes (nested at any number of levels).
140140
*
141141
* In all cases, we're done with traversing as soon as all candidates have been ruled out.
@@ -640,10 +640,11 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
640640
// - the constructor, before the super call (early initialized or a parameter accessor),
641641
// - the constructor, after the super call (regular val).
642642
case ValDef(mods, _, _, rhs) =>
643-
val emitField = memoizeValue(statSym)
644-
moveEffectToCtor(mods, rhs, emitField)
645-
646-
if (emitField) defBuf += deriveValDef(stat)(_ => EmptyTree)
643+
if (statSym.isLazy) {
644+
val emitField = memoizeValue(statSym)
645+
moveEffectToCtor(mods, rhs, emitField)
646+
if (emitField) defBuf += deriveValDef(stat)(_ => EmptyTree)
647+
} else defBuf += stat
647648

648649
// all other statements go into the constructor
649650
case _ => constrStatBuf += intoConstructor(impl.symbol, primaryConstr.symbol)(stat)

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

Lines changed: 387 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -288,18 +288,16 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
288288
def mixinTraitMembers(mixinClass: Symbol) {
289289
// For all members of a trait's interface do:
290290
for (mixinMember <- mixinClass.info.decls) {
291-
if (isConcreteAccessor(mixinMember)) {
291+
if (isConcreteAccessor(mixinMember) && mixinMember.isLazy) {
292292
if (isOverriddenAccessor(mixinMember, clazz.info.baseClasses))
293293
devWarning(s"Overridden concrete accessor: ${mixinMember.fullLocationString}")
294294
else {
295295
// mixin field accessors
296296
val mixedInAccessor = cloneAndAddMixinMember(mixinClass, mixinMember)
297-
if (mixinMember.isLazy) {
298-
initializer(mixedInAccessor) = (
299-
implClass(mixinClass).info.decl(mixinMember.name)
300-
orElse abort("Could not find initializer for " + mixinMember.name)
301-
)
302-
}
297+
initializer(mixedInAccessor) = (
298+
implClass(mixinClass).info.decl(mixinMember.name)
299+
orElse abort("Could not find initializer for " + mixinMember.name)
300+
)
303301
if (!mixinMember.isSetter)
304302
mixinMember.tpe match {
305303
case MethodType(Nil, ConstantType(_)) =>
@@ -539,9 +537,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
539537
else EmptyTree
540538
}
541539
else {
542-
if (currentOwner.isTrait && sym.isSetter && !enteringPickler(sym.isDeferred)) {
543-
sym.addAnnotation(TraitSetterAnnotationClass)
544-
}
545540
tree
546541
}
547542
// !!! What is this doing, and why is it only looking for exactly
@@ -1068,7 +1063,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
10681063
}
10691064
// if class is not a trait add accessor definitions
10701065
else if (!clazz.isTrait) {
1071-
if (isConcreteAccessor(sym)) {
1066+
if (isConcreteAccessor(sym) && sym.isLazy) {
10721067
// add accessor definitions
10731068
addDefDef(sym, {
10741069
if (sym.isSetter) {
@@ -1095,7 +1090,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
10951090

10961091
addDefDef(sym, rhs1)
10971092
}
1098-
else if (!sym.isMethod) {
1093+
else if (!sym.isMethod && sym.isLazy) {
10991094
// add fields
11001095
addValDef(sym)
11011096
}

src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,10 @@ trait MethodSynthesis {
134134
val getterSym = getter.createAndEnterSymbol()
135135

136136
// Create the setter if necessary.
137-
if (getter.needsSetter)
138-
Setter(tree).createAndEnterSymbol()
137+
if (getter.needsSetter) Setter(tree).createAndEnterSymbol()
139138

140-
// If the getter's abstract the tree gets the getter's symbol,
141-
// otherwise, create a field (assume the getter requires storage).
139+
// If the getter's abstract, the tree gets the getter's symbol,
140+
// otherwise, create a field (we have to assume the getter requires storage for now).
142141
// NOTE: we cannot look at symbol info, since we're in the process of deriving them
143142
// (luckily, they only matter for lazy vals, which we've ruled out in this else branch,
144143
// and `doNotDeriveField` will skip them if `!mods.isLazy`)
@@ -314,7 +313,7 @@ trait MethodSynthesis {
314313
// Presently one of: field, getter, setter, beanGetter, beanSetter, param.
315314
// For traits, getter must play role of field as there isn't one until Constructors (we'll triage there)
316315
val categories = this match {
317-
case _: BaseGetter => List(GetterTargetClass)
316+
case _: BaseGetter => if (owner.isTrait) List(FieldTargetClass, GetterTargetClass) else List(GetterTargetClass)
318317
case _: Setter => List(SetterTargetClass)
319318
case _: BeanSetter => List(BeanSetterTargetClass)
320319
case _: AnyBeanGetter => List(BeanGetterTargetClass)
@@ -328,18 +327,22 @@ trait MethodSynthesis {
328327
// By default annotations go to the field, except if the field is
329328
// generated for a class parameter (PARAMACCESSOR).
330329
case _: Field => !mods.isParamAccessor
330+
case _: BaseGetter if owner.isTrait => true // like, Field, but cannot be a param accessor in a trait
331331
case _ => false
332332
}
333333

334334
// The annotations amongst those found on the original symbol which
335335
// should be propagated to this kind of accessor.
336336
val sym = derivedSym setAnnotations (initial filter annotationFilter(categories, defaultRetention))
337337

338+
if (sym.isSetter && owner.isTrait && !isDeferred)
339+
sym addAnnotation TraitSetterAnnotationClass
340+
338341
logDerived(derivedTree)
339342
}
343+
340344
}
341345
sealed trait DerivedGetter extends DerivedFromValDef {
342-
// A getter must be accompanied by a setter if the ValDef is mutable.
343346
def needsSetter = mods.isMutable
344347
}
345348
sealed trait DerivedSetter extends DerivedFromValDef {
@@ -396,6 +399,7 @@ trait MethodSynthesis {
396399
override def derivedSym = if (Field.noFieldFor(tree)) basisSym else basisSym.getterIn(enclClass)
397400
private def derivedRhs = if (Field.noFieldFor(tree)) tree.rhs else fieldSelection
398401

402+
// TODO: more principled approach -- this is a bit bizarre
399403
private def derivedTpt = {
400404
// For existentials, don't specify a type for the getter, even one derived
401405
// from the symbol! This leads to incompatible existentials for the field and
@@ -470,7 +474,9 @@ trait MethodSynthesis {
470474
// No field for these vals (either never emitted or eliminated later on):
471475
// - abstract vals have no value we could store (until they become concrete, potentially)
472476
// - lazy vals of type Unit
473-
// - [Emitted, later removed during AddInterfaces/Mixins] concrete vals in traits can't have a field
477+
// - concrete vals in traits don't yield a field here either (their getter's RHS has the initial value)
478+
// Constructors will move the assignment to the constructor, abstracting over the field using the field setter,
479+
// and Mixin will add a field to the class that mixes in the trait, implementing the accessors in terms of it
474480
// - [Emitted, later removed during Constructors] a concrete val with a statically known value (Unit / ConstantType)
475481
// performs its side effect according to lazy/strict semantics, but doesn't need to store its value
476482
// each access will "evaluate" the RHS (a literal) again

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4221,7 +4221,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
42214221
// if (varsym.isVariable ||
42224222
// // setter-rewrite has been done above, so rule out methods here, but, wait a minute, why are we assigning to non-variables after erasure?!
42234223
// (phase.erasedTypes && varsym.isValue && !varsym.isMethod)) {
4224-
if (varsym.isVariable || varsym.isValue && phase.erasedTypes) {
4224+
if (varsym.isVariable || varsym.isValue && phase.assignsFields) {
42254225
val rhs1 = typedByValueExpr(rhs, lhs1.tpe)
42264226
treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitTpe
42274227
}

src/reflect/scala/reflect/internal/Flags.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ class Flags extends ModifierFlags {
172172

173173
final val SYNCHRONIZED = 1L << 45 // symbol is a method which should be marked ACC_SYNCHRONIZED
174174

175+
final val SYNTHESIZE_IMPL_IN_SUBCLASS = 1L << 50 // Like MIXEDIN, but used in Fields
176+
final val NEEDS_TREES = 1L << 59 // Communicate from Fields' info transform to its tree transform -- this symbol needs a tree. (distinct from SYNTHESIZE_IMPL_IN_SUBCLASS)
177+
final val OVERRIDDEN_TRAIT_SETTER = 1L << 60 // Communicate from Fields' info transform to its tree transform -- this setter gets a unit body.
178+
175179
// ------- shift definitions -------------------------------------------------------
176180
//
177181
// Flags from 1L to (1L << 50) are normal flags.
@@ -269,7 +273,8 @@ class Flags extends ModifierFlags {
269273
/** These modifiers appear in TreePrinter output. */
270274
final val PrintableFlags =
271275
ExplicitFlags | BridgeFlags | LOCAL | SYNTHETIC | STABLE | CASEACCESSOR | MACRO |
272-
ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED | ARTIFACT
276+
ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED | ARTIFACT |
277+
SYNTHESIZE_IMPL_IN_SUBCLASS | NEEDS_TREES | OVERRIDDEN_TRAIT_SETTER
273278

274279
/** When a symbol for a field is created, only these flags survive
275280
* from Modifiers. Others which may be applied at creation time are:
@@ -454,7 +459,7 @@ class Flags extends ModifierFlags {
454459
case JAVA_DEFAULTMETHOD => "<defaultmethod>" // (1L << 47)
455460
case JAVA_ENUM => "<enum>" // (1L << 48)
456461
case JAVA_ANNOTATION => "<annotation>" // (1L << 49)
457-
case 0x4000000000000L => "" // (1L << 50)
462+
case NEEDS_TREES => "<needs_trees>" // (1L << 50)
458463
case `lateDEFERRED` => "<latedeferred>" // (1L << 51)
459464
case `lateFINAL` => "<latefinal>" // (1L << 52)
460465
case `lateMETHOD` => "<latemethod>" // (1L << 53)
@@ -463,8 +468,8 @@ class Flags extends ModifierFlags {
463468
case `notPROTECTED` => "<notprotected>" // (1L << 56)
464469
case `notOVERRIDE` => "<notoverride>" // (1L << 57)
465470
case `notPRIVATE` => "<notprivate>" // (1L << 58)
466-
case 0x800000000000000L => "" // (1L << 59)
467-
case 0x1000000000000000L => "" // (1L << 60)
471+
case SYNTHESIZE_IMPL_IN_SUBCLASS => "<sub_synth>" // (1L << 59)
472+
case OVERRIDDEN_TRAIT_SETTER => "<overridden_trait_setter>" // (1L << 60)
468473
case 0x2000000000000000L => "" // (1L << 61)
469474
case 0x4000000000000000L => "" // (1L << 62)
470475
case 0x8000000000000000L => "" // (1L << 63)

src/reflect/scala/reflect/internal/Phase.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ abstract class Phase(val prev: Phase) {
4141
def checkable: Boolean = true
4242
def specialized: Boolean = false
4343
def erasedTypes: Boolean = false
44+
def assignsFields: Boolean = false // allow assigning to val
4445
def flatClasses: Boolean = false
4546
def refChecked: Boolean = false
4647

src/reflect/scala/reflect/internal/ReificationSupport.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ trait ReificationSupport { self: SymbolTable =>
285285
val (gvdefs, etdefs) = rawEdefs.partition(treeInfo.isEarlyValDef)
286286
val (fieldDefs, UnCtor(ctorMods, ctorVparamss, lvdefs) :: body) = rest.splitAt(indexOfCtor(rest))
287287
val evdefs = gvdefs.zip(lvdefs).map {
288+
// TODO: in traits, early val defs are defdefs
288289
case (gvdef @ ValDef(_, _, tpt: TypeTree, _), ValDef(_, _, _, rhs)) =>
289290
copyValDef(gvdef)(tpt = tpt.original, rhs = rhs)
290291
}

0 commit comments

Comments
 (0)