@kumarshantanu I'm pretty sure it's a bug because cljs.core/const?
does a pretty sloppy check to see if something is a constant or not.
@p-himik Thanks, that’s helpful.
To get more into the details.
Every list gets unwound inside the case
macro. Your (())
becomes just ()
(note that when you use ()
instead of (())
, it will be removed completely).
Every symbol in the test parts of the clauses becomes (quote symbol-name)
inside the macro.
So if you use something like (case a (()) 1 (x y) 2)
it would essentially be (if the bug wasn't there) as if you used
(cond
(= a ()) 1
(or (= a (quote x)) (= a (quote y))) 2
:else (throw ...))
But case
, whenever it sees a list inside already expanded test parts, assumes that it might be that (quote symbol-name)
. It then checks whether symbol-name
is actually a constant that can be inlined.
The const?
check calls (and (list? x) (analyzer/resolve-var (last x)))
. When x
is (quote symbol-name)
, all works just as intended. When x
is something else, it might break. Like in the case of ()
, (last ())
is nil
and (analyzer/resolve-var nil)
throws that NPE that you see.With that being said, you can reproduce it with just (case 1 (()) 1)
. If you put anything inside the innermost list that ends not with a symbol but with something that breaks cljs.analyzer/resolve-var
(a number, a string, another list, etc), it will break as well, just the exception will be different.