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