-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Adapt types according to read-only status #23332
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM overall!
So the benefit of also adding and inferring qualifiers to capture sets are not clear.
not sure if I understood it correctly, does it mean that qualifier inference may not be that useful given the read-only status adaptation?
A related example:
import language.experimental.captureChecking
import caps.*
class Ref extends Mutable:
private var _data: Int = 0
def get: Int = _data
mut def set(x: Int): Unit = _data = x
def test1(a: Ref^{cap.rd}): Unit =
a.set(42) // error, as expected
def test2(a: Ref^{cap.rd}): Unit =
val t: Ref^{cap} = a // should be error, but ok
t.set(42)
right now it only has one error. The second but last line widens Ref^{cap.rd}
to Ref^{cap}
which implicitly lifts the permission of this reference and is clearly unsound. This is the examples that qualifier inference needs to take care of, I guess.
Good point. Yes, we still need the subtyping / inference rules for soundness. The problem is we cannot rely on them exclusively since then our deep capture sets are wrong. What needs to happen is that we adapt read-only status, which according to subtyping means we widen the type. So unlike for box adaptation this widening is possible (since we still conform to the expected type) but not necessary for conformance (since the original type already conformed). It is however necessary to remember that widened type so that we can reduce deep capture sets and don't get spurious separation errors. I'll add that part tomorrow. |
15db46d
to
a096125
Compare
Widen to cap.rd only if parent type does not extend Mutable.
We used to have the case where a RetainingType needs to be tested after a CapturingType. It would be better to reverse that order: If a RetainingType is possible test this first, and then test a CapturingType. If we just test for CapturingType we should crash if it's a RetainingType. That way we can express expectations that a retaining type was transformed. This commit does the first half: RetainingType unapply now can handle a CapturingType (and respond with a None). We still need to do the second half, that a CapturingType unapply does not handle a RetainingType.
- Also adapt in type arguments, refinements, and function results - Also adapt to readonly if target is a mutable, read-only type
Extend type adaptation to read-only status.
Two big improvements:
we also adapt if the target type is a mutable type with a read-only capture set. So the following compiles:
we also adapt deeply inside a type. So the following compiles:
We even try to track arguments through base types, so the following also compiles:
But there are some limits, correlating actual and expected type through base types and as-seen-from is hard.
In the end I had to abandon the original idea to roll everyhting into subtyping. I got the basics to work, but the problem then was that we use deep capture sets in separation checking, and those were not adapted. To do this, we'd have to somehow figure out what a dcs relative to an expected type is. This looks deeply non-trivial, and would probably in the end come down to techniques similar to the adaptation in this PR. So the benefit of also adding and inferring qualifiers to capture sets are not clear.
EDIT: We do need qualifiers for soundness as @Linyxus example below shows. It's now in
ro-mut-conformance.scala
.So the adaptation commit has been rebased on the mutability qualifiers branch.