forked from scala/scala3
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Two fixes to capture checking (scala#17524)
- It changes `isParamDependent` to consider `CaptureDependency` as well, so that when applying a capture dependent function the capture sets in the types of the remaining parameters will be correctly substituted. For example: ```scala def foo[X](x: Foo[X]^, op: () ->{x} Unit): Unit = ??? foo(a, useA) ``` After rechecking `a` in the argument list, the expected type of the second argument, `useA`, should become `() -> {a} Unit`. This example previously does not work. - It fixes scala#17517. It fixes the healing of capture sets, which happens at the end of the capture checking phase, in which we traverse the capture checked tree and try to heal the ill-formed capture sets in type arguments. Here we say a capture set `C` in a type argument is ill-formed if it contains `TermParamRef` bound by other lambdas. For example: ```scala def usingLogger[sealed T](f: OutputStream^)(op: Logger^{f} => T): T = ??? usingLogger[() ->?1 Unit](file)(l => () => l.log("test")) ``` `l` will be propagated to `?1` as a result of capture checking, which makes `?1` ill-formed and we should widen `l` to `file`. The problem is that we heal the capture sets one-after-another but the healing of a later capture set may propagate more `TermParamRef`s to a healed one. For example: ``` usingFile[() ->?2 Unit]( // should be error "foo", file => { usingLogger[() ->?1 Unit](file)(l => () => l.log("test")) } ) ``` After capture checking both `?1` and `?2` will be `{l}`. When traversing the tree we first heal `?2`, wherein we widen `l` to `file`, but `file` is a `TermRef` here and we do nothing. Then, only later when we heal `?1`, we also widen `l` to `file` but this will end up propagating a `TermParamRef` of `file` to `?2`, which we should have widened as well. But `?2` is already healed and is not inspected again. This PR solves this issue by installing a handler when healing the capture sets which will try to heal the new elements propagated later in the healing process.
- Loading branch information
Showing
8 changed files
with
60 additions
and
12 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
-- Error: tests/neg-custom-args/captures/usingLogFile-alt.scala:18:2 --------------------------------------------------- | ||
18 | usingFile( // error | ||
| ^^^^^^^^^ | ||
| Sealed type variable T cannot be instantiated to box () => Unit since | ||
| that type captures the root capability `cap`. | ||
| This is often caused by a local capability in the body of method usingFile | ||
| leaking as part of its result. |
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,23 @@ | ||
// Reported in issue #17517 | ||
|
||
import language.experimental.captureChecking | ||
import java.io.* | ||
|
||
object Test: | ||
class Logger(f: OutputStream^): | ||
def log(msg: String): Unit = ??? | ||
|
||
def usingFile[sealed T](name: String, op: OutputStream^ => T): T = | ||
val f = new FileOutputStream(name) | ||
val result = op(f) | ||
f.close() | ||
result | ||
|
||
def usingLogger[sealed T](f: OutputStream^)(op: Logger^{f} => T): T = ??? | ||
|
||
usingFile( // error | ||
"foo", | ||
file => { | ||
usingLogger(file)(l => () => l.log("test")) | ||
} | ||
) |
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,8 @@ | ||
import language.experimental.captureChecking | ||
|
||
trait Foo[T] | ||
def test(): Unit = | ||
val a: Foo[Int]^ = ??? | ||
val useA: () ->{a} Unit = ??? | ||
def foo[X](x: Foo[X]^, op: () ->{x} Unit): Unit = ??? | ||
foo(a, useA) |