@@ -263,13 +263,19 @@ sealed abstract class CaptureSet extends Showable:
263
263
*/
264
264
def mightAccountFor (x : Capability )(using Context ): Boolean =
265
265
reporting.trace(i " $this mightAccountFor $x, ${x.captureSetOfInfo}? " , show = true ):
266
- CCState .withCollapsedFresh: // OK here since we opportunistically choose an alternative which gets checked later
266
+ CCState .withCollapsedFresh:
267
+ // withCollapsedFresh should be dropped. The problem is that since our level checking
268
+ // does not deal with classes well, we get false negatives here. Observed in the line
269
+ //
270
+ // stateFromIteratorConcatSuffix(it)(flatMapImpl(rest, f).state))))
271
+ //
272
+ // in cc-lib's LazyListIterable.scala.
267
273
TypeComparer .noNotes:
268
274
elems.exists(_.subsumes(x)(using ctx)(using VarState .ClosedUnrecorded ))
269
275
|| ! x.isTerminalCapability
270
276
&& {
271
- val elems = x.captureSetOfInfo.elems
272
- ! elems .isEmpty && elems .forall(mightAccountFor)
277
+ val xelems = x.captureSetOfInfo.elems
278
+ ! xelems .isEmpty && xelems .forall(mightAccountFor)
273
279
}
274
280
275
281
/** A more optimistic version of subCaptures used to choose one of two typing rules
@@ -436,9 +442,18 @@ sealed abstract class CaptureSet extends Showable:
436
442
def adoptClassifier (cls : ClassSymbol )(using Context ): Unit =
437
443
for elem <- elems do
438
444
elem.stripReadOnly match
439
- case fresh : FreshCap => fresh.hiddenSet. adoptClassifier(cls)
445
+ case fresh : FreshCap => fresh.adoptClassifier(cls, freeze = isConst )
440
446
case _ =>
441
447
448
+ /** All capabilities of this set except those Termrefs and FreshCaps that
449
+ * are bound by `mt`.
450
+ */
451
+ def freeInResult (mt : MethodicType )(using Context ): CaptureSet =
452
+ filter :
453
+ case TermParamRef (binder, _) => binder ne mt
454
+ case ResultCap (binder) => binder ne mt
455
+ case _ => true
456
+
442
457
/** A bad root `elem` is inadmissible as a member of this set. What is a bad roots depends
443
458
* on the value of `rootLimit`.
444
459
* If the limit is null, all capture roots are good.
@@ -611,9 +626,13 @@ object CaptureSet:
611
626
then i " under-approximating the result of mapping $ref to $mapped"
612
627
else " "
613
628
629
+ private def capImpliedByCapability (parent : Type )(using Context ): Capability =
630
+ if parent.derivesFromExclusive then GlobalCap .readOnly else GlobalCap
631
+
614
632
/* The same as {cap.rd} but generated implicitly for references of Capability subtypes.
615
633
*/
616
- case class CSImpliedByCapability () extends Const (SimpleIdentitySet (GlobalCap .readOnly))
634
+ class CSImpliedByCapability (parent : Type )(using @ constructorOnly ctx : Context )
635
+ extends Const (SimpleIdentitySet (capImpliedByCapability(parent)))
617
636
618
637
/** A special capture set that gets added to the types of symbols that were not
619
638
* themselves capture checked, in order to admit arbitrary corresponding capture
@@ -692,6 +711,9 @@ object CaptureSet:
692
711
*/
693
712
private [CaptureSet ] var rootLimit : Symbol | Null = null
694
713
714
+ def isBadRoot (elem : Capability )(using Context ): Boolean =
715
+ isBadRoot(rootLimit, elem)
716
+
695
717
private var myClassifier : ClassSymbol = defn.AnyClass
696
718
def classifier : ClassSymbol = myClassifier
697
719
@@ -743,13 +765,14 @@ object CaptureSet:
743
765
else if ! levelOK(elem) then
744
766
failWith(IncludeFailure (this , elem, levelError = true )) // or `elem` is not visible at the level of the set.
745
767
else if ! elem.tryClassifyAs(classifier) then
768
+ // println(i"cannot classify $elem as $classifier, ${elem.asInstanceOf[CoreCapability].classifier}")
746
769
failWith(IncludeFailure (this , elem))
747
770
else
748
771
// id == 108 then assert(false, i"trying to add $elem to $this")
749
772
assert(elem.isWellformed, elem)
750
773
assert(! this .isInstanceOf [HiddenSet ] || summon[VarState ].isSeparating, summon[VarState ])
751
774
includeElem(elem)
752
- if isBadRoot(rootLimit, elem) then
775
+ if isBadRoot(elem) then
753
776
rootAddedHandler()
754
777
val normElem = if isMaybeSet then elem else elem.stripMaybe
755
778
// assert(id != 5 || elems.size != 3, this)
@@ -778,7 +801,7 @@ object CaptureSet:
778
801
case _ => foldOver(b, t)
779
802
find(false , binder)
780
803
781
- private def levelOK (elem : Capability )(using Context ): Boolean = elem match
804
+ def levelOK (elem : Capability )(using Context ): Boolean = elem match
782
805
case _ : FreshCap =>
783
806
! level.isDefined
784
807
|| ccState.symLevel(elem.ccOwner) <= level
@@ -1246,7 +1269,13 @@ object CaptureSet:
1246
1269
* when a subsumes check decides that an existential variable `ex` cannot be
1247
1270
* instantiated to the other capability `other`.
1248
1271
*/
1249
- case class ExistentialSubsumesFailure (val ex : ResultCap , val other : Capability ) extends ErrorNote
1272
+ case class ExistentialSubsumesFailure (val ex : ResultCap , val other : Capability ) extends ErrorNote :
1273
+ def description (using Context ): String =
1274
+ def reason =
1275
+ if other.isTerminalCapability then " "
1276
+ else " since that capability is not a `Sharable` capability"
1277
+ i """ the existential capture root in ${ex.originalBinder.resType}
1278
+ |cannot subsume the capability $other$reason. """
1250
1279
1251
1280
/** Failure indicating that `elem` cannot be included in `cs` */
1252
1281
case class IncludeFailure (cs : CaptureSet , elem : Capability , levelError : Boolean = false ) extends ErrorNote , Showable :
@@ -1258,6 +1287,38 @@ object CaptureSet:
1258
1287
res.myTrace = cs1 :: this .myTrace
1259
1288
res
1260
1289
1290
+ def description (using Context ): String =
1291
+ def why =
1292
+ val reasons = cs.elems.toList.collect:
1293
+ case c : FreshCap if ! c.acceptsLevelOf(elem) =>
1294
+ i " $elem${elem.levelOwner.qualString(" in" )} is not visible from $c${c.ccOwner.qualString(" in" )}"
1295
+ case c : FreshCap if ! elem.tryClassifyAs(c.hiddenSet.classifier) =>
1296
+ i " $c is classified as ${c.hiddenSet.classifier} but $elem is not "
1297
+ if reasons.isEmpty then " "
1298
+ else reasons.mkString(" \n because " , " \n and " , " " )
1299
+ cs match
1300
+ case cs : Var =>
1301
+ if ! cs.levelOK(elem) then
1302
+ val levelStr = elem match
1303
+ case ref : TermRef => i " , defined in ${ref.symbol.maybeOwner}"
1304
+ case _ => " "
1305
+ i """ capability ${elem}$levelStr
1306
+ |cannot be included in outer capture set $cs"""
1307
+ else if ! elem.tryClassifyAs(cs.classifier) then
1308
+ i """ capability ${elem} is not classified as ${cs.classifier}, therefore it
1309
+ |cannot be included in capture set $cs of ${cs.classifier} elements """
1310
+ else if cs.isBadRoot(elem) then
1311
+ elem match
1312
+ case elem : FreshCap =>
1313
+ i """ local capability $elem created in ${elem.ccOwner}
1314
+ |cannot be included in outer capture set $cs"""
1315
+ case _ =>
1316
+ i " universal capability $elem cannot be included in capture set $cs"
1317
+ else
1318
+ i " capability $elem cannot be included in capture set $cs"
1319
+ case _ =>
1320
+ i " capability $elem is not included in capture set $cs$why"
1321
+
1261
1322
override def toText (printer : Printer ): Text =
1262
1323
inContext(printer.printerContext):
1263
1324
if levelError then
@@ -1274,7 +1335,11 @@ object CaptureSet:
1274
1335
* @param lo the lower type of the orginal type comparison, or NoType if not known
1275
1336
* @param hi the upper type of the orginal type comparison, or NoType if not known
1276
1337
*/
1277
- case class MutAdaptFailure (cs : CaptureSet , lo : Type = NoType , hi : Type = NoType ) extends ErrorNote
1338
+ case class MutAdaptFailure (cs : CaptureSet , lo : Type = NoType , hi : Type = NoType ) extends ErrorNote :
1339
+ def description (using Context ): String =
1340
+ def ofType (tp : Type ) = if tp.exists then i " of the mutable type $tp" else " of a mutable type"
1341
+ i """ $cs is an exclusive capture set ${ofType(hi)},
1342
+ |it cannot subsume a read-only capture set ${ofType(lo)}. """
1278
1343
1279
1344
/** A VarState serves as a snapshot mechanism that can undo
1280
1345
* additions of elements or super sets if an operation fails
@@ -1487,14 +1552,10 @@ object CaptureSet:
1487
1552
// `ref` will not seem subsumed by other capabilities in a `++`.
1488
1553
universal
1489
1554
case c : CoreCapability =>
1490
- ofType(c.underlying, followResult = false )
1555
+ ofType(c.underlying, followResult = true )
1491
1556
1492
1557
/** Capture set of a type
1493
1558
* @param followResult If true, also include capture sets of function results.
1494
- * This mode is currently not used. It could be interesting
1495
- * when we change the system so that the capture set of a function
1496
- * is the union of the capture sets if its span.
1497
- * In this case we should use `followResult = true` in the call in ofInfo above.
1498
1559
*/
1499
1560
def ofType (tp : Type , followResult : Boolean )(using Context ): CaptureSet =
1500
1561
def recur (tp : Type ): CaptureSet = trace(i " ofType $tp, ${tp.getClass} $followResult" , show = true ):
@@ -1510,13 +1571,9 @@ object CaptureSet:
1510
1571
recur(parent) ++ refs
1511
1572
case tp @ AnnotatedType (parent, ann) if ann.symbol.isRetains =>
1512
1573
recur(parent) ++ ann.tree.toCaptureSet
1513
- case tpd @ defn.RefinedFunctionOf (rinfo : MethodType ) if followResult =>
1574
+ case tpd @ defn.RefinedFunctionOf (rinfo : MethodOrPoly ) if followResult =>
1514
1575
ofType(tpd.parent, followResult = false ) // pick up capture set from parent type
1515
- ++ recur(rinfo.resType) // add capture set of result
1516
- .filter:
1517
- case TermParamRef (binder, _) => binder ne rinfo
1518
- case ResultCap (binder) => binder ne rinfo
1519
- case _ => true
1576
+ ++ recur(rinfo.resType).freeInResult(rinfo) // add capture set of result
1520
1577
case tpd @ AppliedType (tycon, args) =>
1521
1578
if followResult && defn.isNonRefinedFunction(tpd) then
1522
1579
recur(args.last)
0 commit comments