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

0 votes
in Clojure by
The result of the following macroexpansion cannot be evaluated throwing `java.lang.NegativeArraySizeException`


(macroexpand-1 '(case :a
                  (:a-36 :b-36) 36
                  (:a-37 :b-37) 37
                  (:a-38 :b-38) 38
                  (:a-39 :b-39) 39
                  (:a-40 :b-40) 40
                  (:a-42 :b-42) 42))

;; =>

(clojure.core/let
    [G__8112 :a]
  (case*
    G__8112
    0
    31
    (throw
      (java.lang.IllegalArgumentException.
        (clojure.core/str "No matching clause: " G__8112)))
    {3 [:b-36 36],
     4 [:b-37 37],
     6 [:b-40 40],
     13 [:b-39 39],
     14 [:b-38 38],
     18 [:a-42 42],
     20 [:a-40 40],
     21 [:a-38 38],
     22 [:a-37 37],
     24 [:a-39 39],
     27 [:b-42 42],
     28 [:a-36 36]}
    :compact
    :hash-identity
    nil))



If you remove any of the options in the case above the macroexpansion can be evaluated correctly.

The reason behind this is that `case*` chokes on `clojure.lang.PersistentArrayMap` which `{...}` produces at the REPL. The original type is `clojure.lang.PersistentTreeMap`

The consequence of this is that the macro-expanded lists cannot be freely manipulated in code without checking explicitly for the concrete types of the maps. Particularly, `clojure.walk/walk` will produce un-evaluable results.

Original issue: https://github.com/clojure-emacs/cider/issues/2335

5 Answers

0 votes
by
_Comment made by: jafingerhut_

I have tried on an Ubuntu 16.04 Linux system, running OpenJDK 11.0.1, Clojure 1.10.0, in a Leiningen REPL, and also with the `clj` tool run with these command line options to use Clojure 1.10.0:

{{
clj -Sdeps "{:deps {org.clojure/clojure {:mvn/version \"1.10.0\"}}}"
}}
When I try to evaluate this expression:

{{
(eval (macroexpand-1 '(case :a
                  (:a-36 :b-36) 36
                  (:a-37 :b-37) 37
                  (:a-38 :b-38) 38
                  (:a-39 :b-39) 39
                  (:a-40 :b-40) 40
                  (:a-42 :b-42) 42)))
}}
I do not get a java.lang.NegativeArraySizeException.  Instead I get the same exception as I get from trying to evaluate the (case :a ...) expression without the macroexpand-1 and eval calls, which is an IllegalArgumentException because there is "No matching clause: :a", which is expected here.

Can you say more about what you are doing that causes the java.lang.NegativeArraySizeException exception to be thrown?
0 votes
by

Comment made by: jafingerhut

I hope this link to the original Cider issue is easier to follow than the one above, which looks like it got concatenated with another URL: https://github.com/clojure-emacs/cider/issues/2335

I do not typically use Cider, so not terribly interested in trying to reproduce the issue there, but if it can only be reproduced within Cider, it sounds like perhaps the problem may lie there?

0 votes
by

Comment made by: admin

I can't reproduce this. In a Clojure 1.10.0 repl:

user=> (case :a (:a-36 :b-36) 36 (:a-37 :b-37) 37 (:a-38 :b-38) 38 (:a-39 :b-39) 39 (:a-40 :b-40) 40 (:a-42 :b-42) 42) Execution error (IllegalArgumentException) at user/eval145 (REPL:1). No matching clause: :a (eval (macroexpand-1 '(case :a (:a-36 :b-36) 36 (:a-37 :b-37) 37 (:a-38 :b-38) 38 (:a-39 :b-39) 39 (:a-40 :b-40) 40 (:a-42 :b-42) 42))) Execution error (IllegalArgumentException) at user/eval150 (REPL:1). No matching clause: :a

What am I missing?

0 votes
by
_Comment made by: bronsa_

One way to trigger this is to copy the macroexpanded code from manually. This forces the literal to be read as a normal map rather than a sorted map.


user=> (clojure.core/let
    [G__8112 :a]
  (case*
    G__8112
    0
    31
    (throw
      (java.lang.IllegalArgumentException.
        (clojure.core/str "No matching clause: " G__8112)))
    {3 [:b-36 36],
     4 [:b-37 37],
     6 [:b-40 40],
     13 [:b-39 39],
     14 [:b-38 38],
     18 [:a-42 42],
     20 [:a-40 40],
     21 [:a-38 38],
     22 [:a-37 37],
     24 [:a-39 39],
     27 [:b-42 42],
     28 [:a-36 36]}
    :compact
    :hash-identity
    nil))
Syntax error (NegativeArraySizeException) compiling fn* at (REPL:1:1).
null


I would not consider this a bug: the contract of the `case*` special form requires the map to be sorted, if there's any bug at all it's upstream, in whatever code is responsible for traversing and transforming the macroexpanded form and converting the sorted map into a regular map.

This has caught me offguard a few times in the past, my proposal (as implemented in the attached patch) is to simply add a check at analysis time and throw if the map is not sorted.
0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-2511 (reported by vitoshka)
...