-
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
Toplevel definitions in forgotten class files break compilation #7650
Comments
odersky
added a commit
to dotty-staging/dotty
that referenced
this issue
Dec 1, 2019
Don't load toplevel definitions in the empty package from separate files. Only take them into consideration if they are compiled in the same run.
odersky
added a commit
to dotty-staging/dotty
that referenced
this issue
Dec 1, 2019
I settled on the solution to invalidate toplevel definitions with the same name as a later one, which does outlaw overloaded toplevel definitions in several files. I think that one has the least downside of all considered alternatives. |
odersky
added a commit
that referenced
this issue
Dec 11, 2019
Fix #7650: Drop stale toplevel definitions
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Toplevel definitions have the downside that they can accumulate on the output path and influence subsequent compilations in unexpected ways. Here's an example:
First, compile file
A.scala
Next, compile file
B.scala
If the output directory is on the class path, this gives an error indicating that
Test
needs to be applied to()
. What happens is that the compiler will load and merge the denotations ofTest
in bothA.scala
andB.scala
and then will get confused since it thinksTest
is a method.The problem is more general: Whenever there are several files with toplevel definitions, these definitions go into separate wrapper objects that are named after their file names. This means that compiling one file with toplevel definitions does not invalidate the definitions in another file. This is normally intended, but it does mean that "junk" in long forgotten class files can come back in unexpected ways. For instance, if one has first toplevel definitions in a file A.scala, and then renames the file to B.scala, possibly with with some changes, the definitions of
A
andB
will compete as overloaded variants with each other, as long as A$package.class is on the classpath.The normal way to mitigate the problem is to clean compile whenever a file containing toplevel definitions is renamed. But there's one situation where this is impractical: Toplevel definitions in the empty package. These are created by most simple tests, all the time. It is impractical to require a clean compile before compiling an additional test.
I believe it's better to hide toplevel definitions in the empty package from being visible in other compilation units, unless both units are compiled together. Otherwise we are simply too vulnerable of getting junk in the system that can cause mysterious compilation errors.
Another, more complicated fix would be to drop shadowed toplevel definitions looking at file creation dates. That is, if we have two toplevel definitions of method
foo
with matching signatures in two different wrappers, go back to the wrapper's associated files and get the file creation date of each. Only keep the method in the newer file. This protects against conflicting versions offoo
, but not against overloading. We'd still have the problem if we have toplevel definitions like this:and then compile them with a junk class file that contains a definition of
foo
overInt
. One could make the filtering even more aggressive by dropping all same-named methods independent of their signatures, but that would mean that we cannot define overloaded variants of existing methodsin new toplevel definitions anymore. So, no easy solution in sight...
The text was updated successfully, but these errors were encountered: