Skip to content
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

Add capture checking to some standard library classes #18192

Merged
merged 37 commits into from
Jul 22, 2023

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Jul 12, 2023

Convert some standard library classes to capture checking. The converted classes are represented as tests.
Initially, the Iterator and IterableOnce classes are converted. UPDATE: By now, we have quite a few more, including List, ListBuffer, View, and all their super classes and traits.

Also change capture checker to facilitate the conversion. The main changes are about better interop with classes that are not (yet) capture checked.

Based on #18131

@odersky odersky added the cc-experiment Intended to be merged with cc-experiment branch on origin label Jul 12, 2023
@odersky odersky requested a review from Linyxus July 13, 2023 07:53
@odersky odersky force-pushed the add-unsafeAssumePure branch 2 times, most recently from c0a7fc1 to 1613aac Compare July 13, 2023 14:20
@odersky odersky changed the title Add unsafeAssumePure method Add capture checking to some standard library classes Jul 13, 2023
@odersky odersky force-pushed the add-unsafeAssumePure branch from ddb121e to a32b6d4 Compare July 13, 2023 20:31
@odersky odersky force-pushed the add-unsafeAssumePure branch from 1c6c09c to bae1a9a Compare July 14, 2023 10:51
odersky added 21 commits July 15, 2023 09:51
Add unsafeAssumePure method as an escape hatch which is more specific than a cast.
This would become a problem when adding fluid capture sets to
FromJavaObject types. And it anyway gives better error messages.
The pickler test did not track whether capture checking was on. This meant that files with

  import language.experimental.captureChecking

has to be compiled additionally with the capture checking setting

  -language.experimental.captureChecking

to pass pickler tests. This made it impossible to test mixed code bases where
some files require capture checking and others don't.

We now use the previous setting of `CompilationUnit#needsCaptureChecking` when unpickling.
Similar to the treatment of WithPureFuns, we add an annotation WithCaptureChecks
to toplevel classes if they were compiled with capture checking on. We also
keep a flag on all classes so that we can find out fast whether the class was
capture checked or not. However, that flag is not pickled. It is instead
reconstituted by the unpickler when it sees the WtihCaptureChecks annotation.
When interacting with files that are not captue checked, we should assume
that types can have arbitrary capture sets. This is analogous to interacting
with non-null checked files, where we also assume that types in the interface
can be null or not.

This commit enables interop for normal rechecking by decorating types coming
from sources that are not capture-checked with a special `Fluid` capture set.
This set both super- and subcaptures any other capture set.
Supress bounds checking in CheckCaptures if the type constructor of an applied type has not been
compiled with capture checking.
It seems it's no longer needed.
The original versions of Iterator and IterableOnce before adding capture checking
annotations
 - Also add Fluid to function types
 - Add Fluid recursively in arguments of fluidified types
 - But stop when a capturing type is encountered

Also, fix needsVariable for FromJavaObject. FromJavaObject should behave like
Any (i.e. no capture set variable needs to be added).
User-defined value classes should be able to capture capabilities
Otherwise we force purity where none might exist.
Members

   andThen, compose, curried, tupled

of function types are now given special types for capture checking that
reflect fine-grained capture dependencies.
With the new gradual typing using Fluid capture sets, this should be no longer needed.
def knownSize: Int = -1
}

final class IterableOnceExtensionMethods[A](private val it: IterableOnce[A]) extends AnyVal {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be impure? otherwise the extension methods only apply on pure IterableOnces.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it like that since all extension methods are deprecated anyway,

odersky added 9 commits July 17, 2023 19:25
As `Map` shows, it's possible that an overriding check is from an external
non-capture checked subclass member against a currently compiled & capture-checked
superclass member. Hence, we have to add Fluid sets to either part of an
overriding check if that part is not compiled with capture checking.

This allows to include collection/Map in the set of capture checked stdlib sources.
Plus all supertraits of ListBuffer
There was a subtle difference which causeed the stdlib tests to crash under -Xprint:cc
Without this, we get stray capture sets in match selectors if the match
is not exhaustive.
@odersky
Copy link
Contributor Author

odersky commented Jul 22, 2023

Observations of shortcomings so far:

  • In some instances we will get "cannot establish reference" errors when forming a capture set. For instance in the stringOps test in Test_2.scala.

  • Sometimes we'd like to have mutable variables capturing cap, but this is forbidden. We could do better by maintaining different universal capabilities at each nesting level, and only forbidding instantiating variables with universal capabilities at deeper level.

  • We have to assume that arrays are over sealed type variables

  • We have to ensure transitivity of sealed: A sealed type variable can only be instantiated with other sealed type variables or else with type variables upper bounded by deeply pure types. If there's level checking this has to be integrated.

@odersky odersky assigned Linyxus and unassigned odersky and Linyxus Jul 22, 2023
@odersky
Copy link
Contributor Author

odersky commented Jul 22, 2023

@Linyxus This is about as far as I want to push it for now. Can you give it a rough review? I think you need to look only at the changes to the compiler itself; the test programs don't matter so much. It would be good to get it in fast since there are a lot of accumulated changes and other stuff we do with capture checking should build on these.

@Linyxus
Copy link
Contributor

Linyxus commented Jul 22, 2023

Thanks for the heads-up! I'll do the review right away.

Copy link
Contributor

@Linyxus Linyxus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@Linyxus Linyxus merged commit 1254e14 into scala:main Jul 22, 2023
@Linyxus Linyxus deleted the add-unsafeAssumePure branch July 22, 2023 15:10
@Linyxus
Copy link
Contributor

Linyxus commented Jul 22, 2023

So it seems that test-windows-full is failing for this PR (which is somehow skipped in the CI of this PR but run after merged into main). The error looks a bit weird: syntax error for files that look like this. These files seem to be aliases to other files, but I'm not sure how they are parsed by the test runner and why they stop working on Windows.

/cc @odersky

@odersky
Copy link
Contributor Author

odersky commented Jul 22, 2023

These are symlinks. Maybe Windows does not know about them and interprets them literally?

@Linyxus Linyxus restored the add-unsafeAssumePure branch July 22, 2023 17:30
@odersky
Copy link
Contributor Author

odersky commented Jul 22, 2023

I pushed a new PR that should fix the problem.

@Kordyjan Kordyjan added this to the 3.4.0 milestone Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cc-experiment Intended to be merged with cc-experiment branch on origin
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants