diff options
| author | Stefan Monnier | 2026-01-09 14:17:09 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2026-01-09 14:18:31 -0500 |
| commit | 6c818936e00bf24201dbfa8916cd91aca24f84c9 (patch) | |
| tree | 63d7a78b562cf8e90f163b3e0ae225121471bac5 | |
| parent | e07e14a7be4d97c5e1623d34055c1ee6fe6d9e02 (diff) | |
| download | emacs-6c818936e00bf24201dbfa8916cd91aca24f84c9.tar.gz emacs-6c818936e00bf24201dbfa8916cd91aca24f84c9.zip | |
(cl--generic-build-combined-method): Fix lingering error (bug#80154)
The cycle detection could occasionally leave some lingering
cycle marker leading to bogus errors. While we're here,
streamline the code, to eliminate an unneeded signal+condition-case.
* lisp/emacs-lisp/cl-generic.el (cl--generic-build-combined-method):
Delete error.
(cl--generic-build-combined-method): Rewrite.
| -rw-r--r-- | lisp/emacs-lisp/cl-generic.el | 59 |
1 files changed, 31 insertions, 28 deletions
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index 405500c0987..ea73ce766f5 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el | |||
| @@ -855,33 +855,32 @@ This is particularly useful when many different tags select the same set | |||
| 855 | of methods, since this table then allows us to share a single combined-method | 855 | of methods, since this table then allows us to share a single combined-method |
| 856 | for all those different tags in the method-cache.") | 856 | for all those different tags in the method-cache.") |
| 857 | 857 | ||
| 858 | (define-error 'cl--generic-cyclic-definition "Cyclic definition") | ||
| 859 | |||
| 860 | (defun cl--generic-build-combined-method (generic methods) | 858 | (defun cl--generic-build-combined-method (generic methods) |
| 861 | (if (null methods) | 859 | ;; Since `cl-generic-combine-methods' is itself a generic function, |
| 862 | ;; Special case needed to fix a circularity during bootstrap. | 860 | ;; there is a chicken and egg problem when computing a combined |
| 863 | (cl--generic-standard-method-combination generic methods) | 861 | ;; method for `cl-generic-combine-methods'. |
| 864 | (let ((f | 862 | ;; We break such infinite recursion by detecting it and falling |
| 865 | (with-memoization | 863 | ;; back to `cl--generic-standard-method-combination' when it happens. |
| 866 | ;; FIXME: Since the fields of `generic' are modified, this | 864 | ;; FIXME: Since the fields of `generic' are modified, the |
| 867 | ;; hash-table won't work right, because the hashes will change! | 865 | ;; `cl--generic-combined-method-memoization' hash-table won't work |
| 868 | ;; It's not terribly serious, but reduces the effectiveness of | 866 | ;; right, because the hashes will change! It's not terribly serious, |
| 869 | ;; the table. | 867 | ;; but reduces the effectiveness of the table. |
| 870 | (gethash (cons generic methods) | 868 | (let ((key (cons generic methods))) |
| 871 | cl--generic-combined-method-memoization) | 869 | (pcase (gethash key cl--generic-combined-method-memoization) |
| 872 | (puthash (cons generic methods) :cl--generic--under-construction | 870 | (:cl--generic--under-construction |
| 873 | cl--generic-combined-method-memoization) | 871 | ;; Fallback to the standard method combination. |
| 874 | (condition-case nil | 872 | (setf (gethash key cl--generic-combined-method-memoization) |
| 875 | (cl-generic-combine-methods generic methods) | 873 | (cl--generic-standard-method-combination generic methods))) |
| 876 | ;; Special case needed to fix a circularity during bootstrap. | 874 | ('nil |
| 877 | (cl--generic-cyclic-definition | 875 | (setf (gethash key cl--generic-combined-method-memoization) |
| 878 | (cl--generic-standard-method-combination generic methods)))))) | 876 | :cl--generic--under-construction) |
| 879 | (if (eq f :cl--generic--under-construction) | 877 | (let ((f nil)) |
| 880 | (signal 'cl--generic-cyclic-definition | 878 | (unwind-protect |
| 881 | (list (cl--generic-name generic))) | 879 | (setq f (cl-generic-combine-methods generic methods)) |
| 882 | f)))) | 880 | (setf (gethash key cl--generic-combined-method-memoization) f)))) |
| 883 | 881 | (f f)))) | |
| 884 | (oclosure-define (cl--generic-nnm) | 882 | |
| 883 | (oclosure-define cl--generic-nnm | ||
| 885 | "Special type for `call-next-method's that just call `no-next-method'.") | 884 | "Special type for `call-next-method's that just call `no-next-method'.") |
| 886 | 885 | ||
| 887 | (defun cl-generic-call-method (generic method &optional fun) | 886 | (defun cl-generic-call-method (generic method &optional fun) |
| @@ -986,6 +985,10 @@ FUN is the function that should be called when METHOD calls | |||
| 986 | (setq methods (nreverse (mapcar #'cdr (sort methods #'car-less-than-car)))) | 985 | (setq methods (nreverse (mapcar #'cdr (sort methods #'car-less-than-car)))) |
| 987 | (cl--generic-make-next-function generic dispatches-left methods))) | 986 | (cl--generic-make-next-function generic dispatches-left methods))) |
| 988 | 987 | ||
| 988 | (unless (ignore-errors (cl-generic-generalizers t)) | ||
| 989 | ;; Temporary definition to let the next defgenerics succeed. | ||
| 990 | (fset 'cl-generic-combine-methods #'cl--generic-standard-method-combination)) | ||
| 991 | |||
| 989 | (cl-defgeneric cl-generic-generalizers (specializer) | 992 | (cl-defgeneric cl-generic-generalizers (specializer) |
| 990 | "Return a list of generalizers for a given SPECIALIZER. | 993 | "Return a list of generalizers for a given SPECIALIZER. |
| 991 | To each kind of `specializer', corresponds a `generalizer' which describes | 994 | To each kind of `specializer', corresponds a `generalizer' which describes |
| @@ -1002,11 +1005,11 @@ The code which extracts the tag should be as fast as possible. | |||
| 1002 | The tags should be chosen according to the following rules: | 1005 | The tags should be chosen according to the following rules: |
| 1003 | - The tags should not be too specific: similar objects which match the | 1006 | - The tags should not be too specific: similar objects which match the |
| 1004 | same list of specializers should ideally use the same (`eql') tag. | 1007 | same list of specializers should ideally use the same (`eql') tag. |
| 1005 | This insures that the cached computation of the applicable | 1008 | This ensures that the cached computation of the applicable |
| 1006 | methods for one object can be reused for other objects. | 1009 | methods for one object can be reused for other objects. |
| 1007 | - Corollary: objects which don't match any of the relevant specializers | 1010 | - Corollary: objects which don't match any of the relevant specializers |
| 1008 | should ideally all use the same tag (typically nil). | 1011 | should ideally all use the same tag (typically nil). |
| 1009 | This insures that this cache does not grow unnecessarily large. | 1012 | This ensures that this cache does not grow unnecessarily large. |
| 1010 | - Two different generalizers G1 and G2 should not use the same tag | 1013 | - Two different generalizers G1 and G2 should not use the same tag |
| 1011 | unless they use it for the same set of objects. IOW, if G1.tag(X1) = | 1014 | unless they use it for the same set of objects. IOW, if G1.tag(X1) = |
| 1012 | G2.tag(X2) then G1.tag(X1) = G2.tag(X1) = G1.tag(X2) = G2.tag(X2). | 1015 | G2.tag(X2) then G1.tag(X1) = G2.tag(X1) = G1.tag(X2) = G2.tag(X2). |