@@ -145,6 +145,8 @@ object SepCheck:
145
145
146
146
case class PeaksPair (actual : Refs , hidden : Refs )
147
147
148
+ case class DefInfo (tree : ValOrDefDef , symbol : Symbol , hidden : Refs , hiddenPeaks : Refs )
149
+
148
150
class SepCheck (checker : CheckCaptures .CheckerAPI ) extends tpd.TreeTraverser :
149
151
import checker .*
150
152
import SepCheck .*
@@ -154,15 +156,10 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
154
156
*/
155
157
private var defsShadow : Refs = emptyRefs
156
158
157
- /** A map from definitions to their internal result types.
158
- * Populated during separation checking traversal.
159
- */
160
- private val resultType = EqHashMap [Symbol , Type ]()
161
-
162
- /** The previous val or def definitions encountered during separation checking.
163
- * These all enclose and precede the current traversal node.
159
+ /** The previous val or def definitions encountered during separation checking
160
+ * in reverse order. These all enclose and precede the current traversal node.
164
161
*/
165
- private var previousDefs : List [mutable. ListBuffer [ ValOrDefDef ] ] = Nil
162
+ private var previousDefs : List [DefInfo ] = Nil
166
163
167
164
/** The set of references that were consumed so far in the current method */
168
165
private var consumed : MutConsumedSet = MutConsumedSet ()
@@ -402,31 +399,26 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
402
399
403
400
/** Report a use/definition failure, where a previously hidden capability is
404
401
* used again.
405
- * @param tree the tree where the capability is used
406
- * @param used the footprint of all uses of `tree`
407
- * @param globalOverlap the overlap between `used` and all capabilities hidden
408
- * by previous definitions
402
+ * @param tree the tree where the capability is used
403
+ * @param clashing the tree where the capability is previously hidden,
404
+ * or emptyTree if none exists
405
+ * @param used the uses of `tree`
406
+ * @param hidden the hidden set of the clashing def,
407
+ * or the global hidden set if no clashing def exists
409
408
*/
410
- def sepUseError (tree : Tree , used : Refs , globalOverlap : Refs )(using Context ): Unit =
411
- val individualChecks = for mdefs <- previousDefs.iterator; mdef <- mdefs.iterator yield
412
- val hiddenByDef = captures(mdef.tpt).hiddenSet.footprint
413
- val overlap = defUseOverlap(hiddenByDef, used, tree.symbol)
414
- if ! overlap.isEmpty then
415
- def resultStr = if mdef.isInstanceOf [DefDef ] then " result" else " "
416
- report.error(
417
- em """ Separation failure: Illegal access to ${CaptureSet (overlap)} which is hidden by the previous definition
418
- |of ${mdef.symbol} with $resultStr type ${mdef.tpt.nuType}.
419
- |This type hides capabilities ${CaptureSet (hiddenByDef)}""" ,
420
- tree.srcPos)
421
- true
422
- else false
423
- val clashes = individualChecks.filter(identity)
424
- if clashes.hasNext then clashes.next // issues error as a side effect
425
- else report.error(
426
- em """ Separation failure: Illegal access to ${CaptureSet (globalOverlap)} which is hidden by some previous definitions
427
- |No clashing definitions were found. This might point to an internal error. """ ,
428
- tree.srcPos)
429
- end sepUseError
409
+ def sepUseError (tree : Tree , clashingDef : ValOrDefDef | Null , used : Refs , hidden : Refs )(using Context ): Unit =
410
+ if clashingDef != null then
411
+ def resultStr = if clashingDef.isInstanceOf [DefDef ] then " result" else " "
412
+ report.error(
413
+ em """ Separation failure: Illegal access to ${overlapStr(hidden, used)} which is hidden by the previous definition
414
+ |of ${clashingDef.symbol} with $resultStr type ${clashingDef.tpt.nuType}.
415
+ |This type hides capabilities ${CaptureSet (hidden)}""" ,
416
+ tree.srcPos)
417
+ else
418
+ report.error(
419
+ em """ Separation failure: illegal access to ${overlapStr(hidden, used)} which is hidden by some previous definitions
420
+ |No clashing definitions were found. This might point to an internal error. """ ,
421
+ tree.srcPos)
430
422
431
423
/** Report a failure where a previously consumed capability is used again,
432
424
* @param ref the capability that is used after being consumed
@@ -531,33 +523,37 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
531
523
currentPeaks.hidden ++ argPeaks.hidden)
532
524
end checkApply
533
525
534
- /** The def/use overlap between the references `hiddenByDef` hidden by
535
- * a previous definition and the `used` set of a tree with symbol `sym`.
536
- * Deduct any capabilities referred to or hidden by the (result-) type of `sym`.
537
- */
538
- def defUseOverlap (hiddenByDef : Refs , used : Refs , sym : Symbol )(using Context ): Refs =
539
- val overlap = hiddenByDef.overlapWith(used)
540
- resultType.get(sym) match
541
- case Some (tp) if ! overlap.isEmpty =>
542
- val declared = tp.captureSet.elems
543
- overlap.deduct(declared.footprint).deduct(declared.hiddenSet.footprint)
544
- case _ =>
545
- overlap
546
-
547
526
/** 1. Check that the capabilities used at `tree` don't overlap with
548
527
* capabilities hidden by a previous definition.
549
528
* 2. Also check that none of the used capabilities was consumed before.
550
529
*/
551
- def checkUse (tree : Tree )(using Context ) =
552
- val used = tree.markedFree
553
- if ! used.elems.isEmpty then
554
- val usedFootprint = used.elems.footprint
555
- val overlap = defUseOverlap(defsShadow, usedFootprint, tree.symbol)
556
- if ! overlap.isEmpty then
557
- sepUseError(tree, usedFootprint, overlap)
558
- for ref <- used.elems do
530
+ def checkUse (tree : Tree )(using Context ): Unit =
531
+ val used = tree.markedFree.elems
532
+ if ! used.isEmpty then
533
+ val usedPeaks = used.peaks
534
+ val overlap = defsShadow.peaks.sharedWith(usedPeaks)
535
+ if ! defsShadow.peaks.sharedWith(usedPeaks).isEmpty then
536
+ val sym = tree.symbol
537
+
538
+ def findClashing (prevDefs : List [DefInfo ]): Option [DefInfo ] = prevDefs match
539
+ case prevDef :: prevDefs1 =>
540
+ if prevDef.symbol == sym then Some (prevDef)
541
+ else if ! prevDef.hiddenPeaks.sharedWith(usedPeaks).isEmpty then Some (prevDef)
542
+ else findClashing(prevDefs1)
543
+ case Nil =>
544
+ None
545
+
546
+ findClashing(previousDefs) match
547
+ case Some (clashing) =>
548
+ if clashing.symbol != sym then
549
+ sepUseError(tree, clashing.tree, used, clashing.hidden)
550
+ case None =>
551
+ sepUseError(tree, null , used, defsShadow)
552
+
553
+ for ref <- used do
559
554
val pos = consumed.get(ref)
560
555
if pos != null then consumeError(ref, pos, tree.srcPos)
556
+ end checkUse
561
557
562
558
/** If `tp` denotes some version of a singleton type `x.type` the set `{x}`
563
559
* otherwise the empty set.
@@ -840,6 +836,10 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
840
836
case tree : Apply => tree.symbol == defn.Caps_unsafeAssumeSeparate
841
837
case _ => false
842
838
839
+ def pushDef (tree : ValOrDefDef , hiddenByDef : Refs )(using Context ): Unit =
840
+ defsShadow ++= hiddenByDef
841
+ previousDefs = DefInfo (tree, tree.symbol, hiddenByDef, hiddenByDef.peaks) :: previousDefs
842
+
843
843
/** Check (result-) type of `tree` for separation conditions using `checkType`.
844
844
* Excluded are parameters and definitions that have an =unsafeAssumeSeparate
845
845
* application as right hand sides.
@@ -848,11 +848,18 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
848
848
def checkValOrDefDef (tree : ValOrDefDef )(using Context ): Unit =
849
849
if ! tree.symbol.isOneOf(TermParamOrAccessor ) && ! isUnsafeAssumeSeparate(tree.rhs) then
850
850
checkType(tree.tpt, tree.symbol)
851
- if previousDefs.nonEmpty then
852
- capt.println(i " sep check def ${tree.symbol}: ${tree.tpt} with ${captures(tree.tpt).hiddenSet.footprint}" )
853
- defsShadow ++= captures(tree.tpt).hiddenSet.deductSymRefs(tree.symbol).footprint
854
- resultType(tree.symbol) = tree.tpt.nuType
855
- previousDefs.head += tree
851
+ capt.println(i " sep check def ${tree.symbol}: ${tree.tpt} with ${captures(tree.tpt).hiddenSet.footprint}" )
852
+ pushDef(tree, captures(tree.tpt).hiddenSet.deductSymRefs(tree.symbol))
853
+
854
+ def inSection [T ](op : => T )(using Context ): T =
855
+ val savedDefsShadow = defsShadow
856
+ val savedPrevionsDefs = previousDefs
857
+ try op
858
+ finally
859
+ previousDefs = savedPrevionsDefs
860
+ defsShadow = savedDefsShadow
861
+
862
+ def traverseSection [T ](tree : Tree )(using Context ) = inSection(traverseChildren(tree))
856
863
857
864
/** Traverse `tree` and perform separation checks everywhere */
858
865
def traverse (tree : Tree )(using Context ): Unit =
@@ -870,19 +877,17 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
870
877
tree.tpe match
871
878
case _ : MethodOrPoly =>
872
879
case _ => traverseApply(tree, Nil )
873
- case tree : Block =>
874
- val saved = defsShadow
875
- previousDefs = mutable.ListBuffer () :: previousDefs
876
- try traverseChildren(tree)
877
- finally
878
- previousDefs = previousDefs.tail
879
- defsShadow = saved
880
+ case _ : Block | _ : Template =>
881
+ traverseSection(tree)
880
882
case tree : ValDef =>
881
883
traverseChildren(tree)
882
884
checkValOrDefDef(tree)
883
885
case tree : DefDef =>
884
- withFreshConsumed :
885
- traverseChildren(tree)
886
+ inSection :
887
+ withFreshConsumed :
888
+ for params <- tree.paramss; case param : ValDef <- params do
889
+ pushDef(param, emptyRefs)
890
+ traverseChildren(tree)
886
891
checkValOrDefDef(tree)
887
892
case If (cond, thenp, elsep) =>
888
893
traverse(cond)
0 commit comments