@@ -168,7 +168,7 @@ sealed abstract class CaptureSet extends Showable:
168168 */
169169 protected def addThisElem (elem : CaptureRef )(using Context , VarState ): CompareResult
170170
171- protected def addHiddenElem (elem : CaptureRef )(using ctx : Context , vs : VarState ): CompareResult =
171+ protected def addIfHiddenOrFail (elem : CaptureRef )(using ctx : Context , vs : VarState ): CompareResult =
172172 if elems.exists(_.maxSubsumes(elem, canAddHidden = true ))
173173 then CompareResult .OK
174174 else CompareResult .Fail (this :: Nil )
@@ -438,7 +438,7 @@ object CaptureSet:
438438 def isAlwaysEmpty = elems.isEmpty
439439
440440 def addThisElem (elem : CaptureRef )(using Context , VarState ): CompareResult =
441- addHiddenElem (elem)
441+ addIfHiddenOrFail (elem)
442442
443443 def addDependent (cs : CaptureSet )(using Context , VarState ) = CompareResult .OK
444444
@@ -487,7 +487,10 @@ object CaptureSet:
487487 private var isSolved : Boolean = false
488488
489489 /** The elements currently known to be in the set */
490- var elems : Refs = initialElems
490+ protected var myElems : Refs = initialElems
491+
492+ def elems : Refs = myElems
493+ def elems_= (refs : Refs ): Unit = myElems = refs
491494
492495 /** The sets currently known to be dependent sets (i.e. new additions to this set
493496 * are propagated to these dependent sets.)
@@ -535,7 +538,7 @@ object CaptureSet:
535538
536539 final def addThisElem (elem : CaptureRef )(using Context , VarState ): CompareResult =
537540 if isConst || ! recordElemsState() then // Fail if variable is solved or given VarState is frozen
538- addHiddenElem (elem)
541+ addIfHiddenOrFail (elem)
539542 else if Existential .isBadExistential(elem) then // Fail if `elem` is an out-of-scope existential
540543 CompareResult .Fail (this :: Nil )
541544 else if ! levelOK(elem) then
@@ -925,10 +928,76 @@ object CaptureSet:
925928 def elemIntersection (cs1 : CaptureSet , cs2 : CaptureSet )(using Context ): Refs =
926929 cs1.elems.filter(cs2.mightAccountFor) ++ cs2.elems.filter(cs1.mightAccountFor)
927930
928- /** A capture set variable used to record the references hidden by a Fresh.Cap instance */
931+ /** A capture set variable used to record the references hidden by a Fresh.Cap instance,
932+ * The elems and deps members are repurposed as follows:
933+ * elems: Set of hidden references
934+ * deps : Set of hidden sets for which the Fresh.Cap instance owning this set
935+ * is a hidden element.
936+ * Hidden sets may become aliases of other hidden sets, which means that
937+ * reads and writes of elems go to the alias.
938+ * If H is an alias of R.hidden for some Fresh.Cap R then:
939+ * H.elems == {R}
940+ * H.deps = {R.hidden}
941+ * This encoding was chosen because it relies only on the elems and deps fields
942+ * which are already subject through snapshotting and rollbacks in VarState.
943+ * It's advantageous if we don't need to deal with other pieces of state there.
944+ */
929945 class HiddenSet (initialHidden : Refs = emptyRefs)(using @ constructorOnly ictx : Context )
930946 extends Var (initialElems = initialHidden):
931947
948+ private def aliasRef : AnnotatedType | Null =
949+ if myElems.size == 1 then
950+ myElems.nth(0 ) match
951+ case al @ Fresh .Cap (hidden) if deps.contains(hidden) => al
952+ case _ => null
953+ else null
954+
955+ private def aliasSet : HiddenSet =
956+ if myElems.size == 1 then
957+ myElems.nth(0 ) match
958+ case Fresh .Cap (hidden) if deps.contains(hidden) => hidden
959+ case _ => this
960+ else this
961+
962+ override def elems : Refs =
963+ val al = aliasSet
964+ if al eq this then super .elems else al.elems
965+
966+ override def elems_= (refs : Refs ) =
967+ val al = aliasSet
968+ if al eq this then super .elems_=(refs) else al.elems_=(refs)
969+
970+ /** Add element to hidden set. Also add it to all supersets (as indicated by
971+ * deps of this set). Follow aliases on both hidden set and added element
972+ * before adding. If the added element is also a Fresh.Cap instance with
973+ * hidden set H which is a superset of this set, then make this set an
974+ * alias of H.
975+ */
976+ def add (elem : CaptureRef )(using ctx : Context , vs : VarState ): Unit =
977+ val alias = aliasSet
978+ if alias ne this then alias.add(elem)
979+ else
980+ def addToElems () =
981+ elems += elem
982+ deps.foreach: dep =>
983+ assert(dep != this )
984+ vs.addHidden(dep.asInstanceOf [HiddenSet ], elem)
985+ elem match
986+ case Fresh .Cap (hidden) =>
987+ if this ne hidden then
988+ val alias = hidden.aliasRef
989+ if alias != null then
990+ add(alias)
991+ else if deps.contains(hidden) then // make this an alias of elem
992+ capt.println(i " Alias $this to $hidden" )
993+ elems = SimpleIdentitySet (elem)
994+ deps = SimpleIdentitySet (hidden)
995+ else
996+ addToElems()
997+ hidden.deps += this
998+ case _ =>
999+ addToElems()
1000+
9321001 /** Apply function `f` to `elems` while setting `elems` to empty for the
9331002 * duration. This is used to escape infinite recursions if two Fresh.Caps
9341003 * refer to each other in their hidden sets.
@@ -1075,9 +1144,11 @@ object CaptureSet:
10751144 */
10761145 def addHidden (hidden : HiddenSet , elem : CaptureRef )(using Context ): Boolean =
10771146 elemsMap.get(hidden) match
1078- case None => elemsMap(hidden) = hidden.elems
1147+ case None =>
1148+ elemsMap(hidden) = hidden.elems
1149+ depsMap(hidden) = hidden.deps
10791150 case _ =>
1080- hidden.elems += elem
1151+ hidden.add(elem)( using ctx, this )
10811152 true
10821153
10831154 /** Roll back global state to what was recorded in this VarState */
0 commit comments