Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CC: Drop idempotent type maps #22910

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d72e5ea
Fix override checking of alias vs abstract types
odersky Mar 19, 2025
19dcfa2
Refactor: Drop isSubType parameter for override checking
odersky Mar 19, 2025
e9cdf94
More targeted handling of overriding checks against CapSet^
odersky Mar 19, 2025
cd2b7e6
Fix pathRoot
odersky Mar 15, 2025
58162ff
Simplify levelOK check for of Result(...) instances
odersky Mar 17, 2025
9105cf3
Reject ParamRefs in capture sets that are not in the result type of t…
odersky Mar 17, 2025
f6e5bc6
Harden checkApply duplicate error detection
odersky Mar 18, 2025
fc06af6
Under 3.8 solve all capture sets in types of vals and defs
odersky Mar 20, 2025
67446d2
Solve all capture sets in types of vals and defs by default
odersky Mar 20, 2025
ccf9867
Simplify setup
odersky Mar 21, 2025
1611183
Fix to interpolation
odersky Mar 21, 2025
90cee43
Redo handling of closures without relying on pre-existing maps
odersky Mar 21, 2025
c662bc3
Re-use `NamerOps.methodType when computing initial types of methods
odersky Mar 23, 2025
50b0b4d
Drop some BiTypeMaps
odersky Mar 23, 2025
dbd6f8f
Fuse successive SubstBindings maps and filters
odersky Mar 24, 2025
f33058c
Add tests that failed in CI before
odersky Mar 24, 2025
f43d24b
Drop IdempotentCaptRefMap and Mapped sets
odersky Mar 24, 2025
d8a8084
Unify existentials when matching function types
odersky Mar 27, 2025
6d9dbf6
Tighten subsumption checking of Fresh instances
odersky Mar 28, 2025
56de8df
Fix isPartOf
odersky Mar 29, 2025
09e05f0
Fix SubstBindings BiTypeMap logic
odersky Mar 31, 2025
58d052e
Variations on a test case
odersky Mar 31, 2025
bd0533a
Make captureSetofInfo cache in CaptureRefs depend on iteration count
odersky Apr 5, 2025
bb51ba0
Redo capture checks if necessary
odersky Apr 5, 2025
99f5628
Revert "Split posCC from pos tests"
odersky Apr 5, 2025
d674405
Drop healTypeParam
odersky Apr 5, 2025
3dd4f10
Refactor: move ccConfig into separate file
odersky Apr 6, 2025
053341d
Refactor: Move CCState to separate file and make it more class based
odersky Apr 6, 2025
86970ef
Refactor: Move previously @sharable data to ccState
odersky Apr 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ import dotty.tools.dotc.util.chaining.*
import java.util.{Timer, TimerTask}

/** A compiler run. Exports various methods to compile source files */
class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with ConstraintRunInfo {
class Run(comp: Compiler, ictx: Context)
extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo {

/** Default timeout to stop looking for further implicit suggestions, in ms.
* This is usually for the first import suggestion; subsequent suggestions
Expand Down Expand Up @@ -519,6 +520,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
/** Print summary of warnings and errors encountered */
def printSummary(): Unit = {
printMaxConstraint()
printMaxPath()
val r = runContext.reporter
if !r.errorsReported then
profile.printSummary()
Expand All @@ -529,6 +531,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
override def reset(): Unit = {
super[ImplicitRunInfo].reset()
super[ConstraintRunInfo].reset()
super[CaptureRunInfo].reset()
myCtx = null
myUnits = Nil
myUnitsCached = Nil
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class TreeTypeMap(
/** Replace occurrences of `This(oldOwner)` in some prefix of a type
* by the corresponding `This(newOwner)`.
*/
private val mapOwnerThis = new TypeMap with cc.CaptureSet.IdempotentCaptRefMap {
private val mapOwnerThis = new TypeMap {
private def mapPrefix(from: List[Symbol], to: List[Symbol], tp: Type): Type = from match {
case Nil => tp
case (cls: ClassSymbol) :: from1 => mapPrefix(from1, to.tail, tp.substThis(cls, to.head.thisType))
Expand Down
165 changes: 165 additions & 0 deletions compiler/src/dotty/tools/dotc/cc/CCState.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package dotty.tools
package dotc
package cc

import core.*
import CaptureSet.{CompareResult, CompareFailure, VarState}
import collection.mutable
import reporting.Message
import Contexts.Context
import Types.MethodType
import Symbols.Symbol

/** Capture checking state, which is known to other capture checking components */
class CCState:
import CCState.*

// ------ Error diagnostics -----------------------------

/** Error reprting notes produces since the last call to `test` */
var notes: List[ErrorNote] = Nil

def addNote(note: ErrorNote): Unit =
if !notes.exists(_.getClass == note.getClass) then
notes = note :: notes

def test(op: => CompareResult): CompareResult =
val saved = notes
notes = Nil
try op match
case res: CompareFailure => res.withNotes(notes)
case res => res
finally notes = saved

def testOK(op: => Boolean): CompareResult =
test(if op then CompareResult.OK else CompareResult.Fail(Nil))

/** Warnings relating to upper approximations of capture sets with
* existentially bound variables.
*/
val approxWarnings: mutable.ListBuffer[Message] = mutable.ListBuffer()

// ------ Level handling ---------------------------

private var curLevel: Level = outermostLevel

/** The level of the current environment. Levels start at 0 and increase for
* each nested function or class. -1 means the level is undefined.
*/
def currentLevel(using Context): Level = curLevel

/** Perform `op` in the next inner level */
inline def inNestedLevel[T](inline op: T)(using Context): T =
val saved = curLevel
curLevel = curLevel.nextInner
try op finally curLevel = saved

/** Perform `op` in the next inner level unless `p` holds. */
inline def inNestedLevelUnless[T](inline p: Boolean)(inline op: T)(using Context): T =
val saved = curLevel
if !p then curLevel = curLevel.nextInner
try op finally curLevel = saved

/** A map recording the level of a symbol */
private val mySymLevel: mutable.Map[Symbol, Level] = mutable.Map()

def symLevel(sym: Symbol): Level = mySymLevel.getOrElse(sym, undefinedLevel)

def recordLevel(sym: Symbol)(using Context): Unit = mySymLevel(sym) = curLevel

// ------ BiTypeMap adjustment -----------------------

private var myMapFutureElems = true

/** When mapping a capture set with a BiTypeMap, should we create a BiMapped set
* so that future elements can also be mapped, and elements added to the BiMapped
* are back-propagated? Turned off when creating capture set variables for the
* first time, since we then do not want to change the binder to the original type
* without capture sets when back propagating. Error case where this shows:
* pos-customargs/captures/lists.scala, method m2c.
*/
def mapFutureElems(using Context) = myMapFutureElems

/** Don't map future elements in this `op` */
inline def withoutMappedFutureElems[T](op: => T)(using Context): T =
val saved = mapFutureElems
myMapFutureElems = false
try op finally myMapFutureElems = saved

// ------ Iteration count of capture checking run

private var iterCount = 1

def iterationId = iterCount

def nextIteration[T](op: => T): T =
iterCount += 1
try op finally iterCount -= 1

// ------ Global counters -----------------------

/** Next CaptureSet.Var id */
var varId = 0

/** Next root id */
var rootId = 0

// ------ VarState singleton objects ------------
// See CaptureSet.VarState creation methods for documentation

object Separate extends VarState.Separating
object HardSeparate extends VarState.Separating
object Unrecorded extends VarState.Unrecorded
object ClosedUnrecorded extends VarState.ClosedUnrecorded

// ------ Context info accessed from companion object when isCaptureCheckingOrSetup is true

private var openExistentialScopes: List[MethodType] = Nil

private var capIsRoot: Boolean = false

object CCState:

opaque type Level = Int

val undefinedLevel: Level = -1

val outermostLevel: Level = 0

extension (x: Level)
def isDefined: Boolean = x >= 0
def <= (y: Level) = (x: Int) <= y
def nextInner: Level = if isDefined then x + 1 else x

/** If we are currently in capture checking or setup, and `mt` is a method
* type that is not a prefix of a curried method, perform `op` assuming
* a fresh enclosing existential scope `mt`, otherwise perform `op` directly.
*/
inline def inNewExistentialScope[T](mt: MethodType)(op: => T)(using Context): T =
if isCaptureCheckingOrSetup then
val ccs = ccState
val saved = ccs.openExistentialScopes
if mt.marksExistentialScope then ccs.openExistentialScopes = mt :: ccs.openExistentialScopes
try op finally ccs.openExistentialScopes = saved
else
op

/** The currently opened existential scopes */
def openExistentialScopes(using Context): List[MethodType] = ccState.openExistentialScopes

/** Run `op` under the assumption that `cap` can subsume all other capabilties
* except Result capabilities. Every use of this method should be scrutinized
* for whether it introduces an unsoundness hole.
*/
inline def withCapAsRoot[T](op: => T)(using Context): T =
if isCaptureCheckingOrSetup then
val ccs = ccState
val saved = ccs.capIsRoot
ccs.capIsRoot = true
try op finally ccs.capIsRoot = saved
else op

/** Is `caps.cap` a root capability that is allowed to subsume other capabilities? */
def capIsRoot(using Context): Boolean = ccState.capIsRoot

end CCState
Loading
Loading