Proposal: Add @deprecatedAccessibility Annotation

Proposal

Add a @deprecatedAccessibility (or @deprecatedVisibility) constant annotation allowing you to specify a new access modifier narrower than the current one, and deprecating access outside of that scope.

For example:

@deprecatedAccessibility("private[mutable]")
class ArrayBufferView[A] { ... }

In the example, ArrayBufferView would be accessible within scala.collection.mutable, but would be considered deprecated outside of that package.

Motivation

Sometimes, classes or methods in the standard library (or other libraries) are accidentally made visible or accessible for no reason1, significantly restricting and constraining the ability of maintainers to improve or optimise implementations2 and fix bugs3. Deprecating an accidentally visible class requires multiple steps, each needing to wait for a new, binary-incompatible version:

  • creating a new class with the correct accessibility, changing the original class to extend the new class, and deprecating the original class
  • removing the original class, and renaming the new class to the original class’s name

Currently, you can’t even deprecate the accessibility of the class without waiting for a new, binary-incompatible version.

The annotation would allow you to deprecate the accessibility of the class immediately, and would remove the need to faff about with making a new class with a new (suboptimal) name, and would greatly simplify library maintenance. This is especially helpful for the standard library, which has a quite long lifecycle.


1. I don’t mean in any way to criticise contributors or maintainers who accidentally make code overly accessible; it’s easy to make such mistakes, and also easy for it to be the correct decision when written, but incorrect after some other changes.

2. scala/scala#9258

3. scala/bug#12284

10 Likes

Related:

Are you sure you want to continue this old conversation?

:thinking:

An example is BitSetN and friends which was deprecated.

I’d propose semantics like deprecatedName, where the annotation describes the old behavior. For purposes of compatibility and actually working, the implementation would need to emit the old, wider access in the back end. (This suggests the same is true for parameter names, but I haven’t checked the behavior.)

@deprecatedAccess
private[BitSet] class BitSetN

where the unspecified access is public, and the implementation would be public for that reason.

One may also wish to say that an overridable member will change from private to protected:

class C {
  @deprecatedAccess("private") protected def f = 42
}

but that would require a mangled protected forwarder until the incompatible change is made. Maybe that’s too much trouble.

1 Like