Skip to content

Commit

Permalink
cl-generic.el: lexical-let patch for method dispatch lambdas
Browse files Browse the repository at this point in the history
This patch serves to ensure that the following code will complete
without error, returning an expected value from the 'frob' method
specialized on the class 'b'

~~~~
(require 'eieio)

(defclass a ()
  ())

(defclass b (a)
  ())

(cl-defgeneric frob (obj)
  (:method ((obj a))
    (list :a obj))
  (:method ((obj b))
    (append (when (cl-next-method-p)
              (cl-call-next-method))
            (list :b))))

(frob (b))
;; => (:a #s(b) :b)
~~~~

Previously, there were unbound symbols in anonymous lambda forms
initialized for dispatch in the method call.

An excerpt from the backtrace on the method call for `(frob (b))` with
the unpatched source:
~~~~
  (apply #'(lambda (cl--nmp cl--cnm obj) (progn (append (if (funcall cl--nmp) (progn (funcall cl--cnm))) (list :b)))) cl--nmp cl--cnm cl--args)
  (let ((cl--cnm #'(lambda (&rest args) (apply cl--nm (or args cl--args))))) (apply #'(lambda (cl--nmp cl--cnm obj) (progn (append (if (funcall cl--nmp) (progn ...)) (list :b)))) cl--nmp cl--cnm cl--args))
  (lambda (&rest cl--args) "\n\n(fn OBJ)" (let ((cl--cnm #'(lambda (&rest args) (apply cl--nm ...)))) (apply #'(lambda (cl--nmp cl--cnm obj) (progn (append ... ...))) cl--nmp cl--cnm cl--args)))(#<b b-2083ca3bc>)
  apply((lambda (&rest cl--args) "\n\n(fn OBJ)" (let ((cl--cnm #'(lambda (&rest args) (apply cl--nm ...)))) (apply #'(lambda (cl--nmp cl--cnm obj) (progn (append ... ...))) cl--nmp cl--cnm cl--args))) #<b b-2083ca3bc> nil)
  frob(#<b b-2083ca3bc>)
~~~~

This patch establishes a lexical binding for variables that must be
accessible in some encapsulated lambda form in the initial method
call.

This patch has been tested with GNU Emacs 29.0.50 built from the
FreeBSD port editors/emacs-devel version 29.0.50.20220515,2 with
the NATIVECOMP option enabled in the Emacs build.
  • Loading branch information
spchamp committed Jun 1, 2022
1 parent 84e122d commit a392428
Showing 1 changed file with 6 additions and 4 deletions.
10 changes: 6 additions & 4 deletions lisp/emacs-lisp/cl-generic.el
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,9 @@ the specializer used will be the one returned by BODY."
,nbody))
(cons 'curried
`#'(lambda (,nm) ;Called when constructing the effective method.
(let ((,nmp (if (cl--generic-isnot-nnm-p ,nm)
#'always #'ignore)))
(lexical-let* ((,nm ,nm)
(,nmp (if (cl--generic-isnot-nnm-p ,nm)
#'always #'ignore)))
;; This `(λ (&rest x) .. (apply (λ (args) ..) x))'
;; dance is needed because we need to get the original
;; args as a list when `cl-call-next-method' is
Expand All @@ -458,8 +459,9 @@ the specializer used will be the one returned by BODY."
(setcar ds (help-add-fundoc-usage (car ds)
args)))
prebody)
(let ((,cnm (lambda (&rest args)
(apply ,nm (or args ,arglist)))))
(lexical-let ((,nmp ,nmp)
(,cnm (lambda (&rest args)
(apply ,nm (or args ,arglist)))))
;; This `apply+lambda' basically parses
;; `arglist' according to `args'.
;; A destructuring-bind would do the trick
Expand Down

0 comments on commit a392428

Please sign in to comment.