-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Dependent function application can leak a scoped capability #17517
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
Labels
Milestone
Comments
This is a soundness problem related to capture set inference. To better see the problem we first inspect the typed tree of the usingFile[() ->?1 Unit](
"foo",
file => {
usingLogger[() ->?2 Unit](file)(l => () => l.log("test")) // leaking the local logger
}
) // should be error but it compiles The problem comes down to the inference of the capture set variables
|
odersky
added a commit
that referenced
this issue
May 22, 2023
- 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 #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.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
Uh oh!
There was an error while loading. Please reload this page.
Compiler version
main
branchMinimized code
Output
It compiles, but the last lines should be rejected since the scoped capability is leaked in the result.
Expectation
There should be an error on the last lines.
The text was updated successfully, but these errors were encountered: