Skip to content

Introduce open modifier on classes #7471

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

Merged
merged 10 commits into from
Nov 2, 2019
Merged

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Oct 29, 2019

This does not affect pattern matching exhaustivity (yet) since currently
only abstract sealed classes and sealed traits are checked for exhaustivity,
so defualt classes do not count. I wonder whether we should change that (?).
Also, make all synthetic classes produced in Definitions Open.
@odersky odersky force-pushed the add-open-class branch 2 times, most recently from 72279fc to 3637127 Compare October 30, 2019 20:36
Classes representing sum types need to be be abstract, which default classes are not.

This partially reverts "Treat non-open classes as effectively sealed."
d98c2d6.
Copy link
Contributor

@liufengyun liufengyun left a comment

Choose a reason for hiding this comment

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

LGTM

I think it's a good idea to mark open classes explicit. Open classes may be subject to different and/or more checks. It enables potential opportunities for interesting analysis and optimizations.

For example, for initialization, open classes may be subject to more strict checks to ensure modularity, while non-open classes can be checked by analysis to support more flexible initialization patterns and zero overhead in annotations.

One thing I'm not sure is about the scope of non-open classes: currently it's restricted to the same compilation-unit, but a natural physical boundary for programmers might be the same project, which is not a compiler concept.

|| sym == cls.owner
|| sym.is(Package)
|| sym.isType && isAccessible(sym.owner)
!isAccessible(self.owner)
Copy link
Contributor

Choose a reason for hiding this comment

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

def foo: Unit = {
  object O { class A }
  class B extends O.A
}

According to the code, B is not accessible from A, is this intended?

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 would claim it is accessible!

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe I miss something here, I mean B.isInaccessibleChildOf(A) = true, that is the value I get from the compiler compiling the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I see now. Yes, this is a problem. I'll update the method and add a test.

@mdedetrich
Copy link

I think it's a good idea to mark open classes explicit. Open classes may be subject to different and/or more checks. It enables potential opportunities for interesting analysis and optimizations.

For example, for initialization, open classes may be subject to more strict checks to ensure modularity, while non-open classes can be checked by analysis to support more flexible initialization patterns and zero overhead in annotations.

Doesn't the JVM already do this, i.e. CHA (class hierarchy analysis)? If a class is never extended during the execution of the JVM, then its effectively the same as being final (and will get de-optimized if the class happens to get extended at a later point in time).

Copy link

@arkban arkban left a comment

Choose a reason for hiding this comment

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

xcuse me for stepping into your PR without being invited; I commented on the discussion on the [hhttps://contributors.scala-lang.org/t/pre-sip-make-classes-sealed-by-default/3767/16?u=arkban] and was curious to see the PR.


- The language feature `adhocExtensions` is enabled for the extending class. This is typically enabled by an import statement in the source file of the extension:
```scala
import scala.language.adhocExtensions
Copy link

Choose a reason for hiding this comment

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

Would it be possible to do this as annotation? IMHO a class-level annotation would make it clearer to the reader which class is doing the extending, and potentially make it easier to see what is being extended (since the class is in the extension list).

- `open` is a soft modifier. It is treated as a normal identifier
unless it is in modifier position.
- An `open` class cannot be `final` or `sealed`.
- Traits or `abstract` classes are always `open`, so `open` is redundant for them.
Copy link

Choose a reason for hiding this comment

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

Does the compiler emit a warning if you do open trait or open abstract class?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No.

@liufengyun
Copy link
Contributor

Doesn't the JVM already do this, i.e. CHA (class hierarchy analysis)? If a class is never extended during the execution of the JVM, then its effectively the same as being final (and will get de-optimized if the class happens to get extended at a later point in time).

I mean compile-time analysis and optimization, instead of runtime.

@mdedetrich
Copy link

I mean compile-time analysis and optimization, instead of runtime.

Right, I guess my general point is that "is there going to be a difference", as in is the compiler going to be able to handle more cases than the JVM will?

* such extensions should be limited in scope and clearly documented.
* That's why the language import is required for them.
*/
object adhocExtensions
Copy link
Member

@smarter smarter Oct 31, 2019

Choose a reason for hiding this comment

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

I'm not sure adhocExtensionsis the best name to use here, it sounds like something related to extension methods. I suggest something more descriptive like openAllClasses

@smarter
Copy link
Member

smarter commented Oct 31, 2019

Right, I guess my general point is that "is there going to be a difference", as in is the compiler going to be able to handle more cases than the JVM will?

There are more platforms than the JVM, but irrespective of that, you can do static analysis for other purposes than optimization, like the initialization checker that @liufengyun is working on: https://contributors.scala-lang.org/t/improve-forward-reference-handling/3616/3

@liufengyun
Copy link
Contributor

liufengyun commented Nov 1, 2019

Regarding the project-scope VS compilation-unit-scope problem, I'm thinking if the compiler can accept an option -module, which is supplied by SBT by concatenating the field name and organization, and that information is serialized in Tasty.

  • If a non-open symbol has an explicit module attribute, it may be extended by another symbol of the same module or of the same source file.
  • Otherwise, it may only be extended by another symbol of the same module or source file.

It has the following benefits:

  • a non-open class may be extended in different compilation units, e.g. ProtoType in Dotty.
  • we can treat trait and abstract class as non-open by default

Edited: one technical difficulty is how to deal with incremental compilation & child registration. That is indeed a subtle problem.

@odersky
Copy link
Contributor Author

odersky commented Nov 2, 2019

Introducing -module is interesting, but it's beyond the scope of this PR, or 3.0 in general. We already have the compilation unit as a boundary for sealed, so it makes sense to re-use it.

@odersky odersky merged commit 7577342 into scala:master Nov 2, 2019
@odersky odersky deleted the add-open-class branch November 2, 2019 17:44
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.

5 participants