@@ -30,9 +30,9 @@ object CheckCaptures:
30
30
31
31
override def isEnabled (using Context ) = true
32
32
33
- /** Reset `private` flags of parameter accessors so that we can refine them
34
- * in Setup if they have non-empty capture sets. Special handling of some
35
- * symbols defined for case classes.
33
+ /** - Reset `private` flags of parameter accessors so that we can refine them
34
+ * in Setup if they have non-empty capture sets.
35
+ * - Special handling of some symbols defined for case classes.
36
36
*/
37
37
def transformSym (sym : SymDenotation )(using Context ): SymDenotation =
38
38
if sym.isAllOf(PrivateParamAccessor ) && ! sym.hasAnnotation(defn.ConstructorOnlyAnnot ) then
@@ -89,6 +89,7 @@ object CheckCaptures:
89
89
tp
90
90
case _ =>
91
91
mapOver(tp)
92
+ end SubstParamsMap
92
93
93
94
/** Check that a @retains annotation only mentions references that can be tracked.
94
95
* This check is performed at Typer.
@@ -115,37 +116,32 @@ object CheckCaptures:
115
116
report.warning(em " redundant capture: $parent already accounts for $ref" , pos)
116
117
case _ =>
117
118
118
- /** Warn if `ann`, which is a tree of a @retains annotation, defines some elements that
119
+ /** Warn if `ann`, which is the tree of a @retains annotation, defines some elements that
119
120
* are already accounted for by other elements of the same annotation.
120
121
* Note: We need to perform the check on the original annotation rather than its
121
122
* capture set since the conversion to a capture set already eliminates redundant elements.
122
123
*/
123
124
def warnIfRedundantCaptureSet (ann : Tree )(using Context ): Unit =
124
- // The lists `elems(i) :: prev.reverse :: elems(0),...,elems(i-1),elems(i+1),elems(n)`
125
- // where `n == elems.length-1`, i <- 0..n`.
126
- // I.e.
127
- // choices(Nil, elems) = [[elems(i), elems(0), ..., elems(i-1), elems(i+1), .... elems(n)] | i <- 0..n]
128
- def choices (prev : List [Tree ], elems : List [Tree ]): List [List [Tree ]] = elems match
129
- case Nil => Nil
130
- case elem :: elems =>
131
- List (elem :: (prev reverse_:: : elems)) ++ choices(elem :: prev, elems)
132
- for case first :: others <- choices(Nil , retainedElems(ann)) do
133
- val firstRef = first.toCaptureRef
134
- val remaining = CaptureSet (others.map(_.toCaptureRef)* )
135
- if remaining.accountsFor(firstRef) then
136
- report.warning(em " redundant capture: $remaining already accounts for $firstRef" , ann.srcPos)
137
-
125
+ var retained = retainedElems(ann).toArray
126
+ for i <- 0 until retained.length do
127
+ val ref = retained(i).toCaptureRef
128
+ val others = for j <- 0 until retained.length if j != i yield retained(j).toCaptureRef
129
+ val remaining = CaptureSet (others* )
130
+ if remaining.accountsFor(ref) then
131
+ report.warning(em " redundant capture: $remaining already accounts for $ref" , ann.srcPos)
132
+
133
+ /** Report an error if some part of `tp` contains the root capability in its capture set */
138
134
def disallowRootCapabilitiesIn (tp : Type , what : String , have : String , addendum : String , pos : SrcPos )(using Context ) =
139
135
val check = new TypeTraverser :
140
136
def traverse (t : Type ) =
141
137
if variance >= 0 then
142
- t.captureSet.disallowRootCapability: () =>
143
- def part = if t eq tp then " " else i " the part $t of "
144
- report.error(
145
- em """ $what cannot $have $tp since
146
- | ${part}that type captures the root capability `cap`.
147
- | $addendum""" ,
148
- pos)
138
+ t.captureSet.disallowRootCapability: () =>
139
+ def part = if t eq tp then " " else i " the part $t of "
140
+ report.error(
141
+ em """ $what cannot $have $tp since
142
+ | ${part}that type captures the root capability `cap`.
143
+ | $addendum""" ,
144
+ pos)
149
145
traverseChildren(t)
150
146
check.traverse(tp)
151
147
@@ -235,17 +231,31 @@ class CheckCaptures extends Recheck, SymTransformer:
235
231
if sym.ownersIterator.exists(_.isTerm) then CaptureSet .Var ()
236
232
else CaptureSet .empty)
237
233
234
+ /**
235
+ class anon1 extends Function1:
236
+ def apply: Function1 =
237
+ use(x)
238
+ class anon2 extends Function1:
239
+ use(y)
240
+ def apply: Function1 = ...
241
+ anon2()
242
+ anon1()
243
+ */
238
244
/** For all nested environments up to `limit` perform `op` */
239
245
def forallOuterEnvsUpTo (limit : Symbol )(op : Env => Unit )(using Context ): Unit =
246
+ def stopsPropagation (env : Env ) =
247
+ val sym = env.owner
248
+ env.isOutermost
249
+ || false && sym.is(Method ) && sym.owner.isTerm && ! sym.isConstructor
240
250
def recur (env : Env ): Unit =
241
251
if env.isOpen && env.owner != limit then
242
252
op(env)
243
- if ! env.isOutermost then
253
+ if ! stopsPropagation( env) then
244
254
var nextEnv = env.outer
245
255
if env.owner.isConstructor then
246
- if nextEnv.owner != limit && ! nextEnv.isOutermost then
247
- recur( nextEnv.outer)
248
- else recur(nextEnv)
256
+ if nextEnv.owner != limit && ! stopsPropagation( nextEnv) then
257
+ nextEnv = nextEnv .outer
258
+ recur(nextEnv)
249
259
recur(curEnv)
250
260
251
261
/** Include `sym` in the capture sets of all enclosing environments nested in the
@@ -255,30 +265,26 @@ class CheckCaptures extends Recheck, SymTransformer:
255
265
if sym.exists then
256
266
val ref = sym.termRef
257
267
if ref.isTracked then
258
- forallOuterEnvsUpTo(sym.enclosure) { env =>
268
+ forallOuterEnvsUpTo(sym.enclosure): env =>
259
269
capt.println(i " Mark $sym with cs ${ref.captureSet} free in ${env.owner}" )
260
270
checkElem(ref, env.captured, pos)
261
- }
262
271
263
272
/** Make sure (projected) `cs` is a subset of the capture sets of all enclosing
264
273
* environments. At each stage, only include references from `cs` that are outside
265
274
* the environment's owner
266
275
*/
267
276
def markFree (cs : CaptureSet , pos : SrcPos )(using Context ): Unit =
268
277
if ! cs.isAlwaysEmpty then
269
- forallOuterEnvsUpTo(ctx.owner.topLevelClass) { env =>
270
- val included = cs.filter {
271
- case ref : TermRef =>
272
- (env.nestedInOwner || env.owner != ref.symbol.owner)
273
- && env.owner.isContainedIn(ref.symbol.owner)
274
- case ref : ThisType =>
275
- (env.nestedInOwner || env.owner != ref.cls)
276
- && env.owner.isContainedIn(ref.cls)
278
+ forallOuterEnvsUpTo(ctx.owner.topLevelClass): env =>
279
+ def isVisibleFromEnv (sym : Symbol ) =
280
+ (env.nestedInOwner || env.owner != sym)
281
+ && env.owner.isContainedIn(sym)
282
+ val included = cs.filter:
283
+ case ref : TermRef => isVisibleFromEnv(ref.symbol.owner)
284
+ case ref : ThisType => isVisibleFromEnv(ref.cls)
277
285
case _ => false
278
- }
279
286
capt.println(i " Include call capture $included in ${env.owner}" )
280
287
checkSubset(included, env.captured, pos)
281
- }
282
288
283
289
/** Include references captured by the called method in the current environment stack */
284
290
def includeCallCaptures (sym : Symbol , pos : SrcPos )(using Context ): Unit =
@@ -305,7 +311,7 @@ class CheckCaptures extends Recheck, SymTransformer:
305
311
// This case can arise when we try to merge multiple types that have different
306
312
// capture sets on some part. For instance an asSeenFrom might produce
307
313
// a bi-mapped capture set arising from a substition. Applying the same substitution
308
- // to the same type twice will nevertheless produce different capture setsw which can
314
+ // to the same type twice will nevertheless produce different capture sets which can
309
315
// lead to a failure in disambiguation since neither alternative is better than the
310
316
// other in a frozen constraint. An example test case is disambiguate-select.scala.
311
317
// We address the problem by disambiguating while ignoring all capture sets as a fallback.
@@ -320,7 +326,7 @@ class CheckCaptures extends Recheck, SymTransformer:
320
326
selType
321
327
else
322
328
val qualCs = qualType.captureSet
323
- capt.println(i " intersect $qualType, ${selType.widen}, $qualCs, $selCs in $tree" )
329
+ capt.println(i " pick one of $qualType, ${selType.widen}, $qualCs, $selCs in $tree" )
324
330
if qualCs.mightSubcapture(selCs)
325
331
&& ! selCs.mightSubcapture(qualCs)
326
332
&& ! pt.stripCapturing.isInstanceOf [SingletonType ]
@@ -345,21 +351,22 @@ class CheckCaptures extends Recheck, SymTransformer:
345
351
override def recheckApply (tree : Apply , pt : Type )(using Context ): Type =
346
352
val meth = tree.fun.symbol
347
353
includeCallCaptures(meth, tree.srcPos)
354
+
355
+ // Unsafe box/unbox handlng, only for versions < 3.3
348
356
def mapArgUsing (f : Type => Type ) =
349
357
val arg :: Nil = tree.args: @ unchecked
350
358
val argType0 = f(recheckStart(arg, pt))
351
359
val argType = super .recheckFinish(argType0, arg, pt)
352
360
super .recheckFinish(argType, tree, pt)
353
-
354
361
if meth == defn.Caps_unsafeBox then
355
362
mapArgUsing(_.forceBoxStatus(true ))
356
363
else if meth == defn.Caps_unsafeUnbox then
357
364
mapArgUsing(_.forceBoxStatus(false ))
358
365
else if meth == defn.Caps_unsafeBoxFunArg then
359
- mapArgUsing {
366
+ mapArgUsing :
360
367
case defn.FunctionOf (paramtpe :: Nil , restpe, isContectual) =>
361
368
defn.FunctionOf (paramtpe.forceBoxStatus(true ) :: Nil , restpe, isContectual)
362
- }
369
+
363
370
else
364
371
super .recheckApply(tree, pt) match
365
372
case appType @ CapturingType (appType1, refs) =>
@@ -371,8 +378,8 @@ class CheckCaptures extends Recheck, SymTransformer:
371
378
&& qual.tpe.captureSet.mightSubcapture(refs)
372
379
&& tree.args.forall(_.tpe.captureSet.mightSubcapture(refs))
373
380
=>
374
- val callCaptures = tree.args.foldLeft(qual.tpe.captureSet)( (cs, arg) =>
375
- cs ++ arg.tpe.captureSet)
381
+ val callCaptures = tree.args.foldLeft(qual.tpe.captureSet): (cs, arg) =>
382
+ cs ++ arg.tpe.captureSet
376
383
appType.derivedCapturingType(appType1, callCaptures)
377
384
.showing(i " narrow $tree: $appType, refs = $refs, qual = ${qual.tpe.captureSet} --> $result" , capt)
378
385
case _ => appType
0 commit comments