-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Allow toplevel definitions #5754
Conversation
Current implicit classes must be defined inside of another, but it can be defined in the package object, will lift this restriction in this pull requests? |
Yes. Implicit classes and implicit objects can now be defined at the toplevel. |
With this, will we be getting zero parameter implicit classes instead of having to write a class and a implicit function? |
The plan is to drop implicit classes altogether. See #5458. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't looked too much at the implementation, as I don't think I'm qualified. For tests, overall I'm pleased. Here are a few more suggestions for things that could wrong:
- Top-level
val X
/def X
in one file, and aclass X
in another file - Same with an
object X
in another file - Top-level
type X
in one file, and aclass X
,object X
orval x
in another file.
I'd like to know what would happen if we have a top-level def main(Array[String]): Unit
. Can that work as a JVM main method? If yes, what's the incantation to invoke it? If not, at least we should emit a warning at the definition, because users are bound to try that. In either case there should be an appropriate test.
The documentation should clearly highlight that the enclosing file name becomes significant at the binary level. Changing a file's name is now a binary incompatible change if it contains any top-level definition. We should not lie by omission of saying that.
I also believe the documentation should at least talk about the trade-off with the alternative implementation scheme, which is that top-level definitions are put in a class file named after themselves, e.g., val x
goes into x$toplevel.class
or something like that (overloaded methods would go to the same class file).
Will we also be able to put opaque types and their companion objects at top-level? |
https://github.com/lampepfl/dotty/pull/5754/files#diff-6368aeb78c7b36b5919b14e40a804b34 |
I added the suggested tests. Regarding main methods, it follows directly from the spec. I put a note in the docs stating this, together with a note on binary compatibility. I am not really sure we need a warning. What would such a warning say, anyway? It feels like too much hand-holding to me. |
Regarding the implementation scheme, it felt most straightforward to do it this way. I considered the alternative of naming toplevel files after definitions. Some challenges with this scheme would be:
var a: Int = 1
val b = { a += 1; a }
val c = a it looks like Overall, it seemed more straightforward to just pick the name of the source. It also means we can re-use almost all of the existing package object infrastructure, so the added implementation effort was quite low. |
/cc @lihaoyi, would be interesting to know if this covers all the usecases for the top-level definitions support in https://ammonite.io/#ScalaScripts currently. |
@smarter some, but not all. The main thing that Ammonite scripts provide on top of this is configurable wrapping modes, e.g. :
Another thing that Ammonite scripts provide is automatic package inference: the JVM package path of the script's compilation unit is inferred based on the path of the script relative to the user's working directory or current script, more or less. This isn't fully specced out, and definitely has some holes as currently implemented, but it can be cleaned up with a bit of effort and is of great convenience. The equivalent here would be to have It's not clear to me that we want the default Scala compiler to have the flexibility to do all this stuff, but I would definitely be happy to drop Ammonite's custom logic in favor of upstream support in the Scala compiler where-ever possible |
@@ -954,6 +954,36 @@ object desugar { | |||
else Apply(ref(tupleTypeRef.classSymbol.companionModule.termRef), ts) | |||
} | |||
|
|||
/** Group all definitions that can't be at the toplebel in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
toplebel
-> toplevel
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** Group all definitions that can't be at the toplebel in | |
/** Group all definitions that can't be at the toplevel in |
I think top-level |
The work on private definitions includes a simplification step: |
That's all from my side. |
test performance please |
Rebased on top of #5825. |
@smarter Do you still plan to review this? I want to merge this over the weekend. |
@@ -954,6 +954,36 @@ object desugar { | |||
else Apply(ref(tupleTypeRef.classSymbol.companionModule.termRef), ts) | |||
} | |||
|
|||
/** Group all definitions that can't be at the toplebel in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** Group all definitions that can't be at the toplebel in | |
/** Group all definitions that can't be at the toplevel in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The revert of #3735 reintroduces the issue described in #3392 (comment) where code that works in join compilation fails with an error in separate compilation, I think the only way to avoid this and keep the behavior of this PR would be to emit all classes as public like scalac does.
|
Ah indeed, but I can still reproduce the separate compilation issue described in #3392 (comment), when compiling separately B.scala fails with |
Change scheme how sourcenames are computed from filenames to account for filenames that have additional `.`s in them. Fix comments.
I could not reproduce that. I added a test (i3339/*.scala) to run. |
Ah it does work indeed, good to have the test. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, though this PR depends on #5825 which hasn't been reviewed yet.
It is currently possible to mix a trait into a package object. How would one accomplish something similar when package objects are dropped? Will it no longer be possible? |
@agilesteel It won't be possible indeed, but this is unrelated to this PR, for discussing dropping package objects see scala/scala-dev#441 and scala/scala#7662 |
Adding `// LEGACY` to 061. It seems like TopLevelCantBeImplicit is no longer the case thanks to scala/scala3#5754 This is actually confirmed in https://github.com/lampepfl/dotty/blob/93fc41fcb624df73cc12d52b79d518a30a778a7c/tests/run/toplevel-implicits/a.b.scala#L19-L21
Adding `// LEGACY` to 061. It seems like TopLevelCantBeImplicit is no longer the case thanks to scala/scala3#5754 This is actually confirmed in https://github.com/lampepfl/dotty/blob/93fc41fcb624df73cc12d52b79d518a30a778a7c/tests/run/toplevel-implicits/a.b.scala#L19-L21
Adding `// LEGACY` to 061. It seems like TopLevelCantBeImplicit is no longer the case thanks to scala/scala3#5754 This is actually confirmed in https://github.com/lampepfl/dotty/blob/93fc41fcb624df73cc12d52b79d518a30a778a7c/tests/run/toplevel-implicits/a.b.scala#L19-L21
It seems like TopLevelCantBeImplicit is no longer the case as of scala#5754 And it is actually confirmed in https://github.com/lampepfl/dotty/blob/93fc41fcb624df73cc12d52b79d518a30a778a7c/tests/run/toplevel-implicits/a.b.scala#L19-L21 This commit removes the unnecessary check in from Checking.scala and deleted the `ErrorMessage` definition for `TopLevelCantBeImplicit`. I'm leaving the `TopLevelCantBeImplicitID` in `ErrorMessageID.scala` so we don't screw up the error number.
It seems like TopLevelCantBeImplicit is no longer the case as of scala#5754 And it is actually confirmed in https://github.com/lampepfl/dotty/blob/93fc41fcb624df73cc12d52b79d518a30a778a7c/tests/run/toplevel-implicits/a.b.scala#L19-L21 This commit replace the unnecessary check in from Checking.scala to assertion and deleted the `ErrorMessage` definition for `TopLevelCantBeImplicit`. I'm leaving the `TopLevelCantBeImplicitID` in `ErrorMessageID.scala` so we don't screw up the error number.
It seems like TopLevelCantBeImplicit is no longer the case as of scala#5754 And it is actually confirmed in https://github.com/lampepfl/dotty/blob/93fc41fcb624df73cc12d52b79d518a30a778a7c/tests/run/toplevel-implicits/a.b.scala#L19-L21 This commit replace the unnecessary check in from Checking.scala to assertion and deleted the `ErrorMessage` definition for `TopLevelCantBeImplicit`. I'm leaving the `TopLevelCantBeImplicitID` in `ErrorMessageID.scala` so we don't screw up the error number.
With this PR, we now allow arbitrary definitions at the toplevel. This means package objects are now redundant, and will be phased out.