Skip to content

Commit 4f98208

Browse files
committed
Shorten transitive hidden sets and replace cycles by aliases
1 parent 2d268f9 commit 4f98208

File tree

4 files changed

+95
-17
lines changed

4 files changed

+95
-17
lines changed

Diff for: compiler/src/dotty/tools/dotc/cc/CaptureRef.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,8 @@ trait CaptureRef extends TypeProxy, ValueType:
253253
* fail a comparison.
254254
*/
255255
def maxSubsumes(y: CaptureRef, canAddHidden: Boolean)(using ctx: Context, vs: VarState = VarState.Separate): Boolean =
256-
this.match
256+
(this eq y)
257+
|| this.match
257258
case Fresh.Cap(hidden) =>
258259
vs.ifNotSeen(this)(hidden.elems.exists(_.subsumes(y)))
259260
|| !y.stripReadOnly.isCap && canAddHidden && vs.addHidden(hidden, y)

Diff for: compiler/src/dotty/tools/dotc/cc/CaptureSet.scala

+78-7
Original file line numberDiff line numberDiff line change
@@ -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 */

Diff for: compiler/src/dotty/tools/dotc/cc/Fresh.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ object Fresh:
6262
def apply(owner: Symbol)(using Context): CaptureRef =
6363
apply(ownerToHidden(owner, reach = false))
6464

65-
def unapply(tp: AnnotatedType)(using Context): Option[CaptureSet.HiddenSet] = tp.annot match
65+
def unapply(tp: AnnotatedType): Option[CaptureSet.HiddenSet] = tp.annot match
6666
case Annot(hidden) => Some(hidden)
6767
case _ => None
6868
end Cap

Diff for: compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala

+14-8
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ abstract class SimpleIdentitySet[+Elem <: AnyRef] {
2020
acc
2121
def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A
2222
def toList: List[Elem]
23-
def iterator: Iterator[Elem]
23+
def nth(n: Int): Elem
2424

2525
final def isEmpty: Boolean = size == 0
2626

27+
final def iterator: Iterator[Elem] = Iterator.tabulate(size)(nth)
28+
2729
def forall[E >: Elem <: AnyRef](p: E => Boolean): Boolean = !exists(!p(_))
2830

2931
def filter(p: Elem => Boolean): SimpleIdentitySet[Elem] =
@@ -74,7 +76,7 @@ object SimpleIdentitySet {
7476
override def map[B <: AnyRef](f: Nothing => B): SimpleIdentitySet[B] = empty
7577
def /: [A, E <: AnyRef](z: A)(f: (A, E) => A): A = z
7678
def toList = Nil
77-
def iterator = Iterator.empty
79+
def nth(n: Int): Nothing = throw new IndexOutOfBoundsException(n.toString)
7880
}
7981

8082
private class Set1[+Elem <: AnyRef](x0: AnyRef) extends SimpleIdentitySet[Elem] {
@@ -92,7 +94,9 @@ object SimpleIdentitySet {
9294
def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A =
9395
f(z, x0.asInstanceOf[E])
9496
def toList = x0.asInstanceOf[Elem] :: Nil
95-
def iterator = Iterator.single(x0.asInstanceOf[Elem])
97+
def nth(n: Int) =
98+
if n == 0 then x0.asInstanceOf[Elem]
99+
else throw new IndexOutOfBoundsException(n.toString)
96100
}
97101

98102
private class Set2[+Elem <: AnyRef](x0: AnyRef, x1: AnyRef) extends SimpleIdentitySet[Elem] {
@@ -114,10 +118,10 @@ object SimpleIdentitySet {
114118
def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A =
115119
f(f(z, x0.asInstanceOf[E]), x1.asInstanceOf[E])
116120
def toList = x0.asInstanceOf[Elem] :: x1.asInstanceOf[Elem] :: Nil
117-
def iterator = Iterator.tabulate(2) {
121+
def nth(n: Int) = n match
118122
case 0 => x0.asInstanceOf[Elem]
119123
case 1 => x1.asInstanceOf[Elem]
120-
}
124+
case _ => throw new IndexOutOfBoundsException(n.toString)
121125
}
122126

123127
private class Set3[+Elem <: AnyRef](x0: AnyRef, x1: AnyRef, x2: AnyRef) extends SimpleIdentitySet[Elem] {
@@ -154,11 +158,11 @@ object SimpleIdentitySet {
154158
def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A =
155159
f(f(f(z, x0.asInstanceOf[E]), x1.asInstanceOf[E]), x2.asInstanceOf[E])
156160
def toList = x0.asInstanceOf[Elem] :: x1.asInstanceOf[Elem] :: x2.asInstanceOf[Elem] :: Nil
157-
def iterator = Iterator.tabulate(3) {
161+
def nth(n: Int) = n match
158162
case 0 => x0.asInstanceOf[Elem]
159163
case 1 => x1.asInstanceOf[Elem]
160164
case 2 => x2.asInstanceOf[Elem]
161-
}
165+
case _ => throw new IndexOutOfBoundsException(n.toString)
162166
}
163167

164168
private class SetN[+Elem <: AnyRef](val xs: Array[AnyRef]) extends SimpleIdentitySet[Elem] {
@@ -205,7 +209,9 @@ object SimpleIdentitySet {
205209
foreach(buf += _)
206210
buf.toList
207211
}
208-
def iterator = xs.iterator.asInstanceOf[Iterator[Elem]]
212+
def nth(n: Int) =
213+
if 0 <= n && n < size then xs(n).asInstanceOf[Elem]
214+
else throw new IndexOutOfBoundsException(n.toString)
209215
override def ++ [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] =
210216
that match {
211217
case that: SetN[?] =>

0 commit comments

Comments
 (0)