Subject: bug#6415: [PATCH] fix edebug instrumentation of dotted pairs in macros

From: Steve Yegge <[email-masked]>
Date: Mon, 26 Sep 2011 10:17:52 -0700

** 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  <[email-masked]>

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