Skip to content

Commit 053341d

Browse files
committed
Refactor: Move CCState to separate file and make it more class based
1 parent 3dd4f10 commit 053341d

File tree

6 files changed

+185
-161
lines changed

6 files changed

+185
-161
lines changed

Diff for: compiler/src/dotty/tools/dotc/cc/CCState.scala

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package dotty.tools
2+
package dotc
3+
package cc
4+
5+
import core.*
6+
import CaptureSet.{CompareResult, CompareFailure}
7+
import collection.mutable
8+
import reporting.Message
9+
import Contexts.Context
10+
import Types.MethodType
11+
import Symbols.Symbol
12+
13+
/** Capture checking state, which is known to other capture checking components */
14+
class CCState:
15+
import CCState.*
16+
17+
// ------ Error diagnostics -----------------------------
18+
19+
/** Error reprting notes produces since the last call to `test` */
20+
var notes: List[ErrorNote] = Nil
21+
22+
def addNote(note: ErrorNote): Unit =
23+
if !notes.exists(_.getClass == note.getClass) then
24+
notes = note :: notes
25+
26+
def test(op: => CompareResult): CompareResult =
27+
val saved = notes
28+
notes = Nil
29+
try op match
30+
case res: CompareFailure => res.withNotes(notes)
31+
case res => res
32+
finally notes = saved
33+
34+
def testOK(op: => Boolean): CompareResult =
35+
test(if op then CompareResult.OK else CompareResult.Fail(Nil))
36+
37+
/** Warnings relating to upper approximations of capture sets with
38+
* existentially bound variables.
39+
*/
40+
val approxWarnings: mutable.ListBuffer[Message] = mutable.ListBuffer()
41+
42+
// ------ Level handling ---------------------------
43+
44+
private var curLevel: Level = outermostLevel
45+
46+
/** The level of the current environment. Levels start at 0 and increase for
47+
* each nested function or class. -1 means the level is undefined.
48+
*/
49+
def currentLevel(using Context): Level = curLevel
50+
51+
/** Perform `op` in the next inner level */
52+
inline def inNestedLevel[T](inline op: T)(using Context): T =
53+
val saved = curLevel
54+
curLevel = curLevel.nextInner
55+
try op finally curLevel = saved
56+
57+
/** Perform `op` in the next inner level unless `p` holds. */
58+
inline def inNestedLevelUnless[T](inline p: Boolean)(inline op: T)(using Context): T =
59+
val saved = curLevel
60+
if !p then curLevel = curLevel.nextInner
61+
try op finally curLevel = saved
62+
63+
/** A map recording the level of a symbol */
64+
private val mySymLevel: mutable.Map[Symbol, Level] = mutable.Map()
65+
66+
def symLevel(sym: Symbol): Level = mySymLevel.getOrElse(sym, undefinedLevel)
67+
68+
def recordLevel(sym: Symbol)(using Context): Unit = mySymLevel(sym) = curLevel
69+
70+
// ------ BiTypeMap adjustment -----------------------
71+
72+
private var myMapFutureElems = true
73+
74+
/** When mapping a capture set with a BiTypeMap, should we create a BiMapped set
75+
* so that future elements can also be mapped, and elements added to the BiMapped
76+
* are back-propagated? Turned off when creating capture set variables for the
77+
* first time, since we then do not want to change the binder to the original type
78+
* without capture sets when back propagating. Error case where this shows:
79+
* pos-customargs/captures/lists.scala, method m2c.
80+
*/
81+
def mapFutureElems(using Context) = myMapFutureElems
82+
83+
/** Don't map future elements in this `op` */
84+
inline def withoutMappedFutureElems[T](op: => T)(using Context): T =
85+
val saved = mapFutureElems
86+
myMapFutureElems = false
87+
try op finally myMapFutureElems = saved
88+
89+
// ------ Iteration count of capture checking run
90+
91+
private var iterCount = 1
92+
93+
def iterationId = iterCount
94+
95+
def nextIteration[T](op: => T): T =
96+
iterCount += 1
97+
try op finally iterCount -= 1
98+
99+
// ------ Context info accessed from companion object when isCaptureCheckingOrSetup is true
100+
101+
private var openExistentialScopes: List[MethodType] = Nil
102+
103+
private var capIsRoot: Boolean = false
104+
105+
object CCState:
106+
107+
opaque type Level = Int
108+
109+
val undefinedLevel: Level = -1
110+
111+
val outermostLevel: Level = 0
112+
113+
extension (x: Level)
114+
def isDefined: Boolean = x >= 0
115+
def <= (y: Level) = (x: Int) <= y
116+
def nextInner: Level = if isDefined then x + 1 else x
117+
118+
/** If we are currently in capture checking or setup, and `mt` is a method
119+
* type that is not a prefix of a curried method, perform `op` assuming
120+
* a fresh enclosing existential scope `mt`, otherwise perform `op` directly.
121+
*/
122+
inline def inNewExistentialScope[T](mt: MethodType)(op: => T)(using Context): T =
123+
if isCaptureCheckingOrSetup then
124+
val ccs = ccState
125+
val saved = ccs.openExistentialScopes
126+
if mt.marksExistentialScope then ccs.openExistentialScopes = mt :: ccs.openExistentialScopes
127+
try op finally ccs.openExistentialScopes = saved
128+
else
129+
op
130+
131+
/** The currently opened existential scopes */
132+
def openExistentialScopes(using Context): List[MethodType] = ccState.openExistentialScopes
133+
134+
/** Run `op` under the assumption that `cap` can subsume all other capabilties
135+
* except Result capabilities. Every use of this method should be scrutinized
136+
* for whether it introduces an unsoundness hole.
137+
*/
138+
inline def withCapAsRoot[T](op: => T)(using Context): T =
139+
if isCaptureCheckingOrSetup then
140+
val ccs = ccState
141+
val saved = ccs.capIsRoot
142+
ccs.capIsRoot = true
143+
try op finally ccs.capIsRoot = saved
144+
else op
145+
146+
/** Is `caps.cap` a root capability that is allowed to subsume other capabilities? */
147+
def capIsRoot(using Context): Boolean = ccState.capIsRoot
148+
149+
end CCState

