Skip to content

Commit

Permalink
Fix universal check for inferred types
Browse files Browse the repository at this point in the history
Checking against capturing the universal capability now works also for inferred types.
Also fix breakage from previous rebase
  • Loading branch information
odersky committed Aug 29, 2022
1 parent 2f0bc12 commit 097b179
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 17 deletions.
23 changes: 12 additions & 11 deletions compiler/src/dotty/tools/dotc/typer/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -408,17 +408,18 @@ class CheckCaptures extends Recheck, SymTransformer:
tree.symbol.info
case _ =>
NoType
if typeToCheck.exists then
typeToCheck.widenDealias match
case wtp @ CapturingType(parent, refs, _) =>
refs.disallowRootCapability { () =>
val kind = if tree.isInstanceOf[ValDef] then "mutable variable" else "expression"
report.error(
em"""The $kind's type $wtp is not allowed to capture the root capability `*`.
|This usually means that a capability persists longer than its allowed lifetime.""",
tree.srcPos)
}
case _ =>
def checkNotUniversal(tp: Type): Unit = tp.widenDealias match
case wtp @ CapturingType(parent, refs, _) =>
refs.disallowRootCapability { () =>
val kind = if tree.isInstanceOf[ValDef] then "mutable variable" else "expression"
report.error(
em"""The $kind's type $wtp is not allowed to capture the root capability `*`.
|This usually means that a capability persists longer than its allowed lifetime.""",
tree.srcPos)
}
checkNotUniversal(parent)
case _ =>
checkNotUniversal(typeToCheck)
super.recheckFinish(tpe, tree, pt)

/** This method implements the rule outlined in #14390:
Expand Down
4 changes: 2 additions & 2 deletions docs/_docs/reference/experimental/cc.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ again on access, the capture information "pops out" again. For instance, even th
() => p.fst : {ct} () -> {ct} Int -> String
```
In other words, references to capabilities "tunnel through" in generic instantiations from creation to access; they do not affect the capture set of the enclosing generic data constructor applications.
This principle may seem surprising at first, but it is the key to make capture checking concise and practical.
This principle plays an important part in making capture checking concise and practical.

## Escape Checking

Expand Down Expand Up @@ -398,7 +398,7 @@ This error message was produced by the following logic:

- The `f` parameter has type `{*} FileOutputStream`, which makes it a capability.
- Therefore, the type of the expression `() => f.write(0)` is `{f} () -> Unit`.
- This makes the whole type of the closure passed to `usingLogFile` the dependent function type
- This makes the type of the whole closure passed to `usingLogFile` the dependent function type
`(f: {*} FileOutputStream) -> {f} () -> Unit`.
- The expected type of the closure is a simple, parametric, impure function type `({*} FileOutputStream) => T`,
for some instantiation of the type variable `T`.
Expand Down
15 changes: 15 additions & 0 deletions tests/neg-custom-args/captures/usingLogFile.check
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,18 @@
| ^^^^^^^^
| The expression's type {*} () -> Unit is not allowed to capture the root capability `*`.
| This usually means that a capability persists longer than its allowed lifetime.
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:47:27 ------------------------------------------------------
47 | val later = usingLogFile { f => () => f.write(0) } // error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| The expression's type {*} () -> Unit is not allowed to capture the root capability `*`.
| This usually means that a capability persists longer than its allowed lifetime.
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:62:25 ------------------------------------------------------
62 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| The expression's type {*} (x$0: Int) -> Unit is not allowed to capture the root capability `*`.
| This usually means that a capability persists longer than its allowed lifetime.
-- Error: tests/neg-custom-args/captures/usingLogFile.scala:71:48 ------------------------------------------------------
71 | val later = usingFile("logfile", usingLogger(_, l => () => l.log("test"))) // error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| The expression's type {*} () -> Unit is not allowed to capture the root capability `*`.
| This usually means that a capability persists longer than its allowed lifetime.
36 changes: 35 additions & 1 deletion tests/neg-custom-args/captures/usingLogFile.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import java.io.FileOutputStream
import java.io.*
import annotation.capability

object Test1:
Expand Down Expand Up @@ -36,3 +36,37 @@ object Test2:
usingLogFile { f => later4 = Cell(() => f.write(0)) }
later4.x() // error

object Test3:

def usingLogFile[T](op: ({*} FileOutputStream) => 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: ({*} OutputStream) => T): T =
val f = new FileOutputStream(name)
val result = op(f)
f.close()
result

val xs: List[Int] = ???
def good = usingFile("out", f => xs.foreach(x => f.write(x)))
def fail =
val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error
later(1)


def usingLogger[T](f: {*} OutputStream, op: ({f} Logger) => T): T =
val logger = Logger(f)
op(logger)

def test =
val later = usingFile("logfile", usingLogger(_, l => () => l.log("test"))) // error
later()
3 changes: 0 additions & 3 deletions tests/semanticdb/metac.expect
Original file line number Diff line number Diff line change
Expand Up @@ -2037,9 +2037,6 @@ Occurrences:
[5:4..5:8): List -> scala/package.List.
[5:9..5:10): x -> local0

Synthetics:
[5:4..5:8):List => *.apply[Int]

expect/MatchType.scala
----------------------

Expand Down

0 comments on commit 097b179

Please sign in to comment.