@@ -44,12 +44,24 @@ import Capabilities.*
44
44
*/
45
45
sealed abstract class CaptureSet extends Showable :
46
46
import CaptureSet .*
47
+ import Mutability .*
47
48
48
49
/** The elements of this capture set. For capture variables,
49
50
* the elements known so far.
50
51
*/
51
52
def elems : Refs
52
53
54
+ protected var myMut : Mutability = Ignored
55
+
56
+ /** The access kind of this CaptureSet. */
57
+ def mutability (using Context ): Mutability = myMut
58
+
59
+ def mutability_= (x : Mutability ): Unit =
60
+ myMut = x
61
+
62
+ /** Mark this capture set as belonging to a Mutable type. */
63
+ def setMutable ()(using Context ): Unit
64
+
53
65
/** Is this capture set constant (i.e. not an unsolved capture variable)?
54
66
* Solved capture variables count as constant.
55
67
*/
@@ -127,6 +139,13 @@ sealed abstract class CaptureSet extends Showable:
127
139
final def isExclusive (using Context ): Boolean =
128
140
elems.exists(_.isExclusive)
129
141
142
+ /** Similar to isExlusive, but also includes capture set variables
143
+ * with unknown status.
144
+ */
145
+ final def maybeExclusive (using Context ): Boolean = reporting.trace(i " mabe exclusive $this" ):
146
+ if isConst then elems.exists(_.maybeExclusive)
147
+ else mutability != ReadOnly
148
+
130
149
final def keepAlways : Boolean = this .isInstanceOf [EmptyWithProvenance ]
131
150
132
151
def failWith (fail : TypeComparer .ErrorNote )(using Context ): false =
@@ -164,6 +183,9 @@ sealed abstract class CaptureSet extends Showable:
164
183
// through this method.
165
184
newElems.forall(tryInclude(_, origin))
166
185
186
+ protected def mutableToReader (origin : CaptureSet )(using Context ): Boolean =
187
+ if mutability == Mutable then toReader() else true
188
+
167
189
/** Add an element to this capture set, assuming it is not already accounted for,
168
190
* and omitting any mapping or filtering.
169
191
*
@@ -188,6 +210,8 @@ sealed abstract class CaptureSet extends Showable:
188
210
*/
189
211
protected def addThisElem (elem : Capability )(using Context , VarState ): Boolean
190
212
213
+ protected def toReader ()(using Context ): Boolean
214
+
191
215
protected def addIfHiddenOrFail (elem : Capability )(using ctx : Context , vs : VarState ): Boolean =
192
216
elems.exists(_.maxSubsumes(elem, canAddHidden = true ))
193
217
|| failWith(IncludeFailure (this , elem))
@@ -258,7 +282,12 @@ sealed abstract class CaptureSet extends Showable:
258
282
259
283
/** The subcapturing test, using a given VarState */
260
284
final def subCaptures (that : CaptureSet )(using ctx : Context , vs : VarState = VarState ()): Boolean =
261
- if that.tryInclude(elems, this ) then
285
+ val this1 = this .adaptMutability(that)
286
+ if this1 == null then false
287
+ else if this1 ne this then
288
+ capt.println(i " WIDEN ro $this with ${this .mutability} <:< $that with ${that.mutability} to $this1" )
289
+ this1.subCaptures(that, vs)
290
+ else if that.tryInclude(elems, this ) then
262
291
addDependent(that)
263
292
else
264
293
varState.rollBack()
@@ -271,6 +300,14 @@ sealed abstract class CaptureSet extends Showable:
271
300
this .subCaptures(that, VarState .Separate )
272
301
&& that.subCaptures(this , VarState .Separate )
273
302
303
+ def adaptMutability (that : CaptureSet )(using Context ): CaptureSet | Null =
304
+ val m1 = this .mutability
305
+ val m2 = that.mutability
306
+ if m1 == Mutable && m2 == Reader then this .readOnly
307
+ else if m1 == Reader && m2 == Mutable then
308
+ if that.toReader() then this else null
309
+ else this
310
+
274
311
/** The smallest capture set (via <:<) that is a superset of both
275
312
* `this` and `that`
276
313
*/
@@ -372,7 +409,10 @@ sealed abstract class CaptureSet extends Showable:
372
409
373
410
def maybe (using Context ): CaptureSet = map(MaybeMap ())
374
411
375
- def readOnly (using Context ): CaptureSet = map(ReadOnlyMap ())
412
+ def readOnly (using Context ): CaptureSet =
413
+ val res = map(ReadOnlyMap ())
414
+ if mutability != Ignored then res.mutability = Reader
415
+ res
376
416
377
417
/** A bad root `elem` is inadmissible as a member of this set. What is a bad roots depends
378
418
* on the value of `rootLimit`.
@@ -445,6 +485,25 @@ object CaptureSet:
445
485
type Vars = SimpleIdentitySet [Var ]
446
486
type Deps = SimpleIdentitySet [CaptureSet ]
447
487
488
+ enum Mutability :
489
+ case Mutable , Reader , Ignored
490
+
491
+ def | (that : Mutability ): Mutability =
492
+ if this == that then this
493
+ else if this == Ignored || that == Ignored then Ignored
494
+ else if this == Reader || that == Reader then Reader
495
+ else Mutable
496
+
497
+ def & (that : Mutability ): Mutability =
498
+ if this == that then this
499
+ else if this == Ignored then that
500
+ else if that == Ignored then this
501
+ else if this == Reader then that
502
+ else this
503
+
504
+ end Mutability
505
+ import Mutability .*
506
+
448
507
/** If set to `true`, capture stack traces that tell us where sets are created */
449
508
private final val debugSets = false
450
509
@@ -496,6 +555,8 @@ object CaptureSet:
496
555
false
497
556
}
498
557
558
+ def toReader ()(using Context ) = false
559
+
499
560
def addDependent (cs : CaptureSet )(using Context , VarState ) = true
500
561
501
562
def upperApprox (origin : CaptureSet )(using Context ): CaptureSet = this
@@ -506,6 +567,17 @@ object CaptureSet:
506
567
507
568
def owner = NoSymbol
508
569
570
+ private var isComplete = true
571
+
572
+ def setMutable ()(using Context ): Unit =
573
+ isComplete = false // delay computation of Mutability status
574
+
575
+ override def mutability (using Context ): Mutability =
576
+ if ! isComplete then
577
+ myMut = if maybeExclusive then Mutable else Reader
578
+ isComplete = true
579
+ myMut
580
+
509
581
override def toString = elems.toString
510
582
end Const
511
583
@@ -524,6 +596,7 @@ object CaptureSet:
524
596
object Fluid extends Const (emptyRefs):
525
597
override def isAlwaysEmpty (using Context ) = false
526
598
override def addThisElem (elem : Capability )(using Context , VarState ) = true
599
+ override def toReader ()(using Context ) = true
527
600
override def accountsFor (x : Capability )(using Context )(using VarState ): Boolean = true
528
601
override def mightAccountFor (x : Capability )(using Context ): Boolean = true
529
602
override def toString = " <fluid>"
@@ -563,6 +636,9 @@ object CaptureSet:
563
636
*/
564
637
var deps : Deps = SimpleIdentitySet .empty
565
638
639
+ def setMutable ()(using Context ): Unit =
640
+ mutability = Mutable
641
+
566
642
def isConst (using Context ) = solved >= ccState.iterationId
567
643
def isAlwaysEmpty (using Context ) = isConst && elems.isEmpty
568
644
def isProvisionallySolved (using Context ): Boolean = solved > 0 && solved != Int .MaxValue
@@ -640,6 +716,13 @@ object CaptureSet:
640
716
case note : IncludeFailure => note.addToTrace(this )
641
717
res
642
718
719
+ final def toReader ()(using Context ) =
720
+ if isConst then false // TODO add error note when failing?
721
+ else
722
+ mutability = Reader
723
+ TypeComparer .logUndoAction(() => mutability = Mutable )
724
+ deps.forall(_.mutableToReader(this ))
725
+
643
726
private def isPartOf (binder : Type )(using Context ): Boolean =
644
727
val find = new TypeAccumulator [Boolean ]:
645
728
def apply (b : Boolean , t : Type ) =
@@ -744,6 +827,8 @@ object CaptureSet:
744
827
def markSolved (provisional : Boolean )(using Context ): Unit =
745
828
solved = if provisional then ccState.iterationId else Int .MaxValue
746
829
deps.foreach(_.propagateSolved(provisional))
830
+ if mutability == Mutable && ! maybeExclusive then mutability = Reader
831
+
747
832
748
833
var skippedMaps : Set [TypeMap ] = Set .empty
749
834
@@ -803,8 +888,14 @@ object CaptureSet:
803
888
/** The variable from which this variable is derived */
804
889
def source : Var
805
890
891
+ mutability = source.mutability
892
+
806
893
addAsDependentTo(source)
807
894
895
+ override def mutableToReader (origin : CaptureSet )(using Context ): Boolean =
896
+ super .mutableToReader(origin)
897
+ && ((origin eq source) || source.mutableToReader(this ))
898
+
808
899
override def propagateSolved (provisional : Boolean )(using Context ) =
809
900
if source.isConst && ! isConst then markSolved(provisional)
810
901
@@ -904,6 +995,7 @@ object CaptureSet:
904
995
extends Var (initialElems = cs1.elems ++ cs2.elems):
905
996
addAsDependentTo(cs1)
906
997
addAsDependentTo(cs2)
998
+ mutability = cs1.mutability | cs2.mutability
907
999
908
1000
override def tryInclude (elem : Capability , origin : CaptureSet )(using Context , VarState ): Boolean =
909
1001
if accountsFor(elem) then true
@@ -918,6 +1010,15 @@ object CaptureSet:
918
1010
else res
919
1011
else res
920
1012
1013
+ override def mutableToReader (origin : CaptureSet )(using Context ): Boolean =
1014
+ super .mutableToReader(origin)
1015
+ && {
1016
+ if (origin eq cs1) || (origin eq cs2) then true
1017
+ else if cs1.isConst && cs1.mutability == Mutable then cs2.mutableToReader(this )
1018
+ else if cs2.isConst && cs2.mutability == Mutable then cs1.mutableToReader(this )
1019
+ else true
1020
+ }
1021
+
921
1022
override def propagateSolved (provisional : Boolean )(using Context ) =
922
1023
if cs1.isConst && cs2.isConst && ! isConst then markSolved(provisional)
923
1024
end Union
@@ -928,6 +1029,7 @@ object CaptureSet:
928
1029
addAsDependentTo(cs2)
929
1030
deps += cs1
930
1031
deps += cs2
1032
+ mutability = cs1.mutability & cs2.mutability
931
1033
932
1034
override def tryInclude (elem : Capability , origin : CaptureSet )(using Context , VarState ): Boolean =
933
1035
val inIntersection =
@@ -940,6 +1042,11 @@ object CaptureSet:
940
1042
&& ((origin eq cs1) || cs1.tryInclude(elem, this ))
941
1043
&& ((origin eq cs2) || cs2.tryInclude(elem, this ))
942
1044
1045
+ override def mutableToReader (origin : CaptureSet )(using Context ): Boolean =
1046
+ super .mutableToReader(origin)
1047
+ && ((origin eq cs1) || cs1.mutableToReader(this ))
1048
+ && ((origin eq cs2) || cs2.mutableToReader(this ))
1049
+
943
1050
override def computeApprox (origin : CaptureSet )(using Context ): CaptureSet =
944
1051
if (origin eq cs1) || (origin eq cs2) then
945
1052
// it's a combination of origin with some other set, so not a superset of `origin`,
0 commit comments