Clojure

Unfriendly Java cast expection when *compile-path* is set incorrectly

Details

  • Type: Enhancement Enhancement
  • Status: Open Open
  • Priority: Minor Minor
  • Resolution: Unresolved
  • Affects Version/s: Release 1.6, Release 1.7, Release 1.8, Release 1.9, Release 1.10, Release 1.11
  • Fix Version/s: None
  • Component/s: None
  • Environment:
    Mac OS, boot-clj

Description

When compile-path is not set to a String but to a java.io.File (that's what boot-clj does at the moment). You get a Cast exception in the compiler when trying to use it from the boot repl. Common wisdom seems to be to not compile from the repl, people say don't use gen-class. I find this unfortunate for when you do want to use gen-class and iterate quickly.

I haven't tested all older versions for this issue, but git blame shows that the code that should help here wasn't changed for 10 years:
https://github.com/clojure/clojure/blame/master/src/clj/clojure/core.clj#L6073
https://github.com/clojure/clojure/blame/master/src/jvm/clojure/lang/Compiler.java#L7643

My workaround now is:

(defn compile-safely [ns-name]
  (let [compile-path-class *compile-path*
        corrected-path (cond
                         (instance? java.io.File compile-path-class) (.getAbsolutePath *compile-path*)
                         (instance? String compile-path-class)       *compile-path*
                         :else
                         (throw (ex-info "Unsupported class" {:class (class compile-path-class)})))]
    (binding [*compile-path* corrected-path]
      (compile ns-name))))

I suggest to put the type check in the 'clojure.core/compile function.

Activity

Show
Jeroen van Dijk added a comment - Reference to common wisdom https://clojurians-log.clojureverse.org/clojure/2017-06-05/1496700773.347309
Hide
Jeroen van Dijk added a comment -

The following works in boot-clj (because of classpath issues not in Leiningen and clj cli): https://gist.github.com/jeroenvandijk/8187413d24433545eeb9579538a903f7#file-repl_compile-clj-L39-L52

Show
Jeroen van Dijk added a comment - The following works in boot-clj (because of classpath issues not in Leiningen and clj cli): https://gist.github.com/jeroenvandijk/8187413d24433545eeb9579538a903f7#file-repl_compile-clj-L39-L52
Hide
Alex Miller added a comment -

Can you provide a reproducible example?

Seems like ClassCastException is pretty accurate for having a class of the wrong type.

Show
Alex Miller added a comment - Can you provide a reproducible example? Seems like ClassCastException is pretty accurate for having a class of the wrong type.
Hide
Jeroen van Dijk added a comment -

I've tried to make the example as simple as possible. You are right that the ClassCastException is perfectly accurate, but you realise this only after you have figured out it's about compile-path. This bug report is mostly about managing expectations (of the clojure user).

clj -A:new app example.error
cd example.error
clj

 ;; clj has the *compile-path* set correctly to a string (although not in the classpath)
 *compile-path* ;=> "classes"
 (let [f (clojure.java.io/file "classes/foo.clj")]
   (clojure.java.io/make-parents f)
   (spit f "(ns foo) (gen-class :name my.new.Class)")
   (compile 'foo)
   (clojure.java.shell/sh "ls" "classes"))
 ; success, although you still cannot load the class
 ; #=> {:exit 0, :out  "clojure\nfoo$fn__134.class\nfoo$fn__166.class\nfoo$fn__174.class\nfoo$loading__6549__auto____132.class\nfoo$loading__6549__auto____164.class\nfoo$loading__6549__auto____172.class\nfoo.clj\nfoo__init.class\n", :err ""}
 

 ;; From a boot project:
 ;; boot.user=> *compile-path*
 ;; #object[java.io.File 0x5266b1a "/var/folders/ck/9zqvs0zx5vsf2bc7rvv1pwc40000gn/T/boot-repl8170014881711480931"] 
 ;; This can be reproduced in the clj repl:
 (binding [*compile-path* (clojure.java.io/file "classes")]
   (compile 'foo)
   )

 Syntax error compiling fn* at (foo.clj:1:1).
 Cause: java.io.File cannot be cast to java.lang.String
Show
Jeroen van Dijk added a comment - I've tried to make the example as simple as possible. You are right that the ClassCastException is perfectly accurate, but you realise this only after you have figured out it's about compile-path. This bug report is mostly about managing expectations (of the clojure user).
clj -A:new app example.error
cd example.error
clj

 ;; clj has the *compile-path* set correctly to a string (although not in the classpath)
 *compile-path* ;=> "classes"
 (let [f (clojure.java.io/file "classes/foo.clj")]
   (clojure.java.io/make-parents f)
   (spit f "(ns foo) (gen-class :name my.new.Class)")
   (compile 'foo)
   (clojure.java.shell/sh "ls" "classes"))
 ; success, although you still cannot load the class
 ; #=> {:exit 0, :out  "clojure\nfoo$fn__134.class\nfoo$fn__166.class\nfoo$fn__174.class\nfoo$loading__6549__auto____132.class\nfoo$loading__6549__auto____164.class\nfoo$loading__6549__auto____172.class\nfoo.clj\nfoo__init.class\n", :err ""}
 

 ;; From a boot project:
 ;; boot.user=> *compile-path*
 ;; #object[java.io.File 0x5266b1a "/var/folders/ck/9zqvs0zx5vsf2bc7rvv1pwc40000gn/T/boot-repl8170014881711480931"] 
 ;; This can be reproduced in the clj repl:
 (binding [*compile-path* (clojure.java.io/file "classes")]
   (compile 'foo)
   )

 Syntax error compiling fn* at (foo.clj:1:1).
 Cause: java.io.File cannot be cast to java.lang.String

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated: