Welcome! Please see the About page for a little more info on how this works.

0 votes
in Compiler by

If you try to compile the attached files as follows (assuming they are in "src"):

java -Dclojure.compile.path=out -cp "./clojure-1.8.0.jar:out:src" clojure.lang.Compile implementer protocol consumer

an exception will be thrown:

`
Exception in thread "main" java.lang.ClassCastException: implementer.Obj cannot be cast to protocol.Dependent, compiling:(consumer.clj:5:1)

at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3657)
at clojure.lang.Compiler.compile1(Compiler.java:7474)
at clojure.lang.Compiler.compile(Compiler.java:7541)
at clojure.lang.RT.compile(RT.java:406)
at clojure.lang.RT.load(RT.java:451)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
at clojure.core$load.invokeStatic(core.clj:5892)
at clojure.core$load.doInvoke(core.clj:5876)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5697)
at clojure.core$compile$fn__5682.invoke(core.clj:5903)
at clojure.core$compile.invokeStatic(core.clj:5903)
at clojure.core$compile.invoke(core.clj:5895)
at clojure.lang.Var.invoke(Var.java:379)
at clojure.lang.Compile.main(Compile.java:67)

Caused by: java.lang.ClassCastException: implementer.Obj cannot be cast to protocol.Dependent

at protocol$fn__12$G__8__14.invoke(protocol.clj:3)
at protocol$fn__12$G__7__17.invoke(protocol.clj:3)
at protocol$expand_deps.invokeStatic(protocol.clj:8)
at protocol$expand_deps.invoke(protocol.clj:6)
at clojure.lang.AFn.applyToHelper(AFn.java:154)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3652)
... 15 more

`

  • This does not occur with 1.6 or earlier versions
  • This does not occur if you do not try to invoke AOT
  • This may not occur for some orderings of the arguments

This appears to be related to the class being loaded by two different class loaders, and also may result in the namespace being compiled more than once. This issue has popped up for us multiple times in our production build, but it took a while to realize it was a compiler issue and to find a minimal example.

3 Answers

0 votes
by

Comment made by: admin

The general ordering here:

  • protocol compiles
  • implementer compiles
  • as a side effect, implementer creates an instance of the record implementing the protocol in the my-obj var
  • consumer compiles, which causes the protocol to reload
  • consumer invokes the new version of the protocol on the record instance implementing the old version of the protocol

I've been mulling for a long time whether it would be possible to avoid reloading protocols if we know they didn't change. Which requires us being able to know they didn't change.

0 votes
by

Comment made by: bronsa

Maybe we can do something along the lines of what the impl of proxy does compute a hash for the protocol impl to detect reloading necessity

0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-2073 (reported by alex+import)
...