Skip to content

Commit 510d854

Browse files
committed
Drop treatment of local roots
1 parent 254076f commit 510d854

File tree

13 files changed

+45
-241
lines changed

13 files changed

+45
-241
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -376,17 +376,6 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
376376
case _ =>
377377
tree.tpe.isInstanceOf[ThisType]
378378
}
379-
380-
/** Under capture checking, an extractor for qualified roots `cap[Q]`.
381-
*/
382-
object QualifiedRoot:
383-
384-
def unapply(tree: Apply)(using Context): Option[String] = tree match
385-
case Apply(fn, Literal(lit) :: Nil) if fn.symbol == defn.Caps_capIn =>
386-
Some(lit.value.asInstanceOf[String])
387-
case _ =>
388-
None
389-
end QualifiedRoot
390379
}
391380

392381
trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] =>

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

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,6 @@ class IllegalCaptureRef(tpe: Type) extends Exception(tpe.toString)
5959
/** Capture checking state, which is known to other capture checking components */
6060
class CCState:
6161

62-
/** Associates nesting level owners with the local roots valid in their scopes. */
63-
val localRoots: mutable.HashMap[Symbol, Symbol] = new mutable.HashMap
64-
6562
/** The last pair of capture reference and capture set where
6663
* the reference could not be added to the set due to a level conflict.
6764
*/
@@ -80,14 +77,9 @@ class NoCommonRoot(rs: Symbol*)(using Context) extends Exception(
8077
extension (tree: Tree)
8178

8279
/** Map tree with CaptureRef type to its type, throw IllegalCaptureRef otherwise */
83-
def toCaptureRef(using Context): CaptureRef = tree match
84-
case QualifiedRoot(outer) =>
85-
ctx.owner.levelOwnerNamed(outer)
86-
.orElse(defn.RootClass) // non-existing outer roots are reported in Setup's checkQualifiedRoots
87-
.localRoot.termRef
88-
case _ => tree.tpe match
89-
case ref: CaptureRef => ref
90-
case tpe => throw IllegalCaptureRef(tpe) // if this was compiled from cc syntax, problem should have been reported at Typer
80+
def toCaptureRef(using Context): CaptureRef = tree.tpe match
81+
case ref: CaptureRef => ref
82+
case tpe => throw IllegalCaptureRef(tpe) // if this was compiled from cc syntax, problem should have been reported at Typer
9183

9284
/** Convert a @retains or @retainsByName annotation tree to the capture set it represents.
9385
* For efficience, the result is cached as an Attachment on the tree.
@@ -337,24 +329,13 @@ extension (sym: Symbol)
337329
&& sym != defn.Caps_unsafeBox
338330
&& sym != defn.Caps_unsafeUnbox
339331

340-
/** Can this symbol possibly own a local root?
341-
* TODO: Disallow anonymous functions?
332+
/** Does this symbol define a level where we do not want to let local variables
333+
* escape into outer capture sets?
342334
*/
343335
def isLevelOwner(using Context): Boolean =
344336
sym.isClass
345337
|| sym.is(Method, butNot = Accessor)
346338

347-
/** The level owner enclosing `sym` which has the given name, or NoSymbol
348-
* if none exists.
349-
*/
350-
def levelOwnerNamed(name: String)(using Context): Symbol =
351-
def recur(sym: Symbol): Symbol =
352-
if sym.name.toString == name then
353-
if sym.isLevelOwner then sym else NoSymbol
354-
else if sym == defn.RootClass then NoSymbol
355-
else recur(sym.owner)
356-
recur(sym)
357-
358339
/** The owner of the current level. Qualifying owners are
359340
* - methods other than constructors and anonymous functions
360341
* - anonymous functions, provided they either define a local
@@ -369,14 +350,6 @@ extension (sym: Symbol)
369350
else recur(sym.owner)
370351
recur(sym)
371352

372-
/** The local root corresponding to sym's level owner */
373-
def localRoot(using Context): Symbol =
374-
val owner = sym.levelOwner
375-
assert(owner.exists)
376-
def newRoot = newSymbol(if owner.isClass then newLocalDummy(owner) else owner,
377-
nme.LOCAL_CAPTURE_ROOT, Synthetic, defn.Caps_Cap.typeRef)
378-
ccState.localRoots.getOrElseUpdate(owner, newRoot)
379-
380353
/** The outermost symbol owned by both `sym` and `other`. if none exists
381354
* since the owning scopes of `sym` and `other` are not nested, invoke
382355
* `onConflict` to return a symbol.

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

Lines changed: 15 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,8 @@ sealed abstract class CaptureSet extends Showable:
7777

7878
/** Does this capture set contain the root reference `cap` as element? */
7979
final def isUniversal(using Context) =
80-
elems.exists(_.isUniversalRootCapability)
81-
82-
/** Does this capture set contain the root reference `cap` as element? */
83-
final def containsRoot(using Context) =
8480
elems.exists(_.isRootCapability)
8581

86-
/** Does this capture set disallow an addiiton of `cap`, whereas it
87-
* might allow an addition of a local root?
88-
*/
89-
final def disallowsUniversal(using Context) =
90-
if isConst then !isUniversal && elems.exists(_.isLocalRootCapability)
91-
else asVar.noUniversal
92-
9382
/** Try to include an element in this capture set.
9483
* @param elem The element to be added
9584
* @param origin The set that originated the request, or `empty` if the request came from outside.
@@ -154,16 +143,15 @@ sealed abstract class CaptureSet extends Showable:
154143
cs.addDependent(this)(using ctx, UnrecordedState)
155144
this
156145

157-
extension (x: CaptureRef)(using Context)
158-
159-
/** x subsumes x
160-
* this subsumes this.f
161-
* x subsumes y ==> x* subsumes y
162-
* x subsumes y ==> x* subsumes y*
163-
*/
164-
private def subsumes(y: CaptureRef): Boolean =
146+
/** x subsumes x
147+
* this subsumes this.f
148+
* x subsumes y ==> x* subsumes y
149+
* x subsumes y ==> x* subsumes y*
150+
*/
151+
extension (x: CaptureRef)
152+
private def subsumes(y: CaptureRef)(using Context): Boolean =
165153
(x eq y)
166-
|| x.isSuperRootOf(y)
154+
|| x.isRootCapability
167155
|| y.match
168156
case y: TermRef => !y.isReach && (y.prefix eq x)
169157
case _ => false
@@ -175,26 +163,6 @@ sealed abstract class CaptureSet extends Showable:
175163
case _ =>
176164
false
177165

178-
/** x <:< cap, cap[x] <:< cap
179-
* cap[y] <:< cap[x] if y encloses x
180-
* y <:< cap[x] if y's level owner encloses x's local root owner
181-
*/
182-
private def isSuperRootOf(y: CaptureRef): Boolean = x match
183-
case x: TermRef =>
184-
x.isUniversalRootCapability
185-
|| x.isLocalRootCapability && !y.isUniversalRootCapability && {
186-
val xowner = x.localRootOwner
187-
y match
188-
case y: TermRef =>
189-
xowner.isContainedIn(y.symbol.levelOwner)
190-
case y: ThisType =>
191-
xowner.isContainedIn(y.cls)
192-
case _ =>
193-
false
194-
}
195-
case _ => false
196-
end extension
197-
198166
/** {x} <:< this where <:< is subcapturing, but treating all variables
199167
* as frozen.
200168
*/
@@ -217,7 +185,7 @@ sealed abstract class CaptureSet extends Showable:
217185
*/
218186
def mightAccountFor(x: CaptureRef)(using Context): Boolean =
219187
reporting.trace(i"$this mightAccountFor $x, ${x.captureSetOfInfo}?", show = true) {
220-
elems.exists(elem => elem.subsumes(x) || elem.isRootCapability)
188+
elems.exists(_.subsumes(x))
221189
|| !x.isRootCapability
222190
&& {
223191
val elems = x.captureSetOfInfo.elems
@@ -523,7 +491,7 @@ object CaptureSet:
523491
else
524492
//if id == 34 then assert(!elem.isUniversalRootCapability)
525493
elems += elem
526-
if elem.isUniversalRootCapability then
494+
if elem.isRootCapability then
527495
rootAddedHandler()
528496
newElemAddedHandler(elem)
529497
// assert(id != 5 || elems.size != 3, this)
@@ -534,7 +502,7 @@ object CaptureSet:
534502
res.addToTrace(this)
535503

536504
private def levelOK(elem: CaptureRef)(using Context): Boolean =
537-
if elem.isUniversalRootCapability then !noUniversal
505+
if elem.isRootCapability then !noUniversal
538506
else elem match
539507
case elem: TermRef =>
540508
if elem.isReach then levelOK(elem.reachPrefix)
@@ -575,10 +543,9 @@ object CaptureSet:
575543
* of this set. The universal set {cap} is a sound fallback.
576544
*/
577545
final def upperApprox(origin: CaptureSet)(using Context): CaptureSet =
578-
if isConst then this
579-
else if elems.exists(_.isRootCapability) then
580-
CaptureSet(elems.filter(_.isRootCapability).toList*)
581-
else if computingApprox then
546+
if isConst then
547+
this
548+
else if elems.exists(_.isRootCapability) || computingApprox then
582549
universal
583550
else
584551
computingApprox = true
@@ -1135,7 +1102,7 @@ object CaptureSet:
11351102
override def toAdd(using Context) =
11361103
for CompareResult.LevelError(cs, ref) <- ccState.levelError.toList yield
11371104
ccState.levelError = None
1138-
if ref.isUniversalRootCapability then
1105+
if ref.isRootCapability then
11391106
i"""
11401107
|
11411108
|Note that the universal capability `cap`

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

Lines changed: 10 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,12 @@ object CheckCaptures:
130130
report.error(em"Singleton type $parent cannot have capture set", parent.srcPos)
131131
case _ =>
132132
for elem <- ann.retainedElems do
133-
elem match
134-
case QualifiedRoot(outer) =>
135-
// Will be checked by Setup's checkOuterRoots
136-
case _ => elem.tpe match
137-
case ref: CaptureRef =>
138-
if !ref.isTrackableRef then
139-
report.error(em"$elem cannot be tracked since it is not a parameter or local value", elem.srcPos)
140-
case tpe =>
141-
report.error(em"$elem: $tpe is not a legal element of a capture set", elem.srcPos)
133+
elem.tpe match
134+
case ref: CaptureRef =>
135+
if !ref.isTrackableRef then
136+
report.error(em"$elem cannot be tracked since it is not a parameter or local value", elem.srcPos)
137+
case tpe =>
138+
report.error(em"$elem: $tpe is not a legal element of a capture set", elem.srcPos)
142139

143140
/** Report an error if some part of `tp` contains the root capability in its capture set
144141
* or if it refers to an unsealed type parameter that could possibly be instantiated with
@@ -837,13 +834,9 @@ class CheckCaptures extends Recheck, SymTransformer:
837834
}
838835
checkNotUniversal(parent)
839836
case _ =>
840-
val adapted =
841-
if ccConfig.allowUniversalInBoxed then
842-
adaptUniversal(tpe, pt, tree)
843-
else
844-
if needsUniversalCheck then checkNotUniversal(tpe)
845-
tpe
846-
super.recheckFinish(adapted, tree, pt)
837+
if !ccConfig.allowUniversalInBoxed && needsUniversalCheck then
838+
checkNotUniversal(tpe)
839+
super.recheckFinish(tpe, tree, pt)
847840
end recheckFinish
848841

849842
// ------------------ Adaptation -------------------------------------
@@ -855,56 +848,7 @@ class CheckCaptures extends Recheck, SymTransformer:
855848
// - Relax expected capture set containing `this.type`s by adding references only
856849
// accessible through those types (c.f. addOuterRefs, also #14930 for a discussion).
857850
// - Adapt box status and environment capture sets by simulating box/unbox operations.
858-
// - Instantiate `cap` in actual as needed to a local root.
859-
860-
/** The local root that is implied for the expression `tree`.
861-
* This is the local root of the outermost level owner that includes
862-
* all free variables of the expression that have in their types
863-
* some capturing type occuring in covariant or invariant position.
864-
*/
865-
def impliedRoot(tree: Tree)(using Context) =
866-
def isTrackedSomewhere(sym: Symbol): Boolean =
867-
val search = new TypeAccumulator[Boolean]:
868-
def apply(found: Boolean, tp: Type) =
869-
def isTrackedHere = variance >= 0 && !tp.captureSet.isAlwaysEmpty
870-
found || isTrackedHere || foldOver(found, tp)
871-
if sym.is(Method)
872-
then !capturedVars(sym).elems.isEmpty || search(false, sym.info.finalResultType)
873-
else search(false, sym.info)
874-
875-
val acc = new TreeAccumulator[Symbol]:
876-
val locals = mutable.Set[Symbol]()
877-
private def max(sym1: Symbol, sym2: Symbol)(using Context) =
878-
sym1.maxNested(sym2, onConflict = (_, _) => throw NoCommonRoot(sym1, sym2))
879-
def apply(s: Symbol, t: Tree)(using Context) = t match
880-
case t: (Ident | This)
881-
if !locals.contains(t.symbol) && isTrackedSomewhere(t.symbol) =>
882-
max(s, t.symbol.levelOwner)
883-
case t: DefTree =>
884-
locals += t.symbol
885-
foldOver(s, t)
886-
case _ =>
887-
foldOver(s, t)
888-
acc(NoSymbol, tree).orElse(ctx.owner).localRoot
889-
890-
/** Assume `actual` is the type of an expression `tree` with the given
891-
* `expected` type. If `actual` captures `cap` and `cap` is not allowed
892-
* in the capture set of `expected`, narrow `cap` to the root capability
893-
* that is implied for `tree`.
894-
*/
895-
def adaptUniversal(actual: Type, expected: Type, tree: Tree)(using Context): Type =
896-
if expected.captureSet.disallowsUniversal && actual.captureSet.isUniversal then
897-
if actual.isInstanceOf[SingletonType] then
898-
// capture set is only exposed when widening
899-
adaptUniversal(actual.widen, expected, tree)
900-
else
901-
val localRoot = impliedRoot(tree)
902-
CapturingType(
903-
actual.stripCapturing,
904-
localRoot.termRef.singletonCaptureSet,
905-
actual.isBoxedCapturing)
906-
.showing(i"adapt universal $actual vs $expected = $result", capt)
907-
else actual
851+
// - Instantiate covariant occurrenves of `cap` in actual to reach capabilities.
908852

909853
private inline val debugSuccesses = false
910854

@@ -1369,20 +1313,6 @@ class CheckCaptures extends Recheck, SymTransformer:
13691313
checker.traverse(tree.knownType)
13701314
end healTypeParam
13711315

1372-
def checkNoLocalRootIn(sym: Symbol, info: Type, pos: SrcPos)(using Context): Unit =
1373-
val check = new TypeTraverser:
1374-
def traverse(tp: Type) = tp match
1375-
case tp: TermRef if tp.isLocalRootCapability =>
1376-
if tp.localRootOwner == sym then
1377-
report.error(i"local root $tp cannot appear in type of $sym", pos)
1378-
case tp: ClassInfo =>
1379-
traverseChildren(tp)
1380-
for mbr <- tp.decls do
1381-
if !mbr.is(Private) then checkNoLocalRootIn(sym, mbr.info, mbr.srcPos)
1382-
case _ =>
1383-
traverseChildren(tp)
1384-
check.traverse(info)
1385-
13861316
def checkArraysAreSealedIn(tp: Type, pos: SrcPos)(using Context): Unit =
13871317
val check = new TypeTraverser:
13881318
def traverse(t: Type): Unit =
@@ -1426,8 +1356,6 @@ class CheckCaptures extends Recheck, SymTransformer:
14261356
checkBounds(normArgs, tl)
14271357
args.lazyZip(tl.paramNames).foreach(healTypeParam(_, _, fun.symbol))
14281358
case _ =>
1429-
case _: ValOrDefDef | _: TypeDef =>
1430-
checkNoLocalRootIn(tree.symbol, tree.symbol.info, tree.symbol.srcPos)
14311359
case tree: TypeTree =>
14321360
checkArraysAreSealedIn(tree.tpe, tree.srcPos)
14331361
case _ =>

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

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -275,23 +275,16 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
275275
then CapturingType(tp, defn.expandedUniversalSet, boxed = false)
276276
else tp
277277

278-
private def checkQualifiedRoots(tree: Tree): Unit =
279-
for case elem @ QualifiedRoot(outer) <- tree.retainedElems do
280-
if !ctx.owner.levelOwnerNamed(outer).exists then
281-
report.error(em"`$outer` does not name an outer definition that represents a capture level", elem.srcPos)
282-
283278
private def recur(t: Type): Type = normalizeCaptures(mapOver(t))
284279

285280
def apply(t: Type) =
286281
t match
287282
case t @ CapturingType(parent, refs) =>
288-
checkQualifiedRoots(t.annot.tree) // TODO: NEEDED?
289283
t.derivedCapturingType(this(parent), refs)
290284
case t @ AnnotatedType(parent, ann) =>
291285
val parent1 = this(parent)
292286
if ann.symbol == defn.RetainsAnnot then
293287
for tpt <- tptToCheck do
294-
checkQualifiedRoots(ann.tree)
295288
checkWellformedLater(parent1, ann.tree, tpt)
296289
CapturingType(parent1, ann.tree.toCaptureSet)
297290
else
@@ -588,11 +581,11 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
588581
case CapturingType(parent, refs) =>
589582
needsVariable(parent)
590583
&& refs.isConst // if refs is a variable, no need to add another
591-
&& !refs.containsRoot // if refs is {cap}, an added variable would not change anything
584+
&& !refs.isUniversal // if refs is {cap}, an added variable would not change anything
592585
case RetainingType(parent, refs) =>
593586
needsVariable(parent)
594587
&& !refs.tpes.exists:
595-
case ref: TermRef => ref.isUniversalRootCapability
588+
case ref: TermRef => ref.isRootCapability
596589
case _ => false
597590
case AnnotatedType(parent, _) =>
598591
needsVariable(parent)

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,6 @@ class Definitions {
988988
@tu lazy val CapsModule: Symbol = requiredModule("scala.caps")
989989
@tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("cap")
990990
@tu lazy val Caps_Cap: TypeSymbol = CapsModule.requiredType("Cap")
991-
@tu lazy val Caps_capIn: TermSymbol = CapsModule.requiredMethod("capIn")
992991
@tu lazy val CapsUnsafeModule: Symbol = requiredModule("scala.caps.unsafe")
993992
@tu lazy val Caps_unsafeAssumePure: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumePure")
994993
@tu lazy val Caps_unsafeBox: Symbol = CapsUnsafeModule.requiredMethod("unsafeBox")

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,6 @@ object StdNames {
288288

289289
// Compiler-internal
290290
val CAPTURE_ROOT: N = "cap"
291-
val LOCAL_CAPTURE_ROOT: N = "<local-cap>"
292291
val CONSTRUCTOR: N = "<init>"
293292
val STATIC_CONSTRUCTOR: N = "<clinit>"
294293
val EVT2U: N = "evt2u$"

0 commit comments

Comments
 (0)