Skip to content

Commit 83b12bc

Browse files
committed
Revamp capture roots and captureset vars
- Drop usage of nesting level - Work directly with owner inclusion
1 parent b9e56ac commit 83b12bc

File tree

7 files changed

+140
-172
lines changed

7 files changed

+140
-172
lines changed

compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

Lines changed: 14 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -91,23 +91,18 @@ trait FollowAliases extends TypeMap:
9191
if t2 ne t1 then return t2
9292
mapOver(t)
9393

94-
class mapRoots(from: CaptureRoot, to: CaptureRoot)(using Context) extends BiTypeMap, FollowAliases:
94+
class mapRoots(from0: CaptureRoot, to: CaptureRoot)(using Context) extends BiTypeMap, FollowAliases:
9595
thisMap =>
9696

97+
val from = from0.followAlias
98+
99+
//override val toString = i"mapRoots($from, $to)"
100+
97101
def apply(t: Type): Type =
98102
if t eq from then to
99103
else t match
100-
case t: CaptureRoot.Var =>
101-
val ta = t.followAlias
102-
if ta ne t then apply(ta)
103-
else from match
104-
case from: TermRef
105-
if t.upperLevel >= from.symbol.ccNestingLevel
106-
&& constrainRootsWhenMapping // next two lines do the constraining
107-
&& CaptureRoot.isEnclosingRoot(from, t)
108-
&& CaptureRoot.isEnclosingRoot(t, from) => to
109-
case from: CaptureRoot.Var if from.followAlias eq t => to
110-
case _ => t
104+
case t: CaptureRoot.Var if constrainRootsWhenMapping && t.unifiesWith(from) =>
105+
to
111106
case t @ Setup.Box(t1) =>
112107
t.derivedBox(this(t1))
113108
case _ =>
@@ -382,30 +377,6 @@ extension (sym: Symbol)
382377
&& sym != defn.Caps_unsafeUnbox
383378

384379
def takesCappedParamIn(info: Type)(using Context): Boolean =
385-
386-
def isOwnRoot(tp: Type): Boolean =
387-
tp.isCapabilityClassRef
388-
|| tp.dealias.match
389-
case tp: CaptureRef =>
390-
tp.isGenericRootCapability || tp.localRootOwner == sym
391-
case _ =>
392-
false
393-
394-
def hasUniversalCap(tp: Type): Boolean = tp.dealiasKeepAnnots match
395-
case tp @ AnnotatedType(parent, annot) =>
396-
val found = annot match
397-
case CaptureAnnotation(refs, _) => refs.elems.exists(isOwnRoot)
398-
case _ => annot.tree.retainedElems.exists(tree => isOwnRoot(tree.tpe))
399-
found || hasUniversalCap(parent)
400-
case tp: TypeRef =>
401-
tp.isRef(defn.Caps_Cap) || tp.isCapabilityClassRef
402-
case tp: LazyRef =>
403-
hasUniversalCap(tp.ref)
404-
case tp: TypeVar =>
405-
hasUniversalCap(tp.underlying)
406-
case _ =>
407-
tp.isCapabilityClassRef
408-
409380
info.dealias.stripPoly match
410381
case mt: MethodType =>
411382
(mt.paramInfos.exists(_.hasUniversalRootOf(sym)) || takesCappedParamIn(mt.resType))
@@ -517,17 +488,17 @@ extension (sym: Symbol)
517488
else newRoot
518489
ccState.localRoots.getOrElseUpdate(owner, lclRoot)
519490

520-
def maxNested(other: Symbol)(using Context): Symbol =
521-
if sym.ccNestingLevel < other.ccNestingLevel then other else sym
522-
/* does not work yet, we do mix sets with different levels, for instance in cc-this.scala.
523-
else if sym.ccNestingLevel > other.ccNestingLevel then sym
491+
def maxNested(other: Symbol, pickFirstOnConflict: Boolean = false)(using Context): Symbol =
492+
if !sym.exists || other.isContainedIn(sym) then other
493+
else if !other.exists || sym.isContainedIn(other) then sym
524494
else
525-
assert(sym == other, i"conflicting symbols at same nesting level: $sym, $other")
495+
assert(pickFirstOnConflict, i"incomparable nesting: $sym and $other")
526496
sym
527-
*/
528497

