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

+1 vote
in Compiler by
retagged by

Given a Clojure function which references a class, if that class has a static field with an initializer which fails when run, the function will fail to compile because the compiler (needlessly) runs the initializer even if the respective field is not accessed. Notably, this does not happen when storing the class in a var and then referencing it through that instead (see workaround below). Could the compiler support the direct reference here?

Reproducer

(The following code is also available at https://github.com/bevuta/clojure-fn-compilation-issue-repro)

deps.edn:

{:deps {io.netty/netty-codec {:mvn/version "4.1.108.Final"}}}

src/repro/fail.clj:

(ns repro.fail
  (:import io.netty.handler.codec.compression.BrotliOptions))

(defn bar []
  BrotliOptions)

(defn -main [& args]
  (prn (bar)))

Here, the bar function references the io.netty.handler.codec.compression.BrotliOptions class. That class has a package-private static field named DEFAULT with an initializer. That initializer depends on a class which is provided by an optional third-party library. However, even though bar never references that field, the initializer gets run and fails because that library is not present.

Run it like this:

$ clojure -M -m repro.fail
Execution error (ClassNotFoundException) at jdk.internal.loader.BuiltinClassLoader/loadClass (BuiltinClassLoader.java:641).
com.aayushatharva.brotli4j.encoder.Encoder$Parameters

Workaround

The issue can be worked around by putting the class value into a separate def and using that in the function instead:

src/repro/ok.clj:

(ns repro.ok
  (:import io.netty.handler.codec.compression.BrotliOptions))

(def foo BrotliOptions)

(defn bar []
  foo)

(defn -main [& args]
  (prn (bar)))

Run it like this:

$ clojure -M -m repro.ok
io.netty.handler.codec.compression.BrotliOptions

Relevance

The issue was encountered in the context of https://github.com/clj-commons/aleph/issues/703

1 Answer

0 votes
by
selected by
 
Best answer

Almost certainly the compiler is emitting a Class.forName() somewhere instead of the variation of that with initialize=false, causing the static initializer to run before its needed.

Logged as https://clojure.atlassian.net/browse/CLJ-2842

...