@@ -10,11 +10,28 @@ import Contexts.*, Names.*, Flags.*, Symbols.*, Decorators.*
10
10
import CaptureSet .{Refs , emptySet , HiddenSet }
11
11
import config .Printers .capt
12
12
import StdNames .nme
13
- import util .{SimpleIdentitySet , EqHashMap }
13
+ import util .{SimpleIdentitySet , EqHashMap , SrcPos }
14
+
15
+ object SepChecker :
16
+
17
+ /** Enumerates kinds of captures encountered so far */
18
+ enum Captures :
19
+ case None
20
+ case Explicit // one or more explicitly declared captures
21
+ case Hidden // exacttly one hidden captures
22
+ case NeedsCheck // one hidden capture and one other capture (hidden or declared)
23
+
24
+ def add (that : Captures ): Captures =
25
+ if this == None then that
26
+ else if that == None then this
27
+ else if this == Explicit && that == Explicit then Explicit
28
+ else NeedsCheck
29
+ end Captures
14
30
15
31
class SepChecker (checker : CheckCaptures .CheckerAPI ) extends tpd.TreeTraverser :
16
32
import tpd .*
17
33
import checker .*
34
+ import SepChecker .*
18
35
19
36
/** The set of capabilities that are hidden by a polymorphic result type
20
37
* of some previous definition.
@@ -59,6 +76,10 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
59
76
recur(refs)
60
77
end hidden
61
78
79
+ private def containsHidden (using Context ): Boolean =
80
+ refs.exists: ref =>
81
+ ! hiddenByElem(ref, _ => emptySet).isEmpty
82
+
62
83
/** Deduct the footprint of `sym` and `sym*` from `refs` */
63
84
private def deductSym (sym : Symbol )(using Context ) =
64
85
val ref = sym.termRef
@@ -183,6 +204,7 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
183
204
for (arg, idx) <- indexedArgs do
184
205
if arg.needsSepCheck then
185
206
val ac = formalCaptures(arg)
207
+ checkType(arg.formalType, arg.srcPos, NoSymbol , " the argument's adapted type" )
186
208
val hiddenInArg = ac.hidden.footprint
187
209
// println(i"check sep $arg: $ac, footprint so far = $footprint, hidden = $hiddenInArg")
188
210
val overlap = hiddenInArg.overlapWith(footprint).deductCapturesOf(deps(arg))
@@ -209,32 +231,103 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
209
231
if ! overlap.isEmpty then
210
232
sepUseError(tree, usedFootprint, overlap)
211
233
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
234
+ def checkType (tpt : Tree , sym : Symbol )(using Context ): Unit =
235
+ checkType(tpt.nuType, tpt.srcPos, sym, " " )
236
+
237
+ /** Check that all parts of type `tpe` are separated.
238
+ * @param tpe the type to check
239
+ * @param pos position for error reporting
240
+ * @param sym if `tpe` is the (result-) type of a val or def, the symbol of
241
+ * this definition, otherwise NoSymbol. If `sym` exists we
242
+ * deduct its associated direct and reach capabilities everywhere
243
+ * from the capture sets we check.
244
+ * @param what a string describing what kind of type it is
245
+ */
246
+ def checkType (tpe : Type , pos : SrcPos , sym : Symbol , what : String )(using Context ): Unit =
247
+
248
+ def checkParts (parts : List [Type ]): Unit =
249
+ var footprint : Refs = emptySet
250
+ var hiddenSet : Refs = emptySet
251
+ var checked = 0
252
+ for part <- parts do
253
+
254
+ /** Report an error if `current` and `next` overlap.
255
+ * @param current the footprint or hidden set seen so far
256
+ * @param next the footprint or hidden set of the next part
257
+ * @param mapRefs a function over the capture set elements of the next part
258
+ * that returns the references of the same kind as `current`
259
+ * (i.e. the part's footprint or hidden set)
260
+ * @param prevRel a verbal description of current ("references or "hides")
261
+ * @param nextRel a verbal descriiption of next
262
+ */
263
+ def checkSep (current : Refs , next : Refs , mapRefs : Refs => Refs , prevRel : String , nextRel : String ): Unit =
264
+ val globalOverlap = current.overlapWith(next)
265
+ if ! globalOverlap.isEmpty then
266
+ val (prevStr, prevRefs, overlap) = parts.iterator.take(checked)
267
+ .map: prev =>
268
+ val prevRefs = mapRefs(prev.deepCaptureSet.elems).footprint.deductSym(sym)
269
+ (i " , $prev , " , prevRefs, prevRefs.overlapWith(next))
270
+ .dropWhile(_._3.isEmpty)
271
+ .nextOption
272
+ .getOrElse((" " , current, globalOverlap))
273
+ report.error(
274
+ em """ Separation failure in $what type $tpe.
275
+ |One part, $part , $nextRel ${CaptureSet (next)}.
276
+ |A previous part $prevStr $prevRel ${CaptureSet (prevRefs)}.
277
+ |The two sets overlap at ${CaptureSet (overlap)}. """ ,
278
+ pos)
279
+
280
+ val partRefs = part.deepCaptureSet.elems
281
+ val partFootprint = partRefs.footprint.deductSym(sym)
282
+ val partHidden = partRefs.hidden.footprint.deductSym(sym) -- partFootprint
283
+
284
+ checkSep(footprint, partHidden, identity, " references" , " hides" )
285
+ checkSep(hiddenSet, partHidden, _.hidden, " also hides" , " hides" )
286
+ checkSep(hiddenSet, partFootprint, _.hidden, " hides" , " references" )
287
+
288
+ footprint ++= partFootprint
289
+ hiddenSet ++= partHidden
290
+ checked += 1
291
+ end for
292
+ end checkParts
293
+
294
+ object traverse extends TypeAccumulator [Captures ]:
295
+
296
+ /** A stack of part lists to check. We maintain this since immediately
297
+ * checking parts when traversing the type would check innermost to oputermost.
298
+ * But we want to check outermost parts first since this prioritized errors
299
+ * that are more obvious.
300
+ */
301
+ var toCheck : List [List [Type ]] = Nil
302
+
303
+ private val seen = util.HashSet [Symbol ]()
304
+
305
+ def apply (c : Captures , t : Type ) =
306
+ if variance < 0 then c
307
+ else
308
+ val t1 = t.dealias
309
+ t1 match
310
+ case t @ AppliedType (tycon, args) =>
311
+ val c1 = foldOver(Captures .None , t)
312
+ if c1 == Captures .NeedsCheck then
313
+ toCheck = (tycon :: args) :: toCheck
314
+ c.add(c1)
315
+ case t @ CapturingType (parent, cs) =>
316
+ val c1 = this (c, parent)
317
+ if cs.elems.containsHidden then c1.add(Captures .Hidden )
318
+ else if ! cs.elems.isEmpty then c1.add(Captures .Explicit )
319
+ else c1
320
+ case t : TypeRef if t.symbol.isAbstractOrParamType =>
321
+ if seen.contains(t.symbol) then c
322
+ else
323
+ seen += t.symbol
324
+ apply(apply(c, t.prefix), t.info.bounds.hi)
325
+ case t =>
326
+ foldOver(c, t)
327
+
328
+ if ! tpe.hasAnnotation(defn.UntrackedCapturesAnnot ) then
329
+ traverse(Captures .None , tpe)
330
+ traverse.toCheck.foreach(checkParts)
238
331
end checkType
239
332
240
333
private def collectMethodTypes (tp : Type ): List [TermLambda ] = tp match
0 commit comments