Skip to content

Commit bc522cc

Browse files
committed
Refactor determination of level owners
Do it in setup instead of on demand. Advantage: no need to determine level ownership for externally compiled symbols.
1 parent 13012c8 commit bc522cc

File tree

4 files changed

+84
-42
lines changed

4 files changed

+84
-42
lines changed

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

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ class IllegalCaptureRef(tpe: Type) extends Exception
4242
/** Capture checking state, which is stored in a context property */
4343
class CCState:
4444

45+
val rhsClosure: mutable.HashSet[Symbol] = new mutable.HashSet
46+
47+
val levelOwners: mutable.HashSet[Symbol] = new mutable.HashSet
48+
4549
/** Associates certain symbols (the nesting level owners) with their ccNestingLevel */
4650
val nestingLevels: mutable.HashMap[Symbol, Int] = new mutable.HashMap
4751

@@ -326,17 +330,7 @@ extension (sym: Symbol)
326330
&& sym != defn.Caps_unsafeBox
327331
&& sym != defn.Caps_unsafeUnbox
328332

329-
def isLevelOwner(using Context): Boolean =
330-
def isCaseClassSynthetic =
331-
sym.owner.isClass && sym.owner.is(Case) && sym.is(Synthetic) && sym.info.firstParamNames.isEmpty
332-
if sym.isClass then true
333-
else if sym.is(Method) then
334-
if sym.isAnonymousFunction then
335-
// Setup added anonymous functions counting as level owners to nestingLevels
336-
ccState.nestingLevels.contains(sym)
337-
else
338-
!sym.isConstructor && !isCaseClassSynthetic
339-
else false
333+
def isLevelOwner(using Context): Boolean = ccState.levelOwners.contains(sym)
340334

341335
/** The owner of the current level. Qualifying owners are
342336
* - methods other than constructors and anonymous functions
@@ -366,9 +360,6 @@ extension (sym: Symbol)
366360
def ccNestingLevelOpt(using Context): Option[Int] =
367361
if ctx.property(ccStateKey).isDefined then Some(ccNestingLevel) else None
368362

369-
def setNestingLevel(level: Int)(using Context): Unit =
370-
ccState.nestingLevels(sym) = level
371-
372363
/** The parameter with type caps.Cap in the leading term parameter section,
373364
* or NoSymbol, if none exists.
374365
*/

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,15 +1265,16 @@ class CheckCaptures extends Recheck, SymTransformer:
12651265
end checker
12661266
checker.traverse(unit)(using ctx.withOwner(defn.RootClass))
12671267
if !ctx.reporter.errorsReported then
1268-
// We dont report errors here if previous errors were reported, because other
1269-
// errors often result in bad applied types, but flagging these bad types gives
1270-
// often worse error messages than the original errors.
1271-
val checkApplied = new TreeTraverser:
1272-
def traverse(t: Tree)(using Context) = t match
1273-
case tree: InferredTypeTree =>
1274-
case tree: New =>
1275-
case tree: TypeTree => checkAppliedTypesIn(tree.withKnownType)
1276-
case _ => traverseChildren(t)
1277-
checkApplied.traverse(unit)
1268+
//inContext(ctx.withProperty(LooseRootChecking, Some(()))):
1269+
// We dont report errors here if previous errors were reported, because other
1270+
// errors often result in bad applied types, but flagging these bad types gives
1271+
// often worse error messages than the original errors.
1272+
val checkApplied = new TreeTraverser:
1273+
def traverse(t: Tree)(using Context) = t match
1274+
case tree: InferredTypeTree =>
1275+
case tree: New =>
1276+
case tree: TypeTree => checkAppliedTypesIn(tree.withKnownType)
1277+
case _ => traverseChildren(t)
1278+
checkApplied.traverse(unit)
12781279
end CaptureChecker
12791280
end CheckCaptures

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

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,13 @@ extends tpd.TreeTraverser:
206206
else fntpe
207207
case _ => tp
208208

