@@ -67,11 +67,14 @@ class Semantic {
6767
6868 sealed abstract class Addr extends Value {
6969 def klass : ClassSymbol
70+ def outer : Value
7071 }
7172
7273 /** A reference to the object under initialization pointed by `this`
7374 */
74- case class ThisRef (klass : ClassSymbol ) extends Addr
75+ case class ThisRef (klass : ClassSymbol ) extends Addr {
76+ val outer = Hot
77+ }
7578
7679 /** An object with all fields initialized but reaches objects under initialization
7780 *
@@ -183,6 +186,7 @@ class Semantic {
183186
184187 type Env = Env .Env
185188 def env (using env : Env ) = env
189+ inline def withEnv [T ](env : Env )(op : Env ?=> T ): T = op(using env)
186190
187191 import Env ._
188192
@@ -261,8 +265,8 @@ class Semantic {
261265 def call (meth : Symbol , args : List [ArgInfo ], superType : Type , source : Tree ): Contextual [Result ] =
262266 value.call(meth, args, superType, source) ++ errors
263267
264- def instantiate (klass : ClassSymbol , ctor : Symbol , args : List [ArgInfo ], source : Tree ): Contextual [Result ] =
265- value.instantiate(klass, ctor, args, source) ++ errors
268+ def instantiate (klass : ClassSymbol , ctor : Symbol , args : List [ArgInfo ], source : Tree , inside : ClassSymbol ): Contextual [Result ] =
269+ value.instantiate(klass, ctor, args, source, inside ) ++ errors
266270 }
267271
268272 /** The state that threads through the interpreter */
@@ -284,6 +288,7 @@ class Semantic {
284288
285289 import Trace ._
286290 def trace (using t : Trace ): Trace = t
291+ inline def withTrace [T ](t : Trace )(op : Trace ?=> T ): T = op(using t)
287292
288293// ----- Operations on domains -----------------------------
289294 extension (a : Value )
@@ -305,10 +310,11 @@ class Semantic {
305310
306311 case (RefSet (refs1), RefSet (refs2)) => RefSet (refs1 ++ refs2)
307312
308- def widen : Value =
313+ /** Conservatively approximate the value with `Cold` or `Hot` */
314+ def widenArg : Value =
309315 a match
310316 case _ : Addr | _ : Fun => Cold
311- case RefSet (refs) => refs.map(_.widen ).join
317+ case RefSet (refs) => refs.map(_.widenArg ).join
312318 case _ => a
313319
314320
@@ -317,7 +323,7 @@ class Semantic {
317323 if values.isEmpty then Hot
318324 else values.reduce { (v1, v2) => v1.join(v2) }
319325
320- def widen : List [Value ] = values.map(_.widen ).toList
326+ def widenArgs : List [Value ] = values.map(_.widenArg ).toList
321327
322328 extension (value : Value )
323329 def select (field : Symbol , source : Tree , needResolve : Boolean = true ): Contextual [Result ] = log(" select " + field.show, printer, res => res.asInstanceOf [Result ].show) {
@@ -384,7 +390,7 @@ class Semantic {
384390 Result (Hot , error :: checkArgs)
385391
386392 case addr : Addr =>
387- val isLocal = meth.owner.isClass
393+ val isLocal = ! meth.owner.isClass
388394 val target =
389395 if ! needResolve then
390396 meth
@@ -399,25 +405,28 @@ class Semantic {
399405 given Trace = trace1
400406 val cls = target.owner.enclosingClass.asClass
401407 val ddef = target.defTree.asInstanceOf [DefDef ]
402- val env2 = Env (ddef, args.map(_.value).widen )
408+ val env2 = Env (ddef, args.map(_.value).widenArgs )
403409 if target.isPrimaryConstructor then
404410 given Env = env2
405411 val tpl = cls.defTree.asInstanceOf [TypeDef ].rhs.asInstanceOf [Template ]
406- val res = use (trace.add(cls.defTree)) { eval(tpl, addr, cls, cacheResult = true ) }
412+ val res = withTrace (trace.add(cls.defTree)) { eval(tpl, addr, cls, cacheResult = true ) }
407413 Result (addr, res.errors)
408414 else if target.isConstructor then
409415 given Env = env2
410416 eval(ddef.rhs, addr, cls, cacheResult = true )
411417 else
412- use(if isLocal then env else Env .empty) {
418+ // normal method call
419+ withEnv(if isLocal then env else Env .empty) {
413420 eval(ddef.rhs, addr, cls, cacheResult = true ) ++ checkArgs
414421 }
415422 else if addr.canIgnoreMethodCall(target) then
416423 Result (Hot , Nil )
417424 else
425+ // no source code available
418426 val error = CallUnknown (target, source, trace.toVector)
419427 Result (Hot , error :: checkArgs)
420428 else
429+ // method call resolves to a field
421430 val obj = heap(addr)
422431 if obj.fields.contains(target) then
423432 Result (obj.fields(target), Nil )
@@ -428,7 +437,7 @@ class Semantic {
428437 // meth == NoSymbol for poly functions
429438 if meth.name.toString == " tupled" then Result (value, Nil ) // a call like `fun.tupled`
430439 else
431- use (env) {
440+ withEnv (env) {
432441 eval(body, thisV, klass, cacheResult = true ) ++ checkArgs
433442 }
434443
@@ -441,7 +450,7 @@ class Semantic {
441450 }
442451
443452 /** Handle a new expression `new p.C` where `p` is abstracted by `value` */
444- def instantiate (klass : ClassSymbol , ctor : Symbol , args : List [ArgInfo ], source : Tree ): Contextual [Result ] = log(" instantiating " + klass.show + " , args = " + args, printer, res => res.asInstanceOf [Result ].show) {
453+ def instantiate (klass : ClassSymbol , ctor : Symbol , args : List [ArgInfo ], source : Tree , inside : ClassSymbol ): Contextual [Result ] = log(" instantiating " + klass.show + " , value = " + value + " , args = " + args, printer, res => res.asInstanceOf [Result ].show) {
445454 val trace1 = trace.add(source)
446455 if promoted.isCurrentObjectPromoted then Result (Hot , Nil )
447456 else value match {
@@ -451,15 +460,12 @@ class Semantic {
451460 val errors = arg.promote
452461 buffer ++= errors
453462 if errors.isEmpty then Hot
454- else arg.value.widen
463+ else arg.value.widenArg
455464 }
456465
457466 if buffer.isEmpty then Result (Hot , Errors .empty)
458467 else
459- val value = Warm (klass, Hot , ctor, args2)
460- if ! heap.contains(value) then
461- val obj = Objekt (klass, fields = mutable.Map .empty, outers = mutable.Map (klass -> Hot ))
462- heap.update(value, obj)
468+ val value = Warm (klass, Hot , ctor, args2).ensureExists
463469 val res = value.call(ctor, args, superType = NoType , source)
464470 Result (value, res.errors)
465471
@@ -475,23 +481,23 @@ class Semantic {
475481 case Warm (_, _ : Warm , _, _) => Cold
476482 case _ => addr
477483
478- val value = Warm (klass, outer, ctor, args.map(_.value).widen)
479- if ! heap.contains(value) then
480- val obj = Objekt (klass, fields = mutable.Map .empty, outers = mutable.Map (klass -> outer))
481- heap.update(value, obj)
484+ val value = Warm (klass, outer, ctor, args.map(_.value).widenArgs).ensureExists
482485 val res = value.call(ctor, args, superType = NoType , source)
483486
484- // Abstract instances of local classes inside 2nd constructor as Cold
487+ def inSecondaryConstructor (sym : Symbol ): Boolean =
488+ ! sym.isClass && (sym.isConstructor || inSecondaryConstructor(sym.owner))
489+
490+ // Approximate instances of local classes inside secondary constructor as Cold.
485491 // This way, we avoid complicating the domain for Warm unnecessarily
486- if ! env.isHot then Result (Cold , res.errors)
492+ if klass.isContainedIn(inside) && inSecondaryConstructor(klass.owner) then Result (Cold , res.errors)
487493 else Result (value, res.errors)
488494
489495 case Fun (body, thisV, klass, env) =>
490496 report.error(" unexpected tree in instantiating a function, fun = " + body.show, source)
491497 Result (Hot , Nil )
492498
493499 case RefSet (refs) =>
494- val resList = refs.map(_.instantiate(klass, ctor, args, source))
500+ val resList = refs.map(_.instantiate(klass, ctor, args, source, inside ))
495501 val value2 = resList.map(_.value).join
496502 val errors = resList.flatMap(_.errors)
497503 Result (value2, errors)
@@ -526,15 +532,25 @@ class Semantic {
526532 }
527533 }
528534
535+ /** Ensure the corresponding object exists in the heap */
536+ def ensureExists : addr.type =
537+ if ! heap.contains(addr) then
538+ val obj = Objekt (addr.klass, fields = mutable.Map .empty, outers = mutable.Map (addr.klass -> addr.outer))
539+ heap.update(addr, obj)
540+ addr
541+
542+ end extension
543+
529544 extension (thisRef : ThisRef )
530545 def tryPromoteCurrentObject : Contextual [Boolean ] = log(" tryPromoteCurrentObject " , printer) {
531- promoted.isCurrentObjectPromoted || {
546+ if promoted.isCurrentObjectPromoted then
547+ true
548+ else if thisRef.isFullyFilled then
532549 // If we have all fields initialized, then we can promote This to hot.
533- thisRef.isFullyFilled && {
534- promoted.promoteCurrent(thisRef)
535- true
536- }
537- }
550+ promoted.promoteCurrent(thisRef)
551+ true
552+ else
553+ false
538554 }
539555
540556 extension (value : Value )
@@ -563,7 +579,7 @@ class Semantic {
563579 case fun @ Fun (body, thisV, klass, env) =>
564580 if promoted.contains(fun) then Nil
565581 else
566- val res = eval(body, thisV, klass)
582+ val res = withEnv(env) { eval(body, thisV, klass) }
567583 val errors2 = res.value.promote(msg, source)
568584 if (res.errors.nonEmpty || errors2.nonEmpty)
569585 UnsafePromotion (msg, source, trace.toVector, res.errors ++ errors2) :: Nil
@@ -727,7 +743,7 @@ class Semantic {
727743 val trace2 = trace.add(expr)
728744 locally {
729745 given Trace = trace2
730- (res ++ errors).instantiate(cls, ctor, args, expr)
746+ (res ++ errors).instantiate(cls, ctor, args, source = expr, inside = klass )
731747 }
732748
733749 case Call (ref, argss) =>
@@ -879,16 +895,13 @@ class Semantic {
879895
880896 def default () = Result (Hot , Nil )
881897
882- if sym.is(Flags .Param ) then
883- if sym.owner.isConstructor then
884- // instances of local classes inside secondary constructors cannot
885- // reach here, as those values are abstracted by Cold instead of Warm.
886- // This enables us to simplify the domain without sacrificing
887- // expressiveness nor soundess, as local classes inside secondary
888- // constructors are uncommon.
889- Result (env.lookup(sym), Nil )
890- else
891- default()
898+ if sym.is(Flags .Param ) && sym.owner.isConstructor then
899+ // instances of local classes inside secondary constructors cannot
900+ // reach here, as those values are abstracted by Cold instead of Warm.
901+ // This enables us to simplify the domain without sacrificing
902+ // expressiveness nor soundess, as local classes inside secondary
903+ // constructors are uncommon.
904+ Result (env.lookup(sym), Nil )
892905 else
893906 default()
894907
@@ -897,7 +910,9 @@ class Semantic {
897910
898911 case tp @ ThisType (tref) =>
899912 val cls = tref.classSymbol.asClass
900- if cls.isStaticOwner && ! klass.isContainedIn(cls) then Result (Hot , Nil )
913+ if cls.isStaticOwner && ! klass.isContainedIn(cls) then
914+ // O.this outside the body of the object O
915+ Result (Hot , Nil )
901916 else
902917 val value = resolveThis(cls, thisV, klass, source)
903918 Result (value, Errors .empty)
@@ -962,6 +977,8 @@ class Semantic {
962977 printer.println(acc.show + " initialized with " + value)
963978 }
964979
980+ // Handler is used to schedule super constructor calls.
981+ // Super constructor calls are delayed until all outers are set.
965982 type Handler = (() => Unit ) => Unit
966983 def superCall (tref : TypeRef , ctor : Symbol , args : List [ArgInfo ], source : Tree , handler : Handler )(using Env ): Unit =
967984 val cls = tref.classSymbol.asClass
@@ -982,13 +999,13 @@ class Semantic {
982999 def initParent (parent : Tree , handler : Handler )(using Env ) = parent match {
9831000 case tree @ Block (stats, NewExpr (tref, New (tpt), ctor, argss)) => // can happen
9841001 eval(stats, thisV, klass).foreach { res => errorBuffer ++= res.errors }
985- val (erros , args) = evalArgs(argss.flatten, thisV, klass)
986- errorBuffer ++= erros
1002+ val (errors , args) = evalArgs(argss.flatten, thisV, klass)
1003+ errorBuffer ++= errors
9871004 superCall(tref, ctor, args, tree, handler)
9881005
9891006 case tree @ NewExpr (tref, New (tpt), ctor, argss) => // extends A(args)
990- val (erros , args) = evalArgs(argss.flatten, thisV, klass)
991- errorBuffer ++= erros
1007+ val (errors , args) = evalArgs(argss.flatten, thisV, klass)
1008+ errorBuffer ++= errors
9921009 superCall(tref, ctor, args, tree, handler)
9931010
9941011 case _ => // extends A or extends A[T]
@@ -1087,14 +1104,13 @@ class Semantic {
10871104object Semantic {
10881105
10891106// ----- Utility methods and extractors --------------------------------
1090- inline def use [T , R ](v : T )(inline op : T ?=> R ): R = op(using v)
10911107
10921108 def typeRefOf (tp : Type )(using Context ): TypeRef = tp.dealias.typeConstructor match {
10931109 case tref : TypeRef => tref
10941110 case hklambda : HKTypeLambda => typeRefOf(hklambda.resType)
10951111 }
10961112
1097- type Arg = Tree | ByNameArg
1113+ opaque type Arg = Tree | ByNameArg
10981114 case class ByNameArg (tree : Tree )
10991115
11001116 extension (arg : Arg )
0 commit comments