** Description There were two separate problems conspiring to create this bug. First, the edebug spec for `destructuring-bind' is incorrect. Its definition has three violations of the CL hyperspec for destructuring lambda lists: - it should not support the &environment keyword - it should support the &whole keyword - it should support dotted forms It so happens that the `cl-macro-list1' edebug-spec does all three of these things properly. The second problem is in edebug. The unification algorithm has improper or missing handling for dotted pairs in specs. I chose to add the handling to `edebug-match-specs' since it seemed to be the cleanest place to insert it. ** ChangeLog 2011-09-26 Steve Yegge * emacs-lisp/cl-specs.el: Fixed edebug-spec for `destructuring-bind' to allow dotted pairs in the destructuring lambda list. (Bug#6415) * emacs-lisp/edebug.el: Fixed edebug instrumentation of dotted pairs in edebug specifications for macros. (Bug#6415) ** The patch itself === modified file 'lisp/emacs-lisp/cl-specs.el' --- lisp/emacs-lisp/cl-specs.el 2011-02-11 03:54:12 +0000 +++ lisp/emacs-lisp/cl-specs.el 2011-09-26 16:37:19 +0000 @@ -90,7 +90,7 @@ ((&rest (symbol sexp)) cl-declarations body)) (def-edebug-spec destructuring-bind - (&define cl-macro-list def-form cl-declarations def-body)) + (&define cl-macro-list1 def-form cl-declarations def-body)) ;; Setf === modified file 'lisp/emacs-lisp/edebug.el' --- lisp/emacs-lisp/edebug.el 2011-08-21 17:43:31 +0000 +++ lisp/emacs-lisp/edebug.el 2011-09-26 16:44:39 +0000 @@ -1567,8 +1567,28 @@ (let ((edebug-dotted-spec t));; Containing spec list was dotted. (edebug-match-specs cursor (list specs) remainder-handler))) - ;; Is the form dotted? - ((not (listp (edebug-cursor-expressions cursor)));; allow nil + ;; Special handling for the tail of a dotted form. + ((and + ;; Is the cursor on the tail of a dotted form? + (not (listp (edebug-cursor-expressions cursor)));; allow nil + ;; When matching a dotted form such as (a b . c) against a + ;; spec list that looks like + ;; ([&rest ...] [&optional ...]+ . [&or arg nil]) + ;; ,e.g., the `cl-macro-list1' edebug-spec, then the &rest spec + ;; will consume everything up to the dotted tail (`c' in this + ;; example). At that point the spec list will look like so: + ;; ([&optional ...]+ . [&or arg nil]) + ;; We need to be able to consume zero or more [&optional ...] + ;; spec(s) without moving the cursor or signaling an error. + ;; The current continuation provides no state that tells us + ;; about the upcoming &optional specs, so we use lookahead: + + ;; Recurse normally if we're about to process an optional spec. + (not (eq (car specs) '&optional)) + ;; Recurse normally if the spec is a dotted list. + (not (and (listp specs) + (not (listp (cdr (last specs))))))) + ;; Otherwise we need to be on the tail of a dotted spec. (if (not edebug-dotted-spec) (edebug-no-match cursor "Dotted spec required.")) ;; Cancel dotted spec and dotted form.