@@ -52,18 +52,10 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
5252
5353 private def hidden (using Context ): Refs =
5454 val seen : util.EqHashSet [CaptureRef ] = new util.EqHashSet
55-
56- def hiddenByElem (elem : CaptureRef ): Refs =
57- if seen.add(elem) then elem match
58- case Fresh .Cap (hcs) => hcs.elems.filter(! _.isRootCapability) ++ recur(hcs.elems)
59- case ReadOnlyCapability (ref) => hiddenByElem(ref).map(_.readOnly)
60- case _ => emptySet
61- else emptySet
62-
6355 def recur (cs : Refs ): Refs =
6456 (emptySet /: cs): (elems, elem) =>
65- elems ++ hiddenByElem(elem)
66-
57+ if seen.add(elem) then elems ++ hiddenByElem(elem, recur )
58+ else elems
6759 recur(refs)
6860 end hidden
6961
@@ -79,6 +71,11 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
7971 refs -- captures(dep).footprint
8072 end extension
8173
74+ private def hiddenByElem (ref : CaptureRef , recur : Refs => Refs )(using Context ): Refs = ref match
75+ case Fresh .Cap (hcs) => hcs.elems.filter(! _.isRootCapability) ++ recur(hcs.elems)
76+ case ReadOnlyCapability (ref1) => hiddenByElem(ref1, recur).map(_.readOnly)
77+ case _ => emptySet
78+
8279 /** The captures of an argument or prefix widened to the formal parameter, if
8380 * the latter contains a cap.
8481 */
@@ -212,6 +209,34 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
212209 if ! overlap.isEmpty then
213210 sepUseError(tree, usedFootprint, overlap)
214211
212+ def checkType (tpt : Tree , sym : Symbol )(using Context ) =
213+ def checkSep (hidden : Refs , footprint : Refs , descr : String ) =
214+ val overlap = hidden.overlapWith(footprint)
215+ if ! overlap.isEmpty then
216+ report.error(
217+ em """ Separation failure: ${tpt.nuType} captures a root element hiding ${CaptureSet (hidden)}
218+ |and also $descr ${CaptureSet (footprint)}.
219+ |The two sets overlap at ${CaptureSet (overlap)}""" ,
220+ tpt.srcPos)
221+
222+ val capts = CaptureSet .ofTypeDeeply(tpt.nuType,
223+ union = (xs, ys) => ctx ?=> CaptureSet (xs.elems ++ ys.elems))
224+ .elems
225+ // Note: Can't use captures(...) or deepCaptureSet here because these would map
226+ // e.g (Object^{<cap hiding x}, Object^{x}) to {<cap hiding x>} and we need
227+ // {<cap hiding x>, x} instead.
228+ val explicitFootprint = capts.footprint
229+ var hiddenFootprint : Refs = emptySet
230+ // println(i"checking tp ${tpt.tpe} with $capts fp $explicitFootprint")
231+ for ref <- capts do
232+ val hidden0 = hiddenByElem(ref, _.hidden).footprint
233+ val hiddenByRef = hiddenByElem(ref, _.hidden).footprint.deductSym(sym)
234+ if ! hiddenByRef.isEmpty then
235+ checkSep(hiddenByRef, explicitFootprint, " refers to" )
236+ checkSep(hiddenByRef, hiddenFootprint, " captures another root element hiding" )
237+ hiddenFootprint ++= hiddenByRef
238+ end checkType
239+
215240 private def collectMethodTypes (tp : Type ): List [TermLambda ] = tp match
216241 case tp : MethodType => tp :: collectMethodTypes(tp.resType)
217242 case tp : PolyType => collectMethodTypes(tp.resType)
@@ -269,11 +294,13 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
269294 defsShadow = saved
270295 case tree : ValOrDefDef =>
271296 traverseChildren(tree)
272- if previousDefs.nonEmpty && ! tree.symbol.isOneOf(TermParamOrAccessor ) then
273- capt.println(i " sep check def ${tree.symbol}: ${tree.tpt} with ${captures(tree.tpt).hidden.footprint}" )
274- defsShadow ++= captures(tree.tpt).hidden.footprint.deductSym(tree.symbol)
275- resultType(tree.symbol) = tree.tpt.nuType
276- previousDefs.head += tree
297+ if ! tree.symbol.isOneOf(TermParamOrAccessor ) then
298+ checkType(tree.tpt, tree.symbol)
299+ if previousDefs.nonEmpty then
300+ capt.println(i " sep check def ${tree.symbol}: ${tree.tpt} with ${captures(tree.tpt).hidden.footprint}" )
301+ defsShadow ++= captures(tree.tpt).hidden.footprint.deductSym(tree.symbol)
302+ resultType(tree.symbol) = tree.tpt.nuType
303+ previousDefs.head += tree
277304 case _ =>
278305 traverseChildren(tree)
279306end SepChecker
0 commit comments