Clojure

Using 'def with metadata {:type :anything} throws ClassCastException during printing

Details

  • Type: Defect Defect
  • Status: Open Open
  • Priority: Minor Minor
  • Resolution: Unresolved
  • Affects Version/s: Release 1.4
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None
  • Environment:
    Ubuntu, lein 1.7.1 - lein repl
  • Patch:
    Code and Test
  • Approval:
    Vetted

Description

Specific to setting :type meta on a var:

user=> (def ^{:type :anything} mydef 1)
#<main$repl clojure.main$repl@6193b845>
CompilerException java.lang.ClassNotFoundException: main.clj:257, compiling:(NO_SOURCE_PATH:13:20)
ClassCastException clojure.lang.Var cannot be cast to clojure.lang.IObj  clojure.core/with-meta (core.clj:214)
user=> (pst *e)
ClassCastException clojure.lang.Var cannot be cast to clojure.lang.IObj
	clojure.core/with-meta (core.clj:214)
	clojure.core/vary-meta (core.clj:640)
	clojure.core/fn--5420 (core_print.clj:76)
	clojure.lang.MultiFn.invoke (MultiFn.java:232)
	clojure.core/pr-on (core.clj:3392)
	clojure.core/pr (core.clj:3404)
	clojure.core/apply (core.clj:624)
	clojure.core/prn (core.clj:3437)
	clojure.main/repl/read-eval-print--6627 (main.clj:241)
	clojure.main/repl/fn--6636 (main.clj:258)
	clojure.main/repl (main.clj:258)
	clojure.main/repl-opt (main.clj:324)

If it is intended to forbid setting the :type metadata, then there should be an appropriate error message instead of the ClassCastException.

Cause: This is caused by the printer dispatch function

(defmulti print-method (fn [x writer]
                         (let [t (get (meta x) :type)]
                           (if (keyword? t) t (class x)))))

which ends up calling the default dispatch, which tries to vary-meta.

Solution:

Patch:

Screened by:

Activity

Hide
Stuart Halloway added a comment -

This is caused by the printer dispatch function

(defmulti print-method (fn [x writer]
                         (let [t (get (meta x) :type)]
                           (if (keyword? t) t (class x)))))

which ends up calling the default dispatch, which tries to vary-meta.

Show
Stuart Halloway added a comment - This is caused by the printer dispatch function
(defmulti print-method (fn [x writer]
                         (let [t (get (meta x) :type)]
                           (if (keyword? t) t (class x)))))
which ends up calling the default dispatch, which tries to vary-meta.
Stuart Halloway made changes -
Field Original Value New Value
Approval Vetted [ 10003 ]
Alex Miller made changes -
Description In lein 1.7.1 (and CCW) the form (def ^{:type :anything} mydef 1) throws "ClassCastException clojure.lang.Var cannot be cast to clojure.lang.IObj clojure.core/with-meta (core.clj:211)".
This seems to be due to setting the :type metadata. With other metadata keys it works well.

If it is intended to forbid setting the :type metadata, then there should be an appropriate error message instead of the ClassCastException


In lein2 repl which is using "REPL-y 0.1.0-beta8" the exception does not occur.

Full stacktrace:
java.lang.ClassCastException: clojure.lang.Var cannot be cast to clojure.lang.IObj
 at clojure.core$with_meta.invoke (core.clj:211)
    clojure.core$vary_meta.doInvoke (core.clj:617)
    clojure.lang.RestFn.invoke (RestFn.java:425)
    clojure.core/fn (core_print.clj:76)
    clojure.lang.MultiFn.invoke (MultiFn.java:167)
    clojure.core$pr_on.invoke (core.clj:3266)
    clojure.core$pr.invoke (core.clj:3278)
    clojure.lang.AFn.applyToHelper (AFn.java:161)
    clojure.lang.RestFn.applyTo (RestFn.java:132)
    clojure.core$apply.invoke (core.clj:601)
    clojure.core$prn.doInvoke (core.clj:3311)
    clojure.lang.RestFn.invoke (RestFn.java:408)
    clojure.main$repl$read_eval_print__6405.invoke (main.clj:246)
    clojure.main$repl$fn__6410.invoke (main.clj:266)
    clojure.main$repl.doInvoke (main.clj:266)
    clojure.lang.RestFn.invoke (RestFn.java:512)
    user$eval27$acc__3261__auto____30$fn__32.invoke (NO_SOURCE_FILE:1)
    clojure.lang.AFn.run (AFn.java:24)
    java.lang.Thread.run (Thread.java:662)
