-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix capture checking of dependent functions (#16264)
Fixes #15921. Capture checking can be unsound for dependent functions. Minimized example: ```scala trait Cap { def use(): Unit } def main() = { val f: (io: {*} Cap) -> {} () -> Unit = io => () => io.use() // should be an error val g: ({*} Cap) -> {} () -> Unit = io => () => io.use() // error, as expected } ``` In the above example, we issue an error for `g`, but not for `f`, which is unsound. The root cause of this issue is that in the `Typer` phase, we only create `InferredTypeTree` for the result type of function values when the expected type of the function literal is non-dependent; and later during `Setup` of the capture checking phase, we only create capture set variables and update the information of function symbols when its result type tree is an `InferredTypeTree`. To be more specific, the function literal `io => () => io.use()` in both `f` and `g` would be expaneded into the following tree in `Typer`: ```scala def $anonfun(io: {*} Cap): {} () -> Unit = { { def $anonfun(): Unit = { io.use() } closure($anonfun) } } closure($anonfun) ``` For `g`, where the expected type of the function literal is non-dependent, we would create capture set variables in `Setup` for the return type `{} () -> Unit` and update the symbol info of the outer `$anonfun`. For `f`, we would not do these things because `{} () -> Unit` is not an `InferredTypeTree`. This PR fixes this issue by typing the `DependentTypeTree` as an `InferredTypeTree` in the typer. ~Currently, although this PR fixes the soundness problem, it brings completeness issues, where sometimes we propagate the universal capability to some capture sets incorrectly, which prevents some positive test cases from being accepted. I am still investigating this issue and thus mark this PR as a draft for now.~ The completeness problem is fixed by two additional refinements: - preciser propagation of captured references through mapped instance (see [dd88672](dd88672)), - healing ill-formed type parameter capture sets (see [0e7d33a](0e7d33a)).
- Loading branch information
Showing
12 changed files
with
193 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
trait Cap { def use(): Unit } | ||
|
||
def main() = { | ||
val f: (io: {*} Cap) -> {} () -> Unit = | ||
io => () => io.use() // error | ||
|
||
val g: ({*} Cap) -> {} () -> Unit = | ||
io => () => io.use() // error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import language.experimental.captureChecking | ||
|
||
trait Cap { def use(): Unit } | ||
|
||
def localCap[T](op: (cap: {*} Cap) => T): T = ??? | ||
|
||
def main(io: {*} Cap, net: {*} Cap): Unit = { | ||
val test1 = localCap { cap => // error | ||
() => { cap.use() } | ||
} | ||
|
||
val test2: (cap: {*} Cap) -> {cap} () -> Unit = | ||
localCap { cap => // should work | ||
(cap1: {*} Cap) => () => { cap1.use() } | ||
} | ||
|
||
val test3: (cap: {io} Cap) -> {io} () -> Unit = | ||
localCap { cap => // should work | ||
(cap1: {io} Cap) => () => { cap1.use() } | ||
} | ||
|
||
val test4: (cap: {io} Cap) -> {net} () -> Unit = | ||
localCap { cap => // error | ||
(cap1: {io} Cap) => () => { cap1.use() } | ||
} | ||
|
||
def localCap2[T](op: (cap: {io} Cap) => T): T = ??? | ||
|
||
val test5: {io} () -> Unit = | ||
localCap2 { cap => // ok | ||
() => { cap.use() } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
trait Stream { def close(): Unit = (); def write(x: Any): Unit = () } | ||
|
||
object Test { | ||
def usingLogFile[T](op: (c: {*} Stream) => T): T = | ||
val logFile = new Stream { } | ||
val result = op(logFile) | ||
logFile.close() | ||
result | ||
|
||
val later = usingLogFile { f => () => f.write(0) } // error | ||
later() // writing to closed file! | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters