Skip to content

Commit

Permalink
Drop user-defined local roots
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Oct 18, 2023
1 parent d29d5e3 commit c3b2f8a
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 41 deletions.
13 changes: 1 addition & 12 deletions compiler/src/dotty/tools/dotc/cc/CaptureOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -339,24 +339,13 @@ extension (sym: Symbol)
else recur(sym.owner)
recur(sym)

/** The parameter with type caps.Cap in the leading term parameter section,
* or NoSymbol, if none exists.
*/
def definedLocalRoot(using Context): Symbol =
sym.paramSymss.dropWhile(psyms => psyms.nonEmpty && psyms.head.isType) match
case psyms :: _ => psyms.find(_.info.typeSymbol == defn.Caps_Cap).getOrElse(NoSymbol)
case _ => NoSymbol

/** The local root corresponding to sym's level owner */
def localRoot(using Context): Symbol =
val owner = sym.levelOwner
assert(owner.exists)
def newRoot = newSymbol(if owner.isClass then newLocalDummy(owner) else owner,
nme.LOCAL_CAPTURE_ROOT, Synthetic, defn.Caps_Cap.typeRef)
def lclRoot =
if owner.isTerm then owner.definedLocalRoot.orElse(newRoot)
else newRoot
ccState.localRoots.getOrElseUpdate(owner, lclRoot)
ccState.localRoots.getOrElseUpdate(owner, newRoot)

def maxNested(other: Symbol, onConflict: (Symbol, Symbol) => Context ?=> Symbol)(using Context): Symbol =
if !sym.exists || other.isContainedIn(sym) then other
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/cc/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
case tp: (RefinedOrRecType | MatchType) =>
superTypeIsImpure(tp.underlying)
case tp: AndType =>
superTypeIsImpure(tp.tp1) || needsVariable(tp.tp2)
superTypeIsImpure(tp.tp1) || superTypeIsImpure(tp.tp2)
case tp: OrType =>
superTypeIsImpure(tp.tp1) && superTypeIsImpure(tp.tp2)
case _ =>
Expand Down
4 changes: 1 addition & 3 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2927,9 +2927,7 @@ object Types {
// TODO Try to make local class roots be NonMembers owned directly by the class
val owner = symbol.maybeOwner
def normOwner = if owner.isLocalDummy then owner.owner else owner
if name == nme.LOCAL_CAPTURE_ROOT then normOwner
else if info.isRef(defn.Caps_Cap) && owner.isTerm then normOwner
else NoSymbol
if name == nme.LOCAL_CAPTURE_ROOT then normOwner else NoSymbol

override def normalizedRef(using Context): CaptureRef =
if isTrackableRef then symbol.termRef else this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ object Test:
class Logger(f: OutputStream^):
def log(msg: String): Unit = ???

def usingFile[T](name: String, op: (lcap: caps.Cap) ?-> OutputStream^{lcap} => T): T =
def usingFile[sealed T](name: String, op: OutputStream^ => T): T =
val f = new FileOutputStream(name)
val result = op(f)
f.close()
Expand Down
29 changes: 17 additions & 12 deletions tests/neg-custom-args/captures/usingLogFile.check
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,23 @@
|
| where: x$0 is a reference to a value parameter
| x$0² is a reference to a value parameter
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:47:14 ------------------------------------------------------
47 | val later = usingLogFile { f => () => f.write(0) } // error
| ^^^^^^^^^^^^
| escaping local reference local.type
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:62:16 ------------------------------------------------------
62 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:52:16 ------------------------------------------------------
52 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error
| ^^^^^^^^^
| escaping local reference local.type
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:70:16 ------------------------------------------------------
70 | val later = usingFile("logfile", // error !!! but should be ok, since we can widen `l` to `file` instead of to `cap`
| reference (caps.cap : caps.Cap) is not included in the allowed capture set {x$0, x$0²}
|
| Note that the universal capability `cap`
| cannot be included in capture set {x$0, x$0}
|
| where: x$0 is a reference to a value parameter
| x$0² is a reference to a value parameter
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:60:16 ------------------------------------------------------
60 | val later = usingFile("logfile", // error !!! but should be ok, since we can widen `l` to `file` instead of to `cap`
| ^^^^^^^^^
| reference (_$1 : java.io.OutputStream^{local}) is not included in the allowed capture set {x$0, local}
| reference (_$1 : java.io.OutputStream^) is not included in the allowed capture set {x$0, x$0²}
|
| Note that reference (_$1 : java.io.OutputStream^), defined in method $anonfun
| cannot be included in outer capture set {x$0, x$0} which is associated with method test
|
| Note that reference (_$1 : java.io.OutputStream^{local}), defined in method $anonfun
| cannot be included in outer capture set {x$0, local} which is associated with method test
| where: x$0 is a reference to a value parameter
| x$0² is a reference to a value parameter
14 changes: 2 additions & 12 deletions tests/neg-custom-args/captures/usingLogFile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,10 @@ object Test2:
later4.x()

object Test3:

def usingLogFile[T](op: (local: caps.Cap) ?-> FileOutputStream^{local} => T) =
val logFile = FileOutputStream("log")
val result = op(logFile)
logFile.close()
result

val later = usingLogFile { f => () => f.write(0) } // error

object Test4:
class Logger(f: OutputStream^):
def log(msg: String): Unit = ???

def usingFile[T](name: String, op: (local: caps.Cap) ?-> OutputStream^{local} => T): T =
def usingFile[sealed T](name: String, op: OutputStream^ => T): T =
val f = new FileOutputStream(name)
val result = op(f)
f.close()
Expand All @@ -62,7 +52,7 @@ object Test4:
val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error
later(1)

def usingLogger[T](f: OutputStream^, op: (local: caps.Cap) ?-> Logger^{f} => T): T =
def usingLogger[sealed T](f: OutputStream^, op: Logger^{f} => T): T =
val logger = Logger(f)
op(logger)

Expand Down

0 comments on commit c3b2f8a

Please sign in to comment.