Skip to content

Commit d97dfea

Browse files
committed
Refactorings
Some new comments, simplifications, syntax tweaks
1 parent 0a21ecf commit d97dfea

File tree

2 files changed

+56
-49
lines changed

2 files changed

+56
-49
lines changed

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -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

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6176,7 +6176,7 @@ object Types {
61766176
tp.derivedLambdaType(tp.paramNames, formals, restpe)
61776177
}
61786178

6179-
/** Overridden in TypeOps.avoid */
6179+
/** Overridden in TypeOps.avoid and in CheckCaptures.substParamsMap */
61806180
protected def needsRangeIfInvariant(refs: CaptureSet): Boolean = true
61816181

61826182
override def mapCapturingType(tp: Type, parent: Type, refs: CaptureSet, v: Int): Type =

0 commit comments

Comments
 (0)