@@ -145,6 +145,8 @@ object SepCheck:
145145
146146 case class PeaksPair (actual : Refs , hidden : Refs )
147147
148+ case class DefInfo (tree : ValOrDefDef , symbol : Symbol , hidden : Refs , hiddenPeaks : Refs )
149+
148150class SepCheck (checker : CheckCaptures .CheckerAPI ) extends tpd.TreeTraverser :
149151 import checker .*
150152 import SepCheck .*
@@ -154,15 +156,10 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
154156 */
155157 private var defsShadow : Refs = emptyRefs
156158
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.
164161 */
165- private var previousDefs : List [mutable. ListBuffer [ ValOrDefDef ] ] = Nil
162+ private var previousDefs : List [DefInfo ] = Nil
166163
167164 /** The set of references that were consumed so far in the current method */
168165 private var consumed : MutConsumedSet = MutConsumedSet ()
@@ -402,31 +399,26 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
402399
403400 /** Report a use/definition failure, where a previously hidden capability is
404401 * 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
409408 */
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)
430422
431423 /** Report a failure where a previously consumed capability is used again,
432424 * @param ref the capability that is used after being consumed
@@ -531,33 +523,37 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
531523 currentPeaks.hidden ++ argPeaks.hidden)
532524 end checkApply
533525
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-
547526 /** 1. Check that the capabilities used at `tree` don't overlap with
548527 * capabilities hidden by a previous definition.
549528 * 2. Also check that none of the used capabilities was consumed before.
550529 */
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
559554 val pos = consumed.get(ref)
560555 if pos != null then consumeError(ref, pos, tree.srcPos)
556+ end checkUse
561557
562558 /** If `tp` denotes some version of a singleton type `x.type` the set `{x}`
563559 * otherwise the empty set.
@@ -840,6 +836,10 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
840836 case tree : Apply => tree.symbol == defn.Caps_unsafeAssumeSeparate
841837 case _ => false
842838
839+ def pushDef (tree : ValOrDefDef , hiddenByDef : Refs )(using Context ): Unit =
840+ defsShadow ++= hiddenByDef
841+ previousDefs = DefInfo (tree, tree.symbol, hiddenByDef, hiddenByDef.peaks) :: previousDefs
842+
843843 /** Check (result-) type of `tree` for separation conditions using `checkType`.
844844 * Excluded are parameters and definitions that have an =unsafeAssumeSeparate
845845 * application as right hand sides.
@@ -848,11 +848,18 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
848848 def checkValOrDefDef (tree : ValOrDefDef )(using Context ): Unit =
849849 if ! tree.symbol.isOneOf(TermParamOrAccessor ) && ! isUnsafeAssumeSeparate(tree.rhs) then
850850 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))
856863
857864 /** Traverse `tree` and perform separation checks everywhere */
858865 def traverse (tree : Tree )(using Context ): Unit =
@@ -870,19 +877,17 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
870877 tree.tpe match
871878 case _ : MethodOrPoly =>
872879 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)
880882 case tree : ValDef =>
881883 traverseChildren(tree)
882884 checkValOrDefDef(tree)
883885 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)
886891 checkValOrDefDef(tree)
887892 case If (cond, thenp, elsep) =>
888893 traverse(cond)
0 commit comments