Skip to content

Commit b4264ff

Browse files
adriaanmretronym
authored andcommitted
fields phase
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 - adds mixin super calls to the primary constructor ``` trait OneConcreteVal[T] { var x = 1 // : T = ??? def foo = x } trait OneOtherConcreteVal[T] { var y: T = ??? } class C extends OneConcreteVal[Int] with OneOtherConcreteVal[String] ``` we don't have a field -- only a getter -- so, where will we keep non-getter but-field annotations? 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 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 Use NEEDS_TREES for all comms between InfoTransform and tree transform 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 commit 4b4932e Author: Adriaan Moors <[email protected]> Date: 6 days ago do assignment to trait fields in AddInterfaces regular class vals get assignments during constructors, as before impl classes get accessors + assignments through trait setters in addinterfaces so that constructors acts on them there, and produced the init method in the required spot (the impl class) bootstrapped compiler needs new partest commit baf568d Author: Adriaan Moors <[email protected]> Date: 3 weeks ago produce identical bytecode for constant trait val getters I couldn't bring myself to emit the unused fields that we used to emit for constant vals, even though the getters immediately return the constant, and thus the field goes unused. In the next version, there's no need to synthesize impls for these in subclasses -- the getter can be implemented in the interface. commit b9052da Author: Lukas Rytz <[email protected]> Date: 3 weeks ago Fix enclosing method attribute for classes nested in trait fields Trait fields are now created as MethodSymbol (no longer TermSymbol). This symbol shows up in the `originalOwner` chain of a class declared within the field initializer. This promoted the field getter to being the enclosing method of the nested class, which it is not (the EnclosingMethod attribute is a source-level property). commit cf845ab Author: Adriaan Moors <[email protected]> Date: 3 weeks ago don't suppress field for unit-typed vals it affects the memory model -- even a write of unit to a field is relevant... commit 337a9dd Author: Adriaan Moors <[email protected]> Date: 4 weeks ago unit-typed lazy vals should never receive a field this need was unmasked by test/files/run/t7843-jsr223-service.scala, which no longer printed the output expected from the `0 to 10 foreach` Currently failing tests: - test/files/pos/t6780.scala - test/files/neg/anytrait.scala - test/files/neg/delayed-init-ref.scala - test/files/neg/t562.scala - test/files/neg/t6276.scala - test/files/run/delambdafy_uncurry_byname_inline.scala - test/files/run/delambdafy_uncurry_byname_method.scala - test/files/run/delambdafy_uncurry_inline.scala - test/files/run/delambdafy_uncurry_method.scala - test/files/run/inner-obj-auto.scala - test/files/run/lazy-traits.scala - test/files/run/reify_lazyunit.scala - test/files/run/showraw_mods.scala - test/files/run/t3670.scala - test/files/run/t3980.scala - test/files/run/t4047.scala - test/files/run/t6622.scala - test/files/run/t7406.scala - test/files/run/t7843-jsr223-service.scala - test/files/jvm/innerClassAttribute - test/files/specialized/SI-7343.scala - test/files/specialized/constant_lambda.scala - test/files/specialized/spec-early.scala - test/files/specialized/spec-init.scala - test/files/specialized/spec-matrix-new.scala - test/files/specialized/spec-matrix-old.scala - test/files/presentation/scope-completion-3 commit b1b4e5c Author: Adriaan Moors <[email protected]> Date: 4 weeks ago wip: lambdalift fix test/files/trait-defaults/lambdalift.scala works, but still some related tests failing (note that we can't use a bootstrapped compiler yet due to binary incompatibility in partest) commit eae7dac Author: Adriaan Moors <[email protected]> Date: 4 weeks ago update check now that trait vals can be concrete, use names not letters note the progression in a concrete trait val now being recognized as such ``` -trait T => true -method $init$ => false -value z1 => true -value z2 => true // z2 is actually concrete! ``` commit 6555c74 Author: Adriaan Moors <[email protected]> Date: 4 weeks ago bootstraps again by running info transform once per class... not sure how this ever worked, as separate compilation would transform a trait's info multiple times, resulting in double defs... commit 273cb20 Author: Adriaan Moors <[email protected]> Date: 6 weeks ago skip presuper vals in new encoding commit 728e71e Author: Adriaan Moors <[email protected]> Date: 6 weeks ago incoherent cyclic references between synthesized members must replace old trait accessor symbols by mixed in symbols in the infos of the mixed in symbols ``` trait T { val a: String ; val b: a.type } class C extends T { // a, b synthesized, but the a in b's type, a.type, refers to the original symbol, not the clone in C } ``` symbols occurring in types of synthesized members do not get rebound to other synthesized symbols package <empty>#4 { abstract <defaultparam/trait> trait N#7352 extends scala#22.AnyRef#2378 { <method> <deferred> <mutable> <accessor> <triedcooking> <sub_synth> def N$_setter_$self_$eq#15011(x$1#15012: <empty>#3.this.N#7352): scala#23.this.Unit#2340; <method> <deferred> <mutable> <accessor> <triedcooking> <sub_synth> def N$_setter_$n_$eq#15013(x$1#15014: N#7352.this.self#7442.type): scala#23.this.Unit#2340; <method> def /*N*/$init$scala#7441(): scala#23.this.Unit#2340 = { () }; <method> <deferred> <stable> <accessor> <triedcooking> <sub_synth> def self#7442: <empty>#3.this.N#7352; N#7352.this.N$_setter_$self_$eq#15011(scala#22.Predef#1729.$qmark$qmark$qmark#6917); <method> <deferred> <stable> <accessor> <sub_synth> def n#7443: N#7352.this.self#7442.type; N#7352.this.N$_setter_$n_$eq#15013(N#7352.this.self#7442) }; abstract class M#7353 extends scala#22.AnyRef#2378 { <method> <triedcooking> def <init>#13465(): <empty>#3.this.M#7353 = { M#7353.super.<init>scala#2719(); () }; <method> <deferred> <stable> <accessor> <triedcooking> def self#13466: <empty>#3.this.N#7352; <method> <deferred> <stable> <accessor> def n#13467: M#7353.this.self#13466.type }; class C#7354 extends M#7353 with <empty>#3.this.N#7352 { <method> <stable> <accessor> <triedcooking> def self#15016: <empty>#3.this.N#7352 = C#7354.this.self #15015; <triedcooking> private[this] val self #15015: <empty>#3.this.N#7352 = _; <method> <stable> <accessor> def n#15018: C#7354.this.self#7442.type = C#7354.this.n #15017; <triedcooking> private[this] val n #15017: C#7354.this.self#7442.type = _; <method> <mutable> <accessor> <triedcooking> def N$_setter_$self_$eq#15019(x$1#15021: <empty>#3.this.N#7352): scala#23.this.Unit#2340 = C#7354.this.self #15015 = x$1#15021; <method> <mutable> <accessor> <triedcooking> def N$_setter_$n_$eq#15022(x$1#15025: C#7354.this.self#7442.type): scala#23.this.Unit#2340 = C#7354.this.n #15017 = x$1#15025; <method> def <init>#14997(): <empty>#3.this.C#7354 = { C#7354.super.<init>#13465(); () } } } [running phase pickler on dependent_rebind.scala] [running phase refchecks on dependent_rebind.scala] test/files/trait-defaults/dependent_rebind.scala:16: error: overriding field n#15049 in trait N#7352 of type C#7354.this.self#15016.type; value n #15017 has incompatible type; found : => C#7354.this.self#7442.type (with underlying type => C#7354.this.self#7442.type) required: => N#7352.this.self#7442.type class C extends M with N ^ pos/t9111-inliner-workaround revealed need for: - override def transformInfo(sym: Symbol, tp: Type): Type = synthFieldsAndAccessors(tp) + override def transformInfo(sym: Symbol, tp: Type): Type = if (!sym.isJavaDefined) synthFieldsAndAccessors(tp) else tp commit b56ca2f Author: Adriaan Moors <[email protected]> Date: 6 weeks ago static forwarders & private[this] val in trait object Properties extends PropertiesTrait trait PropertiesTrait { // the protected member is not emitted as a static member of -- it works when dropping the access modifier // somehow, the module class member is considered deferred in BForwardersGen protected val propFilename: String = / } // [log jvm] No forwarder for 'getter propFilename' from Properties to 'module class Properties': false || m.isDeferred == true || false || false // the following method is missing compared to scalac // public final class Properties { // public static String propFilename() { // return Properties$.MODULE$.propFilename(); // } trait Chars { private[this] val char2uescapeArray = Array[Char]('\', 'u', 0, 0, 0, 0) } object Chars extends Chars // +++ w/reflect/scala/reflect/internal/Chars$.class // - private final [C scala83014char2uescapeArray constant fold in forwarder for backwards compat constant-typed final val in trait should yield impl method bean{setter,getter} delegates to setter/getter (commit 1655d1b)
1 parent 5c27e52 commit b4264ff

File tree

58 files changed

+1074
-241
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1074
-241
lines changed

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

+11-2
Original file line numberDiff line numberDiff line change
@@ -461,10 +461,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
461461
val runsRightAfter = None
462462
} with ExtensionMethods
463463

464+
// phaseName = "fields"
465+
object fields extends {
466+
val global: Global.this.type = Global.this
467+
// somewhere between typers, before erasure (so we get bridges for getters) and pickler (separate compilation of trait with fields and subclass)
468+
val runsAfter = List("extmethods")
469+
val runsRightAfter = None
470+
} with Fields
471+
464472
// phaseName = "pickler"
465473
object pickler extends {
466474
val global: Global.this.type = Global.this
467-
val runsAfter = List("extmethods")
475+
val runsAfter = List("fields")
468476
val runsRightAfter = None
469477
} with Pickler
470478

@@ -608,7 +616,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
608616
* This implementation creates a description map at the same time.
609617
*/
610618
protected def computeInternalPhases(): Unit = {
611-
// Note: this fits -Xshow-phases into 80 column width, which it is
619+
// Note: this fits -Xshow-phases into 80 column width, which is
612620
// desirable to preserve.
613621
val phs = List(
614622
syntaxAnalyzer -> "parse source into ASTs, perform simple desugaring",
@@ -618,6 +626,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
618626
patmat -> "translate match expressions",
619627
superAccessors -> "add super accessors in traits and nested classes",
620628
extensionMethods -> "add extension methods for inline classes",
629+
fields -> "synthesize accessors and fields",
621630
pickler -> "serialize symbol tables",
622631
refChecks -> "reference/override checking, translate nested objects",
623632
uncurry -> "uncurry, translate function values to anonymous classes",

src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
161161

162162
def enclosingMethod(sym: Symbol): Option[Symbol] = {
163163
if (sym.isClass || sym == NoSymbol) None
164-
else if (sym.isMethod) {
164+
else if (sym.isMethod && !sym.isGetter) {
165165
if (doesNotExist(sym)) None else Some(sym)
166166
}
167167
else enclosingMethod(nextEnclosing(sym))

src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
665665
(((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym))
666666
&& !sym.enclClass.isInterface
667667
&& !sym.isClassConstructor
668-
&& !sym.isMutable // lazy vals and vars both
668+
&& (!sym.isMutable || nme.isTraitSetterName(sym.name)) // lazy vals and vars and their setters cannot be final, but trait setters are
669669
)
670670

671671
// Primitives are "abstract final" to prohibit instantiation

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

+28-11
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
5858
def needsImplMethod(sym: Symbol) = (
5959
sym.isMethod
6060
&& isInterfaceMember(sym)
61+
// SYNTHESIZE_IMPL_IN_SUBCLASS accessors are mixed in by the fields phase, but others should be treated as regulars methods
62+
// (this will eventually include constant-typed getters, as they can be fully implemented in the interface)
63+
&& (!(sym hasFlag ACCESSOR) || sym.isLazy || !(sym hasFlag SYNTHESIZE_IMPL_IN_SUBCLASS))
6164
&& (!sym.hasFlag(DEFERRED | SUPERACCESSOR) || (sym hasFlag lateDEFERRED))
6265
)
6366

@@ -243,13 +246,25 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
243246
}
244247

245248
private def isInterfaceTree(tree: Tree) = tree.isDef && isInterfaceMember(tree.symbol)
249+
private def isMemoizedTraitGetter(tree: Tree) = (tree.symbol hasFlag SYNTHESIZE_IMPL_IN_SUBCLASS) && tree.symbol.isGetter && !tree.symbol.info.resultType.isInstanceOf[ConstantType]
246250

247-
private def deriveMemberForImplClass(tree: Tree): Tree =
248-
if (isInterfaceTree(tree)) if (needsImplMethod(tree.symbol)) implMethodDef(tree) else EmptyTree
251+
private def mkAssign(clazz: Symbol, assignSym: Symbol, rhs: Tree): Tree = {
252+
val qual = Select(This(clazz), assignSym)
253+
if (assignSym.isSetter) Apply(qual, List(rhs))
254+
else Assign(qual, rhs)
255+
}
256+
257+
private def deriveMemberForImplClass(clazz: Symbol, iface: Symbol, exprOwner: Symbol)(tree: Tree): Tree =
258+
if (isInterfaceTree(tree))
259+
if (needsImplMethod(tree.symbol)) implMethodDef(tree)
260+
else if (isMemoizedTraitGetter(tree)) mkAssign(clazz, tree.symbol.setterIn(iface), tree.asInstanceOf[DefDef].rhs.changeOwner(tree.symbol -> exprOwner))
261+
else EmptyTree
249262
else tree
250263

251264
private def deriveMemberForInterface(tree: Tree): Tree =
252-
if (isInterfaceTree(tree)) if (needsImplMethod(tree.symbol)) DefDef(tree.symbol, EmptyTree) else tree
265+
if (isInterfaceTree(tree))
266+
if (needsImplMethod(tree.symbol) || isMemoizedTraitGetter(tree)) DefDef(tree.symbol, EmptyTree) // TODO: use deriveDefDef(tree)(_ => EmptyTree) ?
267+
else tree
253268
else EmptyTree
254269

255270
private def ifaceTemplate(templ: Template): Template =
@@ -282,20 +297,22 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
282297
if (treeInfo.firstConstructor(stats) != EmptyTree) stats
283298
else DefDef(clazz.primaryConstructor, Block(List(), Literal(Constant(())))) :: stats
284299

285-
private def implTemplate(clazz: Symbol, templ: Template): Template = atPos(templ.pos) {
286-
val templ1 = (
287-
Template(templ.parents, noSelfType, addMixinConstructorDef(clazz, templ.body map deriveMemberForImplClass))
288-
setSymbol clazz.newLocalDummy(templ.pos)
289-
)
290-
templ1.changeOwner(templ.symbol.owner -> clazz, templ.symbol -> templ1.symbol)
291-
templ1
300+
private def implTemplate(clazz: Symbol, iface: ClassDef): Template = atPos(iface.impl.pos) {
301+
val templ = iface.impl
302+
val exprOwner = clazz.newLocalDummy(templ.pos)
303+
val derivedImplClassMembers = templ.body map deriveMemberForImplClass(clazz, iface.symbol, exprOwner)
304+
val templWithMixedinMembers = Template(templ.parents, noSelfType, addMixinConstructorDef(clazz, derivedImplClassMembers))
305+
306+
templWithMixedinMembers setSymbol exprOwner
307+
templWithMixedinMembers changeOwner (templ.symbol.owner -> clazz, templ.symbol -> exprOwner)
308+
templWithMixedinMembers
292309
}
293310

294311
def implClassDefs(trees: List[Tree]): List[Tree] = {
295312
trees collect {
296313
case cd: ClassDef if cd.symbol.needsImplClass =>
297314
val clazz = implClass(cd.symbol).initialize
298-
ClassDef(clazz, implTemplate(clazz, cd.impl))
315+
ClassDef(clazz, implTemplate(clazz, cd))
299316
}
300317
}
301318

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,9 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
631631
// The primary constructor is dealt with separately (we're massaging it here).
632632
case _: DefDef if statSym.isPrimaryConstructor => ()
633633
case _: DefDef if statSym.isConstructor => auxConstructorBuf += stat
634-
case _: DefDef => defBuf += stat
634+
case stat: DefDef =>
635+
defBuf += stat
636+
635637

636638
// If a val needs a field, an empty valdef goes into the template.
637639
// Except for lazy and ConstantTyped vals, the field is initialized by an assignment in:
@@ -641,7 +643,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
641643
case ValDef(mods, _, _, rhs) =>
642644
if (rhs ne EmptyTree) {
643645
val emitField = memoizeValue(statSym)
644-
moveEffectToCtor(mods, rhs, if (emitField) statSym else NoSymbol)
646+
moveEffectToCtor(mods, rhs, if (emitField) statSym else NoSymbol) // TODO: ever NoSymbol?
645647
if (emitField) defBuf += deriveValDef(stat)(_ => EmptyTree)
646648
} else defBuf += stat
647649

0 commit comments

Comments
 (0)