@@ -7,6 +7,7 @@ import Contexts._
77import Symbols ._
88import Types ._
99import StdNames ._
10+ import NameKinds .OuterSelectName
1011
1112import ast .tpd ._
1213import util .EqHashMap
@@ -27,24 +28,20 @@ class Semantic {
2728 * Value = Hot | Cold | Warm | ThisRef | Fun | RefSet
2829 *
2930 * Cold
30- * ┌──────► ▲ ◄ ──┐ ◄────┐
31- * │ │ │ │
32- * │ │ │ │
33- * ThisRef(C) │ │ │
34- * ▲ │ │ │
35- * │ Warm(D) Fun RefSet
36- * │ ▲ ▲ ▲
37- * │ │ │ │
38- * Warm(C) │ │ │
39- * ▲ │ │ │
40- * │ │ │ │
41- * └─────────┴──────┴───────┘
31+ * ┌──────► ▲ ◄── ──┐ ◄────┐
32+ * │ │ │ │
33+ * │ │ │ │
34+ * | │ │ │
35+ * | │ │ │
36+ * ThisRef(C) Warm(D) Fun RefSet
37+ * │ ▲ ▲ ▲
38+ * │ │ │ │
39+ * | │ │ │
40+ * ▲ │ │ │
41+ * │ │ │ │
42+ * └─────────┴─────── ┴───────┘
4243 * Hot
4344 *
44- * The most important ordering is the following:
45- *
46- * Hot ⊑ Warm(C) ⊑ ThisRef(C) ⊑ Cold
47- *
4845 * The diagram above does not reflect relationship between `RefSet`
4946 * and other values. `RefSet` represents a set of values which could
5047 * be `ThisRef`, `Warm` or `Fun`. The following ordering applies for
@@ -194,6 +191,7 @@ class Semantic {
194191 class PromotionInfo {
195192 var isCurrentObjectPromoted : Boolean = false
196193 val values = mutable.Set .empty[Value ]
194+ override def toString (): String = values.toString()
197195 }
198196 /** Values that have been safely promoted */
199197 opaque type Promoted = PromotionInfo
@@ -300,9 +298,6 @@ class Semantic {
300298 case (Cold , _) => Cold
301299 case (_, Cold ) => Cold
302300
303- case (a : Warm , b : ThisRef ) if a.klass == b.klass => b
304- case (a : ThisRef , b : Warm ) if a.klass == b.klass => a
305-
306301 case (a : (Fun | Warm | ThisRef ), b : (Fun | Warm | ThisRef )) => RefSet (a :: b :: Nil )
307302
308303 case (a : (Fun | Warm | ThisRef ), RefSet (refs)) => RefSet (a :: refs)
@@ -495,6 +490,46 @@ class Semantic {
495490 Result (value2, errors)
496491 }
497492 }
493+
494+ def accessLocal (tmref : TermRef , klass : ClassSymbol , source : Tree ): Contextual [Result ] =
495+ val sym = tmref.symbol
496+
497+ def default () = Result (Hot , Nil )
498+
499+ if sym.is(Flags .Param ) && sym.owner.isConstructor then
500+ // instances of local classes inside secondary constructors cannot
501+ // reach here, as those values are abstracted by Cold instead of Warm.
502+ // This enables us to simplify the domain without sacrificing
503+ // expressiveness nor soundess, as local classes inside secondary
504+ // constructors are uncommon.
505+ if sym.isContainedIn(klass) then
506+ Result (env.lookup(sym), Nil )
507+ else
508+ // We don't know much about secondary constructor parameters in outer scope.
509+ // It's always safe to approximate them with `Cold`.
510+ Result (Cold , Nil )
511+ else if sym.is(Flags .Param ) then
512+ default()
513+ else
514+ sym.defTree match {
515+ case vdef : ValDef =>
516+ // resolve this for local variable
517+ val enclosingClass = sym.owner.enclosingClass.asClass
518+ val thisValue2 = resolveThis(enclosingClass, value, klass, source)
519+ thisValue2 match {
520+ case Hot => Result (Hot , Errors .empty)
521+
522+ case Cold => Result (Cold , Nil )
523+
524+ case addr : Addr => eval(vdef.rhs, addr, klass)
525+
526+ case _ =>
527+ report.error(" unexpected defTree when accessing local variable, sym = " + sym.show + " , defTree = " + sym.defTree.show, source)
528+ default()
529+ }
530+
531+ case _ => default()
532+ }
498533 end extension
499534
500535// ----- Promotion ----------------------------------------------------
@@ -756,7 +791,15 @@ class Semantic {
756791 res.call(id.symbol, args, superType = NoType , source = expr)
757792
758793 case Select (qualifier, name) =>
759- eval(qualifier, thisV, klass).select(expr.symbol, expr)
794+ val qualRes = eval(qualifier, thisV, klass)
795+
796+ name match
797+ case OuterSelectName (_, hops) =>
798+ val SkolemType (tp) = expr.tpe
799+ val outer = resolveOuterSelect(tp.classSymbol.asClass, qualRes.value, hops, source = expr)
800+ Result (outer, qualRes.errors)
801+ case _ =>
802+ qualRes.select(expr.symbol, expr)
760803
761804 case _ : This =>
762805 cases(expr.tpe, thisV, klass, expr)
@@ -846,7 +889,7 @@ class Semantic {
846889 case vdef : ValDef =>
847890 // local val definition
848891 // TODO: support explicit @cold annotation for local definitions
849- eval(vdef.rhs, thisV, klass).ensureHot( " Local definitions may only hold initialized values " , vdef )
892+ eval(vdef.rhs, thisV, klass, cacheResult = true )
850893
851894 case ddef : DefDef =>
852895 // local method
@@ -874,24 +917,7 @@ class Semantic {
874917 Result (Hot , Errors .empty)
875918
876919 case tmref : TermRef if tmref.prefix == NoPrefix =>
877- val sym = tmref.symbol
878-
879- def default () = Result (Hot , Nil )
880-
881- if sym.is(Flags .Param ) && sym.owner.isConstructor then
882- // instances of local classes inside secondary constructors cannot
883- // reach here, as those values are abstracted by Cold instead of Warm.
884- // This enables us to simplify the domain without sacrificing
885- // expressiveness nor soundess, as local classes inside secondary
886- // constructors are uncommon.
887- if sym.isContainedIn(klass) then
888- Result (env.lookup(sym), Nil )
889- else
890- // We don't know much about secondary constructor parameters in outer scope.
891- // It's always safe to approximate them with `Cold`.
892- Result (Cold , Nil )
893- else
894- default()
920+ thisV.accessLocal(tmref, klass, source)
895921
896922 case tmref : TermRef =>
897923 cases(tmref.prefix, thisV, klass, source).select(tmref.symbol, source)
@@ -939,6 +965,40 @@ class Semantic {
939965
940966 }
941967
968+ /** Resolve outer select introduced during inlining.
969+ *
970+ * See `tpd.outerSelect` and `ElimOuterSelect`.
971+ */
972+ def resolveOuterSelect (target : ClassSymbol , thisV : Value , hops : Int , source : Tree ): Contextual [Value ] = log(" resolving outer " + target.show + " , this = " + thisV.show + " , hops = " + hops, printer, res => res.asInstanceOf [Value ].show) {
973+ // Is `target` reachable from `cls` with the given `hops`?
974+ def reachable (cls : ClassSymbol , hops : Int ): Boolean =
975+ if hops == 0 then cls == target
976+ else reachable(cls.lexicallyEnclosingClass.asClass, hops - 1 )
977+
978+ thisV match
979+ case Hot => Hot
980+
981+ case addr : Addr =>
982+ val obj = heap(addr)
983+ val curOpt = obj.klass.baseClasses.find(cls => reachable(cls, hops))
984+ curOpt match
985+ case Some (cur) =>
986+ resolveThis(target, thisV, cur, source)
987+
988+ case None =>
989+ report.warning(" unexpected outerSelect, thisV = " + thisV + " , target = " + target.show + " , hops = " + hops, source.srcPos)
990+ Cold
991+
992+ case RefSet (refs) =>
993+ refs.map(ref => resolveOuterSelect(target, ref, hops, source)).join
994+
995+ case fun : Fun =>
996+ report.warning(" unexpected thisV = " + thisV + " , target = " + target.show + " , hops = " + hops, source.srcPos)
997+ Cold
998+
999+ case Cold => Cold
1000+ }
1001+
9421002 /** Compute the outer value that correspond to `tref.prefix` */
9431003 def outerValue (tref : TypeRef , thisV : Addr , klass : ClassSymbol , source : Tree ): Contextual [Result ] =
9441004 val cls = tref.classSymbol.asClass
0 commit comments