-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
A more flexible scheme for handling the universal capability #18699
Conversation
This is needed if we want to do setup transforms as part of denotation transformers.
1. Update symbols owner before recursing to children, so that new owners taking account of try block owners are installed. 2. Query owner chaines at phase checkCpatures when computing enclosing level owners.
I tried several other strategies, but nothing worked. - update after Setup: This means we cannot to levelOwner during setup. - running levelOwner after Setup causes CyclicErrors when transforming symbols in Setup - having a seperate notion of ccOwner maintained in a CCState table does not work since there are too many hidden uses of owner everywhere that would ignore that table.
(except if they are updated). This is the first step towards converting to capturing types in SymTransformer, where we have the context.
Need to copy the denotation, since denotations come with next pointers which would get scrambled otherwise.
Makes things more regular and allows for a non-identity mapping between declared types and infos of vals.
The new check is that a publicly visible inferred type after capture checking must conform to the type before capture checking (which is also the type seen by other separately compiled units).
Widen singleton types when instantiating local roots in checkConforms
Don't force info when checking whether something is a root capability while printing
Refactor everything so that now vals as well as defs can be level owners
Innstead of traversing old types at postCheck, note what needs to be done when these types are first transformed at Setup.
With the new way to construct capturing types at Setup, we can add the correct boxing annotations there, so the previous unmodular hack can be dropped.
Break out operation to add a single element. This makes widenCaptures redundant.
Avoid the mutable state in CapturSet.Var
# Conflicts: # compiler/src/dotty/tools/dotc/cc/Setup.scala
(Configurable)
…ment This might not be enough.
Also: better parameter names for synthesized methods
Showing leak detection with simulated stdlib classes
@Linyxus I am going to merge now, so that we have a stable state for IWACO. When you have time, it would still be good to do a post-merge review. |
I'm testing the new scheme and found the following won't compile: import language.experimental.captureChecking
trait Cap:
def use: Int = 42
def test2(cs: List[Cap^]): Unit =
val t0: Cap^{cap[test2]} = cs.head // error
var t1: Cap^{cap[test2]} = cs.head // error |
Would be good to try some variants. Does it infer the right type if the type is not given? |
Without a type being given, the inferred type is |
For the var as well? |
It behaves differently but fails either: it creates a capture set variable on LHS, but since RHS is |
I investigated into what happened. It turns out that when the input to |
And why does that lead to a problem? It looks like we do adapt to capture set |
It seems that Besides, nested capture set annotations accumulate. So the effective boxed capture set of |
Yes, I guess here they should not accumulate. In fact I am not even sure that accumulation is correct in general. I think |
Or else do the |
I constructed an unsound code snippet with recursion: import language.experimental.captureChecking
trait Cap:
def use: Int = 42
def usingCap[sealed T](op: Cap^ => T): T = ???
def badTest(): Unit =
def bad(b: Boolean)(c: Cap^): Cap^{cap[bad]} =
if b then c
else
val leaked = usingCap[Cap^{cap[bad]}](bad(true))
leaked.use // boom
c
usingCap[Unit]: c0 =>
bad(false)(c0) The setup is classic. We have The function
Remarks:
|
Can you translate the program to explicit levels? Does it typecheck then? Where would it go wrong? |
At first glance with explicit levels that will not be a issue thanks to the polymorphism trick, but I'll verify. |
I think one solution would be that we don't let the local root of a method appear in the external type of that method. So the following line would be in error:
|
See 1065bd1 which fixes the problem and still allows the standard library to compile. It would be good if you could refine that further. (see commit message) |
cool! Thanks. I'll look into it. |
I looked into the commit and find it working in most cases I could come up with. Then I compared it with |
I think this should be rejected: class C:
object inner:
val x: T^{cap[C]} // error But this should be OK: class C:
private object inner:
val x: T^{cap[C]} // ok AFAICR that seems similar to what checkNoPrivate leaks does. |
sealed
type parameters, extended so that class parameters can also besealed
.sealed
type parameters must themselves besealed
, except if the argument is from some outer scope.Supersedes #18566