209-
def isCapabilityClassRef(tp: Type)(using Context) = tp match
209+
extension (tp: Type) def isCapabilityClassRef(using Context) = tp match
210210
case _: TypeRef | _: AppliedType => tp.typeSymbol.hasAnnotation(defn.CapabilityAnnot)
211211
case _ => false
212212

213213
/** Map references to capability classes C to C^ */
214214
private def expandCapabilityClass(tp: Type)(using Context): Type =
215-
if isCapabilityClassRef(tp)
215+
if tp.isCapabilityClassRef
216216
then CapturingType(tp, CaptureSet.universal, boxed = false)
217217
else tp
218218

@@ -232,7 +232,7 @@ extends tpd.TreeTraverser:
232232
case t @ AnnotatedType(t1, ann) =>
233233
checkQualifiedRoots(ann.tree)
234234
val t3 =
235-
if ann.symbol == defn.RetainsAnnot && isCapabilityClassRef(t1) then t1
235+
if ann.symbol == defn.RetainsAnnot && t1.isCapabilityClassRef then t1
236236
else this(t1)
237237
// Don't map capture sets, since that would implicitly normalize sets that
238238
// are not well-formed.
@@ -317,21 +317,61 @@ extends tpd.TreeTraverser:
317317
private def updateOwner(sym: Symbol)(using Context) =
318318
if newOwnerFor(sym) != null then updateInfo(sym, sym.info)
319319

