First of, compiler-macro lambda lists are /macro/ lambda lists, which
has some amusing consequences:
(defun foo (&key ((a ax) t)) (format nil "a=~S" ax))
(define-compiler-macro foo (&key ((a ax) t)) (format nil "a=~S" ax))
(compile nil `(lambda () (foo 'a 42))) =| ERROR, unknown keyword 'A
(funcall (compile nil `(lambda () (foo a 42)))) => "a=42"
Ie. the compiler-macro expects an _unquoted_ A as the keyword --
which, unless you've done
(defconstant a 'a)
beforehand, is going to wreck the evaluation model.
Secondly, it means that compiler-macros think non-constant keywords
arguments are actually unknown keywords:
(defun bar (&key a) (format nil ":a=~S" a))
(define-compiler-macro bar (&key a) (format nil ":a=~S" a))
(compile nil `(lambda (key) (bar key 13))) =| ERROR, unknown keyword KEY
Thirdly, currently if a compiler-macro signals an error, we will
signal a PROGRAM-ERROR at runtime.
Attached patch is my attempt in fixing these issues for SBCL without
taking liberties not allowed by the spec.
1. Treat a compiler-macro signaling an error as if the original form
had been returned instead, while reporting the error in the compiler
progress output. This should IMO be fairly non-controversial.
2. Have the compiler refuse to expand compiler-macros with anything
but self-evaluating symbols in keyword positions. In itself this
should also be fairly non-controversial: any cases where SBCL would
previously have expanded such things would have violated the
evaluation model. The controversial bit is that with this change
non-constant keyword arguments cause the compiler macro to punt
gracefully instead of complaining about unknown keywords -- making it
possibly to accidentally write a non-portable compiler macro by
relying on this, instead of parsing &REST manually.