aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2024-03-08 01:48:59 -0500
committerStefan Monnier2024-03-08 02:04:59 -0500
commitbd017175d4571e24ef1fdf84676136af1d36002d (patch)
treeba1ed773d1b756446d048d1136f45f668bc38333
parent945af4d9d11192d262f4fabbc66ee83f5beefc86 (diff)
downloademacs-bd017175d4571e24ef1fdf84676136af1d36002d.tar.gz
emacs-bd017175d4571e24ef1fdf84676136af1d36002d.zip
Simplify type hierarchy operations
Now that built-in types have classes that describe their relationships exactly like struct/eieio/oclosure classes, we can the code that navigates that DAG. * lisp/emacs-lisp/cl-generic.el (cl--generic-struct-tag): Move to `eieio-core.el`. (cl--generic-type-specializers): Rename from `cl--generic-struct-specializers`. Make it work for any class. (cl--generic-typeof-generalizer, cl--generic-oclosure-generalizer): Use it. (cl--generic-struct-generalizer): Delete generalizer. (cl-generic-generalizers :extra "cl-struct"): Delete method. (prefill 0 cl--generic-generalizer): Move to after the typeof. (cl-generic-generalizers :extra "typeof"): Rewrite to use classes rather than `cl--all-builtin-types`. (cl-generic--oclosure-specializers): Delete function. * lisp/emacs-lisp/cl-preloaded.el (cl--direct-supertypes-of-type) (cl--typeof-types, cl--all-builtin-types): Delete constants. * lisp/emacs-lisp/comp-cstr.el (comp--typeof-builtin-types): Delete constant. (comp--cl-class-hierarchy): Simplify. (comp--compute-typeof-types): Simplify now that `comp--cl-class-hierarchy` and `comp--all-classes` work for built-in types as well. (comp--direct-supertypes): Just use `cl--class-parents`. (comp-supertypes): Simplify since typeof-types should now be complete. * lisp/emacs-lisp/eieio-core.el (eieio-defclass-autoload): Use `superclasses` argument, so we can find parents before it's loaded. (eieio--class-precedence-c3, eieio--class-precedence-dfs): Don't add a `eieio-default-superclass` parent any more. (eieio--class/struct-parents): Delete function. (eieio--class-precedence-bfs): Use `eieio--class-parents` instead. Don't stop when reaching `eieio-default-superclass`. (cl--generic-struct-tag): Move from `cl-generic.el`.
-rw-r--r--lisp/emacs-lisp/cl-generic.el67
-rw-r--r--lisp/emacs-lisp/cl-preloaded.el30
-rw-r--r--lisp/emacs-lisp/comp-cstr.el55
-rw-r--r--lisp/emacs-lisp/eieio-core.el51
4 files changed, 49 insertions, 154 deletions
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el
index f439a97f88c..84eb800ec24 100644
--- a/lisp/emacs-lisp/cl-generic.el
+++ b/lisp/emacs-lisp/cl-generic.el
@@ -1330,62 +1330,31 @@ These match if the argument is `eql' to VAL."
1330(cl--generic-prefill-dispatchers (terminal-parameter nil 'xterm--set-selection) 1330(cl--generic-prefill-dispatchers (terminal-parameter nil 'xterm--set-selection)
1331 (eql nil)) 1331 (eql nil))
1332 1332
1333;;; Support for cl-defstructs specializers. 1333;;; Dispatch on "normal types".
1334 1334
1335(defun cl--generic-struct-tag (name &rest _) 1335(defun cl--generic-type-specializers (tag &rest _)
1336 ;; Use exactly the same code as for `typeof'.
1337 `(if ,name (type-of ,name) 'null))
1338
1339(defun cl--generic-struct-specializers (tag &rest _)
1340 (and (symbolp tag) 1336 (and (symbolp tag)
1341 (let ((class (get tag 'cl--class))) 1337 (let ((class (cl--find-class tag)))
1342 (when (cl-typep class 'cl-structure-class) 1338 (when class
1343 (cl--class-allparents class))))) 1339 (cl--class-allparents class)))))
1344 1340
1345(cl-generic-define-generalizer cl--generic-struct-generalizer
1346 50 #'cl--generic-struct-tag
1347 #'cl--generic-struct-specializers)
1348
1349(cl-defmethod cl-generic-generalizers :extra "cl-struct" (type)
1350 "Support for dispatch on types defined by `cl-defstruct'."
1351 (or
1352 (when (symbolp type)
1353 ;; Use the "cl--struct-class*" (inlinable) functions/macros rather than
1354 ;; the "cl-struct-*" variants which aren't inlined, so that dispatch can
1355 ;; take place without requiring cl-lib.
1356 (let ((class (cl--find-class type)))
1357 (and (cl-typep class 'cl-structure-class)
1358 (or (null (cl--struct-class-type class))
1359 (error "Can't dispatch on cl-struct %S: type is %S"
1360 type (cl--struct-class-type class)))
1361 (progn (cl-assert (null (cl--struct-class-named class))) t)
1362 (list cl--generic-struct-generalizer))))
1363 (cl-call-next-method)))
1364
1365(cl--generic-prefill-dispatchers 0 cl--generic-generalizer)
1366
1367;;; Dispatch on "system types".
1368
1369(cl-generic-define-generalizer cl--generic-typeof-generalizer 1341(cl-generic-define-generalizer cl--generic-typeof-generalizer
1370 ;; FIXME: We could also change `type-of' to return `null' for nil. 1342 ;; FIXME: We could also change `type-of' to return `null' for nil.
1371 10 (lambda (name &rest _) `(if ,name (type-of ,name) 'null)) 1343 10 (lambda (name &rest _) `(if ,name (type-of ,name) 'null))
1372 (lambda (tag &rest _) 1344 #'cl--generic-type-specializers)
1373 (and (symbolp tag) (assq tag cl--typeof-types))))
1374 1345
1375(cl-defmethod cl-generic-generalizers :extra "typeof" (type) 1346(cl-defmethod cl-generic-generalizers :extra "typeof" (type)
1376 "Support for dispatch on builtin types. 1347 "Support for dispatch on types.
1377See the full list and their hierarchy in `cl--typeof-types'." 1348This currently works for built-in types and types built on top of records."
1378 ;; FIXME: Add support for other types accepted by `cl-typep' such 1349 ;; FIXME: Add support for other types accepted by `cl-typep' such
1379 ;; as `character', `face', `function', ... 1350 ;; as `character', `face', `function', ...
1380 (or 1351 (or
1381 (and (memq type cl--all-builtin-types) 1352 (and (symbolp type)
1382 (progn 1353 (not (eq type t)) ;; Handled by the `t-generalizer'.
1383 ;; FIXME: While this wrinkle in the semantics can be occasionally 1354 (let ((class (cl--find-class type)))
1384 ;; problematic, this warning is more often annoying than helpful. 1355 (memq (type-of class)
1385 ;;(if (memq type '(vector array sequence)) 1356 '(built-in-class cl-structure-class eieio--class)))
1386 ;; (message "`%S' also matches CL structs and EIEIO classes" 1357 (list cl--generic-typeof-generalizer))
1387 ;; type))
1388 (list cl--generic-typeof-generalizer)))
1389 (cl-call-next-method))) 1358 (cl-call-next-method)))
1390 1359
1391(cl--generic-prefill-dispatchers 0 integer) 1360(cl--generic-prefill-dispatchers 0 integer)
@@ -1393,6 +1362,8 @@ See the full list and their hierarchy in `cl--typeof-types'."
1393(cl--generic-prefill-dispatchers 0 cl--generic-generalizer integer) 1362(cl--generic-prefill-dispatchers 0 cl--generic-generalizer integer)
1394(cl--generic-prefill-dispatchers 0 (eql 'x) integer) 1363(cl--generic-prefill-dispatchers 0 (eql 'x) integer)
1395 1364
1365(cl--generic-prefill-dispatchers 0 cl--generic-generalizer)
1366
1396;;; Dispatch on major mode. 1367;;; Dispatch on major mode.
1397 1368
1398;; Two parts: 1369;; Two parts:
@@ -1430,19 +1401,13 @@ Used internally for the (major-mode MODE) context specializers."
1430(defun cl--generic-oclosure-tag (name &rest _) 1401(defun cl--generic-oclosure-tag (name &rest _)
1431 `(oclosure-type ,name)) 1402 `(oclosure-type ,name))
1432 1403
1433(defun cl-generic--oclosure-specializers (tag &rest _)
1434 (and (symbolp tag)
1435 (let ((class (cl--find-class tag)))
1436 (when (cl-typep class 'oclosure--class)
1437 (oclosure--class-allparents class)))))
1438
1439(cl-generic-define-generalizer cl--generic-oclosure-generalizer 1404(cl-generic-define-generalizer cl--generic-oclosure-generalizer
1440 ;; Give slightly higher priority than the struct specializer, so that 1405 ;; Give slightly higher priority than the struct specializer, so that
1441 ;; for a generic function with methods dispatching structs and on OClosures, 1406 ;; for a generic function with methods dispatching structs and on OClosures,
1442 ;; we first try `oclosure-type' before `type-of' since `type-of' will return 1407 ;; we first try `oclosure-type' before `type-of' since `type-of' will return
1443 ;; non-nil for an OClosure as well. 1408 ;; non-nil for an OClosure as well.
1444 51 #'cl--generic-oclosure-tag 1409 51 #'cl--generic-oclosure-tag
1445 #'cl-generic--oclosure-specializers) 1410 #'cl--generic-type-specializers)
1446 1411
1447(cl-defmethod cl-generic-generalizers :extra "oclosure-struct" (type) 1412(cl-defmethod cl-generic-generalizers :extra "oclosure-struct" (type)
1448 "Support for dispatch on types defined by `oclosure-define'." 1413 "Support for dispatch on types defined by `oclosure-define'."
diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el
index 1b330e7f761..5743684fa89 100644
--- a/lisp/emacs-lisp/cl-preloaded.el
+++ b/lisp/emacs-lisp/cl-preloaded.el
@@ -433,36 +433,6 @@ For this build of Emacs it's %dbit."
433 (setf (cl--class-parents (cl--find-class 'cl-structure-object)) 433 (setf (cl--class-parents (cl--find-class 'cl-structure-object))
434 (list (cl--find-class 'record)))) 434 (list (cl--find-class 'record))))
435 435
436(defconst cl--direct-supertypes-of-type
437 ;; Please run `sycdoc-update-type-hierarchy' in
438 ;; `admin/syncdoc-type-hierarchy.el' each time this is modified to
439 ;; reflect the change in the documentation.
440 (let ((table (make-hash-table :test #'eq)))
441 (mapatoms
442 (lambda (type)
443 (let ((class (get type 'cl--class)))
444 (when (built-in-class-p class)
445 (puthash type (mapcar #'cl--class-name (cl--class-parents class))
446 table)))))
447 table)
448 "Hash table TYPE -> SUPERTYPES.")
449
450(defconst cl--typeof-types
451 (letrec ((alist nil))
452 (maphash (lambda (type _)
453 (let ((class (get type 'cl--class)))
454 ;; FIXME: Can't remember why `t' is excluded.
455 (push (remq t (cl--class-allparents class)) alist)))
456 cl--direct-supertypes-of-type)
457 alist)
458 "Alist of supertypes.
459Each element has the form (TYPE . SUPERTYPES) where TYPE is one of
460the symbols returned by `type-of', and SUPERTYPES is the list of its
461supertypes from the most specific to least specific.")
462
463(defconst cl--all-builtin-types
464 (delete-dups (copy-sequence (apply #'append cl--typeof-types))))
465
466;; Make sure functions defined with cl-defsubst can be inlined even in 436;; Make sure functions defined with cl-defsubst can be inlined even in
467;; packages which do not require CL. We don't put an autoload cookie 437;; packages which do not require CL. We don't put an autoload cookie
468;; directly on that function, since those cookies only go to cl-loaddefs. 438;; directly on that function, since those cookies only go to cl-loaddefs.
diff --git a/lisp/emacs-lisp/comp-cstr.el b/lisp/emacs-lisp/comp-cstr.el
index 1c6acaa6385..5922a8caf12 100644
--- a/lisp/emacs-lisp/comp-cstr.el
+++ b/lisp/emacs-lisp/comp-cstr.el
@@ -38,12 +38,6 @@
38(require 'cl-lib) 38(require 'cl-lib)
39(require 'cl-extra) ;HACK: For `cl-find-class' when `cl-loaddefs' is missing. 39(require 'cl-extra) ;HACK: For `cl-find-class' when `cl-loaddefs' is missing.
40 40
41(defconst comp--typeof-builtin-types (mapcar (lambda (x)
42 (append x '(t)))
43 cl--typeof-types)
44 ;; TODO can we just add t in `cl--typeof-types'?
45 "Like `cl--typeof-types' but with t as common supertype.")
46
47(cl-defstruct (comp-cstr (:constructor comp--type-to-cstr 41(cl-defstruct (comp-cstr (:constructor comp--type-to-cstr
48 (type &aux 42 (type &aux
49 (null (eq type 'null)) 43 (null (eq type 'null))
@@ -89,15 +83,7 @@ Integer values are handled in the `range' slot.")
89 83
90(defun comp--cl-class-hierarchy (x) 84(defun comp--cl-class-hierarchy (x)
91 "Given a class name `x' return its hierarchy." 85 "Given a class name `x' return its hierarchy."
92 (let ((parents (cl--class-allparents (cl--struct-get-class x)))) 86 (cl--class-allparents (cl--find-class x)))
93 (if (memq t parents)
94 parents
95 `(,@parents
96 ;; FIXME: AFAICT, `comp--all-classes' will also find those struct types
97 ;; which use :type and can thus be either `vector' or `cons' (the latter
98 ;; isn't `atom').
99 atom
100 t))))
101 87
102(defun comp--all-classes () 88(defun comp--all-classes ()
103 "Return all non built-in type names currently defined." 89 "Return all non built-in type names currently defined."
@@ -109,8 +95,7 @@ Integer values are handled in the `range' slot.")
109 res)) 95 res))
110 96
111(defun comp--compute-typeof-types () 97(defun comp--compute-typeof-types ()
112 (append comp--typeof-builtin-types 98 (mapcar #'comp--cl-class-hierarchy (comp--all-classes)))
113 (mapcar #'comp--cl-class-hierarchy (comp--all-classes))))
114 99
115(defun comp--compute--pred-type-h () 100(defun comp--compute--pred-type-h ()
116 (cl-loop with h = (make-hash-table :test #'eq) 101 (cl-loop with h = (make-hash-table :test #'eq)
@@ -275,19 +260,10 @@ Return them as multiple value."
275 (symbol-name y))) 260 (symbol-name y)))
276 261
277(defun comp--direct-supertypes (type) 262(defun comp--direct-supertypes (type)
278 (or 263 (when (symbolp type) ;; FIXME: Can this test ever fail?
279 (gethash type cl--direct-supertypes-of-type) 264 (let* ((class (cl--find-class type))
280 (let ((supers (comp-supertypes type))) 265 (parents (if class (cl--class-parents class))))
281 (cl-assert (eq type (car supers))) 266 (mapcar #'cl--class-name parents))))
282 (cl-loop
283 with notdirect = nil
284 with direct = nil
285 for parent in (cdr supers)
286 unless (memq parent notdirect)
287 do (progn
288 (push parent direct)
289 (setq notdirect (append notdirect (comp-supertypes parent))))
290 finally return direct))))
291 267
292(defsubst comp-subtype-p (type1 type2) 268(defsubst comp-subtype-p (type1 type2)
293 "Return t if TYPE1 is a subtype of TYPE2 or nil otherwise." 269 "Return t if TYPE1 is a subtype of TYPE2 or nil otherwise."
@@ -359,23 +335,8 @@ Return them as multiple value."
359 335
360(defun comp-supertypes (type) 336(defun comp-supertypes (type)
361 "Return the ordered list of supertypes of TYPE." 337 "Return the ordered list of supertypes of TYPE."
362 ;; FIXME: We should probably keep the results in 338 (or (assq type (comp-cstr-ctxt-typeof-types comp-ctxt))
363 ;; `comp-cstr-ctxt-typeof-types' (or maybe even precompute them 339 (error "Type %S missing from typeof-types!" type)))
364 ;; and maybe turn `comp-cstr-ctxt-typeof-types' into a hash-table).
365 ;; Or maybe we shouldn't keep structs and defclasses in it,
366 ;; and just use `cl--class-allparents' when needed (and refuse to
367 ;; compute their direct subtypes since we can't know them).
368 (cl-loop
369 named loop
370 with above
371 for lane in (comp-cstr-ctxt-typeof-types comp-ctxt)
372 do (let ((x (memq type lane)))
373 (cond
374 ((null x) nil)
375 ((eq x lane) (cl-return-from loop x)) ;A base type: easy case.
376 (t (setq above
377 (if above (comp--intersection x above) x)))))
378 finally return above))
379 340
380(defun comp-union-typesets (&rest typesets) 341(defun comp-union-typesets (&rest typesets)
381 "Union types present into TYPESETS." 342 "Union types present into TYPESETS."
diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el
index 9945e19c65c..5418f53be35 100644
--- a/lisp/emacs-lisp/eieio-core.el
+++ b/lisp/emacs-lisp/eieio-core.el
@@ -191,7 +191,7 @@ Abstract classes cannot be instantiated."
191 191
192;; We autoload this because it's used in `make-autoload'. 192;; We autoload this because it's used in `make-autoload'.
193;;;###autoload 193;;;###autoload
194(defun eieio-defclass-autoload (cname _superclasses filename doc) 194(defun eieio-defclass-autoload (cname superclasses filename doc)
195 "Create autoload symbols for the EIEIO class CNAME. 195 "Create autoload symbols for the EIEIO class CNAME.
196SUPERCLASSES are the superclasses that CNAME inherits from. 196SUPERCLASSES are the superclasses that CNAME inherits from.
197DOC is the docstring for CNAME. 197DOC is the docstring for CNAME.
@@ -199,15 +199,9 @@ This function creates a mock-class for CNAME and adds it into
199SUPERCLASSES as children. 199SUPERCLASSES as children.
200It creates an autoload function for CNAME's constructor." 200It creates an autoload function for CNAME's constructor."
201 ;; Assume we've already debugged inputs. 201 ;; Assume we've already debugged inputs.
202
203 ;; We used to store the list of superclasses in the `parent' slot (as a list
204 ;; of class names). But now this slot holds a list of class objects, and
205 ;; those parents may not exist yet, so the corresponding class objects may
206 ;; simply not exist yet. So instead we just don't store the list of parents
207 ;; here in eieio-defclass-autoload at all, since it seems that they're just
208 ;; not needed before the class is actually loaded.
209 (let* ((oldc (cl--find-class cname)) 202 (let* ((oldc (cl--find-class cname))
210 (newc (eieio--class-make cname))) 203 (newc (eieio--class-make cname))
204 (parents (mapcar #'cl-find-class superclasses)))
211 (if (eieio--class-p oldc) 205 (if (eieio--class-p oldc)
212 nil ;; Do nothing if we already have this class. 206 nil ;; Do nothing if we already have this class.
213 207
@@ -218,6 +212,12 @@ It creates an autoload function for CNAME's constructor."
218use '%s or turn off `eieio-backward-compatibility' instead" cname) 212use '%s or turn off `eieio-backward-compatibility' instead" cname)
219 "25.1")) 213 "25.1"))
220 214
215 (when (memq nil parents)
216 ;; If some parents aren't yet fully defined, just ignore them for now.
217 (setq parents (delq nil parents)))
218 (unless parents
219 (setq parents (list (cl--find-class 'eieio-default-superclass))))
220 (setf (cl--class-parents newc) parents)
221 (setf (cl--find-class cname) newc) 221 (setf (cl--find-class cname) newc)
222 222
223 ;; Create an autoload on top of our constructor function. 223 ;; Create an autoload on top of our constructor function.
@@ -958,19 +958,13 @@ need be... May remove that later...)"
958 (cdr tuple) 958 (cdr tuple)
959 nil))) 959 nil)))
960 960
961(defsubst eieio--class/struct-parents (class)
962 (or (eieio--class-parents class)
963 `(,eieio-default-superclass)))
964
965(defun eieio--class-precedence-c3 (class) 961(defun eieio--class-precedence-c3 (class)
966 "Return all parents of CLASS in c3 order." 962 "Return all parents of CLASS in c3 order."
967 (let ((parents (eieio--class-parents class))) 963 (let ((parents (eieio--class-parents class)))
968 (cons class 964 (cons class
969 (merge-ordered-lists 965 (merge-ordered-lists
970 (append 966 (append
971 (or 967 (mapcar #'eieio--class-precedence-c3 parents)
972 (mapcar #'eieio--class-precedence-c3 parents)
973 `((,eieio-default-superclass)))
974 (list parents)) 968 (list parents))
975 (lambda (remaining-inputs) 969 (lambda (remaining-inputs)
976 (signal 'inconsistent-class-hierarchy 970 (signal 'inconsistent-class-hierarchy
@@ -984,13 +978,11 @@ need be... May remove that later...)"
984 (classes (copy-sequence 978 (classes (copy-sequence
985 (apply #'append 979 (apply #'append
986 (list class) 980 (list class)
987 (or 981 (mapcar
988 (mapcar 982 (lambda (parent)
989 (lambda (parent) 983 (cons parent
990 (cons parent 984 (eieio--class-precedence-dfs parent)))
991 (eieio--class-precedence-dfs parent))) 985 parents))))
992 parents)
993 `((,eieio-default-superclass))))))
994 (tail classes)) 986 (tail classes))
995 ;; Remove duplicates. 987 ;; Remove duplicates.
996 (while tail 988 (while tail
@@ -1003,13 +995,12 @@ need be... May remove that later...)"
1003(defun eieio--class-precedence-bfs (class) 995(defun eieio--class-precedence-bfs (class)
1004 "Return all parents of CLASS in breadth-first order." 996 "Return all parents of CLASS in breadth-first order."
1005 (let* ((result) 997 (let* ((result)
1006 (queue (eieio--class/struct-parents class))) 998 (queue (eieio--class-parents class)))
1007 (while queue 999 (while queue
1008 (let ((head (pop queue))) 1000 (let ((head (pop queue)))
1009 (unless (member head result) 1001 (unless (member head result)
1010 (push head result) 1002 (push head result)
1011 (unless (eq head eieio-default-superclass) 1003 (setq queue (append queue (eieio--class-parents head))))))
1012 (setq queue (append queue (eieio--class/struct-parents head)))))))
1013 (cons class (nreverse result))) 1004 (cons class (nreverse result)))
1014 ) 1005 )
1015 1006
@@ -1049,6 +1040,14 @@ method invocation orders of the involved classes."
1049 1040
1050;;;; General support to dispatch based on the type of the argument. 1041;;;; General support to dispatch based on the type of the argument.
1051 1042
1043;; FIXME: We could almost use the typeof-generalizer (i.e. the same as
1044;; used for cl-structs), except that that generalizer doesn't support
1045;; `:method-invocation-order' :-(
1046
1047(defun cl--generic-struct-tag (name &rest _)
1048 ;; Use exactly the same code as for `typeof'.
1049 `(if ,name (type-of ,name) 'null))
1050
1052(cl-generic-define-generalizer eieio--generic-generalizer 1051(cl-generic-define-generalizer eieio--generic-generalizer
1053 ;; Use the exact same tagcode as for cl-struct, so that methods 1052 ;; Use the exact same tagcode as for cl-struct, so that methods
1054 ;; that dispatch on both kinds of objects get to share this 1053 ;; that dispatch on both kinds of objects get to share this