aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2022-02-06 17:43:27 -0500
committerStefan Monnier2022-02-06 17:43:27 -0500
commitf1b7ad1973a88d8efa9e2da9ac3cbbfddc2d6207 (patch)
tree36912b543e45929b28521c874b97941a96b7c7de
parentf75ee15efd83d74640d12483bd7d345114d5f5e5 (diff)
downloademacs-scratch/oclosure.tar.gz
emacs-scratch/oclosure.zip
* lisp/emacs-lisp/cl-generic.el: Add a calling conventionscratch/oclosure
Introduce a new calling convention for the methods, which should make `cl-call-next-method` a bit less costly. (cl--generic-method): Rename `uses-cnm` slot to `call-con`. (cl-defmethod): Adjust accordingly. (cl--generic-lambda): Use the new `curried` calling convention. (cl-generic-call-method): Implement the new `curried` calling convention. (cl--generic-method-info): Adjust to the new `curried` calling convention. * lisp/org/org-attach.el (org-attach): Silence warning.
-rw-r--r--lisp/emacs-lisp/cl-generic.el120
-rw-r--r--lisp/emacs-lisp/oclosure.el2
-rw-r--r--lisp/org/org-attach.el6
3 files changed, 87 insertions, 41 deletions
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el
index a0a06914426..650068ea679 100644
--- a/lisp/emacs-lisp/cl-generic.el
+++ b/lisp/emacs-lisp/cl-generic.el
@@ -144,13 +144,20 @@ SPECIALIZERS-FUNCTION takes as first argument a tag value TAG
144(cl-defstruct (cl--generic-method 144(cl-defstruct (cl--generic-method
145 (:constructor nil) 145 (:constructor nil)
146 (:constructor cl--generic-make-method 146 (:constructor cl--generic-make-method
147 (specializers qualifiers uses-cnm function)) 147 (specializers qualifiers call-con function))
148 (:predicate nil)) 148 (:predicate nil))
149 (specializers nil :read-only t :type list) 149 (specializers nil :read-only t :type list)
150 (qualifiers nil :read-only t :type (list-of atom)) 150 (qualifiers nil :read-only t :type (list-of atom))
151 ;; USES-CNM is a boolean indicating if FUNCTION expects an extra argument 151 ;; CALL-CON indicates the calling convention expected by FUNCTION:
152 ;; holding the next-method. 152 ;; - nil: FUNCTION is just a normal function with no extra arguments for
153 (uses-cnm nil :read-only t :type boolean) 153 ;; `call-next-method' or `next-method-p' (which it hence can't use).
154 ;; - `curried': FUNCTION is a curried function that first takes the
155 ;; "next combined method" and return the resulting combined method.
156 ;; It can distinguish `next-method-p' by checking if that next method
157 ;; is `cl--generic-isnot-nnm-p'.
158 ;; - t: FUNCTION takes the `call-next-method' function as its first (extra)
159 ;; argument.
160 (call-con nil :read-only t :type symbol)
154 (function nil :read-only t :type function)) 161 (function nil :read-only t :type function))
155 162
156(cl-defstruct (cl--generic 163(cl-defstruct (cl--generic
@@ -389,6 +396,8 @@ the specializer used will be the one returned by BODY."
389 (pcase (macroexpand fun macroenv) 396 (pcase (macroexpand fun macroenv)
390 (`#'(lambda ,args . ,body) 397 (`#'(lambda ,args . ,body)
391 (let* ((parsed-body (macroexp-parse-body body)) 398 (let* ((parsed-body (macroexp-parse-body body))
399 (nm (make-symbol "cl--nm"))
400 (arglist (make-symbol "cl--args"))
392 (cnm (make-symbol "cl--cnm")) 401 (cnm (make-symbol "cl--cnm"))
393 (nmp (make-symbol "cl--nmp")) 402 (nmp (make-symbol "cl--nmp"))
394 (nbody (macroexpand-all 403 (nbody (macroexpand-all
@@ -401,15 +410,41 @@ the specializer used will be the one returned by BODY."
401 ;; is used. 410 ;; is used.
402 ;; FIXME: Also, optimize the case where call-next-method is 411 ;; FIXME: Also, optimize the case where call-next-method is
403 ;; only called with explicit arguments. 412 ;; only called with explicit arguments.
404 (uses-cnm (macroexp--fgrep `((,cnm) (,nmp)) nbody))) 413 (uses-cnm (macroexp--fgrep `((,cnm) (,nmp)) nbody))
405 (cons (not (not uses-cnm)) 414 (λ-lift (mapcar #'car uses-cnm)))
406 `#'(lambda (,@(if uses-cnm (list cnm)) ,@args) 415 (if (not uses-cnm)
407 ,@(car parsed-body) 416 (cons nil
408 ,(if (not (assq nmp uses-cnm)) 417 `#'(lambda (,@args)
409 nbody 418 ,@(car parsed-body)
410 `(let ((,nmp (lambda () 419 ,nbody))
411 (cl--generic-isnot-nnm-p ,cnm)))) 420 (cons 'curried
412 ,nbody)))))) 421 `#'(lambda (,nm) ;Called when constructing the effective method.
422 (let ((,nmp (if (cl--generic-isnot-nnm-p ,nm)
423 #'always #'ignore)))
424 ;; The `(λ (&rest x) .. (apply (λ (args) ..) x))'
425 ;; dance is needed because we need to the original
426 ;; args as a list when `cl-call-next-method' is
427 ;; called with no arguments. It's important to
428 ;; capture it as a list since it needs to distinguish
429 ;; the nil case from the absent case in optional
430 ;; arguments and it needs to properly remember the
431 ;; original value if `nbody' mutates some of its
432 ;; formal args.
433 ;; FIXME: This `(λ (&rest ,arglist)' could be skipped
434 ;; when we know `cnm' is always called with args, and
435 ;; it could be implemented more efficiently if `cnm'
436 ;; is always called directly and there are no
437 ;; `&optional' args.
438 (lambda (&rest ,arglist)
439 ,@(car parsed-body)
440 (let ((,cnm (lambda (&rest args)
441 (apply ,nm (or args ,arglist)))))
442 ;; This `apply+lambda' basically parses
443 ;; `the `arglist' accordingly to `args'.
444 ;; FIXME: A destructuring-bind would do the trick
445 ;; as well when/if it's more efficient.
446 (apply (lambda (,@λ-lift ,@args) ,nbody)
447 ,@λ-lift ,arglist)))))))))
413 (f (error "Unexpected macroexpansion result: %S" f)))))) 448 (f (error "Unexpected macroexpansion result: %S" f))))))
414 449
415(put 'cl-defmethod 'function-documentation 450(put 'cl-defmethod 'function-documentation
@@ -507,7 +542,7 @@ The set of acceptable TYPEs (also called \"specializers\") is defined
507 (require 'gv) 542 (require 'gv)
508 (declare-function gv-setter "gv" (name)) 543 (declare-function gv-setter "gv" (name))
509 (setq name (gv-setter (cadr name)))) 544 (setq name (gv-setter (cadr name))))
510 (pcase-let* ((`(,uses-cnm . ,fun) (cl--generic-lambda args body))) 545 (pcase-let* ((`(,call-con . ,fun) (cl--generic-lambda args body)))
511 `(progn 546 `(progn
512 ,(and (get name 'byte-obsolete-info) 547 ,(and (get name 'byte-obsolete-info)
513 (let* ((obsolete (get name 'byte-obsolete-info))) 548 (let* ((obsolete (get name 'byte-obsolete-info)))
@@ -523,7 +558,7 @@ The set of acceptable TYPEs (also called \"specializers\") is defined
523 ;; The ",'" is a no-op that pacifies check-declare. 558 ;; The ",'" is a no-op that pacifies check-declare.
524 (,'declare-function ,name "") 559 (,'declare-function ,name "")
525 (cl-generic-define-method ',name ',(nreverse qualifiers) ',args 560 (cl-generic-define-method ',name ',(nreverse qualifiers) ',args
526 ,uses-cnm ,fun))))) 561 ',call-con ,fun)))))
527 562
528(defun cl--generic-member-method (specializers qualifiers methods) 563(defun cl--generic-member-method (specializers qualifiers methods)
529 (while 564 (while
@@ -541,7 +576,7 @@ The set of acceptable TYPEs (also called \"specializers\") is defined
541 `(,name ,qualifiers . ,specializers)) 576 `(,name ,qualifiers . ,specializers))
542 577
543;;;###autoload 578;;;###autoload
544(defun cl-generic-define-method (name qualifiers args uses-cnm function) 579(defun cl-generic-define-method (name qualifiers args call-con function)
545 (pcase-let* 580 (pcase-let*
546 ((generic (cl-generic-ensure-function name)) 581 ((generic (cl-generic-ensure-function name))
547 (`(,spec-args . ,_) (cl--generic-split-args args)) 582 (`(,spec-args . ,_) (cl--generic-split-args args))
@@ -550,7 +585,7 @@ The set of acceptable TYPEs (also called \"specializers\") is defined
550 spec-arg (cdr spec-arg))) 585 spec-arg (cdr spec-arg)))
551 spec-args)) 586 spec-args))
552 (method (cl--generic-make-method 587 (method (cl--generic-make-method
553 specializers qualifiers uses-cnm function)) 588 specializers qualifiers call-con function))
554 (mt (cl--generic-method-table generic)) 589 (mt (cl--generic-method-table generic))
555 (me (cl--generic-member-method specializers qualifiers mt)) 590 (me (cl--generic-member-method specializers qualifiers mt))
556 (dispatches (cl--generic-dispatches generic)) 591 (dispatches (cl--generic-dispatches generic))
@@ -735,23 +770,30 @@ for all those different tags in the method-cache.")
735 "Return a function that calls METHOD. 770 "Return a function that calls METHOD.
736FUN is the function that should be called when METHOD calls 771FUN is the function that should be called when METHOD calls
737`call-next-method'." 772`call-next-method'."
738 (if (not (cl--generic-method-uses-cnm method)) 773 (let ((met-fun (cl--generic-method-function method)))
739 (cl--generic-method-function method) 774 (pcase (cl--generic-method-call-con method)
740 (let ((met-fun (cl--generic-method-function method))) 775 ('nil met-fun)
741 (lambda (&rest args) 776 ('curried
742 (apply met-fun 777 (funcall met-fun (or fun
743 ;; FIXME: This sucks: passing just `next' would 778 (oclosure-lambda (cl--generic-nnm) (&rest args)
744 ;; be a lot more efficient than the lambda+apply 779 (apply #'cl-no-next-method generic method
745 ;; quasi-η, but we need this to implement the 780 args)))))
746 ;; "if call-next-method is called with no 781 ;; FIXME: backward compatibility with old convention for old `.elc' files.
747 ;; arguments, then use the previous arguments". 782 (_
748 (if fun 783 (lambda (&rest args)
749 (lambda (&rest cnm-args) 784 (apply met-fun
750 (apply fun (or cnm-args args))) 785 ;; FIXME: This sucks: passing just `next' would
751 (oclosure-lambda (cl--generic-nnm) (&rest cnm-args) 786 ;; be a lot more efficient than the lambda+apply
752 (apply #'cl-no-next-method generic method 787 ;; quasi-η, but we need this to implement the
753 (or cnm-args args)))) 788 ;; "if call-next-method is called with no
754 args))))) 789 ;; arguments, then use the previous arguments".
790 (if fun
791 (lambda (&rest cnm-args)
792 (apply fun (or cnm-args args)))
793 (oclosure-lambda (cl--generic-nnm) (&rest cnm-args)
794 (apply #'cl-no-next-method generic method
795 (or cnm-args args))))
796 args))))))
755 797
756;; Standard CLOS name. 798;; Standard CLOS name.
757(defalias 'cl-method-qualifiers #'cl--generic-method-qualifiers) 799(defalias 'cl-method-qualifiers #'cl--generic-method-qualifiers)
@@ -995,9 +1037,13 @@ MET-NAME is as returned by `cl--generic-load-hist-format'."
995(defun cl--generic-method-info (method) 1037(defun cl--generic-method-info (method)
996 (let* ((specializers (cl--generic-method-specializers method)) 1038 (let* ((specializers (cl--generic-method-specializers method))
997 (qualifiers (cl--generic-method-qualifiers method)) 1039 (qualifiers (cl--generic-method-qualifiers method))
998 (uses-cnm (cl--generic-method-uses-cnm method)) 1040 (call-con (cl--generic-method-call-con method))
999 (function (cl--generic-method-function method)) 1041 (function (cl--generic-method-function method))
1000 (args (help-function-arglist function 'names)) 1042 (args (help-function-arglist (if (not (eq call-con 'curried))
1043 function
1044 ;; FIXME: that just gives us "&rest args"!!!
1045 (funcall function #'ignore))
1046 'names))
1001 (docstring (documentation function)) 1047 (docstring (documentation function))
1002 (qual-string 1048 (qual-string
1003 (if (null qualifiers) "" 1049 (if (null qualifiers) ""
@@ -1008,7 +1054,7 @@ MET-NAME is as returned by `cl--generic-load-hist-format'."
1008 (let ((split (help-split-fundoc docstring nil))) 1054 (let ((split (help-split-fundoc docstring nil)))
1009 (if split (cdr split) docstring)))) 1055 (if split (cdr split) docstring))))
1010 (combined-args ())) 1056 (combined-args ()))
1011 (if uses-cnm (setq args (cdr args))) 1057 (if (eq t call-con) (setq args (cdr args)))
1012 (dolist (specializer specializers) 1058 (dolist (specializer specializers)
1013 (let ((arg (if (eq '&rest (car args)) 1059 (let ((arg (if (eq '&rest (car args))
1014 (intern (format "arg%d" (length combined-args))) 1060 (intern (format "arg%d" (length combined-args)))
diff --git a/lisp/emacs-lisp/oclosure.el b/lisp/emacs-lisp/oclosure.el
index c53182fccd2..fd994e2345d 100644
--- a/lisp/emacs-lisp/oclosure.el
+++ b/lisp/emacs-lisp/oclosure.el
@@ -307,7 +307,7 @@
307 (copiers (funcall get-opt :copier 'all)) 307 (copiers (funcall get-opt :copier 'all))
308 (mixin (car (funcall get-opt :mixin)))) 308 (mixin (car (funcall get-opt :mixin))))
309 `(progn 309 `(progn
310 ,(when options (macroexp-warn-and-return 310 ,(when options (macroexp-warn-and-return name
311 (format "Ignored options: %S" options) 311 (format "Ignored options: %S" options)
312 nil)) 312 nil))
313 (eval-and-compile 313 (eval-and-compile
diff --git a/lisp/org/org-attach.el b/lisp/org/org-attach.el
index 36c21b7021c..bba7fd76902 100644
--- a/lisp/org/org-attach.el
+++ b/lisp/org/org-attach.el
@@ -314,14 +314,14 @@ Shows a list of commands and prompts for another key to execute a command."
314 (concat (mapcar #'caar org-attach-commands))))) 314 (concat (mapcar #'caar org-attach-commands)))))
315 (message msg) 315 (message msg)
316 (while (and (setq c (read-char-exclusive)) 316 (while (and (setq c (read-char-exclusive))
317 (memq c '(14 16 22 134217846))) 317 (memq c '(?\C-n ?\C-p ?\C-v ?\M-v)))
318 (org-scroll c t))) 318 (org-scroll c t)))
319 (and (get-buffer "*Org Attach*") (kill-buffer "*Org Attach*")))) 319 (and (get-buffer "*Org Attach*") (kill-buffer "*Org Attach*"))))
320 (let ((command (cl-some (lambda (entry) 320 (let ((command (cl-some (lambda (entry)
321 (and (memq c (nth 0 entry)) (nth 1 entry))) 321 (and (memq c (nth 0 entry)) (nth 1 entry)))
322 org-attach-commands))) 322 org-attach-commands)))
323 (if (commandp command t) 323 (if (commandp command)
324 (call-interactively command) 324 (command-execute command)
325 (error "No such attachment command: %c" c)))))) 325 (error "No such attachment command: %c" c))))))
326 326
327(defun org-attach-dir (&optional create-if-not-exists-p no-fs-check) 327(defun org-attach-dir (&optional create-if-not-exists-p no-fs-check)