Skip to content

Commit 32df0d1

Browse files
committed
Handle outers of trait as the same in concrete semantics
For traits, its outers will be proxy methods of the class that extends the trait. As the prefix is stable and is a valid value before any super constructor calls. Therefore, we may think the outers for traits are immediately set following the class parameters. Also, when trying promotion of warm values, we never try warm values whose fields are not fully filled -- which corresponds to promote ThisRef with non-initialized fields, and errors will be reported when the class is checked separately.
1 parent 344738f commit 32df0d1

File tree

1 file changed

+36
-18
lines changed

1 file changed

+36
-18
lines changed

compiler/src/dotty/tools/dotc/transform/init/Objects.scala

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,13 @@ class Objects {
135135
def instantiate(klass: ClassSymbol, ctor: Symbol, args: List[Value], source: Tree): Contextual[Result] =
136136
value.instantiate(klass, ctor, args, source) ++ errors
137137

138-
def ensureAccess(klass: ClassSymbol, source: Tree): Contextual[Result] =
138+
def ensureAccess(klass: ClassSymbol, source: Tree): Contextual[Result] = log("ensure access " + value.show, printer) {
139139
value match
140140
case obj: ObjectRef =>
141141
if obj.klass == klass then this
142142
else obj.access(source) ++ errors
143143
case _ => this
144+
}
144145
}
145146

146147
/** The state that threads through the interpreter */
@@ -233,7 +234,7 @@ class Objects {
233234
eval(rhs, addr, target.owner.asClass, cacheResult = true)
234235
else
235236
given Trace = trace1
236-
val obj = if heap.contains(addr) then heap(addr) else Objekt(addr.klass, mutable.Map.empty, mutable.Map.empty)
237+
val obj = heap(addr)
237238
if obj.fields.contains(target) then
238239
Result(obj.fields(target), Nil)
239240
else if target.is(Flags.ParamAccessor) then
@@ -658,7 +659,7 @@ class Objects {
658659
}
659660

660661
/** Resolve C.this that appear in `klass` */
661-
def resolveThis(target: ClassSymbol, thisV: Value, klass: ClassSymbol, source: Tree, elideObjectAccess: Boolean = false): Contextual[Result] = log("resolving " + target.show + ", this = " + thisV.show + " in " + klass.show, printer, res => res.asInstanceOf[Value].show) {
662+
def resolveThis(target: ClassSymbol, thisV: Value, klass: ClassSymbol, source: Tree, elideObjectAccess: Boolean = false): Contextual[Result] = log("resolving " + target.show + ", this = " + thisV.show + " in " + klass.show, printer, res => res.asInstanceOf[Result].show) {
662663
if target == klass then Result(thisV, Nil)
663664
else if target.is(Flags.Package) then Result(Bottom, Nil)
664665
else if target.isStaticObjectRef then
@@ -704,7 +705,8 @@ class Objects {
704705

705706
val paramsMap = tpl.constr.termParamss.flatten.map(vdef => vdef.name -> vdef.symbol).toMap
706707

707-
def superCall(tref: TypeRef, ctor: Symbol, args: List[Value], source: Tree): Unit =
708+
type Handler = (() => Unit) => Unit
709+
def superCall(tref: TypeRef, ctor: Symbol, args: List[Value], source: Tree, handler: Handler): Unit =
708710
val cls = tref.classSymbol.asClass
709711
// update outer for super class
710712
val res = outerValue(tref, thisV, klass, source)
@@ -713,53 +715,57 @@ class Objects {
713715

714716
// follow constructor
715717
if cls.hasSource then
716-
use(trace.add(source)) {
717-
val res2 = thisV.call(ctor, args, superType = NoType, source)
718-
errorBuffer ++= res2.errors
718+
handler { () =>
719+
use(trace.add(source)) {
720+
val res2 = thisV.call(ctor, args, superType = NoType, source)
721+
errorBuffer ++= res2.errors
722+
}
719723
}
724+
else
725+
handler { () => () }
720726

721727
// parents
722-
def initParent(parent: Tree) = parent match {
728+
def initParent(parent: Tree, handler: Handler) = parent match {
723729
case tree @ Block(stats, NewExpr(tref, New(tpt), ctor, argss)) => // can happen
724730
eval(stats, thisV, klass).foreach { res => errorBuffer ++= res.errors }
725731
val resArgs = evalArgs(argss.flatten, thisV, klass)
726732
val argsValues = resArgs.map(_.value)
727733
val argsErrors = resArgs.flatMap(_.errors)
728734

729735
errorBuffer ++= argsErrors
730-
superCall(tref, ctor, argsValues, tree)
736+
superCall(tref, ctor, argsValues, tree, handler)
731737

732738
case tree @ NewExpr(tref, New(tpt), ctor, argss) => // extends A(args)
733739
val resArgs = evalArgs(argss.flatten, thisV, klass)
734740
val argsValues = resArgs.map(_.value)
735741
val argsErrors = resArgs.flatMap(_.errors)
736742

737743
errorBuffer ++= argsErrors
738-
superCall(tref, ctor, argsValues, tree)
744+
superCall(tref, ctor, argsValues, tree, handler)
739745

740746
case _ => // extends A or extends A[T]
741747
val tref = typeRefOf(parent.tpe)
742-
superCall(tref, tref.classSymbol.primaryConstructor, Nil, parent)
748+
superCall(tref, tref.classSymbol.primaryConstructor, Nil, parent, handler)
743749
}
744750

745751
// see spec 5.1 about "Template Evaluation".
746752
// https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html
747753
if !klass.is(Flags.Trait) then
754+
// outers are set first
755+
val tasks = new mutable.ArrayBuffer[() => Unit]
756+
val handler: Handler = task => tasks.append(task)
757+
748758
// 1. first init parent class recursively
749759
// 2. initialize traits according to linearization order
750760
val superParent = tpl.parents.head
751761
val superCls = superParent.tpe.classSymbol.asClass
752-
initParent(superParent)
753-
754-
// Access to the object possible after this point
755-
if klass.isStaticOwner then
756-
thisV.updateField(klass, thisV)
762+
initParent(superParent, handler)
757763

758764
val parents = tpl.parents.tail
759765
val mixins = klass.baseClasses.tail.takeWhile(_ != superCls)
760766
mixins.reverse.foreach { mixin =>
761767
parents.find(_.tpe.classSymbol == mixin) match
762-
case Some(parent) => initParent(parent)
768+
case Some(parent) => initParent(parent, handler)
763769
case None =>
764770
// According to the language spec, if the mixin trait requires
765771
// arguments, then the class must provide arguments to it explicitly
@@ -770,9 +776,21 @@ class Objects {
770776
// term arguments to B. That can only be done in a concrete class.
771777
val tref = typeRefOf(klass.typeRef.baseType(mixin).typeConstructor)
772778
val ctor = tref.classSymbol.primaryConstructor
773-
if ctor.exists then superCall(tref, ctor, Nil, superParent)
779+
if ctor.exists then superCall(tref, ctor, Nil, superParent, handler)
774780
}
775781

782+
// initialize super classes after outers are set
783+
// 1. first call super class constructor
784+
// 2. make the object accessible
785+
// 3. call mixin initializations
786+
tasks.head()
787+
788+
// Access to the object possible after this point
789+
if klass.isStaticOwner then
790+
thisV.updateField(klass, thisV)
791+
792+
tasks.tail.foreach(task => task())
793+
776794

777795
// class body
778796
tpl.body.foreach {

0 commit comments

Comments
 (0)