aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2024-03-12 09:26:24 -0400
committerStefan Monnier2024-03-18 09:29:47 -0400
commit706403f2aa3a306369a0150022da0cba1802ca2b (patch)
treec2a2a7e1dc919efdead6ee36f1fb91e3f624c673
parent1a8b34a503e5af32851c1aac27a3f09e2345673b (diff)
downloademacs-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.texi21
-rw-r--r--etc/NEWS5
-rw-r--r--lisp/emacs-lisp/cl-generic.el6
-rw-r--r--lisp/emacs-lisp/cl-preloaded.el12
-rw-r--r--lisp/emacs-lisp/eieio-core.el2
-rw-r--r--src/comp.c2
-rw-r--r--src/data.c40
-rw-r--r--test/src/data-tests.el37
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
2211This function returns a symbol naming @emph{the} type of
2212@var{object}. It usually behaves like @code{type-of}, except
2213that it guarantees to return the most precise type possible, which also
2214implies that the specific type it returns may change depending on the
2215Emacs version. For this reason, as a rule you should never compare its
2216return 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
diff --git a/etc/NEWS b/etc/NEWS
index b02712dd21c..b522fbd338b 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -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'.
1651This function is like 'type-of' except that it sometimes returns
1652a more precise type. For example, for nil and t it returns 'null'
1653and 'boolean' respectively, instead of just 'symbol'.
1654
1650** Built-in types have now corresponding classes. 1655** Built-in types have now corresponding classes.
1651At the Lisp level, this means that things like (cl-find-class 'integer) 1656At the Lisp level, this means that things like (cl-find-class 'integer)
1652will now return a class object, and at the UI level it means that 1657will 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,
193DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, 193DEFUN ("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.
195The symbol returned names the object's basic type; 195The symbol returned names the object's basic type;
196for example, (type-of 1) returns `integer'. */) 196for example, (type-of 1) returns `integer'.
197Contrary to `cl-type-of', the returned type is not always the most
198precise type possible, because instead this function tries to preserve
199compatibility 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
208DEFUN ("cl-type-of", Fcl_type_of, Scl_type_of, 1, 1, 0,
209 doc: /* Return a symbol representing the type of OBJECT.
210The returned symbol names the most specific possible type of the object.
211for example, (cl-type-of nil) returns `null'.
212The specific type returned may change depending on Emacs versions,
213so we recommend you use `cl-typep', `cl-typecase', or other predicates
214rather than compare the return value of this function against
215a 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