Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cl-generic.el: lexical-let patch for method dispatch lambdas #27

Closed
wants to merge 1 commit into from

Conversation

spchamp
Copy link

@spchamp spchamp commented Jun 1, 2022

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)

Originally, 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:

Debugger entered--Lisp error: (void-variable cl--nmp)
  (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 -- with a shoutout to the Emacs Wiki, for an introduction to Emacs lexical-let

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.

System information from report-emacs-bug:

In GNU Emacs 29.0.50 (build 1, amd64-portbld-freebsd13.1, GTK+ Version 3.24.33)
Repository revision: 4cba465
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12014000
System Description: 13.1-STABLE

Configured using:
 'configure --disable-build-details --localstatedir=/var
 --without-libsystemd --without-selinux --with-x --disable-acl
 --with-sound=alsa --without-cairo --without-dbus --with-gconf
 --with-gif --with-gnutls --with-gsettings --with-x-toolkit=gtk3
 --with-harfbuzz --with-jpeg --with-json --with-file-notification=kqueue
 --without-lcms2 --without-m17n-flt --without-imagemagick
 --without-mailutils --with-modules --with-native-compilation
 --with-libotf --without-pgtk --with-png --with-toolkit-scroll-bars
 --with-sqlite3 --without-rsvg --with-threads --with-tiff --without-webp
 --with-xft --with-xim --with-xml2 --with-xpm --with-xwidgets
 --x-libraries=/usr/local/lib --x-includes=/usr/local/include
 --prefix=/usr/local --mandir=/usr/local/man --disable-silent-rules
 --infodir=/usr/local/share/emacs/info/
 --build=amd64-portbld-freebsd13.1 'CFLAGS=-O2 -pipe -Wno-everything
 -Wno-thread-safety -fstack-protector -Wno-everything -Wno-thread-safety
 -DGDK_DISABLE_DEPRECATION_WARNINGS -DGLIB_DISABLE_DEPRECATION_WARNINGS
 -isystem /usr/local/include -fno-strict-aliasing ' 'CPPFLAGS=
 -Wno-unused-command-line-argument -isystem /usr/local/include'
 'LDFLAGS= -Wl,--as-needed -L/usr/local/lib/gcc11 -fstack-protector
 -L/usr/local/lib ''

Configured features:
FREETYPE GCONF GIF GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG JSON LIBOTF
LIBXML2 MODULES NATIVE_COMP NOTIFY KQUEUE PDUMPER PNG SOUND SQLITE3
THREADS TIFF TOOLKIT_SCROLL_BARS X11 XDBE XFT XIM XINPUT2 XPM XWIDGETS
GTK3 ZLIB

Important settings:
  value of $LANG: C.UTF-8
  locale-coding-system: utf-8-unix

Major mode: ELisp/l

Minor modes in effect:
  global-git-commit-mode: t
  shell-dirtrack-mode: t
  cl-indent-minor-mode: t
  whitespace-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  show-paren-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  buffer-read-only: t
  column-number-mode: t
  line-number-mode: t
  auto-fill-function: do-auto-fill
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
/usr/home/thisuser/.emacs.d/elpa/dash-20220417.2250/dash hides /usr/local/share/emacs/29.0.50/site-lisp/dash
/usr/local/share/emacs/site-lisp/git/git hides /usr/local/share/emacs/29.0.50/site-lisp/git
/usr/local/share/emacs/29.0.50/site-lisp/transient hides /usr/local/share/emacs/29.0.50/lisp/transient
/usr/home/thisuser/.emacs.d/lib/tempo/src/tempo hides /usr/local/share/emacs/29.0.50/lisp/tempo
/usr/home/thisuser/.emacs.d/lib/markdown-mode/markdown-mode hides /usr/home/thisuser/.emacs.d/lib/emacs-goodies-el/elisp/emacs-goodies-el/markdown-mode
/usr/home/thisuser/.emacs.d/lib/quack hides /usr/home/thisuser/.emacs.d/lib/emacs-goodies-el/elisp/emacs-goodies-el/quack

Features:
(shadow sort mail-extr emacsbug ssh-agency magit-extras face-remap
magit-threads magit-version magit-bookmark magit-submodule
magit-obsolete magit-blame magit-stash magit-reflog magit-bisect
magit-push magit-pull magit-fetch magit-clone magit-remote magit-commit
magit-sequence magit-notes magit-worktree magit-tag magit-merge
magit-branch magit-reset magit-files magit-refs magit-status magit
magit-repos magit-apply magit-wip magit-log which-func magit-diff
smerge-mode diff git-commit log-edit message yank-media rmc puny dired-k
dired dired-loaddefs rfc822 mml mml-sec epa derived epg rfc6068
epg-config gnus-util mm-decode mm-bodies mm-encode mail-parse rfc2231
mailabbrev gmm-utils mailheader add-log magit-core magit-libgit libgit
libegit2 magit-autorevert autorevert magit-margin magit-transient
magit-process with-editor magit-mode transient edmacro kmacro magit-git
magit-base magit-section format-spec crm dash compat-27 compat-26 compat
shell pcomplete bug-reference term/screen term/xterm xterm mule-util
cl-indent cl-print pulse color help-fns radix-tree time-date misearch
multi-isearch elib-text vc-mtn vc-hg diff-mode vc-bzr vc-src vc-sccs
vc-svn vc-cvs vc-rcs log-view pcvs-util vc vc-dispatcher jka-compr
bookmark slime-autoloads session delsel cus-start cus-load company eglot
array filenotify jsonrpc ert pp ewoc debug backtrace find-func xref
flymake-proc flymake thingatpt pcase project imenu grep compile
text-property-search comint ansi-color ring server easy-mmode
unicode-whitespace advice ucs-utils list-utils whitespace color-theme
wid-edit sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr
mail-utils reporter elib-frame elib-display elib-face slime-user
program-aux elib-libutil lib-utils elib-compat elib-el elib-seq
platform-profiles elib-proc elib-inter elib-cl elib-sym comp comp-cstr
warnings rx cl-extra help-mode cl info package browse-url url url-proxy
url-privacy url-expand url-methods url-history url-cookie url-domsuf
url-util mailcap url-handlers url-parse auth-source cl-seq eieio
eieio-core cl-macs eieio-loaddefs password-cache json map url-vars seq
gv subr-x byte-opt bytecomp byte-compile cconv cl-loaddefs cl-lib
iso-transl tooltip eldoc paren electric uniquify ediff-hook vc-hooks
lisp-float-type elisp-mode mwheel term/x-win x-win term/common-win x-dnd
tool-bar dnd fontset image regexp-opt fringe tabulated-list replace
newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar
rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock
font-lock syntax font-core term/tty-colors frame minibuffer nadvice
simple cl-generic cham georgian utf-8-lang misc-lang vietnamese tibetan
thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek romanian
slovak czech european ethiopic indian cyrillic chinese composite
emoji-zwj charscript charprop case-table epa-hook jka-cmpr-hook help
abbrev obarray oclosure cl-preloaded button loaddefs faces cus-face
macroexp files window text-properties overlay sha1 md5 base64 format env
code-pages mule custom widget keymap hashtable-print-readable backquote
threads xwidget-internal kqueue dynamic-setting system-font-setting
font-render-setting move-toolbar gtk x-toolkit xinput2 x multi-tty
make-network-process native-compile emacs)

Memory information:
((conses 16 459395 46578)
 (symbols 48 23623 1)
 (strings 32 90551 5549)
 (string-bytes 1 2881040)
 (vectors 16 53455)
 (vector-slots 8 1278202 63545)
 (floats 8 240 739)
 (intervals 56 32059 263)
 (buffers 992 26))

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.
@spchamp spchamp marked this pull request as draft June 1, 2022 20:19
@spchamp
Copy link
Author

spchamp commented Jun 1, 2022

This pull request should have been created from a patch branch singularly for this changeset: cl-generic.el: lexical-let patch for method dispatch lambdas

Albeit already having published this pull request, I've created a patch branch for this, in the local contrib fork: ci/patch/cl-generic-lexical-let. This would have been the origin branch from which to have created the initial pull request. The latter branch will be updated with changesets only for this patch set.

I've converted this initial pull request to a draft, understanding that any later additions to the 'thinkum' branch in my contrib fork would be automatically appended to this initial pull request. I'm not seeing any 'delete pull request' option here at GitHub.

I'll try to send the patch along with a reference to the diff here at GitHub, using report-emacs-bug

@spchamp spchamp closed this Jun 1, 2022
@spchamp
Copy link
Author

spchamp commented Jun 1, 2022

Pursuant towards developing a complete patch for this and sending a pull request from a patch branch singularly for that patch set, I've closed this pull request.

With the patch as contributed in that first changeset -- as referred above -- I'd tested the patch with the updated cl-generic.el evaluated from source. When building emacs with the patched file, the build failed

Loading /wrkdirs/usr/ports/editors/emacs-devel/work-full/emacs-4cba465/lisp/emacs-lisp/cl-generic.el (source)...
Unknown specializer cons

Error: void-function (lexical-let*)
  gmake[3]: *** [Makefile:924: bootstrap-emacs.pdmp] Error 255
gmake[3]: Leaving directory '/wrkdirs/usr/ports/editors/emacs-devel/work-full/emacs-4cba465/src'
gmake[2]: *** [Makefile:469: src] Error 2
gmake[2]: Leaving directory '/wrkdirs/usr/ports/editors/emacs-devel/work-full/emacs-4cba465'
===> Compilation failed unexpectedly.
Try to set MAKE_JOBS_UNSAFE=yes and rebuild before reporting the failure to
the maintainer.
*** Error code 1

Stop.
make[1]: stopped in /usr/ports/editors/emacs-devel
*** Error code 1

Stop.
make: stopped in /usr/ports/editors/emacs-devel

Of course, I see now that lexical-let is defined in cl.el. I'll add an eval-when-compile/require form to the patched file and will try the build again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant