Invalid defrecord results in exception attributed to namespace that imports namespace with defrecord

Description

I was introducing a namespace that included a defrecord.

My defrecord was wrong; it used a keyword to define a field, not a symbol. Minimal test case:

% cat src/useclj16/init.clj (ns useclj16.init) (defrecord Application [:shutdown-fn])
% cat src/useclj16/app.clj (ns useclj16.app (:require [useclj16.init :as init]))

However, the exception was perplexing:

% java -cp clojure-1.6.0-master-SNAPSHOT.jar:src clojure.main user=> (require 'useclj16.app) ClassCastException clojure.lang.Keyword cannot be cast to clojure.lang.IObj clojure.core/with-meta (core.clj:214) user=> (pst *e 100) ClassCastException clojure.lang.Keyword cannot be cast to clojure.lang.IObj clojure.core/with-meta (core.clj:214) clojure.core/defrecord/fn--147 (core_deftype.clj:362) clojure.core/map/fn--4210 (core.clj:2494) clojure.lang.LazySeq.sval (LazySeq.java:42) clojure.lang.LazySeq.seq (LazySeq.java:60) clojure.lang.RT.seq (RT.java:484) clojure.lang.LazilyPersistentVector.create (LazilyPersistentVector.java:31) clojure.core/vec (core.clj:354) clojure.core/defrecord (core_deftype.clj:362) clojure.lang.Var.invoke (Var.java:427) clojure.lang.Var.applyTo (Var.java:532) clojure.lang.Compiler.macroexpand1 (Compiler.java:6483) clojure.lang.Compiler.macroexpand (Compiler.java:6544) clojure.lang.Compiler.eval (Compiler.java:6618) clojure.lang.Compiler.load (Compiler.java:7079) clojure.lang.RT.loadResourceScript (RT.java:370) clojure.lang.RT.loadResourceScript (RT.java:361) clojure.lang.RT.load (RT.java:440) clojure.lang.RT.load (RT.java:411) clojure.core/load/fn--5024 (core.clj:5546) clojure.core/load (core.clj:5545) clojure.core/load-one (core.clj:5352) clojure.core/load-lib/fn--4973 (core.clj:5391) clojure.core/load-lib (core.clj:5390) clojure.core/apply (core.clj:619) clojure.core/load-libs (core.clj:5429) clojure.core/apply (core.clj:619) clojure.core/require (core.clj:5512) useclj16.app/eval322/loading--4916--auto----323 (app.clj:1) useclj16.app/eval322 (app.clj:1) clojure.lang.Compiler.eval (Compiler.java:6634) clojure.lang.Compiler.eval (Compiler.java:6623) clojure.lang.Compiler.load (Compiler.java:7079) clojure.lang.RT.loadResourceScript (RT.java:370) clojure.lang.RT.loadResourceScript (RT.java:361) clojure.lang.RT.load (RT.java:440) clojure.lang.RT.load (RT.java:411) clojure.core/load/fn--5024 (core.clj:5546) clojure.core/load (core.clj:5545) clojure.core/load-one (core.clj:5352) clojure.core/load-lib/fn--4973 (core.clj:5391) clojure.core/load-lib (core.clj:5390) clojure.core/apply (core.clj:619) clojure.core/load-libs (core.clj:5429) clojure.core/apply (core.clj:619) clojure.core/require (core.clj:5512) user/eval318 (NO_SOURCE_FILE:1) clojure.lang.Compiler.eval (Compiler.java:6634) clojure.lang.Compiler.eval (Compiler.java:6597) clojure.core/eval (core.clj:2864) clojure.main/repl/read-eval-print--6594/fn--6597 (main.clj:260) clojure.main/repl/read-eval-print--6594 (main.clj:260) clojure.main/repl/fn--6603 (main.clj:278) clojure.main/repl (main.clj:278) clojure.main/repl-opt (main.clj:344) clojure.main/main (main.clj:442) clojure.lang.Var.invoke (Var.java:411) clojure.lang.Var.applyTo (Var.java:532) clojure.main.main (main.java:37) nil

The error was attributed to app.clj (useclj16.app), a namespace which requires useclj16.init, the namespace containing the defrecord.

No indication that this concerned a defrecord, or even what namespace contained the error, was present in the exception.

Patch: clj-1261-5.diff

Approach: Check explicitly that the fields are all symbols, for both defrecord and deftype, and throw a CompilerException with file, line, and column number if not. Example of exception after patch is applied, in the case give above:

user=> (require 'useclj16.app) CompilerException java.lang.AssertionError: defrecord and deftype fields must be symbols, useclj16.init.Application had: :shutdown-fn, compiling:(useclj16/init.clj:3:1)

Screened by: Alex Miller

Environment

None

Attachments

4
  • 14 May 2014, 04:41 AM
  • 11 May 2014, 12:43 AM
  • 30 Apr 2014, 03:32 PM
  • 23 Nov 2013, 07:06 AM

Activity

Show:

Alex MillerMay 14, 2014 at 5:54 AM

seemed pretty clear

Andy FingerhutMay 14, 2014 at 5:40 AM

Thanks for the catch on that typo in the tests. You changed it to what I had intended.

Alex MillerMay 14, 2014 at 4:41 AM

same as -4 but changed final defrecord to deftype in test (seemed like a typo)

Andy FingerhutMay 11, 2014 at 12:43 AM

Patch clj-1261-4.diff is identical to clj-1261-3.diff except that it adds a couple of unit tests verifying that an exception of the desired type and with an appropriate message is thrown when keywords are used as defrecord or deftype fields.

Alex MillerMay 5, 2014 at 9:56 PM

I think you should be able to test the right error message here by just invoking the defrecord form.

Otherwise, maybe https://github.com/clojure/clojure/blob/master/test/clojure/test_clojure/ns_libs.clj#L87 ?

Completed

Details

Assignee

Reporter

Approval

Ok

Patch

Code and Test

Priority

Affects versions

Fix versions

Created September 12, 2013 at 9:42 PM
Updated August 29, 2014 at 3:24 PM
Resolved August 29, 2014 at 3:24 PM