The bug is not in `case` but is a result of calling `with-symbol-macros` on it.

Normally `case` expands to use a `sorted-map` for the lookup of case values 
when you have a compact set of test values (as opposed to a sparse set). You 
can see that here:

user=> (macroexpand '(case \a (\0 \1 \2 \3 \4 \5 \6 \7 \8) true false))

(let* [G__873 \a] (case* G__873 0 0 false {48 [\0 true], 49 [\1 true], 50 [\2 
true], 51 [\3 true], 52 [\4 true], 53 [\5 true], 54 [\6 true], 55 [\7 true], 56 
[\8 true]} :compact :hash-equiv nil))

Here, the keys are in order and so the compiler can create an array to hold the 
values based on (56-48)+1 as the length.

Based on my reading of the clojure.tools.macro source, `with-symbol-macros` 
forces the expansion and recursively goes into the map and reconstructs it as a 
regular (unsorted) map as can be seen here:

user=> (macroexpand '(clojure.tools.macro/with-symbol-macros (case \a (\0 \1 \2 
\3 \4 \5 \6 \7 \8) true false)))

(do (let* [G__865 \a] (case* G__865 0 0 false {55 [\7 true], 54 [\6 true], 48 
[\0 true], 50 [\2 true], 56 [\8 true], 51 [\3 true], 53 [\5 true], 52 [\4 
true], 49 [\1 true]} :compact :hash-equiv nil)))

When confronted with this, the compiler attempts to calculate (49-55)+1 and 
gets the exception you saw.

I believe that the fix - to clojure.tools.macro - would be to add special 
treatment for the `case*` form:

https://github.com/clojure/tools.macro/blob/master/src/main/clojure/clojure/tools/macro.clj#L167-179

If you want to report that as a bug: http://dev.clojure.org/jira/browse/TMACRO

Although it sounds like you have a reasonable workaround?

Sean

On Jul 4, 2014, at 1:38 PM, François-René Rideau <[email protected]> wrote:
> Using clojure 1.6.0 and clojure.algo.monads, I got a compiler error that I 
> could reduce to this case:
> 
> (fn [c] (domonad sequence-m [_ (case c (\0 \1 \2 \3 \4 \5 \6 \7 \8) c)] c))
> 
> CompilerException java.lang.NegativeArraySizeException, 
> compiling:(/tmp/form-init1574284988203454652.clj:1:1) 
> 
> Macro-expanding it, I could further reduce it to:
> 
> (fn [c] (clojure.tools.macro/with-symbol-macros (case c (\0 \1 \2 \3 \4 \5 \6 
> \7 \8) c) c))
> 
> Note that if I remove one case, the error disappears; 9 cases seems to be the 
> magic number. Also, I don't know what clojure.tools.macro/with-symbol-macros 
> does, but without it the code compiles fine.
> 
> Even if I remove this particular bit from the case statement in my parser and 
> replace it by a last-case test, the code compiles but my larger case fails, 
> in a way that condp = doesn't.
> 
> Can anyone fix the case macro?

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

Reply via email to