Specific to setting :type meta on a var:

{code}
user=> (def ^{:type :anything} mydef 1)
#<main$repl clojure.main$repl@6193b845>
CompilerException java.lang.ClassNotFoundException: main.clj:257, compiling:(NO_SOURCE_PATH:13:20)
ClassCastException clojure.lang.Var cannot be cast to clojure.lang.IObj clojure.core/with-meta (core.clj:214)
user=> (pst *e)
ClassCastException clojure.lang.Var cannot be cast to clojure.lang.IObj
clojure.core/with-meta (core.clj:214)
clojure.core/vary-meta (core.clj:640)
clojure.core/fn--5420 (core_print.clj:76)
clojure.lang.MultiFn.invoke (MultiFn.java:232)
clojure.core/pr-on (core.clj:3392)
clojure.core/pr (core.clj:3404)
clojure.core/apply (core.clj:624)
clojure.core/prn (core.clj:3437)
clojure.main/repl/read-eval-print--6627 (main.clj:241)
clojure.main/repl/fn--6636 (main.clj:258)
clojure.main/repl (main.clj:258)
clojure.main/repl-opt (main.clj:324)
{code}

If it is intended to forbid setting the :type metadata, then there should be an appropriate error message instead of the ClassCastException.

*Cause:* This is caused by the printer dispatch function

{code}
(defmulti print-method (fn [x writer]
                         (let [t (get (meta x) :type)]
                           (if (keyword? t) t (class x)))))
{code}

which ends up calling the default dispatch, which tries to vary-meta.

*Solution:*

*Patch:*

*Screened by:*
Summary Using 'def with metadata {:type :anything} throws ClassCastException Using 'def with metadata {:type :anything} throws ClassCastException during printing
Hide
Steve Miner added a comment -

The :type metadata is used internally by Clojure. For the situation in this bug report, you have to take responsibility for providing a print-method if you put :type metatdata on your var.

This is not a good example, but it shows one way to work around the bug:

(defmethod print-method :anything [obj w] (print-method {:anything @obj} w))
Show
Steve Miner added a comment - The :type metadata is used internally by Clojure. For the situation in this bug report, you have to take responsibility for providing a print-method if you put :type metatdata on your var. This is not a good example, but it shows one way to work around the bug:
(defmethod print-method :anything [obj w] (print-method {:anything @obj} w))
Steve Miner made changes -
Patch Code and Test [ 10002 ]
Attachment CLJ-1039-tolerate-misleading-type-metadata-on-var-wh.patch [ 12940 ]
Hide
Steve Miner added a comment -

On the other hand, the :default print-method probably should be more robust. I think a check for (instance? clojure.lang.IObj o) before calling vary-meta would be appropriate. Added a patch that calls print-simple if the o isn't an IObj. That works around the issue for Var, and seems reasonable for other exotic types. The only downside I can imagine is if someone had a custom print-method but accidentally had a typo in their :type metadata, they will no longer get an error. This was an edge case to begin with so that probably doesn't matter.

Show
Steve Miner added a comment - On the other hand, the :default print-method probably should be more robust. I think a check for (instance? clojure.lang.IObj o) before calling vary-meta would be appropriate. Added a patch that calls print-simple if the o isn't an IObj. That works around the issue for Var, and seems reasonable for other exotic types. The only downside I can imagine is if someone had a custom print-method but accidentally had a typo in their :type metadata, they will no longer get an error. This was an edge case to begin with so that probably doesn't matter.

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated: