Skip to content

Commit 46a2b2c

Browse files
committed
Revert "New capture escape checking based on levels (scala#18463)"
This reverts commit 64c3138, reversing changes made to 08f2faf. # Conflicts: # compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala # compiler/src/dotty/tools/dotc/cc/Setup.scala # compiler/src/dotty/tools/dotc/transform/Recheck.scala
1 parent 48bb59c commit 46a2b2c

File tree

102 files changed

+964
-2111
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+964
-2111
lines changed

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,3 @@ docs/_spec/.jekyll-metadata
9999
# scaladoc related
100100
scaladoc/output/
101101

102-
#coverage
103-
coverage/
104-

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,8 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
247247
profiler.afterPhase(phase, profileBefore)
248248
if (ctx.settings.Xprint.value.containsPhase(phase))
249249
for (unit <- units)
250-
def printCtx(unit: CompilationUnit) = phase.printingContext(
251-
ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
252-
lastPrintedTree = printTree(lastPrintedTree)(using printCtx(unit))
250+
lastPrintedTree =
251+
printTree(lastPrintedTree)(using ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
253252
report.informTime(s"$phase ", start)
254253
Stats.record(s"total trees at end of $phase", ast.Trees.ntrees)
255254
for (unit <- units)

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,12 +205,12 @@ object desugar {
205205

206206
def makeImplicitParameters(
207207
tpts: List[Tree], implicitFlag: FlagSet,
208-
mkParamName: Int => TermName,
208+
mkParamName: () => TermName,
209209
forPrimaryConstructor: Boolean = false
210210
)(using Context): List[ValDef] =
211211
for (tpt, i) <- tpts.zipWithIndex yield {
212212
val paramFlags: FlagSet = if (forPrimaryConstructor) LocalParamAccessor else Param
213-
val epname = mkParamName(i)
213+
val epname = mkParamName()
214214
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | implicitFlag)
215215
}
216216

@@ -254,7 +254,7 @@ object desugar {
254254
// using clauses, we only need names that are unique among the
255255
// parameters of the method since shadowing does not affect
256256
// implicit resolution in Scala 3.
257-
mkParamName = i =>
257+
mkParamName = () =>
258258
val index = seenContextBounds + 1 // Start at 1 like FreshNameCreator.
259259
val ret = ContextBoundParamName(EmptyTermName, index)
260260
seenContextBounds += 1
@@ -1602,12 +1602,9 @@ object desugar {
16021602
case vd: ValDef => vd
16031603
}
16041604

1605-
def makeContextualFunction(formals: List[Tree], paramNamesOrNil: List[TermName], body: Tree, erasedParams: List[Boolean])(using Context): Function = {
1605+
def makeContextualFunction(formals: List[Tree], body: Tree, erasedParams: List[Boolean])(using Context): Function = {
16061606
val mods = Given
1607-
val params = makeImplicitParameters(formals, mods,
1608-
mkParamName = i =>
1609-
if paramNamesOrNil.isEmpty then ContextFunctionParamName.fresh()
1610-
else paramNamesOrNil(i))
1607+
val params = makeImplicitParameters(formals, mods, mkParamName = () => ContextFunctionParamName.fresh())
16111608
FunctionWithMods(params, body, Modifiers(mods), erasedParams)
16121609
}
16131610

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

Lines changed: 0 additions & 36 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] =>
@@ -810,37 +799,12 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
810799
}
811800
}
812801

813-
/** An extractor for def of a closure contained the block of the closure,
814-
* possibly with type ascriptions.
815-
*/
816-
object possiblyTypedClosureDef:
817-
def unapply(tree: Tree)(using Context): Option[DefDef] = tree match
818-
case Typed(expr, _) => unapply(expr)
819-
case _ => closureDef.unapply(tree)
820-
821802
/** If tree is a closure, its body, otherwise tree itself */
822803
def closureBody(tree: Tree)(using Context): Tree = tree match {
823804
case closureDef(meth) => meth.rhs
824805
case _ => tree
825806
}
826807

827-
/** Is `mdef` an eta-expansion of a method reference? To recognize this, we use
828-
* the following criterion: A method definition is an eta expansion, if
829-
* it contains at least one term paramter, the parameter has a zero extent span,
830-
* and the right hand side is either an application or a closure with'
831-
* an anonymous method that's itself characterized as an eta expansion.
832-
*/
833-
def isEtaExpansion(mdef: DefDef)(using Context): Boolean =
834-
!rhsOfEtaExpansion(mdef).isEmpty
835-
836-
def rhsOfEtaExpansion(mdef: DefDef)(using Context): Tree = mdef.paramss match
837-
case (param :: _) :: _ if param.asInstanceOf[Tree].span.isZeroExtent =>
838-
mdef.rhs match
839-
case rhs: Apply => rhs
840-
case closureDef(mdef1) => rhsOfEtaExpansion(mdef1)
841-
case _ => EmptyTree
842-
case _ => EmptyTree
843-
844808
/** The variables defined by a pattern, in reverse order of their appearance. */
845809
def patVars(tree: Tree)(using Context): List[Symbol] = {
846810
val acc = new TreeAccumulator[List[Symbol]] { outer =>

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
149149
case Floating
150150
}
151151

152-
/** {x1, ..., xN} T (only relevant under captureChecking)
153-
* Created when parsing function types so that capture set and result type
154-
* is combined in a single node.
155-
*/
152+
/** {x1, ..., xN} T (only relevant under captureChecking) */
156153
case class CapturesAndResult(refs: List[Tree], parent: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree
157154

158155
/** A type tree appearing somewhere in the untyped DefDef of a lambda, it will be typed using `tpFun`.
@@ -515,9 +512,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
515512
def captureRoot(using Context): Select =
516513
Select(scalaDot(nme.caps), nme.CAPTURE_ROOT)
517514

518-
def captureRootIn(using Context): Select =
519-
Select(scalaDot(nme.caps), nme.capIn)
520-
521515
def makeRetaining(parent: Tree, refs: List[Tree], annotName: TypeName)(using Context): Annotated =
522516
Annotated(parent, New(scalaAnnotationDot(annotName), List(refs)))
523517

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

Lines changed: 4 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ import config.SourceVersion
1010
import config.Printers.capt
1111
import util.Property.Key
1212
import tpd.*
13-
import StdNames.nme
1413
import config.Feature
15-
import collection.mutable
1614

1715
private val Captures: Key[CaptureSet] = Key()
1816
private val BoxedType: Key[BoxedTypeCache] = Key()
@@ -23,11 +21,6 @@ private val BoxedType: Key[BoxedTypeCache] = Key()
2321
*/
2422
private val adaptUnpickledFunctionTypes = false
2523

26-
/** Switch whether we constrain a root var that includes the source of a
27-
* root map to be an alias of that source (so that it can be mapped)
28-
*/
29-
private val constrainRootsWhenMapping = true
30-
3124
/** The arguments of a @retains or @retainsByName annotation */
3225
private[cc] def retainedElems(tree: Tree)(using Context): List[Tree] = tree match
3326
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) => elems
@@ -39,82 +32,12 @@ def allowUniversalInBoxed(using Context) =
3932
/** An exception thrown if a @retains argument is not syntactically a CaptureRef */
4033
class IllegalCaptureRef(tpe: Type) extends Exception
4134

42-
/** Capture checking state, which is stored in a context property */
43-
class CCState:
44-
45-
val rhsClosure: mutable.HashSet[Symbol] = new mutable.HashSet
46-
47-
val levelOwners: mutable.HashSet[Symbol] = new mutable.HashSet
48-
49-
/** Associates certain symbols (the nesting level owners) with their ccNestingLevel */
50-
val nestingLevels: mutable.HashMap[Symbol, Int] = new mutable.HashMap
51-
52-
/** Associates nesting level owners with the local roots valid in their scopes. */
53-
val localRoots: mutable.HashMap[Symbol, Symbol] = new mutable.HashMap
54-
55-
/** The last pair of capture reference and capture set where
56-
* the reference could not be added to the set due to a level conflict.
57-
*/
58-
var levelError: Option[(CaptureRef, CaptureSet)] = None
59-
60-
/** Under saferExceptions: The <try block> symbol generated for a try.
61-
* Installed by Setup, removed by CheckCaptures.
62-
*/
63-
val tryBlockOwner: mutable.HashMap[Try, Symbol] = new mutable.HashMap
64-
end CCState
65-
66-
/** Property key for capture checking state */
67-
val ccStateKey: Key[CCState] = Key()
68-
69-
/** The currently valid CCState */
70-
def ccState(using Context) = ctx.property(ccStateKey).get
71-
72-
trait FollowAliases extends TypeMap:
73-
def mapOverFollowingAliases(t: Type): Type = t match
74-
case t: LazyRef =>
75-
val t1 = this(t.ref)
76-
if t1 ne t.ref then t1 else t
77-
case _ =>
78-
val t1 = t.dealiasKeepAnnots
79-
if t1 ne t then
80-
val t2 = this(t1)
81-
if t2 ne t1 then return t2
82-
mapOver(t)
83-
84-
class mapRoots(from: CaptureRoot, to: CaptureRoot)(using Context) extends BiTypeMap, FollowAliases:
85-
thisMap =>
86-
87-
def apply(t: Type): Type =
88-
if t eq from then to
89-
else t match
90-
case t: CaptureRoot.Var =>
91-
val ta = t.followAlias
92-
if ta ne t then apply(ta)
93-
else from match
94-
case from: TermRef
95-
if t.upperLevel >= from.symbol.ccNestingLevel
96-
&& constrainRootsWhenMapping // next two lines do the constraining
97-
&& CaptureRoot.isEnclosingRoot(from, t)
98-
&& CaptureRoot.isEnclosingRoot(t, from) => to
99-
case from: CaptureRoot.Var if from.followAlias eq t => to
100-
case _ => t
101-
case _ =>
102-
mapOverFollowingAliases(t)
103-
104-
def inverse = mapRoots(to, from)
105-
end mapRoots
106-
10735
extension (tree: Tree)
10836

10937
/** Map tree with CaptureRef type to its type, throw IllegalCaptureRef otherwise */
110-
def toCaptureRef(using Context): CaptureRef = tree match
111-
case QualifiedRoot(outer) =>
112-
ctx.owner.levelOwnerNamed(outer)
113-
.orElse(defn.captureRoot) // non-existing outer roots are reported in Setup's checkQualifiedRoots
114-
.localRoot.termRef
115-
case _ => tree.tpe match
116-
case ref: CaptureRef => ref
117-
case tpe => throw IllegalCaptureRef(tpe) // if this was compiled from cc syntax, problem should have been reported at Typer
38+
def toCaptureRef(using Context): CaptureRef = tree.tpe match
39+
case ref: CaptureRef => ref
40+
case tpe => throw IllegalCaptureRef(tpe)
11841

11942
/** Convert a @retains or @retainsByName annotation tree to the capture set it represents.
12043
* For efficience, the result is cached as an Attachment on the tree.
@@ -241,7 +164,7 @@ extension (tp: Type)
241164
* a by name parameter type, turning the latter into an impure by name parameter type.
242165
*/
243166
def adaptByNameArgUnderPureFuns(using Context): Type =
244-
if adaptUnpickledFunctionTypes && Feature.pureFunsEnabledSomewhere then
167+
if Feature.pureFunsEnabledSomewhere then
245168
AnnotatedType(tp,
246169
CaptureAnnotation(CaptureSet.universal, boxed = false)(defn.RetainsByNameAnnot))
247170
else
@@ -330,91 +253,6 @@ extension (sym: Symbol)
330253
&& sym != defn.Caps_unsafeBox
331254
&& sym != defn.Caps_unsafeUnbox
332255

333-
def isLevelOwner(using Context): Boolean = ccState.levelOwners.contains(sym)
334-
335-
/** The owner of the current level. Qualifying owners are
336-
* - methods other than constructors and anonymous functions
337-
* - anonymous functions, provided they either define a local
338-
* root of type caps.Cap, or they are the rhs of a val definition.
339-
* - classes, if they are not staticOwners
340-
* - _root_
341-
*/
342-
def levelOwner(using Context): Symbol =
343-
if !sym.exists || sym.isRoot || sym.isStaticOwner then defn.RootClass
344-
else if sym.isLevelOwner then sym
345-
else sym.owner.levelOwner
346-
347-
/** The nesting level of `sym` for the purposes of `cc`,
348-
* -1 for NoSymbol
349-
*/
350-
def ccNestingLevel(using Context): Int =
351-
if sym.exists then
352-
val lowner = sym.levelOwner
353-
ccState.nestingLevels.getOrElseUpdate(lowner,
354-
if lowner.isRoot then 0 else lowner.owner.ccNestingLevel + 1)
355-
else -1
356-
357-
/** Optionally, the nesting level of `sym` for the purposes of `cc`, provided
358-
* a capture checker is running.
359-
*/
360-
def ccNestingLevelOpt(using Context): Option[Int] =
361-
if ctx.property(ccStateKey).isDefined then Some(ccNestingLevel) else None
362-
363-
/** The parameter with type caps.Cap in the leading term parameter section,
364-
* or NoSymbol, if none exists.
365-
*/
366-
def definedLocalRoot(using Context): Symbol =
367-
sym.paramSymss.dropWhile(psyms => psyms.nonEmpty && psyms.head.isType) match
368-
case psyms :: _ => psyms.find(_.info.typeSymbol == defn.Caps_Cap).getOrElse(NoSymbol)
369-
case _ => NoSymbol
370-
371-
/** The local root corresponding to sym's level owner */
372-
def localRoot(using Context): Symbol =
373-
val owner = sym.levelOwner
374-
assert(owner.exists)
375-
def newRoot = newSymbol(if owner.isClass then newLocalDummy(owner) else owner,
376-
nme.LOCAL_CAPTURE_ROOT, Synthetic, defn.Caps_Cap.typeRef, nestingLevel = owner.ccNestingLevel)
377-
def lclRoot =
378-
if owner.isTerm then owner.definedLocalRoot.orElse(newRoot)
379-
else newRoot
380-
ccState.localRoots.getOrElseUpdate(owner, lclRoot)
381-
382-
/** The level owner enclosing `sym` which has the given name, or NoSymbol if none exists.
383-
* If name refers to a val that has a closure as rhs, we return the closure as level
384-
* owner.
385-
*/
386-
def levelOwnerNamed(name: String)(using Context): Symbol =
387-
def recur(owner: Symbol, prev: Symbol): Symbol =
388-
if owner.name.toString == name then
389-
if owner.isLevelOwner then owner
390-
else if owner.isTerm && !owner.isOneOf(Method | Module) && prev.exists then prev
391-
else NoSymbol
392-
else if owner == defn.RootClass then
393-
NoSymbol
394-
else
395-
val prev1 = if owner.isAnonymousFunction && owner.isLevelOwner then owner else NoSymbol
396-
recur(owner.owner, prev1)
397-
recur(sym, NoSymbol)
398-
.showing(i"find outer $sym [ $name ] = $result", capt)
399-
400-
def maxNested(other: Symbol)(using Context): Symbol =
401-
if sym.ccNestingLevel < other.ccNestingLevel then other else sym
402-
/* does not work yet, we do mix sets with different levels, for instance in cc-this.scala.
403-
else if sym.ccNestingLevel > other.ccNestingLevel then sym
404-
else
405-
assert(sym == other, i"conflicting symbols at same nesting level: $sym, $other")
406-
sym
407-
*/
408-
409-
def minNested(other: Symbol)(using Context): Symbol =
410-
if sym.ccNestingLevel > other.ccNestingLevel then other else sym
411-
412-
extension (tp: TermRef | ThisType)
413-
/** The nesting level of this reference as defined by capture checking */
414-
def ccNestingLevel(using Context): Int = tp match
415-
case tp: TermRef => tp.symbol.ccNestingLevel
416-
case tp: ThisType => tp.cls.ccNestingLevel
417-
418256
extension (tp: AnnotatedType)
419257
/** Is this a boxed capturing type? */
420258
def isBoxed(using Context): Boolean = tp.annot match

0 commit comments

Comments
 (0)