diff options
| author | Stefan Monnier | 2024-03-12 09:26:24 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2024-03-18 09:29:47 -0400 |
| commit | 706403f2aa3a306369a0150022da0cba1802ca2b (patch) | |
| tree | c2a2a7e1dc919efdead6ee36f1fb91e3f624c673 | |
| parent | 1a8b34a503e5af32851c1aac27a3f09e2345673b (diff) | |
| download | emacs-706403f2aa3a306369a0150022da0cba1802ca2b.tar.gz emacs-706403f2aa3a306369a0150022da0cba1802ca2b.zip | |
(cl-type-of): New function to return more precise types (bug#69739)
* src/data.c (Fcl_type_of): New function, extracted from `Ftype_of`.
Make it return more precise types for symbols, integers, and subrs.
(Ftype_of): Use it.
(syms_of_data): Define the corresponding new symbols and defsubr
the new function.
* doc/lispref/objects.texi (Type Predicates): Document it.
* src/comp.c (emit_limple_insn): Use `Fcl_type_of`.
* lisp/emacs-lisp/cl-preloaded.el (subr): Demote it to `atom`.
(subr-native-elisp, subr-primitive): Add `compiled-function` as
parent instead.
(special-form): New type.
* lisp/obsolete/eieio-core.el (cl--generic-struct-tag):
* lisp/emacs-lisp/cl-generic.el (cl--generic-typeof-generalizer):
Use `cl-type-of`.
cl--generic--unreachable-types): Update accordingly.
test/src/data-tests.el (data-tests--cl-type-of): New test.
| -rw-r--r-- | doc/lispref/objects.texi | 21 | ||||
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-generic.el | 6 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-preloaded.el | 12 | ||||
| -rw-r--r-- | lisp/emacs-lisp/eieio-core.el | 2 | ||||
| -rw-r--r-- | src/comp.c | 2 | ||||
| -rw-r--r-- | src/data.c | 40 | ||||
| -rw-r--r-- | test/src/data-tests.el | 37 |
8 files changed, 108 insertions, 17 deletions
diff --git a/doc/lispref/objects.texi b/doc/lispref/objects.texi index 279f449a994..1e448b64296 100644 --- a/doc/lispref/objects.texi +++ b/doc/lispref/objects.texi | |||
| @@ -2207,6 +2207,27 @@ slot is returned; @ref{Records}. | |||
| 2207 | @end example | 2207 | @end example |
| 2208 | @end defun | 2208 | @end defun |
| 2209 | 2209 | ||
| 2210 | @defun cl-type-of object | ||
| 2211 | This function returns a symbol naming @emph{the} type of | ||
| 2212 | @var{object}. It usually behaves like @code{type-of}, except | ||
| 2213 | that it guarantees to return the most precise type possible, which also | ||
| 2214 | implies that the specific type it returns may change depending on the | ||
| 2215 | Emacs version. For this reason, as a rule you should never compare its | ||
| 2216 | return value against some fixed set of types. | ||
| 2217 | |||
| 2218 | @example | ||
| 2219 | (cl-type-of 1) | ||
| 2220 | @result{} fixnum | ||
| 2221 | @group | ||
| 2222 | (cl-type-of 'nil) | ||
| 2223 | @result{} null | ||
| 2224 | (cl-type-of (record 'foo)) | ||
| 2225 | @result{} foo | ||
| 2226 | @end group | ||
| 2227 | @end example | ||
| 2228 | @end defun | ||
| 2229 | |||
| 2230 | |||
| 2210 | @node Equality Predicates | 2231 | @node Equality Predicates |
| 2211 | @section Equality Predicates | 2232 | @section Equality Predicates |
| 2212 | @cindex equality | 2233 | @cindex equality |
| @@ -1647,6 +1647,11 @@ values. | |||
| 1647 | 1647 | ||
| 1648 | * Lisp Changes in Emacs 30.1 | 1648 | * Lisp Changes in Emacs 30.1 |
| 1649 | 1649 | ||
| 1650 | ** New function 'cl-type-of'. | ||
| 1651 | This function is like 'type-of' except that it sometimes returns | ||
| 1652 | a more precise type. For example, for nil and t it returns 'null' | ||
| 1653 | and 'boolean' respectively, instead of just 'symbol'. | ||
| 1654 | |||
| 1650 | ** Built-in types have now corresponding classes. | 1655 | ** Built-in types have now corresponding classes. |
| 1651 | At the Lisp level, this means that things like (cl-find-class 'integer) | 1656 | At the Lisp level, this means that things like (cl-find-class 'integer) |
| 1652 | will now return a class object, and at the UI level it means that | 1657 | will now return a class object, and at the UI level it means that |
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index 613ecf82a92..62abe8d1589 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el | |||
| @@ -1334,8 +1334,7 @@ These match if the argument is `eql' to VAL." | |||
| 1334 | 1334 | ||
| 1335 | (defconst cl--generic--unreachable-types | 1335 | (defconst cl--generic--unreachable-types |
| 1336 | ;; FIXME: Try to make that list empty? | 1336 | ;; FIXME: Try to make that list empty? |
| 1337 | '(fixnum bignum boolean keyword | 1337 | '(keyword) |
| 1338 | special-form subr-primitive subr-native-elisp) | ||
| 1339 | "Built-in classes on which we cannot dispatch for technical reasons.") | 1338 | "Built-in classes on which we cannot dispatch for technical reasons.") |
| 1340 | 1339 | ||
| 1341 | (defun cl--generic-type-specializers (tag &rest _) | 1340 | (defun cl--generic-type-specializers (tag &rest _) |
| @@ -1345,8 +1344,7 @@ These match if the argument is `eql' to VAL." | |||
| 1345 | (cl--class-allparents class))))) | 1344 | (cl--class-allparents class))))) |
| 1346 | 1345 | ||
| 1347 | (cl-generic-define-generalizer cl--generic-typeof-generalizer | 1346 | (cl-generic-define-generalizer cl--generic-typeof-generalizer |
| 1348 | ;; FIXME: We could also change `type-of' to return `null' for nil. | 1347 | 10 (lambda (name &rest _) `(cl-type-of ,name)) |
| 1349 | 10 (lambda (name &rest _) `(if ,name (type-of ,name) 'null)) | ||
| 1350 | #'cl--generic-type-specializers) | 1348 | #'cl--generic-type-specializers) |
| 1351 | 1349 | ||
| 1352 | (cl-defmethod cl-generic-generalizers :extra "typeof" (type) | 1350 | (cl-defmethod cl-generic-generalizers :extra "typeof" (type) |
diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el index 515aa99549d..3e89afea452 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el | |||
| @@ -339,8 +339,6 @@ | |||
| 339 | ',parents)))))) | 339 | ',parents)))))) |
| 340 | 340 | ||
| 341 | ;; FIXME: Our type DAG has various quirks: | 341 | ;; FIXME: Our type DAG has various quirks: |
| 342 | ;; - `subr' says it's a `compiled-function' but that's not true | ||
| 343 | ;; for those subrs that are special forms! | ||
| 344 | ;; - Some `keyword's are also `symbol-with-pos' but that's not reflected | 342 | ;; - Some `keyword's are also `symbol-with-pos' but that's not reflected |
| 345 | ;; in the DAG. | 343 | ;; in the DAG. |
| 346 | ;; - An OClosure can be an interpreted function or a `byte-code-function', | 344 | ;; - An OClosure can be an interpreted function or a `byte-code-function', |
| @@ -428,15 +426,17 @@ For this build of Emacs it's %dbit." | |||
| 428 | "Abstract type of functions that have been compiled.") | 426 | "Abstract type of functions that have been compiled.") |
| 429 | (cl--define-built-in-type byte-code-function (compiled-function) | 427 | (cl--define-built-in-type byte-code-function (compiled-function) |
| 430 | "Type of functions that have been byte-compiled.") | 428 | "Type of functions that have been byte-compiled.") |
| 431 | (cl--define-built-in-type subr (compiled-function) | 429 | (cl--define-built-in-type subr (atom) |
| 432 | "Abstract type of functions compiled to machine code.") | 430 | "Abstract type of functions compiled to machine code.") |
| 433 | (cl--define-built-in-type module-function (function) | 431 | (cl--define-built-in-type module-function (function) |
| 434 | "Type of functions provided via the module API.") | 432 | "Type of functions provided via the module API.") |
| 435 | (cl--define-built-in-type interpreted-function (function) | 433 | (cl--define-built-in-type interpreted-function (function) |
| 436 | "Type of functions that have not been compiled.") | 434 | "Type of functions that have not been compiled.") |
| 437 | (cl--define-built-in-type subr-native-elisp (subr) | 435 | (cl--define-built-in-type special-form (subr) |
| 438 | "Type of function that have been compiled by the native compiler.") | 436 | "Type of the core syntactic elements of the Emacs Lisp language.") |
| 439 | (cl--define-built-in-type subr-primitive (subr) | 437 | (cl--define-built-in-type subr-native-elisp (subr compiled-function) |
| 438 | "Type of functions that have been compiled by the native compiler.") | ||
| 439 | (cl--define-built-in-type subr-primitive (subr compiled-function) | ||
| 440 | "Type of functions hand written in C.") | 440 | "Type of functions hand written in C.") |
| 441 | 441 | ||
| 442 | (unless (cl--class-parents (cl--find-class 'cl-structure-object)) | 442 | (unless (cl--class-parents (cl--find-class 'cl-structure-object)) |
diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el index a2f7c4172a3..cf8bd749f2a 100644 --- a/lisp/emacs-lisp/eieio-core.el +++ b/lisp/emacs-lisp/eieio-core.el | |||
| @@ -1046,7 +1046,7 @@ method invocation orders of the involved classes." | |||
| 1046 | 1046 | ||
| 1047 | (defun cl--generic-struct-tag (name &rest _) | 1047 | (defun cl--generic-struct-tag (name &rest _) |
| 1048 | ;; Use exactly the same code as for `typeof'. | 1048 | ;; Use exactly the same code as for `typeof'. |
| 1049 | `(if ,name (type-of ,name) 'null)) | 1049 | `(cl-type-of ,name)) |
| 1050 | 1050 | ||
| 1051 | (cl-generic-define-generalizer eieio--generic-generalizer | 1051 | (cl-generic-define-generalizer eieio--generic-generalizer |
| 1052 | ;; 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 |
diff --git a/src/comp.c b/src/comp.c index 3f989c722d4..76cf1f3ab6e 100644 --- a/src/comp.c +++ b/src/comp.c | |||
| @@ -2442,7 +2442,7 @@ emit_limple_insn (Lisp_Object insn) | |||
| 2442 | { | 2442 | { |
| 2443 | Lisp_Object arg1 = arg[1]; | 2443 | Lisp_Object arg1 = arg[1]; |
| 2444 | 2444 | ||
| 2445 | if (EQ (Ftype_of (arg1), Qcomp_mvar)) | 2445 | if (EQ (Fcl_type_of (arg1), Qcomp_mvar)) |
| 2446 | res = emit_mvar_rval (arg1); | 2446 | res = emit_mvar_rval (arg1); |
| 2447 | else if (EQ (FIRST (arg1), Qcall)) | 2447 | else if (EQ (FIRST (arg1), Qcall)) |
| 2448 | res = emit_limple_call (XCDR (arg1)); | 2448 | res = emit_limple_call (XCDR (arg1)); |
diff --git a/src/data.c b/src/data.c index 35f4c82c68f..5d6b6e0ba9d 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -193,16 +193,37 @@ DEFUN ("null", Fnull, Snull, 1, 1, 0, | |||
| 193 | DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, | 193 | DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, |
| 194 | doc: /* Return a symbol representing the type of OBJECT. | 194 | doc: /* Return a symbol representing the type of OBJECT. |
| 195 | The symbol returned names the object's basic type; | 195 | The symbol returned names the object's basic type; |
| 196 | for example, (type-of 1) returns `integer'. */) | 196 | for example, (type-of 1) returns `integer'. |
| 197 | Contrary to `cl-type-of', the returned type is not always the most | ||
| 198 | precise type possible, because instead this function tries to preserve | ||
| 199 | compatibility with the return value of previous Emacs versions. */) | ||
| 200 | (Lisp_Object object) | ||
| 201 | { | ||
| 202 | return SYMBOLP (object) ? Qsymbol | ||
| 203 | : INTEGERP (object) ? Qinteger | ||
| 204 | : SUBRP (object) ? Qsubr | ||
| 205 | : Fcl_type_of (object); | ||
| 206 | } | ||
| 207 | |||
| 208 | DEFUN ("cl-type-of", Fcl_type_of, Scl_type_of, 1, 1, 0, | ||
| 209 | doc: /* Return a symbol representing the type of OBJECT. | ||
| 210 | The returned symbol names the most specific possible type of the object. | ||
| 211 | for example, (cl-type-of nil) returns `null'. | ||
| 212 | The specific type returned may change depending on Emacs versions, | ||
| 213 | so we recommend you use `cl-typep', `cl-typecase', or other predicates | ||
| 214 | rather than compare the return value of this function against | ||
| 215 | a fixed set of types. */) | ||
| 197 | (Lisp_Object object) | 216 | (Lisp_Object object) |
| 198 | { | 217 | { |
| 199 | switch (XTYPE (object)) | 218 | switch (XTYPE (object)) |
| 200 | { | 219 | { |
| 201 | case_Lisp_Int: | 220 | case_Lisp_Int: |
| 202 | return Qinteger; | 221 | return Qfixnum; |
| 203 | 222 | ||
| 204 | case Lisp_Symbol: | 223 | case Lisp_Symbol: |
| 205 | return Qsymbol; | 224 | return NILP (object) ? Qnull |
| 225 | : EQ (object, Qt) ? Qboolean | ||
| 226 | : Qsymbol; | ||
| 206 | 227 | ||
| 207 | case Lisp_String: | 228 | case Lisp_String: |
| 208 | return Qstring; | 229 | return Qstring; |
| @@ -215,7 +236,7 @@ for example, (type-of 1) returns `integer'. */) | |||
| 215 | switch (PSEUDOVECTOR_TYPE (XVECTOR (object))) | 236 | switch (PSEUDOVECTOR_TYPE (XVECTOR (object))) |
| 216 | { | 237 | { |
| 217 | case PVEC_NORMAL_VECTOR: return Qvector; | 238 | case PVEC_NORMAL_VECTOR: return Qvector; |
| 218 | case PVEC_BIGNUM: return Qinteger; | 239 | case PVEC_BIGNUM: return Qbignum; |
| 219 | case PVEC_MARKER: return Qmarker; | 240 | case PVEC_MARKER: return Qmarker; |
| 220 | case PVEC_SYMBOL_WITH_POS: return Qsymbol_with_pos; | 241 | case PVEC_SYMBOL_WITH_POS: return Qsymbol_with_pos; |
| 221 | case PVEC_OVERLAY: return Qoverlay; | 242 | case PVEC_OVERLAY: return Qoverlay; |
| @@ -224,7 +245,10 @@ for example, (type-of 1) returns `integer'. */) | |||
| 224 | case PVEC_WINDOW_CONFIGURATION: return Qwindow_configuration; | 245 | case PVEC_WINDOW_CONFIGURATION: return Qwindow_configuration; |
| 225 | case PVEC_PROCESS: return Qprocess; | 246 | case PVEC_PROCESS: return Qprocess; |
| 226 | case PVEC_WINDOW: return Qwindow; | 247 | case PVEC_WINDOW: return Qwindow; |
| 227 | case PVEC_SUBR: return Qsubr; | 248 | case PVEC_SUBR: |
| 249 | return XSUBR (object)->max_args == UNEVALLED ? Qspecial_form | ||
| 250 | : SUBR_NATIVE_COMPILEDP (object) ? Qsubr_native_elisp | ||
| 251 | : Qsubr_primitive; | ||
| 228 | case PVEC_COMPILED: return Qcompiled_function; | 252 | case PVEC_COMPILED: return Qcompiled_function; |
| 229 | case PVEC_BUFFER: return Qbuffer; | 253 | case PVEC_BUFFER: return Qbuffer; |
| 230 | case PVEC_CHAR_TABLE: return Qchar_table; | 254 | case PVEC_CHAR_TABLE: return Qchar_table; |
| @@ -4202,7 +4226,9 @@ syms_of_data (void) | |||
| 4202 | "Variable binding depth exceeds max-specpdl-size"); | 4226 | "Variable binding depth exceeds max-specpdl-size"); |
| 4203 | 4227 | ||
| 4204 | /* Types that type-of returns. */ | 4228 | /* Types that type-of returns. */ |
| 4229 | DEFSYM (Qboolean, "boolean"); | ||
| 4205 | DEFSYM (Qinteger, "integer"); | 4230 | DEFSYM (Qinteger, "integer"); |
| 4231 | DEFSYM (Qbignum, "bignum"); | ||
| 4206 | DEFSYM (Qsymbol, "symbol"); | 4232 | DEFSYM (Qsymbol, "symbol"); |
| 4207 | DEFSYM (Qstring, "string"); | 4233 | DEFSYM (Qstring, "string"); |
| 4208 | DEFSYM (Qcons, "cons"); | 4234 | DEFSYM (Qcons, "cons"); |
| @@ -4218,6 +4244,9 @@ syms_of_data (void) | |||
| 4218 | DEFSYM (Qprocess, "process"); | 4244 | DEFSYM (Qprocess, "process"); |
| 4219 | DEFSYM (Qwindow, "window"); | 4245 | DEFSYM (Qwindow, "window"); |
| 4220 | DEFSYM (Qsubr, "subr"); | 4246 | DEFSYM (Qsubr, "subr"); |
| 4247 | DEFSYM (Qspecial_form, "special-form"); | ||
| 4248 | DEFSYM (Qsubr_primitive, "subr-primitive"); | ||
| 4249 | DEFSYM (Qsubr_native_elisp, "subr-native-elisp"); | ||
| 4221 | DEFSYM (Qcompiled_function, "compiled-function"); | 4250 | DEFSYM (Qcompiled_function, "compiled-function"); |
| 4222 | DEFSYM (Qbuffer, "buffer"); | 4251 | DEFSYM (Qbuffer, "buffer"); |
| 4223 | DEFSYM (Qframe, "frame"); | 4252 | DEFSYM (Qframe, "frame"); |
| @@ -4255,6 +4284,7 @@ syms_of_data (void) | |||
| 4255 | defsubr (&Seq); | 4284 | defsubr (&Seq); |
| 4256 | defsubr (&Snull); | 4285 | defsubr (&Snull); |
| 4257 | defsubr (&Stype_of); | 4286 | defsubr (&Stype_of); |
| 4287 | defsubr (&Scl_type_of); | ||
| 4258 | defsubr (&Slistp); | 4288 | defsubr (&Slistp); |
| 4259 | defsubr (&Snlistp); | 4289 | defsubr (&Snlistp); |
| 4260 | defsubr (&Sconsp); | 4290 | defsubr (&Sconsp); |
diff --git a/test/src/data-tests.el b/test/src/data-tests.el index ad3b2071254..9d76c58224d 100644 --- a/test/src/data-tests.el +++ b/test/src/data-tests.el | |||
| @@ -838,4 +838,41 @@ comparing the subr with a much slower Lisp implementation." | |||
| 838 | (dolist (sym (list nil t 'xyzzy (make-symbol ""))) | 838 | (dolist (sym (list nil t 'xyzzy (make-symbol ""))) |
| 839 | (should (eq sym (bare-symbol (position-symbol sym 0))))))) | 839 | (should (eq sym (bare-symbol (position-symbol sym 0))))))) |
| 840 | 840 | ||
| 841 | (require 'cl-extra) ;For `cl--class-children'. | ||
| 842 | |||
| 843 | (ert-deftest data-tests--cl-type-of () | ||
| 844 | ;; Make sure that `cl-type-of' returns the most precise type. | ||
| 845 | ;; Note: This doesn't work for list/vector structs since those types | ||
| 846 | ;; are too difficult/unreliable to detect (so `cl-type-of' only says | ||
| 847 | ;; it's a `cons' or a `vector'). | ||
| 848 | (dolist (val (list -2 10 (expt 2 128) nil t 'car | ||
| 849 | (symbol-function 'car) | ||
| 850 | (symbol-function 'progn) | ||
| 851 | (position-symbol 'car 7))) | ||
| 852 | (let* ((type (cl-type-of val)) | ||
| 853 | (class (cl-find-class type)) | ||
| 854 | (alltypes (cl--class-allparents class)) | ||
| 855 | ;; FIXME: Our type DAG is affected by `symbols-with-pos-enabled'. | ||
| 856 | ;; (e.g. `symbolp' returns nil on a sympos if that var is nil). | ||
| 857 | (symbols-with-pos-enabled t)) | ||
| 858 | (dolist (parent alltypes) | ||
| 859 | (should (cl-typep val parent)) | ||
| 860 | (dolist (subtype (cl--class-children (cl-find-class parent))) | ||
| 861 | (unless (memq subtype alltypes) | ||
| 862 | (unless (memq subtype | ||
| 863 | ;; FIXME: Some types don't have any associated | ||
| 864 | ;; predicate, | ||
| 865 | '( font-spec font-entity font-object | ||
| 866 | finalizer condvar terminal | ||
| 867 | native-comp-unit interpreted-function | ||
| 868 | tree-sitter-compiled-query | ||
| 869 | tree-sitter-node tree-sitter-parser | ||
| 870 | ;; `functionp' also matches things of type | ||
| 871 | ;; `symbol' and `cons'. | ||
| 872 | ;; FIXME: `subr-primitive-p' also matches | ||
| 873 | ;; special-forms. | ||
| 874 | function subr-primitive)) | ||
| 875 | (should-not (cl-typep val subtype))))))))) | ||
| 876 | |||
| 877 | |||
| 841 | ;;; data-tests.el ends here | 878 | ;;; data-tests.el ends here |