Skip to content

Commit 4362be6

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 ebcbdcc commit 4362be6

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

+36-18
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
@@ -699,7 +700,8 @@ class Objects {
699700

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

702-
def superCall(tref: TypeRef, ctor: Symbol, args: List[Value], source: Tree): Unit =
703+
type Handler = (() => Unit) => Unit
704+
def superCall(tref: TypeRef, ctor: Symbol, args: List[Value], source: Tree, handler: Handler): Unit =
703705
val cls = tref.classSymbol.asClass
704706
// update outer for super class
705707
val res = outerValue(tref, thisV, klass, source)
@@ -708,53 +710,57 @@ class Objects {
708710

709711
// follow constructor
710712
if cls.hasSource then
711-
use(trace.add(source)) {
712-
val res2 = thisV.call(ctor, args, superType = NoType, source)
713-
errorBuffer ++= res2.errors
713+
handler { () =>
714+
use(trace.add(source)) {
715+
val res2 = thisV.call(ctor, args, superType = NoType, source)
716+
errorBuffer ++= res2.errors
717+
}
714718
}
719+
else
720+
handler { () => () }
715721

716722
// parents
717-
def initParent(parent: Tree) = parent match {
723+
def initParent(parent: Tree, handler: Handler) = parent match {
718724
case tree @ Block(stats, NewExpr(tref, New(tpt), ctor, argss)) => // can happen
719725
eval(stats, thisV, klass).foreach { res => errorBuffer ++= res.errors }
720726
val resArgs = evalArgs(argss.flatten, thisV, klass)
721727
val argsValues = resArgs.map(_.value)
722728
val argsErrors = resArgs.flatMap(_.errors)
723729

724730
errorBuffer ++= argsErrors
725-
superCall(tref, ctor, argsValues, tree)
731+
superCall(tref, ctor, argsValues, tree, handler)
726732

727733
case tree @ NewExpr(tref, New(tpt), ctor, argss) => // extends A(args)
728734
val resArgs = evalArgs(argss.flatten, thisV, klass)
729735
val argsValues = resArgs.map(_.value)
730736
val argsErrors = resArgs.flatMap(_.errors)
731737

732738
errorBuffer ++= argsErrors
733-
superCall(tref, ctor, argsValues, tree)
739+
superCall(tref, ctor, argsValues, tree, handler)
734740

735741
case _ => // extends A or extends A[T]
736742
val tref = typeRefOf(parent.tpe)
737-
superCall(tref, tref.classSymbol.primaryConstructor, Nil, parent)
743+
superCall(tref, tref.classSymbol.primaryConstructor, Nil, parent, handler)
738744
}
739745

740746
// see spec 5.1 about "Template Evaluation".
741747
// https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html
742748
if !klass.is(Flags.Trait) then
749+
// outers are set first
750+
val tasks = new mutable.ArrayBuffer[() => Unit]
751+
val handler: Handler = task => tasks.append(task)
752+
743753
// 1. first init parent class recursively
744754
// 2. initialize traits according to linearization order
745755
val superParent = tpl.parents.head
746756
val superCls = superParent.tpe.classSymbol.asClass
747-
initParent(superParent)
748-
749-
// Access to the object possible after this point
750-
if klass.isStaticOwner then
751-
thisV.updateField(klass, thisV)
757+
initParent(superParent, handler)
752758

753759
val parents = tpl.parents.tail
754760
val mixins = klass.baseClasses.tail.takeWhile(_ != superCls)
755761
mixins.reverse.foreach { mixin =>
756762
parents.find(_.tpe.classSymbol == mixin) match
757-
case Some(parent) => initParent(parent)
763+
case Some(parent) => initParent(parent, handler)
758764
case None =>
759765
// According to the language spec, if the mixin trait requires
760766
// arguments, then the class must provide arguments to it explicitly
@@ -765,9 +771,21 @@ class Objects {
765771
// term arguments to B. That can only be done in a concrete class.
766772
val tref = typeRefOf(klass.typeRef.baseType(mixin).typeConstructor)
767773
val ctor = tref.classSymbol.primaryConstructor
768-
if ctor.exists then superCall(tref, ctor, Nil, superParent)
774+
if ctor.exists then superCall(tref, ctor, Nil, superParent, handler)
769775
}
770776

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

772790
// class body
773791
tpl.body.foreach {

0 commit comments

Comments
 (0)