529498
def minNested(other: Symbol)(using Context): Symbol =
530-
if sym.ccNestingLevel > other.ccNestingLevel then other else sym
499+
if !other.exists || other.isContainedIn(sym) then sym
500+
else if !sym.exists || sym.isContainedIn(other) then other
501+
else sym.owner.minNested(other.owner)
531502

532503
extension (tp: TermRef | ThisType)
533504
/** The nesting level of this reference as defined by capture checking */

compiler/src/dotty/tools/dotc/cc/CaptureRoot.scala

Lines changed: 64 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -14,98 +14,85 @@ type CaptureRoot = TermRef | CaptureRoot.Var
1414

1515
object CaptureRoot:
1616

17-
case class Var(owner: Symbol, source: Symbol = NoSymbol)(using @constructorOnly ictx: Context) extends CaptureRef, Showable:
17+
private var nextId = 0
1818

19-
var upperBound: Symbol = owner
20-
var lowerBound: Symbol = NoSymbol
21-
var upperLevel: Int = owner.ccNestingLevel
22-
var lowerLevel: Int = Int.MinValue
23-
private[CaptureRoot] var lowerRoots: SimpleIdentitySet[Var] = SimpleIdentitySet.empty
24-
private[CaptureRoot] var upperRoots: SimpleIdentitySet[Var] = SimpleIdentitySet.empty
25-
private[CaptureRoot] var alias: CaptureRoot = this
19+
case class Var(owner: Symbol, source: Symbol)(using @constructorOnly ictx: Context) extends CaptureRef, Showable:
2620

27-
override def localRootOwner(using Context) = owner
28-
override def isTrackableRef(using Context): Boolean = true
29-
override def captureSetOfInfo(using Context) = CaptureSet.universal
21+
val id =
22+
nextId += 1
23+
nextId
3024

31-
def setAlias(target: CaptureRoot) =
32-
alias = target
25+
var innerLimit: Symbol = owner.levelOwner
26+
var outerLimit: Symbol = defn.RootClass
27+
var outerRoots: SimpleIdentitySet[Var] = SimpleIdentitySet.empty
3328

34-
def followAlias: CaptureRoot = alias match
35-
case alias: Var if alias ne this => alias.followAlias
36-
case _ => alias
29+
override def isTrackableRef(using Context): Boolean = true
30+
override def captureSetOfInfo(using Context) = CaptureSet.universal
3731

38-
def locallyConsistent =
39-
lowerLevel <= upperLevel
40-
&& lowerRoots.forall(_.upperLevel <= upperLevel)
41-
&& upperRoots.forall(_.lowerLevel >= lowerLevel)
32+
private var myAlias: CaptureRoot = this
33+
def alias = myAlias
34+
def alias_=(r: CaptureRoot)(using Context) =
35+
//assert(id != 2, i"$this := $r")
36+
alias match
37+
case alias: TermRef =>
38+
val owner = alias.localRootOwner
39+
assert(
40+
owner.isContainedIn(outerLimit) && innerLimit.isContainedIn(owner),
41+
i"illegal alias $owner for $this")
42+
case _ =>
43+
myAlias = r
4244

4345
def computeHash(bs: Binders): Int = hash
4446
def hash: Int = System.identityHashCode(this)
4547
def underlying(using Context): Type = defn.Caps_Cap.typeRef
4648
end Var
4749