Diff for: compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

+2-130
Original file line numberDiff line numberDiff line change
@@ -48,134 +48,6 @@ class IllegalCaptureRef(tpe: Type)(using Context) extends Exception(tpe.show)
4848
/** A base trait for data producing addenda to error messages */
4949
trait ErrorNote
5050

51-
/** Capture checking state, which is known to other capture checking components */
52-
class CCState:
53-
54-
/** Error reprting notes produces since the last call to `test` */
55-
var notes: List[ErrorNote] = Nil
56-
57-
def addNote(note: ErrorNote): Unit =
58-
if !notes.exists(_.getClass == note.getClass) then
59-
notes = note :: notes
60-
61-
def test(op: => CompareResult): CompareResult =
62-
val saved = notes
63-
notes = Nil
64-
try op match
65-
case res: CompareFailure => res.withNotes(notes)
66-
case res => res
67-
finally notes = saved
68-
69-
def testOK(op: => Boolean): CompareResult =
70-
test(if op then CompareResult.OK else CompareResult.Fail(Nil))
71-
72-
/** Warnings relating to upper approximations of capture sets with
73-
* existentially bound variables.
74-
*/
75-
val approxWarnings: mutable.ListBuffer[Message] = mutable.ListBuffer()
76-
77-
private var curLevel: Level = outermostLevel
78-
private val symLevel: mutable.Map[Symbol, Int] = mutable.Map()
79-
80-
private var openExistentialScopes: List[MethodType] = Nil
81-
82-
private var capIsRoot: Boolean = false
83-
84-
/** If true, apply a BiTypeMap also to elements added to the set in the future
85-
* (and use its inverse when back-progating).
86-
*/
87-
private var mapFutureElems = true
88-
89-
var iterCount = 1
90-
91-
object CCState:
92-
93-
opaque type Level = Int
94-
95-
val undefinedLevel: Level = -1
96-
97-
val outermostLevel: Level = 0
98-
99-
/** The level of the current environment. Levels start at 0 and increase for
100-
* each nested function or class. -1 means the level is undefined.
101-
*/
102-
def currentLevel(using Context): Level = ccState.curLevel
103-
104-
/** Perform `op` in the next inner level
105-
* @pre We are currently in capture checking or setup
106-
*/
107-
inline def inNestedLevel[T](inline op: T)(using Context): T =
108-
val ccs = ccState
109-
val saved = ccs.curLevel
110-
ccs.curLevel = ccs.curLevel.nextInner
111-
try op finally ccs.curLevel = saved
112-
113-
/** Perform `op` in the next inner level unless `p` holds.
114-
* @pre We are currently in capture checking or setup
115-
*/
116-
inline def inNestedLevelUnless[T](inline p: Boolean)(inline op: T)(using Context): T =
117-
val ccs = ccState
118-
val saved = ccs.curLevel
119-
if !p then ccs.curLevel = ccs.curLevel.nextInner
120-
try op finally ccs.curLevel = saved
121-
122-
/** If we are currently in capture checking or setup, and `mt` is a method
123-
* type that is not a prefix of a curried method, perform `op` assuming
124-
* a fresh enclosing existential scope `mt`, otherwise perform `op` directly.
125-
*/
126-
inline def inNewExistentialScope[T](mt: MethodType)(op: => T)(using Context): T =
127-
if isCaptureCheckingOrSetup then
128-
val ccs = ccState
129-
val saved = ccs.openExistentialScopes
130-
if mt.marksExistentialScope then ccs.openExistentialScopes = mt :: ccs.openExistentialScopes
131-
try op finally ccs.openExistentialScopes = saved
132-
else
133-
op
134-
135-
/** Run `op` under the assumption that `cap` can subsume all other capabilties
136-
* except Result capabilities. Every use of this method should be scrutinized
137-
* for whether it introduces an unsoundness hole.
138-
*/
139-
inline def withCapAsRoot[T](op: => T)(using Context): T =
140-
if isCaptureCheckingOrSetup then
141-
val ccs = ccState
142-
val saved = ccs.capIsRoot
143-
ccs.capIsRoot = true
144-
try op finally ccs.capIsRoot = saved
145-
else op
146-
147-
/** Don't map future elements in this `op` */
148-
inline def withoutMappedFutureElems[T](op: => T)(using Context): T =
149-
val ccs = ccState
150-
val saved = ccs.mapFutureElems
151-
ccs.mapFutureElems = false
152-
try op finally ccs.mapFutureElems = saved
153-
154-
/** Is `caps.cap` a root capability that is allowed to subsume other capabilities? */
155-
def capIsRoot(using Context): Boolean = ccState.capIsRoot
156-
157-
/** When mapping a capture set with a BiTypeMap, should we create a BiMapped set
158-
* so that future elements can also be mapped, and elements added to the BiMapped
159-
* are back-propagated? Turned off when creating capture set variables for the
160-
* first time, since we then do not want to change the binder to the original type
161-
* without capture sets when back propagating. Error case where this shows:
162-
* pos-customargs/captures/lists.scala, method m2c.
163-
*/
164-
def mapFutureElems(using Context) = ccState.mapFutureElems
165-
166-
/** The currently opened existential scopes */
167-
def openExistentialScopes(using Context): List[MethodType] = ccState.openExistentialScopes
168-
169-
extension (x: Level)
170-
def isDefined: Boolean = x >= 0
171-
def <= (y: Level) = (x: Int) <= y
172-
def nextInner: Level = if isDefined then x + 1 else x
173-
174-
extension (sym: Symbol)(using Context)
175-
def ccLevel: Level = ccState.symLevel.getOrElse(sym, -1)
176-
def recordLevel() = ccState.symLevel(sym) = currentLevel
177-
end CCState
178-
17951
/** The currently valid CCState */
18052
def ccState(using Context): CCState =
18153
Phases.checkCapturesPhase.asInstanceOf[CheckCaptures].ccState1
@@ -631,8 +503,8 @@ extension (tp: Type)
631503

