Uploaded image for project: 'Clojure'
  1. CLJ-1261

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

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Minor
    • Resolution: Completed
    • Affects versions: Release 1.5
    • Fix versions: Release 1.7
    • Approval:
      Ok
    • Patch:
      Code and Test

      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

        Attachments

        1. clj-1261-5.diff
          3 kB
        2. clj-1261-4.diff
          3 kB
        3. clj-1261-3.diff
          2 kB
        4. clj-1261-2.diff
          1 kB

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              hlewisship Howard Lewis Ship
            • Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: