Skip to content

Conversation

@odersky
Copy link
Contributor

@odersky odersky commented Nov 6, 2025

  1. Mutable and ExclsuiveCapability are not longer classifiers. This means Mutable classes can capture other capabilities. Read is still a classifier.

  2. Update methods in nested classes can access exclusive capabilities external to enclosing classes.

@odersky odersky requested a review from a team as a code owner November 6, 2025 12:48
@odersky odersky force-pushed the drop-mutable-classifier branch from d8ec775 to 676b85f Compare November 6, 2025 12:56
@Linyxus Linyxus self-requested a review November 6, 2025 13:48
@natsukagami natsukagami self-requested a review November 6, 2025 13:58
 1. Mutable and ExclsuiveCapability are not longer classifiers. This means
    Mutable classes can capture other capabilities. Red is still a classifier.

 2. Update methods in nested classes can access exclsuive capabilities external
    to enclosing classes.
 - Allow to charge read-only set on unboxing (previously it was always the exclusive set that
   was charged).
 - Treat also methods outside Mutable types as read-only members. Previously we charged
   read-only when calling a read-only method of a mutable type, but not when calling an inherited
   method such as `eq`.
 - Use the same criterion for read-only everywhere.
 - Distinguish between read- and write-accesses to mutable fields.
…hecking

We do get sometimes capabilities like `async.rd` where `async` is shared. It
does not look straightforward to prevent that, so instead we count async.rd
as shared if async is.
@odersky odersky force-pushed the drop-mutable-classifier branch from 2bc9435 to d40f1f9 Compare November 8, 2025 19:40
We need a way to distinguish C^{} and C^{cap} when printing. We
print C^{cap} as C is C is a capability class.
This fix removed quite a few spurious error notes.
@Linyxus
Copy link
Contributor

Linyxus commented Nov 11, 2025

I am playing with this PR, and found the following code compile:

import language.experimental.captureChecking
import language.experimental.separationChecking
import caps.*
class Logger extends SharedCapability, Mutable:  // (1) does this make sense?
  private var _state: Int = 0
  update def log(msg: String): Unit = ()
def onlyShared(x: Object^{cap.only[SharedCapability]}): Unit = ()
def main(): Unit =
  onlyShared(Logger())  // even if we allow (1), why would this type check?
  val t: Logger^{} = Logger()  // and this type checks too, thus the above line I guess
  • We have a Logger that extends both SharedCapability and Mutable. Does it make sense? If "shared" means can be safely exempted from separation checking, this feels conflicting.
  • The last line seems to be a bug. Logger extends Mutable, has a mutable field, and an update method, yet its instance is pure.

@odersky
Copy link
Contributor Author

odersky commented Nov 11, 2025

We currently have deep problems with Mutables carrying the empty capture set. I am trying to fix them but it turns out it's not easy.

Also, no probably combining SharedCapability and Mutable should be forbidden.

@Linyxus
Copy link
Contributor

Linyxus commented Nov 11, 2025

Another example:

import language.experimental.captureChecking
import language.experimental.separationChecking
import caps.*
class Logger extends SharedCapability:
  def log(msg: String): Unit = ()
class TracedRef(logger: Logger^{cap.only[SharedCapability]}) extends Mutable:
  private var _data: Int = 0
  update def set(newValue: Int): Unit =
    logger.log("set")
    _data = newValue
  def get: Int =
    logger.log("get")  // error, but should it be allowed?
    _data

Right now using a shared capability in a non-update method inside a Mutable class is disallowed. Again, if "shared" means "we don't care about them in separation checking", should it also be exempted?

@natsukagami
Copy link
Contributor

Right now using a shared capability in a non-update method inside a Mutable class is disallowed. Again, if "shared" means "we don't care about them in separation checking", should it also be exempted?

This might be conflicting, since it would mean a TracedRef^{caps.only[Read]} would actually capture the logger, even though Read and SharedCapability have no relationship on the classifier tree.

However from a practical point of view I agree we should exempt this case

Needed to invalidate the relation

    C^{} <: C^

for Mutable types C
@odersky odersky force-pushed the drop-mutable-classifier branch from 5bbca48 to a737c80 Compare November 11, 2025 12:54
@odersky odersky force-pushed the drop-mutable-classifier branch from bdecf4b to acf9df6 Compare November 13, 2025 09:48
 - New section in separation checking doc page
 - Slight generalization of expectsReadOnly
 - Lots of tests variations of scala#24373
@odersky odersky force-pushed the drop-mutable-classifier branch from acf9df6 to e853b59 Compare November 13, 2025 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants