clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
p-himik 2020-10-11T20:32:07.396900Z

@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.

Shantanu Kumar 2020-10-13T05:46:36.431100Z

@p-himik Thanks, that’s helpful.

p-himik 2020-10-11T20:40:29.397Z

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.

p-himik 2020-10-11T20:42:40.397200Z

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.