AOT'ed code that defs a var with clojure.core symbol name causes IllegalStateException
Description
Environment
Attachments
- 09 Jan 2015, 03:27 PM
- 02 Dec 2014, 03:04 PM
- 26 Nov 2014, 10:25 AM
Activity

import February 21, 2015 at 6:28 PM
Comment made by: deaddowney
I ran into this issue with Korma 0.4.0. I'm still running into it, but there is a twist.
My project depends on an artifact that was built with Clojure 1.7.0-alpha1. If I remove this dependency, everything is fine. However, with this dependency, I run into this issue, even if I declare a dependency on 1.7.0-master-SNAPSHOT in my project and exclude any dependency on clojure-1.7.0-alpha1.
I'm not sure if this is a Maven issue or a Clojure issue. Running Maven with debug on seems to show that it's using the correct version of Clojure.
I have created a dummy project that reproduces this issue if you are interested.
https://github.com/deaddowney/UpdateProblem
Check it out, run "mvn install", and you will get
java.lang.RuntimeException: No such var: korma.core/update
.

Tom Crayford January 10, 2015 at 10:52 PM
Thought I'd add a minor note in here to say I tried testing this patch out on my app (which is where I discovered this AOT bug), and the bug doesn't turn up with this patch applied to clojure (tested by applying 0001-fix-AOT-bug-preventing-overriding-of-clojure.core-fu-v2.patch to 1.7-alpha5)

Alex Miller January 9, 2015 at 3:27 PM
Attached 1604-context.diff for purely informational purposes - same diff just more context in it for easier reading.

Nicola Mometto December 2, 2014 at 3:04 PM
The updated patch only emits the interning bytecode when necessary, avoiding the emission when a clojure.core var with the same name exists but is not mapped to the current namespace

Andy Fingerhut November 27, 2014 at 4:53 PM
Copying a comment here from CLJ-1591, since it is more appropriate here. It is responding to Tom Crayford's posting of his example project to demonstrate the issue: https://github.com/yeller/compiler_update_not_referenced_bug
Tom, looked at your project. Thanks for that. It appears not to have anything like (def inc inc) in it. It throws exception during test step of 'lein do clean, uberjar, test' consistently for me, too, but compiles with only warnings and passes tests with 'lein do clean, test'. I have more test results showing in which Clojure versions these results change. To summarize, the changes to Clojure that appear to make the biggest difference in the results are below (these should be added to the new ticket you create – you are welcome to do so):
Clojure 1.6.0, 1.7.0-alpha1, and later changes up through the commit with description "CLJ-1378: Allows FnExpr to override its reported class with a type hint": No errors or warnings for either lein command above.
Next commit with description "Add clojure.core/update, like update-in but takes a single key" that adds clojure.core/update: 'lein do clean, test' is fine, but 'lein do clean, uberjar' throws exception during compilation, probably due to CLJ-1241.
Next commit with description "fix CLJ-1241": 'lein do clean, test' and 'lein do clean, uberjar' give warnings about clojure.core/update, but no errors or exceptions. 'lein do clean, uberjar, test' throws exception during test step that is same as the one I see with Clojure 1.7.0-alpha4. Debug prints of values of clojure.core/update and int-map/update (in data.int-map and in Tom's namespace compiler-update-not-referenced-bug.core) show things look fine when printed inside data.int-map, and in Tom's namespace when not doing the uberjar, but when doing the uberjar, test, int-map/update is unbound in Tom's namespace.
In case it makes a difference, my testing was done with Mac OS X 10.9.5, Leiningen 2.5.0 on Java 1.7.0_45 Java HotSpot(TM) 64-Bit Server VM
Details
Assignee
UnassignedUnassignedReporter
Nicola MomettoNicola MomettoApproval
OkPatch
CodePriority
CriticalAffects versions
Fix versions
Details
Details
Assignee
Reporter

AOT'ed code that defs a var that is also a symbol in clojure.core results in an exception at runtime. This problem can be avoided with (:refer-clojure :exclude ...) but this requires a library author to update and release a new version. AOT'ed applications must then wait for all transitive dependencies to update before they can update to a new Clojure version. For some users, this problem prevents them from trying or adopting new releases.
For example, the contrib library
data.int-map
defines anupdate
function. clojure.core will also have a newupdate
function as of 1.7.0. If this library is AOT'ed, then users of the clojure.data.int-map/update function will see the exception below. This situation can commonly occur when an application useslein uberjar
to compile all of the project+libs. In this case, applications or libraries that use data.int-map (either directly or indirectly) are affected.java.lang.IllegalStateException: Attempting to call unbound fn: #'clojure.data.int-map/update at clojure.lang.Var$Unbound.throwArity (Var.java:43) clojure.lang.AFn.invoke (AFn.java:40) compiler_update_not_referenced_bug.core$foo.invoke (core.clj:5)
Reproduce with this sample project: https://github.com/yeller/compiler_update_not_referenced_bug
Cause: When AOT compiling a namespace, the def forms are hoisted into the ns__init class (in the example here, clojure.data.int_map__init). The static initializer in this class creates each var in the ns via a call to RT.var(ns, name). For data.int-map the static initializer will properly create the var for clojure.data.int-map/update. But when the ns is loaded (via the clojure.data.int_map.load() method), (refer-clojure) will be called, which will remap clojure.data.int-map/update to point to clojure.core/update.
This problem does not affect non-AOT loading (which doesn't use the ns__init class) and does not affect collisions from any other namespace. Only collisions from clojure.core create this possibility.
Proposed: The proposed patch explicitly refers the Var during ns__init.load() (after Clojure symbols are referred) rather than implicitly during ns__init static {}.
This change in behavior only happens during AOT in the specific case where a core symbol is being shadowed. In that case, clojure.core has already been loaded and v (the looked up var) will have ns=clojure.core. The currentNS will be (for example) data.int-map. If that's the case, and the sym has no ns, then the new logic will be emitted.
In the case of clojure.core itself, NO new bytecode is emitted. From tests on several projects, only shadowed vars during AOT get this additional bytecode.
Patch: 0001-fix-AOT-bug-preventing-overriding-of-clojure.core-fu-v2.patch
Screened by: Alex Miller