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

0 votes
in Compiler by

When evaluating or compiling an implementer of clojure.lang.IType, the compiler tries to reflectively access its fields. This fails, when a field is marked mutable (hence private):

Clojure 1.9.0-master-SNAPSHOT
user=> (deftype T [^:unsynchronized-mutable t])
user=> (T. :t)

object[user.T 0x2654635 "user.T@2654635"]

user=> (eval (T. :t))
CompilerException java.lang.IllegalArgumentException: No matching field found: t for class user.T

        Reflector.java:  271  clojure.lang.Reflector/getInstanceField
         Compiler.java: 4724  clojure.lang.Compiler$ObjExpr/emitValue
         Compiler.java: 4851  clojure.lang.Compiler$ObjExpr/emitConstants
         Compiler.java: 4529  clojure.lang.Compiler$ObjExpr/compile
         Compiler.java: 4049  clojure.lang.Compiler$FnExpr/parse
         Compiler.java: 6866  clojure.lang.Compiler/analyzeSeq
         Compiler.java: 6669  clojure.lang.Compiler/analyze
         Compiler.java: 6924  clojure.lang.Compiler/eval
         Compiler.java: 6890  clojure.lang.Compiler/eval
              core.clj: 3105  clojure.core/eval


For classes that don't implement IType, no such problem exists.

user> (deftype* user/U user.U

    [^:unsynchronized-mutable u]
    :implements [])

user> (eval (user.U. :u))

object[user.U 0x34699051 "user.U@34699051"]


This problem commonly occurs, when implementing a tagged literal for a deftype with cached hash.

2 Answers

0 votes

Comment made by: alexmiller

Yeah, this is interesting. The compiler compiles a deftype into a call to the constructor with the current values of the fields, but mutable fields are not accessible. One alternative would be to provide some standard method to "read" the field set rather than relying on reflection. (Another would be changing the access modifiers for mutable fields but I think that's probably a non-starter.)

0 votes
Reference: https://clojure.atlassian.net/browse/CLJ-2092 (reported by bendlas)