@@ -436,6 +436,39 @@ class Objects(using Context @constructorOnly):
436
436
case _ =>
437
437
throw new RuntimeException (" Incorrect local environment for initializing " + x.show)
438
438
439
+ /**
440
+ * Resolve the environment by searching for a given symbol.
441
+ *
442
+ * Searches for the environment that owns `target`, starting from `env` as the innermost.
443
+ *
444
+ * Due to widening, the corresponding environment might not exist. As a result reading the local
445
+ * variable will return `Cold` and it's forbidden to write to the local variable.
446
+ *
447
+ * @param target The symbol to search for.
448
+ * @param thisV The value for `this` of the enclosing class where the local variable is referenced.
449
+ * @param env The local environment where the local variable is referenced.
450
+ *
451
+ * @return the environment that owns the `target` and value for `this` owned by the given method.
452
+ */
453
+ def resolveEnvByValue (target : Symbol , thisV : ThisValue , env : Data )(using Context ): Option [(ThisValue , Data )] = log(" Resolving env by value for " + target.show + " , this = " + thisV.show + " , env = " + env.show, printer) {
454
+ env match
455
+ case localEnv : LocalEnv =>
456
+ if localEnv.getVal(target).isDefined then Some (thisV -> localEnv)
457
+ else if localEnv.getVar(target).isDefined then Some (thisV -> localEnv)
458
+ else resolveEnvByValue(target, thisV, localEnv.outer)
459
+ case NoEnv =>
460
+ thisV match
461
+ case ref : OfClass =>
462
+ ref.outer match
463
+ case outer : ThisValue =>
464
+ resolveEnvByValue(target, outer, ref.env)
465
+ case _ =>
466
+ // TODO: properly handle the case where ref.outer is ValueSet
467
+ None
468
+ case _ =>
469
+ None
470
+ }
471
+
439
472
/**
440
473
* Resolve the environment owned by the given method.
441
474
*
@@ -451,17 +484,17 @@ class Objects(using Context @constructorOnly):
451
484
*
452
485
* @return the environment and value for `this` owned by the given method.
453
486
*/
454
- def resolveEnv (meth : Symbol , thisV : ThisValue , env : Data )(using Context ): Option [(ThisValue , Data )] = log(" Resolving env for " + meth.show + " , this = " + thisV.show + " , env = " + env.show, printer) {
487
+ def resolveEnvByOwner (meth : Symbol , thisV : ThisValue , env : Data )(using Context ): Option [(ThisValue , Data )] = log(" Resolving env by owner for " + meth.show + " , this = " + thisV.show + " , env = " + env.show, printer) {
455
488
env match
456
489
case localEnv : LocalEnv =>
457
490
if localEnv.meth == meth then Some (thisV -> env)
458
- else resolveEnv (meth, thisV, localEnv.outer)
491
+ else resolveEnvByOwner (meth, thisV, localEnv.outer)
459
492
case NoEnv =>
460
493
thisV match
461
494
case ref : OfClass =>
462
495
ref.outer match
463
496
case outer : ThisValue =>
464
- resolveEnv (meth, outer, ref.env)
497
+ resolveEnvByOwner (meth, outer, ref.env)
465
498
case _ =>
466
499
// TODO: properly handle the case where ref.outer is ValueSet
467
500
None
@@ -724,7 +757,7 @@ class Objects(using Context @constructorOnly):
724
757
if meth.owner.isClass then
725
758
(ref, Env .NoEnv )
726
759
else
727
- Env .resolveEnv (meth.owner.enclosingMethod, ref, summon[Env .Data ]).getOrElse(Cold -> Env .NoEnv )
760
+ Env .resolveEnvByOwner (meth.owner.enclosingMethod, ref, summon[Env .Data ]).getOrElse(Cold -> Env .NoEnv )
728
761
729
762
val env2 = Env .ofDefDef(ddef, args.map(_.value), outerEnv)
730
763
extendTrace(ddef) {
@@ -771,9 +804,9 @@ class Objects(using Context @constructorOnly):
771
804
end if
772
805
773
806
case _ =>
774
- // by-name closure
775
- given Env . Data = env
776
- extendTrace(code) { eval(code, thisV, klass, cacheResult = true ) }
807
+ // Should be unreachable, by-name closures are handled by readLocal
808
+ report.warning( " [Internal error] Only DefDef should be possible here, but found " + code.show + " . " + Trace .show, Trace .position)
809
+ Bottom
777
810
778
811
case ValueSet (vs) =>
779
812
vs.map(v => call(v, meth, args, receiver, superType)).join
@@ -962,7 +995,7 @@ class Objects(using Context @constructorOnly):
962
995
(thisV.widenRefOrCold(1 ), Env .NoEnv )
963
996
else
964
997
// klass.enclosingMethod returns its primary constructor
965
- Env .resolveEnv (klass.owner.enclosingMethod, thisV, summon[Env .Data ]).getOrElse(Cold -> Env .NoEnv )
998
+ Env .resolveEnvByOwner (klass.owner.enclosingMethod, thisV, summon[Env .Data ]).getOrElse(Cold -> Env .NoEnv )
966
999
967
1000
val instance = OfClass (klass, outerWidened, ctor, args.map(_.value), envWidened)
968
1001
callConstructor(instance, ctor, args)
@@ -992,7 +1025,9 @@ class Objects(using Context @constructorOnly):
992
1025
*/
993
1026
def readLocal (thisV : ThisValue , sym : Symbol ): Contextual [Value ] = log(" reading local " + sym.show, printer, (_ : Value ).show) {
994
1027
def isByNameParam (sym : Symbol ) = sym.is(Flags .Param ) && sym.info.isInstanceOf [ExprType ]
995
- Env .resolveEnv(sym.enclosingMethod, thisV, summon[Env .Data ]) match
1028
+ // Can't use enclosingMethod here because values defined in a by-name closure will have the wrong enclosingMethod,
1029
+ // since our phase is before elimByName.
1030
+ Env .resolveEnvByValue(sym, thisV, summon[Env .Data ]) match
996
1031
case Some (thisV -> env) =>
997
1032
if sym.is(Flags .Mutable ) then
998
1033
// Assume forward reference check is doing a good job
@@ -1047,8 +1082,9 @@ class Objects(using Context @constructorOnly):
1047
1082
*/
1048
1083
def writeLocal (thisV : ThisValue , sym : Symbol , value : Value ): Contextual [Value ] = log(" write local " + sym.show + " with " + value.show, printer, (_ : Value ).show) {
1049
1084
assert(sym.is(Flags .Mutable ), " Writing to immutable variable " + sym.show)
1050
-
1051
- Env .resolveEnv(sym.enclosingMethod, thisV, summon[Env .Data ]) match
1085
+ // Can't use enclosingMethod here because values defined in a by-name closure will have the wrong enclosingMethod,
1086
+ // since our phase is before elimByName.
1087
+ Env .resolveEnvByValue(sym, thisV, summon[Env .Data ]) match
1052
1088
case Some (thisV -> env) =>
1053
1089
given Env .Data = env
1054
1090
Env .getVar(sym) match
0 commit comments