@@ -7,6 +7,7 @@ import Contexts._
7
7
import Symbols ._
8
8
import Types ._
9
9
import StdNames ._
10
+ import NameKinds .OuterSelectName
10
11
11
12
import ast .tpd ._
12
13
import util .EqHashMap
@@ -27,24 +28,20 @@ class Semantic {
27
28
* Value = Hot | Cold | Warm | ThisRef | Fun | RefSet
28
29
*
29
30
* 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
+ * └─────────┴─────── ┴───────┘
42
43
* Hot
43
44
*
44
- * The most important ordering is the following:
45
- *
46
- * Hot ⊑ Warm(C) ⊑ ThisRef(C) ⊑ Cold
47
- *
48
45
* The diagram above does not reflect relationship between `RefSet`
49
46
* and other values. `RefSet` represents a set of values which could
50
47
* be `ThisRef`, `Warm` or `Fun`. The following ordering applies for
@@ -301,9 +298,6 @@ class Semantic {
301
298
case (Cold , _) => Cold
302
299
case (_, Cold ) => Cold
303
300
304
- case (a : Warm , b : ThisRef ) if a.klass == b.klass => b
305
- case (a : ThisRef , b : Warm ) if a.klass == b.klass => a
306
-
307
301
case (a : (Fun | Warm | ThisRef ), b : (Fun | Warm | ThisRef )) => RefSet (a :: b :: Nil )
308
302
309
303
case (a : (Fun | Warm | ThisRef ), RefSet (refs)) => RefSet (a :: refs)
@@ -406,7 +400,6 @@ class Semantic {
406
400
given Trace = trace1
407
401
val cls = target.owner.enclosingClass.asClass
408
402
val ddef = target.defTree.asInstanceOf [DefDef ]
409
- // try early promotion here; if returns error, returns cold
410
403
val env2 = Env (ddef, args.map(_.value).widenArgs)
411
404
if target.isPrimaryConstructor then
412
405
given Env = env2
@@ -497,6 +490,46 @@ class Semantic {
497
490
Result (value2, errors)
498
491
}
499
492
}
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
+ }
500
533
end extension
501
534
502
535
// ----- Promotion ----------------------------------------------------
@@ -609,33 +642,24 @@ class Semantic {
609
642
if classRef.memberClasses.nonEmpty || ! warm.isFullyFilled then
610
643
return PromoteError (msg, source, trace.toVector) :: Nil
611
644
612
- val fields = classRef.fields
613
- val methods = classRef.membersBasedOnFlags(Flags .Method , Flags .Deferred | Flags .Accessor )
614
645
val buffer = new mutable.ArrayBuffer [Error ]
615
646
616
- fields.exists { denot =>
617
- val f = denot.symbol
618
- if ! f.isOneOf(Flags .Deferred | Flags .Private | Flags .Protected ) && f.hasSource then
619
- val trace2 = trace.add(f.defTree)
620
- val res = warm.select(f, source)
621
- locally {
622
- given Trace = trace2
623
- buffer ++= res.ensureHot(msg, source).errors
624
- }
625
- buffer.nonEmpty
626
- }
627
-
628
- buffer.nonEmpty || methods.exists { denot =>
629
- val m = denot.symbol
630
- if ! m.isConstructor && m.hasSource then
631
- val trace2 = trace.add(m.defTree)
632
- locally {
633
- given Trace = trace2
634
- val args = m.info.paramInfoss.flatten.map(_ => ArgInfo (Hot , EmptyTree ))
635
- val res = warm.call(m, args, superType = NoType , source = source)
636
- buffer ++= res.ensureHot(msg, source).errors
637
- }
638
- buffer.nonEmpty
647
+ warm.klass.baseClasses.exists { klass =>
648
+ klass.hasSource && klass.info.decls.exists { member =>
649
+ if ! member.isType && ! member.isConstructor && member.hasSource && ! member.is(Flags .Deferred ) then
650
+ if member.is(Flags .Method ) then
651
+ val trace2 = trace.add(source)
652
+ locally {
653
+ given Trace = trace2
654
+ val args = member.info.paramInfoss.flatten.map(_ => ArgInfo (Hot , EmptyTree ))
655
+ val res = warm.call(member, args, superType = NoType , source = member.defTree)
656
+ buffer ++= res.ensureHot(msg, source).errors
657
+ }
658
+ else
659
+ val res = warm.select(member, source)
660
+ buffer ++= res.ensureHot(msg, source).errors
661
+ buffer.nonEmpty
662
+ }
639
663
}
640
664
641
665
if buffer.isEmpty then Nil
@@ -694,7 +718,7 @@ class Semantic {
694
718
}
695
719
696
720
/** Evaluate a list of expressions */
697
- def eval (exprs : List [Tree ], thisV : Addr , klass : ClassSymbol ): Contextual [List [Result ]] =
721
+ def eval (exprs : List [Tree ], thisV : Addr , klass : ClassSymbol ): Contextual [List [Result ]] =
698
722
exprs.map { expr => eval(expr, thisV, klass) }
699
723
700
724
/** Evaluate arguments of methods */
@@ -767,7 +791,15 @@ class Semantic {
767
791
res.call(id.symbol, args, superType = NoType , source = expr)
768
792
769
793
case Select (qualifier, name) =>
770
- 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)
771
803
772
804
case _ : This =>
773
805
cases(expr.tpe, thisV, klass, expr)
@@ -857,8 +889,7 @@ class Semantic {
857
889
case vdef : ValDef =>
858
890
// local val definition
859
891
// TODO: support explicit @cold annotation for local definitions
860
- eval(vdef.rhs, thisV, klass, true )
861
- // .ensureHot("Local definitions may only hold initialized values", vdef)
892
+ eval(vdef.rhs, thisV, klass, cacheResult = true )
862
893
863
894
case ddef : DefDef =>
864
895
// local method
@@ -886,43 +917,7 @@ class Semantic {
886
917
Result (Hot , Errors .empty)
887
918
888
919
case tmref : TermRef if tmref.prefix == NoPrefix =>
889
- val sym = tmref.symbol
890
-
891
- def default () = Result (Hot , Nil )
892
-
893
- if sym.is(Flags .Param ) && sym.owner.isConstructor then
894
- // instances of local classes inside secondary constructors cannot
895
- // reach here, as those values are abstracted by Cold instead of Warm.
896
- // This enables us to simplify the domain without sacrificing
897
- // expressiveness nor soundess, as local classes inside secondary
898
- // constructors are uncommon.
899
- if sym.isContainedIn(klass) then
900
- Result (env.lookup(sym), Nil )
901
- else
902
- // We don't know much about secondary constructor parameters in outer scope.
903
- // It's always safe to approximate them with `Cold`.
904
- Result (Cold , Nil )
905
- else
906
- sym.defTree match {
907
- case vdef : ValDef => {
908
- // resolve this for local variable
909
- val enclosingClass = sym.owner.enclosingClass.asClass
910
- val thisValue2 = resolveThis(enclosingClass, thisV, klass, source)
911
- thisValue2 match {
912
- case Hot => Result (Hot , Errors .empty)
913
- case Cold => {
914
- val error = AccessCold (sym, source, trace.toVector)
915
- Result (Hot , error :: Nil )
916
- }
917
- case addr : Addr => {
918
- val res = eval(vdef.rhs, addr, klass)
919
- if res.value.promote(" Try promote" , source).isEmpty then Result (Hot , Errors .empty) else res
920
- }
921
- case _ => ???
922
- }
923
- }
924
- case _ => default()
925
- }
920
+ thisV.accessLocal(tmref, klass, source)
926
921
927
922
case tmref : TermRef =>
928
923
cases(tmref.prefix, thisV, klass, source).select(tmref.symbol, source)
@@ -970,6 +965,40 @@ class Semantic {
970
965
971
966
}
972
967
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
+
973
1002
/** Compute the outer value that correspond to `tref.prefix` */
974
1003
def outerValue (tref : TypeRef , thisV : Addr , klass : ClassSymbol , source : Tree ): Contextual [Result ] =
975
1004
val cls = tref.classSymbol.asClass
0 commit comments