~technomancy/fennel#125: 
nils in quoted lists affect macros imported via import macros but not inline macros

I've updated Fennel, and one of my projects broke in a very bizarre way. Some functions become empty. After a bit of bisecting, and debugging, I've found that this commit is where it breaks: d064522bac302c967947c7f648b4468ceb493895

Here's the minimal macro defn which re-arranges docstring and argument list (this was the reason why my project wasn't compiling properly):

(fn defn [name doc? args ...]
  (let [doc (if (= :string (type doc?)) doc?)
        args* (if doc args doc?)
        body (if doc `(do ,...) `(do ,args ,...))]
    `(local ,name (fn ,name ,args* ,doc ,body))))

{: defn}

Here, when I import it, the first function foo compiles as an empty function, and the bar function compiles correctly:

(import-macros {: defn} :m)

(defn foo [bar]                         ; compiles to empty function
  bar)

;; local foo
;; local function foo0(...)
;; end
;; foo = foo0

(defn bar "doc" [baz]                   ; works
  baz)

;; local bar
;; local function bar0(...)
;;   return baz
;; end
;; bar = bar0

However, macrodebugging these definitions in the REPL shows that the code generated is correct:

>> (macrodebug (defn foo [bar] bar))
(local foo (fn foo [bar] nil (do bar)))

>> (macrodebug (defn bar "doc" [baz] baz))
(local bar (fn bar [baz] "doc" (do baz)))

And even more interestingly, if I use macro instead of importing the defn macro, it compiles just fine:

;; (import-macros {: defn} :m)

(macro defn [name doc? args ...]
  (let [doc (if (= :string (type doc?)) doc?)
        args* (if doc args doc?)
        body (if doc `(do ,...) `(do ,args ,...))]
    `(local ,name (fn ,name ,args* ,doc ,body))))

(defn foo [bar]
  bar)

;; local foo
;; local function foo0(bar)
;;   return bar
;; end
;; foo = foo0
;; 

(defn bar "doc" [baz]
  baz)

;; local bar
;; local function bar0(baz)
;;   return baz
;; end
;; bar = bar0

These macros are exactly the same, the only difference is how they're brought into the scope.

Status
RESOLVED CLOSED
Submitter
~andreyorst
Assigned to
No-one
Submitted
a month ago
Updated
a month ago
Labels
bug

~technomancy a month ago

The root cause of this problem is that (length ast) behaves unpredictably on sparse tables.

I think it's a good idea when writing macros to avoid sparse tables. However, we could check for these immediately following macroexpansion and replace the nils in any list with (utils.sym :nil) and that should solve the problem, since it is easy for sparse tables to sneak in by accident.

~technomancy REPORTED CLOSED a month ago

Register here or Log in to comment, or comment via email.