632504
def level(using Context): Level =
633505
tp match
634-
case tp: TermRef => tp.symbol.ccLevel
635-
case tp: ThisType => tp.cls.ccLevel.nextInner
506+
case tp: TermRef => ccState.symLevel(tp.symbol)
507+
case tp: ThisType => ccState.symLevel(tp.cls).nextInner
636508
case _ => undefinedLevel
637509

638510
extension (tp: MethodType)

Diff for: compiler/src/dotty/tools/dotc/cc/CaptureRef.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ object CaptureRef:
2020
opaque type Validity = Int
2121
def validId(runId: Int, iterId: Int): Validity =
2222
runId + (iterId << RunWidth)
23-
def currentId(using Context): Validity = validId(ctx.runId, ccState.iterCount)
23+
def currentId(using Context): Validity = validId(ctx.runId, ccState.iterationId)
2424
val invalid: Validity = validId(NoRunId, 0)
2525

2626
/** A trait for references in CaptureSets. These can be NamedTypes, ThisTypes or ParamRefs,

Diff for: compiler/src/dotty/tools/dotc/cc/CaptureSet.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ sealed abstract class CaptureSet extends Showable:
322322
if isConst then
323323
if mappedElems == elems then this
324324
else Const(mappedElems)
325-
else if CCState.mapFutureElems then
325+
else if ccState.mapFutureElems then
326326
def unfused = BiMapped(asVar, tm, mappedElems)
327327
this match
328328
case self: BiMapped => self.bimap.fuse(tm) match
@@ -521,7 +521,7 @@ object CaptureSet:
521521
*/
522522
var deps: Deps = SimpleIdentitySet.empty
523523

524-
def isConst(using Context) = solved >= ccState.iterCount
524+
def isConst(using Context) = solved >= ccState.iterationId
525525
def isAlwaysEmpty(using Context) = isConst && elems.isEmpty
526526
def isProvisionallySolved(using Context): Boolean = solved > 0 && solved != Int.MaxValue
527527

@@ -613,9 +613,9 @@ object CaptureSet:
613613
case prefix: CaptureRef =>
614614
levelOK(prefix)
615615
case _ =>
616-
elem.symbol.ccLevel <= level
616+
ccState.symLevel(elem.symbol) <= level
617617
case elem: ThisType if level.isDefined =>
618-
elem.cls.ccLevel.nextInner <= level
618+
ccState.symLevel(elem.cls).nextInner <= level
619619
case elem: ParamRef if !this.isInstanceOf[BiMapped] =>
620620
isPartOf(elem.binder.resType)
621621
|| {
@@ -700,7 +700,7 @@ object CaptureSet:
700700

701701
/** Mark set as solved and propagate this info to all dependent sets */
702702
def markSolved(provisional: Boolean)(using Context): Unit =
703-
solved = if provisional then ccState.iterCount else Int.MaxValue
703+
solved = if provisional then ccState.iterationId else Int.MaxValue
704704
deps.foreach(_.propagateSolved(provisional))
705705

706706
var skippedMaps: Set[TypeMap] = Set.empty

0 commit comments

Comments
 (0)