@@ -30,9 +30,9 @@ object CheckCaptures:
3030
3131 override def isEnabled (using Context ) = true
3232
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.
3636 */
3737 def transformSym (sym : SymDenotation )(using Context ): SymDenotation =
3838 if sym.isAllOf(PrivateParamAccessor ) && ! sym.hasAnnotation(defn.ConstructorOnlyAnnot ) then
@@ -89,6 +89,7 @@ object CheckCaptures:
8989 tp
9090 case _ =>
9191 mapOver(tp)
92+ end SubstParamsMap
9293
9394 /** Check that a @retains annotation only mentions references that can be tracked.
9495 * This check is performed at Typer.
@@ -115,37 +116,32 @@ object CheckCaptures:
115116 report.warning(em " redundant capture: $parent already accounts for $ref" , pos)
116117 case _ =>
117118
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
119120 * are already accounted for by other elements of the same annotation.
120121 * Note: We need to perform the check on the original annotation rather than its
121122 * capture set since the conversion to a capture set already eliminates redundant elements.
122123 */
123124 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 */
138134 def disallowRootCapabilitiesIn (tp : Type , what : String , have : String , addendum : String , pos : SrcPos )(using Context ) =
139135 val check = new TypeTraverser :
140136 def traverse (t : Type ) =
141137 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)
149145 traverseChildren(t)
150146 check.traverse(tp)
151147
@@ -235,17 +231,31 @@ class CheckCaptures extends Recheck, SymTransformer:
235231 if sym.ownersIterator.exists(_.isTerm) then CaptureSet .Var ()
236232 else CaptureSet .empty)
237233
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+ */
238244 /** For all nested environments up to `limit` perform `op` */
239245 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
240250 def recur (env : Env ): Unit =
241251 if env.isOpen && env.owner != limit then
242252 op(env)
243- if ! env.isOutermost then
253+ if ! stopsPropagation( env) then
244254 var nextEnv = env.outer
245255 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)
249259 recur(curEnv)
250260
251261 /** Include `sym` in the capture sets of all enclosing environments nested in the
@@ -255,30 +265,26 @@ class CheckCaptures extends Recheck, SymTransformer:
255265 if sym.exists then
256266 val ref = sym.termRef
257267 if ref.isTracked then
258- forallOuterEnvsUpTo(sym.enclosure) { env =>
268+ forallOuterEnvsUpTo(sym.enclosure): env =>
259269 capt.println(i " Mark $sym with cs ${ref.captureSet} free in ${env.owner}" )
260270 checkElem(ref, env.captured, pos)
261- }
262271
263272 /** Make sure (projected) `cs` is a subset of the capture sets of all enclosing
264273 * environments. At each stage, only include references from `cs` that are outside
265274 * the environment's owner
266275 */
267276 def markFree (cs : CaptureSet , pos : SrcPos )(using Context ): Unit =
268277 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)
277285 case _ => false
278- }
279286 capt.println(i " Include call capture $included in ${env.owner}" )
280287 checkSubset(included, env.captured, pos)
281- }
282288
283289 /** Include references captured by the called method in the current environment stack */
284290 def includeCallCaptures (sym : Symbol , pos : SrcPos )(using Context ): Unit =
@@ -305,7 +311,7 @@ class CheckCaptures extends Recheck, SymTransformer:
305311 // This case can arise when we try to merge multiple types that have different
306312 // capture sets on some part. For instance an asSeenFrom might produce
307313 // 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
309315 // lead to a failure in disambiguation since neither alternative is better than the
310316 // other in a frozen constraint. An example test case is disambiguate-select.scala.
311317 // We address the problem by disambiguating while ignoring all capture sets as a fallback.
@@ -320,7 +326,7 @@ class CheckCaptures extends Recheck, SymTransformer:
320326 selType
321327 else
322328 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" )
324330 if qualCs.mightSubcapture(selCs)
325331 && ! selCs.mightSubcapture(qualCs)
326332 && ! pt.stripCapturing.isInstanceOf [SingletonType ]
@@ -345,21 +351,22 @@ class CheckCaptures extends Recheck, SymTransformer:
345351 override def recheckApply (tree : Apply , pt : Type )(using Context ): Type =
346352 val meth = tree.fun.symbol
347353 includeCallCaptures(meth, tree.srcPos)
354+
355+ // Unsafe box/unbox handlng, only for versions < 3.3
348356 def mapArgUsing (f : Type => Type ) =
349357 val arg :: Nil = tree.args: @ unchecked
350358 val argType0 = f(recheckStart(arg, pt))
351359 val argType = super .recheckFinish(argType0, arg, pt)
352360 super .recheckFinish(argType, tree, pt)
353-
354361 if meth == defn.Caps_unsafeBox then
355362 mapArgUsing(_.forceBoxStatus(true ))
356363 else if meth == defn.Caps_unsafeUnbox then
357364 mapArgUsing(_.forceBoxStatus(false ))
358365 else if meth == defn.Caps_unsafeBoxFunArg then
359- mapArgUsing {
366+ mapArgUsing :
360367 case defn.FunctionOf (paramtpe :: Nil , restpe, isContectual) =>
361368 defn.FunctionOf (paramtpe.forceBoxStatus(true ) :: Nil , restpe, isContectual)
362- }
369+
363370 else
364371 super .recheckApply(tree, pt) match
365372 case appType @ CapturingType (appType1, refs) =>
@@ -371,8 +378,8 @@ class CheckCaptures extends Recheck, SymTransformer:
371378 && qual.tpe.captureSet.mightSubcapture(refs)
372379 && tree.args.forall(_.tpe.captureSet.mightSubcapture(refs))
373380 =>
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
376383 appType.derivedCapturingType(appType1, callCaptures)
377384 .showing(i " narrow $tree: $appType, refs = $refs, qual = ${qual.tpe.captureSet} --> $result" , capt)
378385 case _ => appType
0 commit comments