@@ -67,11 +67,14 @@ class Semantic {
67
67
68
68
sealed abstract class Addr extends Value {
69
69
def klass : ClassSymbol
70
+ def outer : Value
70
71
}
71
72
72
73
/** A reference to the object under initialization pointed by `this`
73
74
*/
74
- case class ThisRef (klass : ClassSymbol ) extends Addr
75
+ case class ThisRef (klass : ClassSymbol ) extends Addr {
76
+ val outer = Hot
77
+ }
75
78
76
79
/** An object with all fields initialized but reaches objects under initialization
77
80
*
@@ -183,6 +186,7 @@ class Semantic {
183
186
184
187
type Env = Env .Env
185
188
def env (using env : Env ) = env
189
+ inline def withEnv [T ](env : Env )(op : Env ?=> T ): T = op(using env)
186
190
187
191
import Env ._
188
192
@@ -261,8 +265,8 @@ class Semantic {
261
265
def call (meth : Symbol , args : List [ArgInfo ], superType : Type , source : Tree ): Contextual [Result ] =
262
266
value.call(meth, args, superType, source) ++ errors
263
267
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
266
270
}
267
271
268
272
/** The state that threads through the interpreter */
@@ -284,6 +288,7 @@ class Semantic {
284
288
285
289
import Trace ._
286
290
def trace (using t : Trace ): Trace = t
291
+ inline def withTrace [T ](t : Trace )(op : Trace ?=> T ): T = op(using t)
287
292
288
293
// ----- Operations on domains -----------------------------
289
294
extension (a : Value )
@@ -305,10 +310,11 @@ class Semantic {
305
310
306
311
case (RefSet (refs1), RefSet (refs2)) => RefSet (refs1 ++ refs2)
307
312
308
- def widen : Value =
313
+ /** Conservatively approximate the value with `Cold` or `Hot` */
314
+ def widenArg : Value =
309
315
a match
310
316
case _ : Addr | _ : Fun => Cold
311
- case RefSet (refs) => refs.map(_.widen ).join
317
+ case RefSet (refs) => refs.map(_.widenArg ).join
312
318
case _ => a
313
319
314
320
@@ -317,7 +323,7 @@ class Semantic {
317
323
if values.isEmpty then Hot
318
324
else values.reduce { (v1, v2) => v1.join(v2) }
319
325
320
- def widen : List [Value ] = values.map(_.widen ).toList
326
+ def widenArgs : List [Value ] = values.map(_.widenArg ).toList
321
327
322
328
extension (value : Value )
323
329
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 {
384
390
Result (Hot , error :: checkArgs)
385
391
386
392
case addr : Addr =>
387
- val isLocal = meth.owner.isClass
393
+ val isLocal = ! meth.owner.isClass
388
394
val target =
389
395
if ! needResolve then
390
396
meth
@@ -399,25 +405,28 @@ class Semantic {
399
405
given Trace = trace1
400
406
val cls = target.owner.enclosingClass.asClass
401
407
val ddef = target.defTree.asInstanceOf [DefDef ]
402
- val env2 = Env (ddef, args.map(_.value).widen )
408
+ val env2 = Env (ddef, args.map(_.value).widenArgs )
403
409
if target.isPrimaryConstructor then
404
410
given Env = env2
405
411
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 ) }
407
413
Result (addr, res.errors)
408
414
else if target.isConstructor then
409
415
given Env = env2
410
416
eval(ddef.rhs, addr, cls, cacheResult = true )
411
417
else
412
- use(if isLocal then env else Env .empty) {
418
+ // normal method call
419
+ withEnv(if isLocal then env else Env .empty) {
413
420
eval(ddef.rhs, addr, cls, cacheResult = true ) ++ checkArgs
414
421
}
415
422
else if addr.canIgnoreMethodCall(target) then
416
423
Result (Hot , Nil )
417
424
else
425
+ // no source code available
418
426
val error = CallUnknown (target, source, trace.toVector)
419
427
Result (Hot , error :: checkArgs)
420
428
else
429
+ // method call resolves to a field
421
430
val obj = heap(addr)
422
431
if obj.fields.contains(target) then
423
432
Result (obj.fields(target), Nil )
@@ -428,7 +437,7 @@ class Semantic {
428
437
// meth == NoSymbol for poly functions
429
438
if meth.name.toString == " tupled" then Result (value, Nil ) // a call like `fun.tupled`
430
439
else
431
- use (env) {
440
+ withEnv (env) {
432
441
eval(body, thisV, klass, cacheResult = true ) ++ checkArgs
433
442
}
434
443
@@ -441,7 +450,7 @@ class Semantic {
441
450
}
442
451
443
452
/** 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) {
445
454
val trace1 = trace.add(source)
446
455
if promoted.isCurrentObjectPromoted then Result (Hot , Nil )
447
456
else value match {
@@ -451,15 +460,12 @@ class Semantic {
451
460
val errors = arg.promote
452
461
buffer ++= errors
453
462
if errors.isEmpty then Hot
454
- else arg.value.widen
463
+ else arg.value.widenArg
455
464
}
456
465
457
466
if buffer.isEmpty then Result (Hot , Errors .empty)
458
467
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
463
469
val res = value.call(ctor, args, superType = NoType , source)
464
470
Result (value, res.errors)
465
471
@@ -475,23 +481,23 @@ class Semantic {
475
481
case Warm (_, _ : Warm , _, _) => Cold
476
482
case _ => addr
477
483
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
482
485
val res = value.call(ctor, args, superType = NoType , source)
483
486
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.
485
491
// 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)
487
493
else Result (value, res.errors)
488
494
489
495
case Fun (body, thisV, klass, env) =>
490
496
report.error(" unexpected tree in instantiating a function, fun = " + body.show, source)
491
497
Result (Hot , Nil )
492
498
493
499
case RefSet (refs) =>
494
- val resList = refs.map(_.instantiate(klass, ctor, args, source))
500
+ val resList = refs.map(_.instantiate(klass, ctor, args, source, inside ))
495
501
val value2 = resList.map(_.value).join
496
502
val errors = resList.flatMap(_.errors)
497
503
Result (value2, errors)
@@ -526,15 +532,25 @@ class Semantic {
526
532
}
527
533
}
528
534
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
+
529
544
extension (thisRef : ThisRef )
530
545
def tryPromoteCurrentObject : Contextual [Boolean ] = log(" tryPromoteCurrentObject " , printer) {
531
- promoted.isCurrentObjectPromoted || {
546
+ if promoted.isCurrentObjectPromoted then
547
+ true
548
+ else if thisRef.isFullyFilled then
532
549
// 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
538
554
}
539
555
540
556
extension (value : Value )
@@ -563,7 +579,7 @@ class Semantic {
563
579
case fun @ Fun (body, thisV, klass, env) =>
564
580
if promoted.contains(fun) then Nil
565
581
else
566
- val res = eval(body, thisV, klass)
582
+ val res = withEnv(env) { eval(body, thisV, klass) }
567
583
val errors2 = res.value.promote(msg, source)
568
584
if (res.errors.nonEmpty || errors2.nonEmpty)
569
585
UnsafePromotion (msg, source, trace.toVector, res.errors ++ errors2) :: Nil
@@ -727,7 +743,7 @@ class Semantic {
727
743
val trace2 = trace.add(expr)
728
744
locally {
729
745
given Trace = trace2
730
- (res ++ errors).instantiate(cls, ctor, args, expr)
746
+ (res ++ errors).instantiate(cls, ctor, args, source = expr, inside = klass )
731
747
}
732
748
733
749
case Call (ref, argss) =>
@@ -879,16 +895,13 @@ class Semantic {
879
895
880
896
def default () = Result (Hot , Nil )
881
897
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 )
892
905
else
893
906
default()
894
907
@@ -897,7 +910,9 @@ class Semantic {
897
910
898
911
case tp @ ThisType (tref) =>
899
912
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 )
901
916
else
902
917
val value = resolveThis(cls, thisV, klass, source)
903
918
Result (value, Errors .empty)
@@ -962,6 +977,8 @@ class Semantic {
962
977
printer.println(acc.show + " initialized with " + value)
963
978
}
964
979
980
+ // Handler is used to schedule super constructor calls.
981
+ // Super constructor calls are delayed until all outers are set.
965
982
type Handler = (() => Unit ) => Unit
966
983
def superCall (tref : TypeRef , ctor : Symbol , args : List [ArgInfo ], source : Tree , handler : Handler )(using Env ): Unit =
967
984
val cls = tref.classSymbol.asClass
@@ -982,13 +999,13 @@ class Semantic {
982
999
def initParent (parent : Tree , handler : Handler )(using Env ) = parent match {
983
1000
case tree @ Block (stats, NewExpr (tref, New (tpt), ctor, argss)) => // can happen
984
1001
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
987
1004
superCall(tref, ctor, args, tree, handler)
988
1005
989
1006
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
992
1009
superCall(tref, ctor, args, tree, handler)
993
1010
994
1011
case _ => // extends A or extends A[T]
@@ -1087,14 +1104,13 @@ class Semantic {
1087
1104
object Semantic {
1088
1105
1089
1106
// ----- Utility methods and extractors --------------------------------
1090
- inline def use [T , R ](v : T )(inline op : T ?=> R ): R = op(using v)
1091
1107
1092
1108
def typeRefOf (tp : Type )(using Context ): TypeRef = tp.dealias.typeConstructor match {
1093
1109
case tref : TypeRef => tref
1094
1110
case hklambda : HKTypeLambda => typeRefOf(hklambda.resType)
1095
1111
}
1096
1112
1097
- type Arg = Tree | ByNameArg
1113
+ opaque type Arg = Tree | ByNameArg
1098
1114
case class ByNameArg (tree : Tree )
1099
1115
1100
1116
extension (arg : Arg )
0 commit comments