48-
def isEnclosingRoot(c1: CaptureRoot, c2: CaptureRoot)(using Context): Boolean =
49-
if c1 eq c2 then return true
50-
c1 match
51-
case c1: Var if c1.alias ne c1 => return isEnclosingRoot(c1.alias, c2)
52-
case _ =>
53-
c2 match
54-
case c2: Var if c2.alias ne c2 => return isEnclosingRoot(c1, c2.alias)
55-
case _ =>
56-
(c1, c2) match
57-
case (c1: TermRef, c2: TermRef) =>
58-
c1.ccNestingLevel <= c2.ccNestingLevel
59-
case (c1: TermRef, c2: Var) =>
60-
val level1 = c1.ccNestingLevel
61-
if level1 <= c2.lowerLevel then
62-
true // no change
63-
else if level1 <= c2.upperLevel && c2.upperRoots.forall(isEnclosingRoot(c1, _)) then
64-
if level1 == c2.upperLevel then
65-
c2.alias = c1
50+
extension (r: CaptureRoot)
51+
52+
def followAlias(using Context): CaptureRoot = r match
53+
case r: Var if r.alias ne r => r.alias.followAlias
54+
case _ => r
55+
56+
def unifiesWith(other: CaptureRoot)(using Context): Boolean =
57+
r.encloses(other) && other.encloses(r)
58+
59+
def encloses(other: CaptureRoot)(using Context): Boolean =
60+
val (r1, r2) = (followAlias, other.followAlias)
61+
(r1 eq r2) || (r1, r2).match
62+
case (r1: TermRef, r2: TermRef) =>
63+
r2.localRootOwner.isContainedIn(r1.localRootOwner)
64+
case (r1: TermRef, r2: Var) =>
65+
val r1Owner = r1.localRootOwner
66+
if r2.outerLimit.isContainedIn(r1Owner) then true
67+
else if !r2.innerLimit.isContainedIn(r1Owner) then false
6668
else
67-
c2.lowerBound = c1.symbol
68-
c2.lowerLevel = level1
69-
true
70-
else false
71-
case (c1: Var, c2: TermRef) =>
72-
val level2 = c2.ccNestingLevel
73-
if c1.upperLevel <= level2 then
74-
true // no change
75-
else if c1.lowerLevel <= level2 && c1.lowerRoots.forall(isEnclosingRoot(_, c2)) then
76-
if level2 == c1.lowerLevel then
77-
c1.alias = c2
69+
if r2.innerLimit == r1Owner then r2.alias = r1
70+
else r2.outerLimit = r1Owner
71+
true
72+
case (r1: Var, r2: TermRef) =>
73+
val r2Owner = r2.localRootOwner
74+
if r2Owner.isContainedIn(r1.innerLimit) then true
75+
else if !r2Owner.isContainedIn(r1.outerLimit) then false
7876
else
79-
c1.upperBound = c2.symbol
80-
c1.upperLevel = level2
81-
true
82-
else false
83-
case (c1: Var, c2: Var) =>
84-
if c1.upperRoots.contains(c2) then
85-
true // no change
86-
else if c1.lowerLevel > c2.upperLevel then
87-
false // local inconsistency
88-
else
89-
c1.upperRoots += c2 // set early to prevent infinite looping
90-
if c1.lowerRoots.forall(isEnclosingRoot(_, c2))
91-
&& c2.upperRoots.forall(isEnclosingRoot(c1, _))
92-
then
93-
if c1.lowerRoots.contains(c2) then
94-
val c2a = c2.followAlias
95-
if c2a ne c1 then c1.alias = c2a
96-
else
97-
if c1.upperLevel > c2.upperLevel then
98-
c1.upperBound = c2.upperBound
99-
c1.upperLevel = c2.upperLevel
100-
if c2.lowerLevel < c1.lowerLevel then
101-
c2.lowerBound = c1.lowerBound
102-
c2.lowerLevel = c1.lowerLevel
103-
c2.lowerRoots += c1
77+
if r1.outerLimit == r2Owner then r1.alias = r2
78+
else r1.innerLimit = r2Owner
79+
true
80+
case (r1: Var, r2: Var) =>
81+
if r2.outerRoots.contains(r1) then true // no change
82+
else if !r2.innerLimit.isContainedIn(r1.outerLimit) then false // no overlap
83+
else if r1.outerRoots.contains(r2) then // unify
84+
r1.alias = r2
85+
r2.outerLimit = r1.outerLimit.maxNested(r2.outerLimit)
86+
r2.innerLimit = r1.innerLimit.minNested(r2.innerLimit)
10487
true
10588
else
106-
c1.upperRoots -= c2
107-
false
108-
end isEnclosingRoot
89+
r2.outerRoots += r1 // set early to prevent infinite looping
90+
if r1.outerRoots.forall(_.encloses(r2)) then true
91+
else
92+
r2.outerRoots -= r2
93+
false
94+
end encloses
95+
10996
end CaptureRoot
11097

11198

0 commit comments

Comments
 (0)