tools.analyzer

clojure.tools.analyzer.passes.jvm/validate throws an exception when :tag metadata cannot be resolved to a Class

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Minor Minor
  • Resolution: Completed
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None

Description

While the Clojure Compiler silently ignores :tag metadata in some forms when it cannot be resolved to a class, c.t.a.passes.jvm/validate throws an exception every time.

Some libraries (test.generators) use :tag metadata to store instructions for generating test input data, those can be everything, from symbols to quoted clojure expressions – the Clojure Compiler allows for this but validate throws an exception.

Generally however, when :tag cannot be resolved to a class it's not beacause of a design choice, but because of a Clojure bug or a user-code bug.

Some examples are:

  • tagging a defn form in the fn name with a primitive type hint:
    user=> (defn ^long x [] 1)
    #'user/x
    user=> (:tag (meta #'x))
    #<core$long clojure.core$long@6596f6ef>
  • using extend-type with a run-time resolved class http://dev.clojure.org/jira/browse/CLJ-1308
    user=> (defprotocol p (f [_]))
    p
    user=> (binding [*print-meta* true] (pr (macroexpand-1 '(extend-type (class 1) p (f [_])))))
    (clojure.core/extend (class 1) p {:f (fn ([^(class 1) _]))})
    nil
  • type hinting a form in a namespace with an imported class doesn't qualify the class in :tag http://dev.clojure.org/jira/browse/CLJ-1232
    user=> (ns foo (:import clojure.lang.RT))
    nil
    foo=> (defn x ^RT [])
    #'foo/x
    foo=> (ns bar (:use foo))
    nil
    bar=> (:tag (meta #'x))
    RT
    bar=> (.hashCode (x))
    CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: RT, compiling:(NO_SOURCE_PATH:5:1)

A clojure-dev post has been opened asking whether tools.analyzer.jvm is correct in assuming that non class-resolvible tags are an error and thus an exception could be thrown, or if the current behaviour of the Cloure implementation of throwing in some special places and silently ignoring malformed :tag values is by design. It hasn't have received an official reply yet. https://groups.google.com/d/msg/clojure-dev/hRZFuaiB_50/mzKLirgZWmUJ

Activity

Nicola Mometto made changes -
Field Original Value New Value
Resolution Completed [ 1 ]
Status Open [ 1 ] Closed [ 6 ]
Nicola Mometto made changes -
Status Closed [ 6 ] Reopened [ 4 ]
Resolution Completed [ 1 ]
Nicola Mometto made changes -
Comment [ [EDITED] ]
Nicola Mometto made changes -
Comment [ ... wrong issue. ]
Nicola Mometto made changes -
Description Steps to reproduce, at least within a Leiningen project with dependencies on the appropriate contrib libraries:
{code}
(require '[clojure.tools.analyzer.jvm :as aj])
(require '[clojure.data.fressian :as fress])
(require '[clojure.data.generators :as gen])
(require '[clojure.test.generative :refer (defspec)])

(def env (aj/empty-env))
(def form (read-string "
(defn roundtrip
  [x]
  (-> x fress/write fress/read))
"))
(def an (aj/analyze form env))

(def env (aj/empty-env))
(def form (read-string "
(def clojure-fressianable
  gen/anything)
"))
(def an (aj/analyze form env))

(def env (aj/empty-env))
(def form (read-string "
(defspec test-roundtrip-anything
  roundtrip
  [^{:tag `clojure-fressianable} o]
  )
"))
(def an (aj/analyze form env))
{code}

When I did this in a REPL, the class not found was simply (quote user/clojure-fressianable), but it was a much longer expression that looked like a significant-sized chunk of code when I analyzed similar code from within Eastwood. I can try to make a minimal project that does that if that would help, but hoping this might be enough to see what may be wrong.
While the Clojure Compiler silently ignores :tag metadata in some forms when it cannot be resolved to a class, c.t.a.passes.jvm/validate throws an exception every time.

Some libraries (test.generators) use :tag metadata to store instructions for generating test input data, those can be everything, from symbols to quoted clojure expressions -- the Clojure Compiler allows for this but validate throws an exception.

Generally however, when :tag cannot be resolved to a class it's not beacause of a design choice, but because of a Clojure bug or a user-code bug.

Some examples are:

- tagging a defn form in the fn name with a primitive type hint:
{code}
user=> (defn ^long x [] 1)
#'user/x
user=> (:tag (meta #'x))
#<core$long clojure.core$long@6596f6ef>
{code}

- using extend-type with a run-time resolved class http://dev.clojure.org/jira/browse/CLJ-1308
{code}
user=> (defprotocol p (f [_]))
p
user=> (binding [*print-meta* true] (pr (macroexpand-1 '(extend-type (class 1) p (f [_])))))
(clojure.core/extend (class 1) p {:f (fn ([^(class 1) _]))})
nil
{code}

- type hinting a form in a namespace with an imported class doesn't qualify the class in :tag http://dev.clojure.org/jira/browse/CLJ-1232
{code}
user=> (ns foo (:import clojure.lang.RT))
nil
foo=> (defn x ^RT [])
#'foo/x
foo=> (ns bar (:use foo))
nil
bar=> (:tag (meta #'x))
RT
bar=> (.hashCode (x))
CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: RT, compiling:(NO_SOURCE_PATH:5:1)
{code}
Environment u
Summary clojure.tools.analyzer.jvm/analyze throws exception for a class not being found clojure.tools.analyzer.passes.jvm/validate throws an exception when :tag metadata cannot be resolved to a Class
Nicola Mometto made changes -
Description While the Clojure Compiler silently ignores :tag metadata in some forms when it cannot be resolved to a class, c.t.a.passes.jvm/validate throws an exception every time.

Some libraries (test.generators) use :tag metadata to store instructions for generating test input data, those can be everything, from symbols to quoted clojure expressions -- the Clojure Compiler allows for this but validate throws an exception.

Generally however, when :tag cannot be resolved to a class it's not beacause of a design choice, but because of a Clojure bug or a user-code bug.

Some examples are:

- tagging a defn form in the fn name with a primitive type hint:
{code}
user=> (defn ^long x [] 1)
#'user/x
user=> (:tag (meta #'x))
#<core$long clojure.core$long@6596f6ef>
{code}

- using extend-type with a run-time resolved class http://dev.clojure.org/jira/browse/CLJ-1308
{code}
user=> (defprotocol p (f [_]))
p
user=> (binding [*print-meta* true] (pr (macroexpand-1 '(extend-type (class 1) p (f [_])))))
(clojure.core/extend (class 1) p {:f (fn ([^(class 1) _]))})
nil
{code}

- type hinting a form in a namespace with an imported class doesn't qualify the class in :tag http://dev.clojure.org/jira/browse/CLJ-1232
{code}
user=> (ns foo (:import clojure.lang.RT))
nil
foo=> (defn x ^RT [])
#'foo/x
foo=> (ns bar (:use foo))
nil
bar=> (:tag (meta #'x))
RT
bar=> (.hashCode (x))
CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: RT, compiling:(NO_SOURCE_PATH:5:1)
{code}
While the Clojure Compiler silently ignores :tag metadata in some forms when it cannot be resolved to a class, c.t.a.passes.jvm/validate throws an exception every time.

Some libraries (test.generators) use :tag metadata to store instructions for generating test input data, those can be everything, from symbols to quoted clojure expressions -- the Clojure Compiler allows for this but validate throws an exception.

Generally however, when :tag cannot be resolved to a class it's not beacause of a design choice, but because of a Clojure bug or a user-code bug.

Some examples are:

- tagging a defn form in the fn name with a primitive type hint:
{code}
user=> (defn ^long x [] 1)
#'user/x
user=> (:tag (meta #'x))
#<core$long clojure.core$long@6596f6ef>
{code}

- using extend-type with a run-time resolved class http://dev.clojure.org/jira/browse/CLJ-1308
{code}
user=> (defprotocol p (f [_]))
p
user=> (binding [*print-meta* true] (pr (macroexpand-1 '(extend-type (class 1) p (f [_])))))
(clojure.core/extend (class 1) p {:f (fn ([^(class 1) _]))})
nil
{code}

- type hinting a form in a namespace with an imported class doesn't qualify the class in :tag http://dev.clojure.org/jira/browse/CLJ-1232
{code}
user=> (ns foo (:import clojure.lang.RT))
nil
foo=> (defn x ^RT [])
#'foo/x
foo=> (ns bar (:use foo))
nil
bar=> (:tag (meta #'x))
RT
bar=> (.hashCode (x))
CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: RT, compiling:(NO_SOURCE_PATH:5:1)
{code}


A clojure-dev post has been opened asking whether tools.analyzer.jvm is correct in assuming that non class-resolvible tags are an error and thus an exception could be thrown, or if the current behaviour of the Cloure implementation of throwing in some special places and silently ignoring malformed :tag values is by design. It hasn't have received an official reply yet. https://groups.google.com/d/msg/clojure-dev/hRZFuaiB_50/mzKLirgZWmUJ
Environment u
Nicola Mometto made changes -
Resolution Completed [ 1 ]
Status Reopened [ 4 ] Closed [ 6 ]

People

Vote (1)
Watch (2)

Dates

  • Created:
    Updated:
    Resolved: