We're updating the issue view to help you get more done. 

Clojure resolves to wrong deftype classes when AOT compiling or reloading

Description

Compiling a class via `deftype` during AOT compilation gives different results for the different constructors. These hashes should be identical.

1 2 3 4 5 6 user=> (binding [*compile-files* true] (eval '(deftype Abc []))) user.Abc user=> (hash Abc) 16446700 user=> (hash (class (->Abc))) 31966239 ;; should be 16446700

This also means that whenever there's a stale AOT compiled deftype class in the classpath, that class will be used rather then the JIT compiled one, breaking repl interaction.

Another demonstration of this classloader issue (from CLJ-1495) when reloading deftypes (no AOT) :

1 2 3 4 5 6 7 8 9 user> (defrecord Foo [bar]) user.Foo user> (= (->Foo 42) #user.Foo{:bar 42}) ;;expect this to evaluate to true true user> (defrecord Foo [bar]) user.Foo user> (= (->Foo 42) #user.Foo{:bar 42}) ;;expect this to evaluate to true also -- but it doesn't! false user>

This bug also affects AOT compilation of multimethods that dispatch on a class, this affected core.match for years see http://dev.clojure.org/jira/browse/MATCH-86, http://dev.clojure.org/jira/browse/MATCH-98. David had to work-around this issue by using a bunch of protocols instead of multimethods.

Cause of the bug: currently clojure uses Class.forName to resolve a class from a class name, which ignores the class cache from DynamicClassLoader thus reloading deftypes or mixing AOT compilation at the repl with deftypes breaks, resolving to the wrong class.

Approach: the current patch (CLJ-979-v7.patch) addresses this issue in multiple ways:

  • it makes RT.classForName/classForNameNonLoading look in the class cache before delegating to Class/forName if the current classloader is not a DynamicClassLoader (this incidentally addresses also CLJ-1457)

  • it makes clojure use RT.classForName/classForNameNonLoading instead of Class/forName

  • it overrides Classloader/loadClass so that it's class cache aware – this method is used by the jvm to load classes

  • it changes gen-interface to always emit an in-memory interface along with the [optional] on-disk interface so that the in-memory class is always updated.

Patch: CLJ-979-v7.patch

Screened by: Alex Miller

Environment

None

Status

Assignee

Unassigned

Reporter

import

Approval

Ok

Patch

Code and Test

Fix versions

Affects versions

Release 1.5
Release 1.4
Release 1.3
Release 1.7
Release 1.6

Priority

Critical