Skip to content

Conversation

@odersky
Copy link
Contributor

@odersky odersky commented Jan 2, 2026

This is a fundamental paradigm shift. One of the tenets of the traditional capability model is that an object holds a capability only if it created the capability itself, or it was given to it as a parameter in a method call. This rules out global capabilities. If capabilities are only runtime checked, this restriction is necessary, since otherwise everyone can access global capabilities without any safety checks.

The downside of this rule is that it makes it somewhat inconvenient to implement programs with a fine-grained capability structure. For instance, traditional software can use a global output stream for printing or a global network socket for communicating. Passing these capabilities into the main function and from there on down to all code that needs to print or communicate is possible, but can become tedious.

With capture checking we have a new tool that allows us to support global capabilities. In that context, every object or function that uses a global capability has to declare that usage in its capture set. Conversely, we can demand objects and functions that don't access global capabilities simply by constraining their types.

So far, the capture checker did not track global capabilities since they were presumed to be impossible according to the traditional capability model. This PR aims to change that by properly handling global capabilities.

Based on #24881

@odersky odersky requested a review from a team as a code owner January 2, 2026 14:35
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/object-uses.scala:15:18 ----------------------------------
15 | val _: Object = b // error
| ^
| Found: (A.this.b : A.this.B.type)
Copy link
Member

Choose a reason for hiding this comment

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

singleton types hiding capture sets, this seems like something that might want to be recovered in error messages

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A hint is in the error note below. It should be made clearer, for sure.

@odersky odersky force-pushed the global-caps branch 2 times, most recently from cb4c1c1 to 6a79065 Compare January 4, 2026 12:29
@odersky odersky removed the stat:wip label Jan 4, 2026
odersky added 11 commits January 5, 2026 17:03
Also:

 - Add interpolation of types of module vals, so that we can decide whether they are tracked or not
 - Treat non-capture checked global refs as non-trackable
 1. Add capture sets also for self types of global classes
 2. Also mark free capabilities in global classes
 3. Handle interactions with opaque type refinements on self types
We already have a sentinel value in rootEnv; use that instead.
Require explicit types also for static definitions that have capture sets in their
(result-) types.
Compensate by allowing Unscoped fresh capabilities to escape to outer levels.
Also: Don't require a uses clause if an object only refers to external
capabilities. Those can't be declared anyway.
@odersky odersky merged commit 97cdaf4 into scala:main Jan 8, 2026
56 checks passed
@odersky odersky deleted the global-caps branch January 8, 2026 15:35
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