buggy "case" macro

Showing 1-2 of 2 messages
buggy "case" macro François-René Rideau 7/4/14 1:38 PM
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?

—♯ƒ • François-René ÐVB Rideau •Reflection&Cybernethics• http://fare.tunes.org
Luck occurs when preparedness meets opportunity.

Re: buggy "case" macro Sean Corfield 7/4/14 7:01 PM
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:


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 <fah...@gmail.com> 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?