diff options
| author | Stefan Monnier | 2022-02-06 17:43:27 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2022-02-06 17:43:27 -0500 |
| commit | f1b7ad1973a88d8efa9e2da9ac3cbbfddc2d6207 (patch) | |
| tree | 36912b543e45929b28521c874b97941a96b7c7de | |
| parent | f75ee15efd83d74640d12483bd7d345114d5f5e5 (diff) | |
| download | emacs-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.el | 120 | ||||
| -rw-r--r-- | lisp/emacs-lisp/oclosure.el | 2 | ||||
| -rw-r--r-- | lisp/org/org-attach.el | 6 |
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. |
| 736 | FUN is the function that should be called when METHOD calls | 771 | FUN 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) |