320+
extension (sym: Symbol) def takesCappedParam(using Context): Boolean =
321+
def search = new TypeAccumulator[Boolean]:
322+
def apply(x: Boolean, t: Type): Boolean = //reporting.trace.force(s"hasCapAt $v, $t"):
323+
if x then true
324+
else t match
325+
case t @ AnnotatedType(t1, annot)
326+
if annot.symbol == defn.RetainsAnnot || annot.symbol == defn.RetainsByNameAnnot =>
327+
val elems = annot match
328+
case CaptureAnnotation(refs, _) => refs.elems.toList
329+
case _ => retainedElems(annot.tree).map(_.tpe)
330+
if elems.exists(_.widen.isRef(defn.Caps_Cap)) then true
331+
else !t1.isCapabilityClassRef && this(x, t1)
332+
case t: PolyType =>
333+
apply(x, t.resType)
334+
case t: MethodType =>
335+
t.paramInfos.exists(apply(false, _))
336+
case _ =>
337+
if t.isRef(defn.Caps_Cap) || t.isCapabilityClassRef then true
338+
else
339+
val t1 = t.dealiasKeepAnnots
340+
if t1 ne t then this(x, t1)
341+
else foldOver(x, t)
342+
true || sym.info.stripPoly.match
343+
case mt: MethodType =>
344+
mt.paramInfos.exists(search(false, _))
345+
case _ =>
346+
false
347+
end extension
348+
320349
def traverse(tree: Tree)(using Context): Unit =
321350
tree match
322351
case tree @ DefDef(_, paramss, tpt: TypeTree, _) =>
323-
if isExcluded(tree.symbol) then
352+
val meth = tree.symbol
353+
if isExcluded(meth) then
324354
return
325-
inContext(ctx.withOwner(tree.symbol)):
326-
if tree.symbol.isAnonymousFunction && tree.symbol.definedLocalRoot.exists then
327-
// closures that define parameters of type caps.Cap count as level owners
328-
tree.symbol.setNestingLevel(ctx.owner.nestingLevel + 1)
355+
356+
def isCaseClassSynthetic = // TODO drop
357+
meth.owner.isClass && meth.owner.is(Case) && meth.is(Synthetic) && meth.info.firstParamNames.isEmpty
358+
359+
inContext(ctx.withOwner(meth)):
360+
val canHaveLocalRoot =
361+
if meth.isAnonymousFunction then
362+
ccState.rhsClosure.remove(meth)
363+
|| meth.definedLocalRoot.exists // TODO drop
364+
else !meth.isConstructor && !isCaseClassSynthetic
365+
if canHaveLocalRoot && meth.takesCappedParam then
366+
//println(i"level owner: $meth")
367+
ccState.levelOwners += meth
329368
paramss.foreach(traverse)
330369
transformTT(tpt, boxed = false,
331370
exact = tree.symbol.allOverriddenSymbols.hasNext,
332371
mapRoots = true)
333372
traverse(tree.rhs)
334373
//println(i"TYPE of ${tree.symbol.showLocated} = ${tpt.knownType}")
374+
335375
case tree @ ValDef(_, tpt: TypeTree, _) =>
336376
def containsCap(tp: Type) = tp.existsPart:
337377
case CapturingType(_, refs) => refs.isUniversal
@@ -342,9 +382,12 @@ extends tpd.TreeTraverser:
342382
case _: InferredTypeTree => false
343383
case _: TypeTree => containsCap(expandAliases(tree.tpe))
344384
case _ => false
385+
386+
val sym = tree.symbol
345387
val mapRoots = tree.rhs match
346388
case possiblyTypedClosureDef(ddef) if !mentionsCap(rhsOfEtaExpansion(ddef)) =>
347-
ddef.symbol.setNestingLevel(ctx.owner.nestingLevel + 1)
389+
//ddef.symbol.setNestingLevel(ctx.owner.nestingLevel + 1)
390+
ccState.rhsClosure += ddef.symbol
348391
// Toplevel closures bound to vals count as level owners
349392
// unless the closure is an implicit eta expansion over a type application
350393
// that mentions `cap`. In that case we prefer not to silently rebind
@@ -354,22 +397,29 @@ extends tpd.TreeTraverser:
354397
// in this case roots in inferred val type count as polymorphic
355398
case _ =>
356399
true
357-
transformTT(tpt,
358-
boxed = tree.symbol.is(Mutable), // types of mutable variables are boxed
359-
exact = tree.symbol.allOverriddenSymbols.hasNext, // types of symbols that override a parent don't get a capture set
360-
mapRoots
361-
)
362-
capt.println(i"mapped $tree = ${tpt.knownType}")
363-
traverse(tree.rhs)
400+
transformTT(tpt,
401+
boxed = sym.is(Mutable), // types of mutable variables are boxed
402+
exact = sym.allOverriddenSymbols.hasNext, // types of symbols that override a parent don't get a capture set
403+
mapRoots
404+
)
405+
capt.println(i"mapped $tree = ${tpt.knownType}")
406+
traverse(tree.rhs)
407+
364408
case tree @ TypeApply(fn, args) =>
365409
traverse(fn)
366410
for case arg: TypeTree <- args do
367411
transformTT(arg, boxed = true, exact = false, mapRoots = true) // type arguments in type applications are boxed
412+
368413
case tree: Template =>
369-
inContext(ctx.withOwner(tree.symbol.owner)):
414+
val cls = tree.symbol.owner
415+
inContext(ctx.withOwner(cls)):
416+
if cls.primaryConstructor.takesCappedParam then
417+
//println(i"level owner $cls")
418+
ccState.levelOwners += cls
370419
traverseChildren(tree)
371420
case tree: Try if Feature.enabled(Feature.saferExceptions) =>
372421
val tryOwner = newSymbol(ctx.owner, nme.TRY_BLOCK, SyntheticMethod, MethodType(Nil, defn.UnitType))
422+
ccState.levelOwners += tryOwner
373423
ccState.tryBlockOwner(tree) = tryOwner
374424
inContext(ctx.withOwner(tryOwner)):
375425
traverseChildren(tree)

tests/neg-custom-args/captures/usingLogFile-alt.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
| ^^^^^^^^^
44
| reference (file : java.io.OutputStream^{lcap}) is not included in the allowed capture set {x$0, x$0²}
55
|
6-
| Note that reference (file : java.io.OutputStream^{lcap}), defined at level 4
6+
| Note that reference (file : java.io.OutputStream^{lcap}), defined at level 1
77
| cannot be included in outer capture set {x$0, x$0}, defined at level 0 in package <root>
88
|
99
| where: x$0 is a reference to a value parameter

0 commit comments

Comments
 (0)