[CLJ-1741] deftype class literals and instances loaded from different classloaders when recompiling namespace Created: 30/May/15 Updated: 08/Oct/18
|Affects Version/s:||Release 1.7|
|Fix Version/s:||Release 1.11|
|Labels:||aot, classloader, compiler|
Scenario: Given two files:
Compile first core, then dispatch:
This scenario more commonly occurs in a leiningen project with :aot :all. Files are compiled in alphabetical order with :all. In this case, dispatch.core will be compiled first, then dispatch.dispatch.
In 1.6, RT.classForName() did not check dynamic classloader cache, so loaded T from disk as with instances. This was changed in
1) Compile in reverse dependency order to avoid compiling twice.
Either swap the order of compilation in the first example or specify the order in project.clj:
This is a short-term workaround.
2) Move the deftype into a separate namespace from where it is used so it is not redefined on the second compile. This is another short-term workaround.
3) Do not put compile-path on the classpath (this violates current expectations, but avoids loading dispatch__init)
This is not easy to set up via Leiningen currently.
4) Compile each file with an independent Clojure runtime - avoids using cached classes in DCL for class literals.
Probably too annoying to actually do right now in Leiningen or otherwise.
5) Make compilation non-transitive. This is in the ballpark of
Screening: I do not believe the proposed patch is a good idea - it papers over the symptom without addressing the root cause. I think we need to re-evaluate how compilation works with regard to compile-path (#3) and transitivity (
|Comment by Alex Miller [ 30/May/15 8:50 PM ]|
Pulling into 1.7 for consideration.
|Comment by Stephen Nelson [ 30/May/15 8:55 PM ]|
I've added a debug flag to my example that causes type instance hashcodes and their class-loaders to be printed.
|Comment by Nicola Mometto [ 01/Jun/15 7:23 AM ]|
The compiler has weird loading rules when using `compile` and both a clj file and a class file are present in the classpath.
This bug happens because RT.load will load the AOT class file rebinding the ->Ctor to use the AOT deftype instance.
A fix for this would be making load "loaded libs" aware to avoid unnecessary/harmful reloadings.
|Comment by Nicola Mometto [ 01/Jun/15 10:55 AM ]|
The attached patch fixes this bug by keeping track of what has already been loaded and loading the AOT class only if necessary
|Comment by Alex Miller [ 16/Jun/15 2:24 PM ]|
Original description (since replaced):
Type-dispatching multimethods are defined using the wrong type instance
When using a multimethod that dispatches on types, such as print-dup/print-method, the type reference passed to addMethod in the presence of aot is incorrect on the second load of the namespace. This means that if the namespace has already been loaded as a dependency of another file, the second load when the namespace is loaded for aot compilation will produce a multimethod that fails to dispatch correctly.
I've created an example repository:
To reproduce independently, create a namespace that contains a deftype and a multimethod dispatching on the type, and a second namespace that requires the first and sorts alphabetically before the first. Aot-compile both namespaces. When the type-defining namespace is loaded via require it produces a class file for the deftype. When it is loaded the second time for aot-compilation, the type corresponding to the existing class file is given to the defmethod, instead of the new class constructed by loading the namespace. This causes the multimethod it fail to dispatch correctly.
To me this issue seems similar to
|Comment by Nicola Mometto [ 18/Jun/15 11:09 AM ]|
I just realized this ticket is a duplicate of