@@ -44,12 +44,24 @@ import Capabilities.*
4444 */  
4545sealed  abstract  class  CaptureSet  extends  Showable : 
4646  import  CaptureSet .* 
47+   import  Mutability .* 
4748
4849  /**  The elements of this capture set. For capture variables, 
4950   *  the elements known so far. 
5051   */  
5152  def  elems :  Refs 
5253
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+ 
5365  /**  Is this capture set constant (i.e. not an unsolved capture variable)? 
5466   *  Solved capture variables count as constant. 
5567   */  
@@ -127,6 +139,13 @@ sealed abstract class CaptureSet extends Showable:
127139  final  def  isExclusive (using  Context ):  Boolean  = 
128140    elems.exists(_.isExclusive)
129141
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+ 
130149  final  def  keepAlways :  Boolean  =  this .isInstanceOf [EmptyWithProvenance ]
131150
132151  def  failWith (fail : TypeComparer .ErrorNote )(using  Context ):  false  = 
@@ -164,6 +183,9 @@ sealed abstract class CaptureSet extends Showable:
164183      //  through this method.
165184      newElems.forall(tryInclude(_, origin))
166185
186+   protected  def  mutableToReader (origin : CaptureSet )(using  Context ):  Boolean  = 
187+     if  mutability ==  Mutable  then  toReader() else  true 
188+ 
167189  /**  Add an element to this capture set, assuming it is not already accounted for, 
168190   *  and omitting any mapping or filtering. 
169191   * 
@@ -188,6 +210,8 @@ sealed abstract class CaptureSet extends Showable:
188210   */  
189211  protected  def  addThisElem (elem : Capability )(using  Context , VarState ):  Boolean 
190212
213+   protected  def  toReader ()(using  Context ):  Boolean 
214+ 
191215  protected  def  addIfHiddenOrFail (elem : Capability )(using  ctx : Context , vs : VarState ):  Boolean  = 
192216    elems.exists(_.maxSubsumes(elem, canAddHidden =  true ))
193217    ||  failWith(IncludeFailure (this , elem))
@@ -258,7 +282,12 @@ sealed abstract class CaptureSet extends Showable:
258282
259283  /**  The subcapturing test, using a given VarState */  
260284  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 
262291      addDependent(that)
263292    else 
264293      varState.rollBack()
@@ -271,6 +300,14 @@ sealed abstract class CaptureSet extends Showable:
271300       this .subCaptures(that, VarState .Separate )
272301    &&  that.subCaptures(this , VarState .Separate )
273302
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+ 
274311  /**  The smallest capture set (via <:<) that is a superset of both 
275312   *  `this` and `that` 
276313   */  
@@ -372,7 +409,10 @@ sealed abstract class CaptureSet extends Showable:
372409
373410  def  maybe (using  Context ):  CaptureSet  =  map(MaybeMap ())
374411
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
376416
377417  /**  A bad root `elem` is inadmissible as a member of this set. What is a bad roots depends 
378418   *  on the value of `rootLimit`. 
@@ -445,6 +485,25 @@ object CaptureSet:
445485  type  Vars  =  SimpleIdentitySet [Var ]
446486  type  Deps  =  SimpleIdentitySet [CaptureSet ]
447487
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+ 
448507  /**  If set to `true`, capture stack traces that tell us where sets are created */  
449508  private  final  val  debugSets  =  false 
450509
@@ -496,6 +555,8 @@ object CaptureSet:
496555        false 
497556      }
498557
558+     def  toReader ()(using  Context ) =  false 
559+ 
499560    def  addDependent (cs : CaptureSet )(using  Context , VarState ) =  true 
500561
501562    def  upperApprox (origin : CaptureSet )(using  Context ):  CaptureSet  =  this 
@@ -506,6 +567,17 @@ object CaptureSet:
506567
507568    def  owner  =  NoSymbol 
508569
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+ 
509581    override  def  toString  =  elems.toString
510582  end  Const 
511583
@@ -524,6 +596,7 @@ object CaptureSet:
524596  object  Fluid  extends  Const (emptyRefs): 
525597    override  def  isAlwaysEmpty (using  Context ) =  false 
526598    override  def  addThisElem (elem : Capability )(using  Context , VarState ) =  true 
599+     override  def  toReader ()(using  Context ) =  true 
527600    override  def  accountsFor (x : Capability )(using  Context )(using  VarState ):  Boolean  =  true 
528601    override  def  mightAccountFor (x : Capability )(using  Context ):  Boolean  =  true 
529602    override  def  toString  =  " <fluid>" 
@@ -563,6 +636,9 @@ object CaptureSet:
563636     */  
564637    var  deps :  Deps  =  SimpleIdentitySet .empty
565638
639+     def  setMutable ()(using  Context ):  Unit  = 
640+       mutability =  Mutable 
641+ 
566642    def  isConst (using  Context ) =  solved >=  ccState.iterationId
567643    def  isAlwaysEmpty (using  Context ) =  isConst &&  elems.isEmpty
568644    def  isProvisionallySolved (using  Context ):  Boolean  =  solved >  0  &&  solved !=  Int .MaxValue 
@@ -640,6 +716,13 @@ object CaptureSet:
640716            case  note : IncludeFailure  =>  note.addToTrace(this )
641717        res
642718
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+ 
643726    private  def  isPartOf (binder : Type )(using  Context ):  Boolean  = 
644727      val  find  =  new  TypeAccumulator [Boolean ]: 
645728        def  apply (b : Boolean , t : Type ) = 
@@ -744,6 +827,8 @@ object CaptureSet:
744827    def  markSolved (provisional : Boolean )(using  Context ):  Unit  = 
745828      solved =  if  provisional then  ccState.iterationId else  Int .MaxValue 
746829      deps.foreach(_.propagateSolved(provisional))
830+       if  mutability ==  Mutable  &&  ! maybeExclusive then  mutability =  Reader 
831+ 
747832
748833    var  skippedMaps :  Set [TypeMap ] =  Set .empty
749834
@@ -803,8 +888,14 @@ object CaptureSet:
803888    /**  The variable from which this variable is derived */  
804889    def  source :  Var 
805890
891+     mutability =  source.mutability
892+ 
806893    addAsDependentTo(source)
807894
895+     override  def  mutableToReader (origin : CaptureSet )(using  Context ):  Boolean  = 
896+       super .mutableToReader(origin)
897+       &&  ((origin eq source) ||  source.mutableToReader(this ))
898+ 
808899    override  def  propagateSolved (provisional : Boolean )(using  Context ) = 
809900      if  source.isConst &&  ! isConst then  markSolved(provisional)
810901
@@ -904,6 +995,7 @@ object CaptureSet:
904995  extends  Var (initialElems =  cs1.elems ++  cs2.elems): 
905996    addAsDependentTo(cs1)
906997    addAsDependentTo(cs2)
998+     mutability =  cs1.mutability |  cs2.mutability
907999
9081000    override  def  tryInclude (elem : Capability , origin : CaptureSet )(using  Context , VarState ):  Boolean  = 
9091001      if  accountsFor(elem) then  true 
@@ -918,6 +1010,15 @@ object CaptureSet:
9181010          else  res
9191011        else  res
9201012
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+ 
9211022    override  def  propagateSolved (provisional : Boolean )(using  Context ) = 
9221023      if  cs1.isConst &&  cs2.isConst &&  ! isConst then  markSolved(provisional)
9231024  end  Union 
@@ -928,6 +1029,7 @@ object CaptureSet:
9281029    addAsDependentTo(cs2)
9291030    deps +=  cs1
9301031    deps +=  cs2
1032+     mutability =  cs1.mutability &  cs2.mutability
9311033
9321034    override  def  tryInclude (elem : Capability , origin : CaptureSet )(using  Context , VarState ):  Boolean  = 
9331035      val  inIntersection  = 
@@ -940,6 +1042,11 @@ object CaptureSet:
9401042        &&  ((origin eq cs1) ||  cs1.tryInclude(elem, this ))
9411043        &&  ((origin eq cs2) ||  cs2.tryInclude(elem, this ))
9421044
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+ 
9431050    override  def  computeApprox (origin : CaptureSet )(using  Context ):  CaptureSet  = 
9441051      if  (origin eq cs1) ||  (origin eq cs2) then 
9451052        //  it's a combination of origin with some other set, so not a superset of `origin`,
0 commit comments