@@ -10,7 +10,9 @@ import config.SourceVersion
1010import config .Printers .capt
1111import util .Property .Key
1212import tpd .*
13+ import StdNames .nme
1314import config .Feature
15+ import collection .mutable
1416
1517private val Captures : Key [CaptureSet ] = Key ()
1618private val BoxedType : Key [BoxedTypeCache ] = Key ()
@@ -21,6 +23,11 @@ private val BoxedType: Key[BoxedTypeCache] = Key()
2123 */
2224private val adaptUnpickledFunctionTypes = false
2325
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+
2431/** The arguments of a @retains or @retainsByName annotation */
2532private [cc] def retainedElems (tree : Tree )(using Context ): List [Tree ] = tree match
2633 case Apply (_, Typed (SeqLiteral (elems, _), _) :: Nil ) => elems
@@ -32,12 +39,82 @@ def allowUniversalInBoxed(using Context) =
3239/** An exception thrown if a @retains argument is not syntactically a CaptureRef */
3340class IllegalCaptureRef (tpe : Type ) extends Exception
3441
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+
35107extension (tree : Tree )
36108
37109 /** Map tree with CaptureRef type to its type, throw IllegalCaptureRef otherwise */
38- def toCaptureRef (using Context ): CaptureRef = tree.tpe match
39- case ref : CaptureRef => ref
40- case tpe => throw IllegalCaptureRef (tpe)
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
41118
42119 /** Convert a @retains or @retainsByName annotation tree to the capture set it represents.
43120 * For efficience, the result is cached as an Attachment on the tree.
@@ -164,7 +241,7 @@ extension (tp: Type)
164241 * a by name parameter type, turning the latter into an impure by name parameter type.
165242 */
166243 def adaptByNameArgUnderPureFuns (using Context ): Type =
167- if Feature .pureFunsEnabledSomewhere then
244+ if adaptUnpickledFunctionTypes && Feature .pureFunsEnabledSomewhere then
168245 AnnotatedType (tp,
169246 CaptureAnnotation (CaptureSet .universal, boxed = false )(defn.RetainsByNameAnnot ))
170247 else
@@ -253,6 +330,91 @@ extension (sym: Symbol)
253330 && sym != defn.Caps_unsafeBox
254331 && sym != defn.Caps_unsafeUnbox
255332
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+
256418extension (tp : AnnotatedType )
257419 /** Is this a boxed capturing type? */
258420 def isBoxed (using Context ): Boolean = tp.annot match
0 commit comments